5db1928da8fb3684816de6e25cc6b55e190c81aa
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / corext / template / php / JavaContext.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2004 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.internal.corext.template.php;
12
13 import java.lang.reflect.InvocationTargetException;
14
15 import net.sourceforge.phpdt.core.ICompilationUnit;
16 import net.sourceforge.phpdt.internal.corext.Assert;
17 import net.sourceforge.phpdt.internal.corext.template.php.CompilationUnitCompletion.LocalVariable;
18 import net.sourceforge.phpdt.internal.corext.util.Strings;
19 import net.sourceforge.phpdt.internal.ui.preferences.CodeFormatterPreferencePage;
20 import net.sourceforge.phpdt.internal.ui.text.template.contentassist.MultiVariable;
21 import net.sourceforge.phpdt.internal.ui.util.ExceptionHandler;
22 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
23
24 import org.eclipse.core.runtime.CoreException;
25 import org.eclipse.core.runtime.IStatus;
26 import org.eclipse.core.runtime.Status;
27 import org.eclipse.jface.dialogs.MessageDialog;
28 import org.eclipse.jface.preference.IPreferenceStore;
29 import org.eclipse.jface.text.BadLocationException;
30 import org.eclipse.jface.text.Document;
31 import org.eclipse.jface.text.IDocument;
32 import org.eclipse.jface.text.IRegion;
33 import org.eclipse.jface.text.templates.Template;
34 import org.eclipse.jface.text.templates.TemplateBuffer;
35 import org.eclipse.jface.text.templates.TemplateContextType;
36 import org.eclipse.jface.text.templates.TemplateException;
37 import org.eclipse.jface.text.templates.TemplateTranslator;
38 import org.eclipse.jface.text.templates.TemplateVariable;
39 import org.eclipse.swt.widgets.Shell;
40
41 /**
42  * A context for java source.
43  */
44 public class JavaContext extends CompilationUnitContext {
45
46         /** The platform default line delimiter. */
47         private static final String PLATFORM_LINE_DELIMITER = System
48                         .getProperty("line.separator"); //$NON-NLS-1$
49
50         /** A code completion requestor for guessing local variable names. */
51         private CompilationUnitCompletion fCompletion;
52
53         /**
54          * Creates a java template context.
55          * 
56          * @param type
57          *            the context type.
58          * @param document
59          *            the document.
60          * @param completionOffset
61          *            the completion offset within the document.
62          * @param completionLength
63          *            the completion length.
64          * @param compilationUnit
65          *            the compilation unit (may be <code>null</code>).
66          */
67         public JavaContext(TemplateContextType type, IDocument document,
68                         int completionOffset, int completionLength,
69                         ICompilationUnit compilationUnit) {
70                 super(type, document, completionOffset, completionLength,
71                                 compilationUnit);
72         }
73
74         /**
75          * Returns the indentation level at the position of code completion.
76          */
77         private int getIndentation() {
78                 int start = getStart();
79                 IDocument document = getDocument();
80                 try {
81                         IRegion region = document.getLineInformationOfOffset(start);
82                         String lineContent = document.get(region.getOffset(), region
83                                         .getLength());
84                         return Strings.computeIndent(lineContent,
85                                         CodeFormatterPreferencePage.getTabSize());
86                         // return Strings.computeIndent(lineContent,
87                         // CodeFormatterUtil.getTabWidth());
88                 } catch (BadLocationException e) {
89                         return 0;
90                 }
91         }
92
93         /*
94          * @see TemplateContext#evaluate(Template template)
95          */
96         public TemplateBuffer evaluate(Template template)
97                         throws BadLocationException, TemplateException {
98
99                 if (!canEvaluate(template))
100                         throw new TemplateException(JavaTemplateMessages
101                                         .getString("Context.error.cannot.evaluate")); //$NON-NLS-1$
102
103                 TemplateTranslator translator = new TemplateTranslator() {
104                         /*
105                          * @see org.eclipse.jface.text.templates.TemplateTranslator#createVariable(java.lang.String,
106                          *      java.lang.String, int[])
107                          */
108                         protected TemplateVariable createVariable(String type, String name,
109                                         int[] offsets) {
110                                 return new MultiVariable(type, name, offsets);
111                         }
112                 };
113                 TemplateBuffer buffer = translator.translate(template);
114
115                 getContextType().resolve(buffer, this);
116                 String lineDelimiter = null;
117                 try {
118                         lineDelimiter = getDocument().getLineDelimiter(0);
119                 } catch (BadLocationException e) {
120                 }
121
122                 if (lineDelimiter == null)
123                         lineDelimiter = PLATFORM_LINE_DELIMITER;
124                 IPreferenceStore prefs = PHPeclipsePlugin.getDefault()
125                                 .getPreferenceStore();
126                 // axelcl start
127                 // boolean useCodeFormatter =
128                 // prefs.getBoolean(PreferenceConstants.TEMPLATES_USE_CODEFORMATTER);
129                 boolean useCodeFormatter = false;
130                 // axelcl end
131
132                 JavaFormatter formatter = new JavaFormatter(lineDelimiter,
133                                 getIndentation(), useCodeFormatter);
134                 formatter.format(buffer, this);
135                 // debug start
136                 // String res = buffer.getString();
137                 // res = res.replaceAll("\n","/n");
138                 // res = res.replaceAll("\t","/t");
139                 // System.out.println(res);
140                 // debug end
141                 return buffer;
142         }
143
144         /*
145          * @see TemplateContext#canEvaluate(Template templates)
146          */
147         public boolean canEvaluate(Template template) {
148                 String key = getKey();
149
150                 if (fForceEvaluation)
151                         return true;
152
153                 return template.matches(key, getContextType().getId())
154                                 && key.length() != 0
155                                 && template.getName().toLowerCase().startsWith(
156                                                 key.toLowerCase());
157         }
158
159         public boolean canEvaluate(String identifier) {
160                 String prefix = getKey();
161                 return identifier.toLowerCase().startsWith(prefix.toLowerCase());
162         }
163
164         /*
165          * @see DocumentTemplateContext#getCompletionPosition();
166          */
167         public int getStart() {
168
169                 try {
170                         IDocument document = getDocument();
171
172                         if (getCompletionLength() == 0) {
173
174                                 int start = getCompletionOffset();
175                                 while ((start != 0)
176                                                 && Character.isUnicodeIdentifierPart(document
177                                                                 .getChar(start - 1)))
178                                         start--;
179
180                                 if ((start != 0)
181                                                 && (Character.isUnicodeIdentifierStart(document
182                                                                 .getChar(start - 1)) || (document
183                                                                 .getChar(start - 1) == '$')))
184                                         start--;
185
186                                 return start;
187
188                         } else {
189
190                                 int start = getCompletionOffset();
191                                 int end = getCompletionOffset() + getCompletionLength();
192
193                                 while (start != 0
194                                                 && Character.isUnicodeIdentifierPart(document
195                                                                 .getChar(start - 1)))
196                                         start--;
197                                 if ((start != 0)
198                                                 && (Character.isUnicodeIdentifierStart(document
199                                                                 .getChar(start - 1)) || (document
200                                                                 .getChar(start - 1) == '$')))
201                                         start--;
202                                 while (start != end
203                                                 && Character.isWhitespace(document.getChar(start)))
204                                         start++;
205
206                                 if (start == end)
207                                         start = getCompletionOffset();
208
209                                 return start;
210                         }
211
212                 } catch (BadLocationException e) {
213                         return super.getStart();
214                 }
215         }
216
217         /*
218          * @see net.sourceforge.phpdt.internal.corext.template.DocumentTemplateContext#getEnd()
219          */
220         public int getEnd() {
221
222                 if (getCompletionLength() == 0)
223                         return super.getEnd();
224
225                 try {
226                         IDocument document = getDocument();
227
228                         int start = getCompletionOffset();
229                         int end = getCompletionOffset() + getCompletionLength();
230
231                         while (start != end
232                                         && Character.isWhitespace(document.getChar(end - 1)))
233                                 end--;
234
235                         return end;
236
237                 } catch (BadLocationException e) {
238                         return super.getEnd();
239                 }
240         }
241
242         /*
243          * @see net.sourceforge.phpdt.internal.corext.template.DocumentTemplateContext#getKey()
244          */
245         public String getKey() {
246
247                 // if (getCompletionLength() == 0) {
248                 // return super.getKey();
249                 // }
250
251                 try {
252                         IDocument document = getDocument();
253
254                         int start = getStart();
255                         int end = getCompletionOffset();
256                         return start <= end ? document.get(start, end - start) : ""; //$NON-NLS-1$
257
258                 } catch (BadLocationException e) {
259                         return super.getKey();
260                 }
261         }
262
263         /**
264          * Returns the character before start position of completion.
265          */
266         public char getCharacterBeforeStart() {
267                 int start = getStart();
268
269                 try {
270                         return start == 0 ? ' ' : getDocument().getChar(start - 1);
271
272                 } catch (BadLocationException e) {
273                         return ' ';
274                 }
275         }
276
277         private static void handleException(Shell shell, Exception e) {
278                 String title = JavaTemplateMessages
279                                 .getString("JavaContext.error.title"); //$NON-NLS-1$
280                 if (e instanceof CoreException)
281                         ExceptionHandler.handle((CoreException) e, shell, title, null);
282                 else if (e instanceof InvocationTargetException)
283                         ExceptionHandler.handle((InvocationTargetException) e, shell,
284                                         title, null);
285                 else {
286                         PHPeclipsePlugin.log(e);
287                         MessageDialog.openError(shell, title, e.getMessage());
288                 }
289         }
290
291         // private CompilationUnitCompletion getCompletion() {
292         // ICompilationUnit compilationUnit= getCompilationUnit();
293         // if (fCompletion == null) {
294         // fCompletion= new CompilationUnitCompletion(compilationUnit);
295         //                      
296         // if (compilationUnit != null) {
297         // try {
298         // compilationUnit.codeComplete(getStart(), fCompletion);
299         // } catch (JavaModelException e) {
300         // // ignore
301         // }
302         // }
303         // }
304         //              
305         // return fCompletion;
306         // }
307
308         /**
309          * Returns the name of a guessed local array, <code>null</code> if no
310          * local array exists.
311          */
312         // public String guessArray() {
313         // return firstOrNull(guessArrays());
314         // }
315         /**
316          * Returns the name of a guessed local array, <code>null</code> if no
317          * local array exists.
318          */
319         // public String[] guessArrays() {
320         // CompilationUnitCompletion completion= getCompletion();
321         // LocalVariable[] localArrays= completion.findLocalArrays();
322         //                              
323         // String[] ret= new String[localArrays.length];
324         // for (int i= 0; i < ret.length; i++) {
325         // ret[ret.length - i - 1]= localArrays[i].name;
326         // }
327         // return ret;
328         // }
329         /**
330          * Returns the name of the type of a local array, <code>null</code> if no
331          * local array exists.
332          */
333         // public String guessArrayType() {
334         // return firstOrNull(guessArrayTypes());
335         // }
336         private String firstOrNull(String[] strings) {
337                 if (strings.length > 0)
338                         return strings[0];
339                 else
340                         return null;
341         }
342
343         /**
344          * Returns the name of the type of a local array, <code>null</code> if no
345          * local array exists.
346          */
347         // public String[][] guessGroupedArrayTypes() {
348         // CompilationUnitCompletion completion= getCompletion();
349         // LocalVariable[] localArrays= completion.findLocalArrays();
350         //              
351         // String[][] ret= new String[localArrays.length][];
352         //              
353         // for (int i= 0; i < localArrays.length; i++) {
354         // String type= getArrayTypeFromLocalArray(completion,
355         // localArrays[localArrays.length - i - 1]);
356         // ret[i]= new String[] {type};
357         // }
358         //              
359         // return ret;
360         // }
361         /**
362          * Returns the name of the type of a local array, <code>null</code> if no
363          * local array exists.
364          */
365         // public String[] guessArrayTypes() {
366         // CompilationUnitCompletion completion= getCompletion();
367         // LocalVariable[] localArrays= completion.findLocalArrays();
368         //              
369         // List ret= new ArrayList();
370         //              
371         // for (int i= 0; i < localArrays.length; i++) {
372         // String type= getArrayTypeFromLocalArray(completion,
373         // localArrays[localArrays.length - i - 1]);
374         // if (!ret.contains(type))
375         // ret.add(type);
376         // }
377         //              
378         // return (String[]) ret.toArray(new String[ret.size()]);
379         // }
380         private String getArrayTypeFromLocalArray(
381                         CompilationUnitCompletion completion, LocalVariable array) {
382                 String arrayTypeName = array.typeName;
383                 String typeName = getScalarType(arrayTypeName);
384                 int dimension = getArrayDimension(arrayTypeName) - 1;
385                 Assert.isTrue(dimension >= 0);
386
387                 String qualifiedName = createQualifiedTypeName(array.typePackageName,
388                                 typeName);
389                 String innerTypeName = completion.simplifyTypeName(qualifiedName);
390
391                 return innerTypeName == null ? createArray(typeName, dimension)
392                                 : createArray(innerTypeName, dimension);
393         }
394
395         private static String createArray(String type, int dimension) {
396                 StringBuffer buffer = new StringBuffer(type);
397                 for (int i = 0; i < dimension; i++)
398                         buffer.append("[]"); //$NON-NLS-1$
399                 return buffer.toString();
400         }
401
402         private static String getScalarType(String type) {
403                 return type.substring(0, type.indexOf('['));
404         }
405
406         private static int getArrayDimension(String type) {
407
408                 int dimension = 0;
409                 int index = type.indexOf('[');
410
411                 while (index != -1) {
412                         dimension++;
413                         index = type.indexOf('[', index + 1);
414                 }
415
416                 return dimension;
417         }
418
419         private static String createQualifiedTypeName(String packageName,
420                         String className) {
421                 StringBuffer buffer = new StringBuffer();
422
423                 if (packageName.length() != 0) {
424                         buffer.append(packageName);
425                         buffer.append('.');
426                 }
427                 buffer.append(className);
428
429                 return buffer.toString();
430         }
431
432         /**
433          * Returns a proposal for a variable name of a local array element,
434          * <code>null</code> if no local array exists.
435          */
436         // public String guessArrayElement() {
437         // return firstOrNull(guessArrayElements());
438         // }
439         /**
440          * Returns a proposal for a variable name of a local array element,
441          * <code>null</code> if no local array exists.
442          */
443         // public String[] guessArrayElements() {
444         // ICompilationUnit cu= getCompilationUnit();
445         // if (cu == null) {
446         // return new String[0];
447         // }
448         //              
449         // CompilationUnitCompletion completion= getCompletion();
450         // LocalVariable[] localArrays= completion.findLocalArrays();
451         //              
452         // List ret= new ArrayList();
453         //              
454         // for (int i= 0; i < localArrays.length; i++) {
455         // int idx= localArrays.length - i - 1;
456         //                      
457         // LocalVariable var= localArrays[idx];
458         //                      
459         // IJavaProject project= cu.getJavaProject();
460         // String typeName= var.typeName;
461         // String baseTypeName= typeName.substring(0, typeName.lastIndexOf('['));
462         //
463         // String indexName= getIndex();
464         // String[] excludedNames= completion.getLocalVariableNames();
465         // if (indexName != null) {
466         // ArrayList excludedNamesList= new ArrayList(Arrays.asList(excludedNames));
467         // excludedNamesList.add(indexName);
468         // excludedNames= (String[])excludedNamesList.toArray(new
469         // String[excludedNamesList.size()]);
470         // }
471         // String[] proposals= NamingConventions.suggestLocalVariableNames(project,
472         // var.typePackageName, baseTypeName, 0, excludedNames);
473         // for (int j= 0; j < proposals.length; j++) {
474         // if (!ret.contains(proposals[j]))
475         // ret.add(proposals[j]);
476         // }
477         // }
478         //              
479         // return (String[]) ret.toArray(new String[ret.size()]);
480         // }
481         /**
482          * Returns a proposal for a variable name of a local array element,
483          * <code>null</code> if no local array exists.
484          */
485         // public String[][] guessGroupedArrayElements() {
486         // ICompilationUnit cu= getCompilationUnit();
487         // if (cu == null) {
488         // return new String[0][];
489         // }
490         //              
491         // CompilationUnitCompletion completion= getCompletion();
492         // LocalVariable[] localArrays= completion.findLocalArrays();
493         //              
494         // String[][] ret= new String[localArrays.length][];
495         //              
496         // for (int i= 0; i < localArrays.length; i++) {
497         // int idx= localArrays.length - i - 1;
498         //                      
499         // LocalVariable var= localArrays[idx];
500         //                      
501         // IJavaProject project= cu.getJavaProject();
502         // String typeName= var.typeName;
503         // int dim= -1; // we expect at least one array
504         // int lastIndex= typeName.length();
505         // int bracket= typeName.lastIndexOf('[');
506         // while (bracket != -1) {
507         // lastIndex= bracket;
508         // dim++;
509         // bracket= typeName.lastIndexOf('[', bracket - 1);
510         // }
511         // typeName= typeName.substring(0, lastIndex);
512         //                      
513         // String indexName= getIndex();
514         // String[] excludedNames= completion.getLocalVariableNames();
515         // if (indexName != null) {
516         // ArrayList excludedNamesList= new ArrayList(Arrays.asList(excludedNames));
517         // excludedNamesList.add(indexName);
518         // excludedNames= (String[])excludedNamesList.toArray(new
519         // String[excludedNamesList.size()]);
520         // }
521         // String[] proposals= NamingConventions.suggestLocalVariableNames(project,
522         // var.typePackageName, typeName, dim, excludedNames);
523         //                      
524         // ret[i]= proposals;
525         // }
526         //              
527         // return ret;
528         // }
529         /**
530          * Returns an array index name. 'i', 'j', 'k' are tried until no name
531          * collision with an existing local variable occurs. If all names collide,
532          * <code>null</code> is returned.
533          */
534         // public String getIndex() {
535         // CompilationUnitCompletion completion= getCompletion();
536         // String[] proposals= {"i", "j", "k"}; //$NON-NLS-1$ //$NON-NLS-2$
537         // //$NON-NLS-3$
538         //              
539         // for (int i= 0; i != proposals.length; i++) {
540         // String proposal = proposals[i];
541         //
542         // if (!completion.existsLocalName(proposal))
543         // return proposal;
544         // }
545         //
546         // return null;
547         // }
548         /**
549          * Returns the name of a local collection, <code>null</code> if no local
550          * collection exists.
551          */
552         // public String guessCollection() {
553         // return firstOrNull(guessCollections());
554         // }
555         /**
556          * Returns the names of local collections.
557          */
558         // public String[] guessCollections() {
559         // CompilationUnitCompletion completion= getCompletion();
560         // try {
561         // LocalVariable[] localCollections= completion.findLocalCollections();
562         // String[] ret= new String[localCollections.length];
563         // for (int i= 0; i < ret.length; i++) {
564         // ret[ret.length - i - 1]= localCollections[i].name;
565         // }
566         //                      
567         // return ret;
568         //
569         // } catch (JavaModelException e) {
570         // JavaPlugin.log(e);
571         // }
572         //
573         // return new String[0];
574         // }
575         /**
576          * Returns an iterator name ('iter'). If 'iter' already exists as local
577          * variable, <code>null</code> is returned.
578          */
579         // public String getIterator() {
580         // CompilationUnitCompletion completion= getCompletion();
581         // String[] proposals= {"iter"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
582         //              
583         // for (int i= 0; i != proposals.length; i++) {
584         // String proposal = proposals[i];
585         //
586         // if (!completion.existsLocalName(proposal))
587         // return proposal;
588         // }
589         //
590         // return null;
591         // }
592         // public void addIteratorImport() {
593         // ICompilationUnit cu= getCompilationUnit();
594         // if (cu == null) {
595         // return;
596         // }
597         //      
598         // try {
599         // Position position= new Position(getCompletionOffset(),
600         // getCompletionLength());
601         // IDocument document= getDocument();
602         // final String category= "__template_position_importer" +
603         // System.currentTimeMillis(); //$NON-NLS-1$
604         // IPositionUpdater updater= new DefaultPositionUpdater(category);
605         // document.addPositionCategory(category);
606         // document.addPositionUpdater(updater);
607         // document.addPosition(position);
608         //
609         // CodeGenerationSettings settings=
610         // JavaPreferencesSettings.getCodeGenerationSettings();
611         // ImportsStructure structure= new ImportsStructure(cu,
612         // settings.importOrder, settings.importThreshold, true);
613         // structure.addImport("java.util.Iterator"); //$NON-NLS-1$
614         // structure.create(false, null);
615         //
616         // document.removePosition(position);
617         // document.removePositionUpdater(updater);
618         // document.removePositionCategory(category);
619         //                      
620         // setCompletionOffset(position.getOffset());
621         // setCompletionLength(position.getLength());
622         //                      
623         // } catch (CoreException e) {
624         // handleException(null, e);
625         // } catch (BadLocationException e) {
626         // handleException(null, e);
627         // } catch (BadPositionCategoryException e) {
628         // handleException(null, e);
629         // }
630         // }
631         /**
632          * Evaluates a 'java' template in thecontext of a compilation unit
633          */
634         public static String evaluateTemplate(Template template,
635                         ICompilationUnit compilationUnit, int position)
636                         throws CoreException, BadLocationException, TemplateException {
637
638                 TemplateContextType contextType = PHPeclipsePlugin.getDefault()
639                                 .getTemplateContextRegistry().getContextType("java"); //$NON-NLS-1$
640                 if (contextType == null)
641                         throw new CoreException(
642                                         new Status(
643                                                         IStatus.ERROR,
644                                                         PHPeclipsePlugin.PLUGIN_ID,
645                                                         IStatus.ERROR,
646                                                         JavaTemplateMessages
647                                                                         .getString("JavaContext.error.message"), null)); //$NON-NLS-1$
648
649                 IDocument document = new Document();
650                 if (compilationUnit != null && compilationUnit.exists())
651                         document.set(compilationUnit.getSource());
652
653                 JavaContext context = new JavaContext(contextType, document, position,
654                                 0, compilationUnit);
655                 context.setForceEvaluation(true);
656
657                 TemplateBuffer buffer = context.evaluate(template);
658                 if (buffer == null)
659                         return null;
660                 return buffer.getString();
661         }
662
663 }