Changes:
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / core / JavaElement.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2003 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.core;
12
13 import java.util.ArrayList;
14
15 import net.sourceforge.phpdt.core.ICompilationUnit;
16 import net.sourceforge.phpdt.core.IJavaElement;
17 import net.sourceforge.phpdt.core.IJavaModel;
18 import net.sourceforge.phpdt.core.IJavaModelStatusConstants;
19 import net.sourceforge.phpdt.core.IJavaProject;
20 import net.sourceforge.phpdt.core.IOpenable;
21 import net.sourceforge.phpdt.core.JavaModelException;
22
23 import org.eclipse.core.runtime.Path;
24 import org.eclipse.core.runtime.PlatformObject;
25
26 /**
27  * Root of Java element handle hierarchy.
28  *
29  * @see IJavaElement
30  */
31 public abstract class JavaElement extends PlatformObject implements IJavaElement {
32
33         public static final char JEM_JAVAPROJECT= '=';
34         public static final char JEM_PACKAGEFRAGMENTROOT= Path.SEPARATOR;
35         public static final char JEM_PACKAGEFRAGMENT= '<';
36         public static final char JEM_FIELD= '^';
37         public static final char JEM_METHOD= '~';
38         public static final char JEM_INITIALIZER= '|';
39         public static final char JEM_COMPILATIONUNIT= '{';
40         public static final char JEM_CLASSFILE= '(';
41         public static final char JEM_TYPE= '[';
42         public static final char JEM_PACKAGEDECLARATION= '%';
43         public static final char JEM_IMPORTDECLARATION= '#';
44
45         /**
46          * A count to uniquely identify this element in the case
47          * that a duplicate named element exists. For example, if
48          * there are two fields in a compilation unit with the
49          * same name, the occurrence count is used to distinguish
50          * them.  The occurrence count starts at 1 (thus the first 
51          * occurrence is occurrence 1, not occurrence 0).
52          */
53         protected int fOccurrenceCount = 1;
54
55
56         /**
57          * This element's type - one of the constants defined
58          * in IJavaLanguageElementTypes.
59          */
60         protected int fLEType = 0;
61
62         /**
63          * This element's parent, or <code>null</code> if this
64          * element does not have a parent.
65          */
66         protected IJavaElement fParent;
67
68         /**
69          * This element's name, or an empty <code>String</code> if this
70          * element does not have a name.
71          */
72         protected String fName;
73
74         protected static final Object NO_INFO = new Object();
75         
76         /**
77          * Constructs a handle for a java element of the specified type, with
78          * the given parent element and name.
79          *
80          * @param type - one of the constants defined in IJavaLanguageElement
81          *
82          * @exception IllegalArgumentException if the type is not one of the valid
83          *              Java element type constants
84          *
85          */
86         protected JavaElement(int type, IJavaElement parent, String name) throws IllegalArgumentException {
87                 if (type < JAVA_MODEL || type > IMPORT_DECLARATION) {
88                         throw new IllegalArgumentException(Util.bind("element.invalidType")); //$NON-NLS-1$
89                 }
90                 fLEType= type;
91                 fParent= parent;
92                 fName= name;
93         }
94         /**
95          * @see IOpenable
96          */
97 //      public void close() throws JavaModelException {
98 //              Object info = JavaModelManager.getJavaModelManager().peekAtInfo(this);
99 //              if (info != null) {
100 //                      boolean wasVerbose = false;
101 //                      try {
102 //                              if (JavaModelManager.VERBOSE) {
103 //                                      System.out.println("CLOSING Element ("+ Thread.currentThread()+"): " + this.toStringWithAncestors());  //$NON-NLS-1$//$NON-NLS-2$
104 //                                      wasVerbose = true;
105 //                                      JavaModelManager.VERBOSE = false;
106 //                              }
107 //                              if (this instanceof IParent) {
108 //                                      IJavaElement[] children = ((JavaElementInfo) info).getChildren();
109 //                                      for (int i = 0, size = children.length; i < size; ++i) {
110 //                                              JavaElement child = (JavaElement) children[i];
111 //                                              child.close();
112 //                                      }
113 //                              }
114 //                              closing(info);
115 //                              JavaModelManager.getJavaModelManager().removeInfo(this);
116 //                              if (wasVerbose) {
117 //                                      System.out.println("-> Package cache size = " + JavaModelManager.getJavaModelManager().cache.pkgSize()); //$NON-NLS-1$
118 //                                      System.out.println("-> Openable cache filling ratio = " + JavaModelManager.getJavaModelManager().cache.openableFillingRatio() + "%"); //$NON-NLS-1$//$NON-NLS-2$
119 //                              }
120 //                      } finally {
121 //                              JavaModelManager.VERBOSE = wasVerbose;
122 //                      }
123 //              }
124 //      }
125         /**
126          * This element is being closed.  Do any necessary cleanup.
127          */
128         protected void closing(Object info) throws JavaModelException {
129         }
130         /**
131          * Returns true if this handle represents the same Java element
132          * as the given handle. By default, two handles represent the same
133          * element if they are identical or if they represent the same type
134          * of element, have equal names, parents, and occurrence counts.
135          *
136          * <p>If a subclass has other requirements for equality, this method
137          * must be overridden.
138          *
139          * @see Object#equals
140          */
141         public boolean equals(Object o) {
142                 
143                 if (this == o) return true;
144         
145                 // Java model parent is null
146                 if (fParent == null) return super.equals(o);
147         
148                 if (o instanceof JavaElement) {
149                         JavaElement other = (JavaElement) o;
150                         if (fLEType != other.fLEType) return false;
151                         
152                         return fName.equals(other.fName) &&
153                                         fParent.equals(other.fParent) &&
154                                         fOccurrenceCount == other.fOccurrenceCount;
155                 }
156                 return false;
157         }
158         /**
159          * Returns true if this <code>JavaElement</code> is equivalent to the given
160          * <code>IDOMNode</code>.
161          */
162 //      protected boolean equalsDOMNode(IDOMNode node) throws JavaModelException {
163 //              return false;
164 //      }
165         /**
166          * @see IJavaElement
167          */
168         public boolean exists() {
169                 
170                 try {
171                         getElementInfo();
172                         return true;
173                 } catch (JavaModelException e) {
174                 }
175                 return false;
176         }
177         
178         /**
179          * Returns the <code>IDOMNode</code> that corresponds to this <code>JavaElement</code>
180          * or <code>null</code> if there is no corresponding node.
181          */
182 //      public IDOMNode findNode(IDOMCompilationUnit dom) {
183 //              int type = getElementType();
184 //              if (type == IJavaElement.COMPILATION_UNIT || 
185 //                      type == IJavaElement.FIELD || 
186 //                      type == IJavaElement.IMPORT_DECLARATION || 
187 //                      type == IJavaElement.INITIALIZER || 
188 //                      type == IJavaElement.METHOD || 
189 //                      type == IJavaElement.PACKAGE_DECLARATION || 
190 //                      type == IJavaElement.TYPE) {
191 //                      ArrayList path = new ArrayList();
192 //                      IJavaElement element = this;
193 //                      while (element != null && element.getElementType() != IJavaElement.COMPILATION_UNIT) {
194 //                              if (element.getElementType() != IJavaElement.IMPORT_CONTAINER) {
195 //                                      // the DOM does not have import containers, so skip them
196 //                                      path.add(0, element);
197 //                              }
198 //                              element = element.getParent();
199 //                      }
200 //                      if (path.size() == 0) {
201 //                              try {
202 //                                      if (equalsDOMNode(dom)) {
203 //                                              return dom;
204 //                                      } else {
205 //                                              return null;
206 //                                      }
207 //                              } catch(JavaModelException e) {
208 //                                      return null;
209 //                              }
210 //                      }
211 //                      return ((JavaElement) path.get(0)).followPath(path, 0, dom.getFirstChild());
212 //              } else {
213 //                      return null;
214 //              }
215 //      }
216 //      /**
217 //       */
218 //      protected IDOMNode followPath(ArrayList path, int position, IDOMNode node) {
219 //      
220 //              try {
221 //                      if (equalsDOMNode(node)) {
222 //                              if (position == (path.size() - 1)) {
223 //                                      return node;
224 //                              } else {
225 //                                      if (node.getFirstChild() != null) {
226 //                                              position++;
227 //                                              return ((JavaElement)path.get(position)).followPath(path, position, node.getFirstChild());
228 //                                      } else {
229 //                                              return null;
230 //                                      }
231 //                              }
232 //                      } else if (node.getNextNode() != null) {
233 //                              return followPath(path, position, node.getNextNode());
234 //                      } else {
235 //                              return null;
236 //                      }
237 //              } catch (JavaModelException e) {
238 //                      return null;
239 //              }
240 //      
241 //      }
242         /**
243          * @see IJavaElement
244          */
245         public IJavaElement getAncestor(int ancestorType) {
246                 
247                 IJavaElement element = this;
248                 while (element != null) {
249                         if (element.getElementType() == ancestorType)  return element;
250                         element= element.getParent();
251                 }
252                 return null;                            
253         }
254         /**
255          * @see IParent 
256          */
257         public IJavaElement[] getChildren() throws JavaModelException {
258                 return ((JavaElementInfo)getElementInfo()).getChildren();
259         }
260         /**
261          * Returns a collection of (immediate) children of this node of the
262          * specified type.
263          *
264          * @param type - one of constants defined by IJavaLanguageElementTypes
265          */
266         public ArrayList getChildrenOfType(int type) throws JavaModelException {
267                 IJavaElement[] children = getChildren();
268                 int size = children.length;
269                 ArrayList list = new ArrayList(size);
270                 for (int i = 0; i < size; ++i) {
271                         JavaElement elt = (JavaElement)children[i];
272                         if (elt.getElementType() == type) {
273                                 list.add(elt);
274                         }
275                 }
276                 return list;
277         }
278         /**
279          * @see IMember
280          */
281 //      public IClassFile getClassFile() {
282 //              return null;
283 //      }
284         /**
285          * @see IMember
286          */
287         public ICompilationUnit getCompilationUnit() {
288                 return null;
289         }
290         /**
291          * Returns the info for this handle.  
292          * If this element is not already open, it and all of its parents are opened.
293          * Does not return null.
294          * NOTE: BinaryType infos are NJOT rooted under JavaElementInfo.
295          * @exception JavaModelException if the element is not present or not accessible
296          */
297         public Object getElementInfo() throws JavaModelException {
298 return null;
299                 // workaround to ensure parent project resolved classpath is available to avoid triggering initializers
300                 // while the JavaModelManager lock is acquired (can cause deadlocks in clients)
301 //              IJavaProject project = getJavaProject();
302 //              if (project != null && !project.isOpen()) {
303 //                      // TODO: need to revisit, since deadlock could still occur if perProjectInfo is removed concurrent before entering the lock
304 //                      try {
305 //                              project.getResolvedClasspath(true); // trigger all possible container/variable initialization outside the model lock
306 //                      } catch (JavaModelException e) {
307 //                              // project is not accessible or is not a java project
308 //                      }
309 //              }
310 //
311 //              // element info creation is done inside a lock on the JavaModelManager
312 //              JavaModelManager manager;
313 //              synchronized(manager = JavaModelManager.getJavaModelManager()){
314 //                      Object info = manager.getInfo(this);
315 //                      if (info == null) {
316 //                              openHierarchy();
317 //                              info= manager.getInfo(this);
318 //                              if (info == null) {
319 //                                      throw newNotPresentException();
320 //                              }
321 //                      }
322 //                      return info;
323 //              }
324         }
325         /**
326          * @see IAdaptable
327          */
328         public String getElementName() {
329                 return fName;
330         }
331         /**
332          * @see IJavaElement
333          */
334         public int getElementType() {
335                 return fLEType;
336         }
337         /**
338          * @see IJavaElement
339          */
340         public String getHandleIdentifier() {
341                 return getHandleMemento();
342         }
343         /**
344          * @see JavaElement#getHandleMemento()
345          */
346         public String getHandleMemento(){
347                 StringBuffer buff= new StringBuffer(((JavaElement)getParent()).getHandleMemento());
348                 buff.append(getHandleMementoDelimiter());
349                 buff.append(getElementName());
350                 return buff.toString();
351         }
352         /**
353          * Returns the <code>char</code> that marks the start of this handles
354          * contribution to a memento.
355          */
356         protected abstract char getHandleMementoDelimiter();
357         /**
358          * @see IJavaElement
359          */
360         public IJavaModel getJavaModel() {
361                 IJavaElement current = this;
362                 do {
363                         if (current instanceof IJavaModel) return (IJavaModel) current;
364                 } while ((current = current.getParent()) != null);
365                 return null;
366         }
367
368         /**
369          * @see IJavaElement
370          */
371         public IJavaProject getJavaProject() {
372                 IJavaElement current = this;
373                 do {
374                         if (current instanceof IJavaProject) return (IJavaProject) current;
375                 } while ((current = current.getParent()) != null);
376                 return null;
377         }
378         /**
379          * Returns the occurrence count of the handle.
380          */
381         protected int getOccurrenceCount() {
382                 return fOccurrenceCount;
383         }
384         /*
385          * @see IJavaElement
386          */
387         public IOpenable getOpenable() {
388                 return this.getOpenableParent();        
389         }
390         /**
391          * Return the first instance of IOpenable in the parent
392          * hierarchy of this element.
393          *
394          * <p>Subclasses that are not IOpenable's must override this method.
395          */
396         public IOpenable getOpenableParent() {
397                 
398                 return (IOpenable)fParent;
399         }
400         /**
401          * @see IJavaElement
402          */
403         public IJavaElement getParent() {
404                 return fParent;
405         }
406         
407         /**
408          * Returns the element that is located at the given source position
409          * in this element.  This is a helper method for <code>ICompilationUnit#getElementAt</code>,
410          * and only works on compilation units and types. The position given is
411          * known to be within this element's source range already, and if no finer
412          * grained element is found at the position, this element is returned.
413          */
414 //      protected IJavaElement getSourceElementAt(int position) throws JavaModelException {
415 //              if (this instanceof ISourceReference) {
416 //                      IJavaElement[] children = getChildren();
417 //                      int i;
418 //                      for (i = 0; i < children.length; i++) {
419 //                              IJavaElement aChild = children[i];
420 //                              if (aChild instanceof SourceRefElement) {
421 //                                      SourceRefElement child = (SourceRefElement) children[i];
422 //                                      ISourceRange range = child.getSourceRange();
423 //                                      if (position < range.getOffset() + range.getLength() && position >= range.getOffset()) {
424 //                                              if (child instanceof IParent) {
425 //                                                      return child.getSourceElementAt(position);
426 //                                              } else {
427 //                                                      return child;
428 //                                              }
429 //                                      }
430 //                              }
431 //                      }
432 //              } else {
433 //                      // should not happen
434 //                      Assert.isTrue(false);
435 //              }
436 //              return this;
437 //      }
438         /**
439          * Returns the SourceMapper facility for this element, or
440          * <code>null</code> if this element does not have a
441          * SourceMapper.
442          */
443 //      public SourceMapper getSourceMapper() {
444 //              return ((JavaElement)getParent()).getSourceMapper();
445 //      }
446
447         /**
448          * Returns the hash code for this Java element. By default,
449          * the hash code for an element is a combination of its name
450          * and parent's hash code. Elements with other requirements must
451          * override this method.
452          */
453         public int hashCode() {
454                 if (fParent == null) return super.hashCode();
455                 return Util.combineHashCodes(fName.hashCode(), fParent.hashCode());
456         }
457         /**
458          * Returns true if this element is an ancestor of the given element,
459          * otherwise false.
460          */
461         protected boolean isAncestorOf(IJavaElement e) {
462                 IJavaElement parent= e.getParent();
463                 while (parent != null && !parent.equals(this)) {
464                         parent= parent.getParent();
465                 }
466                 return parent != null;
467         }
468         
469         /**
470          * @see IJavaElement
471          */
472         public boolean isReadOnly() {
473                 return false;
474         }
475         /**
476          * @see IJavaElement
477          */
478 //      public boolean isStructureKnown() throws JavaModelException {
479 //              return ((JavaElementInfo)getElementInfo()).isStructureKnown();
480 //      }
481         /**
482          * Creates and returns and not present exception for this element.
483          */
484         protected JavaModelException newNotPresentException() {
485                 return new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST, this));
486         }
487 //      /**
488 //       * Opens this element and all parents that are not already open.
489 //       *
490 //       * @exception JavaModelException this element is not present or accessible
491 //       */
492 //      protected void openHierarchy() throws JavaModelException {
493 //              if (this instanceof IOpenable) {
494 //                      ((Openable) this).openWhenClosed(null);
495 //              } else {
496 //                      Openable openableParent = (Openable)getOpenableParent();
497 //                      if (openableParent != null) {
498 //                              JavaElementInfo openableParentInfo = (JavaElementInfo) JavaModelManager.getJavaModelManager().getInfo((IJavaElement) openableParent);
499 //                              if (openableParentInfo == null) {
500 //                                      openableParent.openWhenClosed(null);
501 //                              } else {
502 //                                      throw newNotPresentException();
503 //                              }
504 //                      }
505 //              }
506 //      }
507         /**
508          * This element has just been opened.  Do any necessary setup.
509          */
510         protected void opening(Object info) {
511         }
512         /**
513          */
514         public String readableName() {
515                 return this.getElementName();
516         }
517         /**
518          * Removes all cached info from the Java Model, including all children,
519          * but does not close this element.
520          */
521 //      protected void removeInfo() {
522 //              Object info = JavaModelManager.getJavaModelManager().peekAtInfo(this);
523 //              if (info != null) {
524 //                      if (this instanceof IParent) {
525 //                              IJavaElement[] children = ((JavaElementInfo)info).getChildren();
526 //                              for (int i = 0, size = children.length; i < size; ++i) {
527 //                                      JavaElement child = (JavaElement) children[i];
528 //                                      child.removeInfo();
529 //                              }
530 //                      }
531 //                      JavaModelManager.getJavaModelManager().removeInfo(this);
532 //              }
533 //      }
534 //      /**
535 //       * Returns a copy of this element rooted at the given project.
536 //       */
537         public abstract IJavaElement rootedAt(IJavaProject project);
538 //      /**
539 //       * Runs a Java Model Operation
540 //       */
541 //      public static void runOperation(JavaModelOperation operation, IProgressMonitor monitor) throws JavaModelException {
542 //              try {
543 //                      if (operation.isReadOnly() || ResourcesPlugin.getWorkspace().isTreeLocked()) {
544 //                              operation.run(monitor);
545 //                      } else {
546 //                              // use IWorkspace.run(...) to ensure that a build will be done in autobuild mode
547 //                              ResourcesPlugin.getWorkspace().run(operation, monitor);
548 //                      }
549 //              } catch (CoreException ce) {
550 //                      if (ce instanceof JavaModelException) {
551 //                              throw (JavaModelException)ce;
552 //                      } else {
553 //                              if (ce.getStatus().getCode() == IResourceStatus.OPERATION_FAILED) {
554 //                                      Throwable e= ce.getStatus().getException();
555 //                                      if (e instanceof JavaModelException) {
556 //                                              throw (JavaModelException) e;
557 //                                      }
558 //                              }
559 //                              throw new JavaModelException(ce);
560 //                      }
561 //              }
562 //      }
563         /**
564          * Sets the occurrence count of the handle.
565          */
566         protected void setOccurrenceCount(int count) {
567                 fOccurrenceCount = count;
568         }
569         protected String tabString(int tab) {
570                 StringBuffer buffer = new StringBuffer();
571                 for (int i = tab; i > 0; i--)
572                         buffer.append("  "); //$NON-NLS-1$
573                 return buffer.toString();
574         }
575         /**
576          * Debugging purposes
577          */
578         public String toDebugString() {
579                 StringBuffer buffer = new StringBuffer();
580                 this.toStringInfo(0, buffer, NO_INFO);
581                 return buffer.toString();
582         }
583         /**
584          *  Debugging purposes
585          */
586         public String toString() {
587                 StringBuffer buffer = new StringBuffer();
588                 toString(0, buffer);
589                 return buffer.toString();
590         }
591         /**
592          *  Debugging purposes
593          */
594         protected void toString(int tab, StringBuffer buffer) {
595         //      Object info = this.toStringInfo(tab, buffer);
596                 Object info = null;
597                 if (tab == 0) {
598                         this.toStringAncestors(buffer);
599                 }
600                 this.toStringChildren(tab, buffer, info);
601         }
602         /**
603          *  Debugging purposes
604          */
605         public String toStringWithAncestors() {
606                 StringBuffer buffer = new StringBuffer();
607                 this.toStringInfo(0, buffer, NO_INFO);
608                 this.toStringAncestors(buffer);
609                 return buffer.toString();
610         }
611         /**
612          *  Debugging purposes
613          */
614         protected void toStringAncestors(StringBuffer buffer) {
615                 JavaElement parent = (JavaElement)this.getParent();
616                 if (parent != null && parent.getParent() != null) {
617                         buffer.append(" [in "); //$NON-NLS-1$
618                         parent.toStringInfo(0, buffer, NO_INFO);
619                         parent.toStringAncestors(buffer);
620                         buffer.append("]"); //$NON-NLS-1$
621                 }
622         }
623         /**
624          *  Debugging purposes
625          */
626         protected void toStringChildren(int tab, StringBuffer buffer, Object info) {
627                 if (info == null || !(info instanceof JavaElementInfo)) return;
628                 IJavaElement[] children = ((JavaElementInfo)info).getChildren();
629                 for (int i = 0; i < children.length; i++) {
630                         buffer.append("\n"); //$NON-NLS-1$
631                         ((JavaElement)children[i]).toString(tab + 1, buffer);
632                 }
633         }
634         /**
635          *  Debugging purposes
636          */
637 //      public Object toStringInfo(int tab, StringBuffer buffer) {
638 //              Object info = JavaModelManager.getJavaModelManager().peekAtInfo(this);
639 //              this.toStringInfo(tab, buffer, info);
640 //              return info;
641 //      }
642         /**
643          *  Debugging purposes
644          */
645         protected void toStringInfo(int tab, StringBuffer buffer, Object info) {
646                 buffer.append(this.tabString(tab));
647                 buffer.append(getElementName());
648                 if (info == null) {
649                         buffer.append(" (not open)"); //$NON-NLS-1$
650                 }
651         }
652 }