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