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