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