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
 
   9  *     IBM Corporation - initial API and implementation
 
  10  *******************************************************************************/
 
  11 package net.sourceforge.phpdt.internal.core;
 
  13 import java.util.ArrayList;
 
  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.IParent;
 
  22 import net.sourceforge.phpdt.core.ISourceRange;
 
  23 import net.sourceforge.phpdt.core.ISourceReference;
 
  24 import net.sourceforge.phpdt.core.JavaModelException;
 
  25 import net.sourceforge.phpdt.core.jdom.IDOMCompilationUnit;
 
  26 import net.sourceforge.phpdt.core.jdom.IDOMNode;
 
  27 import net.sourceforge.phpdt.internal.corext.Assert;
 
  29 import org.eclipse.core.resources.IResourceStatus;
 
  30 import org.eclipse.core.resources.ResourcesPlugin;
 
  31 import org.eclipse.core.runtime.CoreException;
 
  32 import org.eclipse.core.runtime.IProgressMonitor;
 
  33 import org.eclipse.core.runtime.Path;
 
  34 import org.eclipse.core.runtime.PlatformObject;
 
  37  * Root of Java element handle hierarchy.
 
  41 public abstract class JavaElement extends PlatformObject implements IJavaElement {
 
  43         public static final char JEM_JAVAPROJECT= '=';
 
  44         public static final char JEM_PACKAGEFRAGMENTROOT= Path.SEPARATOR;
 
  45         public static final char JEM_PACKAGEFRAGMENT= '<';
 
  46         public static final char JEM_FIELD= '^';
 
  47         public static final char JEM_METHOD= '~';
 
  48         public static final char JEM_INITIALIZER= '|';
 
  49         public static final char JEM_COMPILATIONUNIT= '{';
 
  50         public static final char JEM_CLASSFILE= '(';
 
  51         public static final char JEM_TYPE= '[';
 
  52         public static final char JEM_PACKAGEDECLARATION= '%';
 
  53         public static final char JEM_IMPORTDECLARATION= '#';
 
  56          * A count to uniquely identify this element in the case
 
  57          * that a duplicate named element exists. For example, if
 
  58          * there are two fields in a compilation unit with the
 
  59          * same name, the occurrence count is used to distinguish
 
  60          * them.  The occurrence count starts at 1 (thus the first 
 
  61          * occurrence is occurrence 1, not occurrence 0).
 
  63         protected int fOccurrenceCount = 1;
 
  67          * This element's type - one of the constants defined
 
  68          * in IJavaLanguageElementTypes.
 
  70         protected int fLEType = 0;
 
  73          * This element's parent, or <code>null</code> if this
 
  74          * element does not have a parent.
 
  76         protected IJavaElement fParent;
 
  79          * This element's name, or an empty <code>String</code> if this
 
  80          * element does not have a name.
 
  82         protected String fName;
 
  84         protected static final Object NO_INFO = new Object();
 
  87          * Constructs a handle for a java element of the specified type, with
 
  88          * the given parent element and name.
 
  90          * @param type - one of the constants defined in IJavaLanguageElement
 
  92          * @exception IllegalArgumentException if the type is not one of the valid
 
  93          *              Java element type constants
 
  96         protected JavaElement(int type, IJavaElement parent, String name) throws IllegalArgumentException {
 
  97                 if (type < JAVA_MODEL || type > IMPORT_DECLARATION) {
 
  98                         throw new IllegalArgumentException(Util.bind("element.invalidType")); //$NON-NLS-1$
 
 107         public void close() throws JavaModelException {
 
 108                 Object info = JavaModelManager.getJavaModelManager().peekAtInfo(this);
 
 110                         boolean wasVerbose = false;
 
 112                                 if (JavaModelManager.VERBOSE) {
 
 113                                         System.out.println("CLOSING Element ("+ Thread.currentThread()+"): " + this.toStringWithAncestors());  //$NON-NLS-1$//$NON-NLS-2$
 
 115                                         JavaModelManager.VERBOSE = false;
 
 117                                 if (this instanceof IParent) {
 
 118                                         IJavaElement[] children = ((JavaElementInfo) info).getChildren();
 
 119                                         for (int i = 0, size = children.length; i < size; ++i) {
 
 120                                                 JavaElement child = (JavaElement) children[i];
 
 125                                 JavaModelManager.getJavaModelManager().removeInfo(this);
 
 127                                         System.out.println("-> Package cache size = " + JavaModelManager.getJavaModelManager().cache.pkgSize()); //$NON-NLS-1$
 
 128                                         System.out.println("-> Openable cache filling ratio = " + JavaModelManager.getJavaModelManager().cache.openableFillingRatio() + "%"); //$NON-NLS-1$//$NON-NLS-2$
 
 131                                 JavaModelManager.VERBOSE = wasVerbose;
 
 136          * This element is being closed.  Do any necessary cleanup.
 
 138         protected void closing(Object info) throws JavaModelException {
 
 141          * Returns true if this handle represents the same Java element
 
 142          * as the given handle. By default, two handles represent the same
 
 143          * element if they are identical or if they represent the same type
 
 144          * of element, have equal names, parents, and occurrence counts.
 
 146          * <p>If a subclass has other requirements for equality, this method
 
 147          * must be overridden.
 
 151         public boolean equals(Object o) {
 
 153                 if (this == o) return true;
 
 155                 // Java model parent is null
 
 156                 if (fParent == null) return super.equals(o);
 
 158                 if (o instanceof JavaElement) {
 
 159                         JavaElement other = (JavaElement) o;
 
 160                         if (fLEType != other.fLEType) return false;
 
 162                         return fName.equals(other.fName) &&
 
 163                                         fParent.equals(other.fParent) &&
 
 164                                         fOccurrenceCount == other.fOccurrenceCount;
 
 169          * Returns true if this <code>JavaElement</code> is equivalent to the given
 
 170          * <code>IDOMNode</code>.
 
 172         protected boolean equalsDOMNode(IDOMNode node) throws JavaModelException {
 
 178         public boolean exists() {
 
 183                 } catch (JavaModelException e) {
 
 189          * Returns the <code>IDOMNode</code> that corresponds to this <code>JavaElement</code>
 
 190          * or <code>null</code> if there is no corresponding node.
 
 192         public IDOMNode findNode(IDOMCompilationUnit dom) {
 
 193                 int type = getElementType();
 
 194                 if (type == IJavaElement.COMPILATION_UNIT || 
 
 195                         type == IJavaElement.FIELD || 
 
 196                         type == IJavaElement.IMPORT_DECLARATION || 
 
 197                         type == IJavaElement.INITIALIZER || 
 
 198                         type == IJavaElement.METHOD || 
 
 199                         type == IJavaElement.PACKAGE_DECLARATION || 
 
 200                         type == IJavaElement.TYPE) {
 
 201                         ArrayList path = new ArrayList();
 
 202                         IJavaElement element = this;
 
 203                         while (element != null && element.getElementType() != IJavaElement.COMPILATION_UNIT) {
 
 204                                 if (element.getElementType() != IJavaElement.IMPORT_CONTAINER) {
 
 205                                         // the DOM does not have import containers, so skip them
 
 206                                         path.add(0, element);
 
 208                                 element = element.getParent();
 
 210                         if (path.size() == 0) {
 
 212                                         if (equalsDOMNode(dom)) {
 
 217                                 } catch(JavaModelException e) {
 
 221                         return ((JavaElement) path.get(0)).followPath(path, 0, dom.getFirstChild());
 
 228         protected IDOMNode followPath(ArrayList path, int position, IDOMNode node) {
 
 231                         if (equalsDOMNode(node)) {
 
 232                                 if (position == (path.size() - 1)) {
 
 235                                         if (node.getFirstChild() != null) {
 
 237                                                 return ((JavaElement)path.get(position)).followPath(path, position, node.getFirstChild());
 
 242                         } else if (node.getNextNode() != null) {
 
 243                                 return followPath(path, position, node.getNextNode());
 
 247                 } catch (JavaModelException e) {
 
 255         public IJavaElement getAncestor(int ancestorType) {
 
 257                 IJavaElement element = this;
 
 258                 while (element != null) {
 
 259                         if (element.getElementType() == ancestorType)  return element;
 
 260                         element= element.getParent();
 
 267         public IJavaElement[] getChildren() throws JavaModelException {
 
 268                 return ((JavaElementInfo)getElementInfo()).getChildren();
 
 271          * Returns a collection of (immediate) children of this node of the
 
 274          * @param type - one of constants defined by IJavaLanguageElementTypes
 
 276         public ArrayList getChildrenOfType(int type) throws JavaModelException {
 
 277                 IJavaElement[] children = getChildren();
 
 278                 int size = children.length;
 
 279                 ArrayList list = new ArrayList(size);
 
 280                 for (int i = 0; i < size; ++i) {
 
 281                         JavaElement elt = (JavaElement)children[i];
 
 282                         if (elt.getElementType() == type) {
 
 291 //      public IClassFile getClassFile() {
 
 297         public ICompilationUnit getCompilationUnit() {
 
 301          * Returns the info for this handle.  
 
 302          * If this element is not already open, it and all of its parents are opened.
 
 303          * Does not return null.
 
 304          * NOTE: BinaryType infos are NJOT rooted under JavaElementInfo.
 
 305          * @exception JavaModelException if the element is not present or not accessible
 
 307         public Object getElementInfo() throws JavaModelException {
 
 308                 // workaround to ensure parent project resolved classpath is available to avoid triggering initializers
 
 309                 // while the JavaModelManager lock is acquired (can cause deadlocks in clients)
 
 310                 IJavaProject project = getJavaProject();
 
 311                 if (project != null && !project.isOpen()) {
 
 312                         // TODO: need to revisit, since deadlock could still occur if perProjectInfo is removed concurrent before entering the lock
 
 314                                 project.getResolvedClasspath(true); // trigger all possible container/variable initialization outside the model lock
 
 315                         } catch (JavaModelException e) {
 
 316                                 // project is not accessible or is not a java project
 
 320                 // element info creation is done inside a lock on the JavaModelManager
 
 321                 JavaModelManager manager;
 
 322                 synchronized(manager = JavaModelManager.getJavaModelManager()){
 
 323                         Object info = manager.getInfo(this);
 
 326                                 info= manager.getInfo(this);
 
 328                                         throw newNotPresentException();
 
 337         public String getElementName() {
 
 343         public int getElementType() {
 
 349         public String getHandleIdentifier() {
 
 350                 return getHandleMemento();
 
 353          * @see JavaElement#getHandleMemento()
 
 355         public String getHandleMemento(){
 
 356                 StringBuffer buff= new StringBuffer(((JavaElement)getParent()).getHandleMemento());
 
 357                 buff.append(getHandleMementoDelimiter());
 
 358                 buff.append(getElementName());
 
 359                 return buff.toString();
 
 362          * Returns the <code>char</code> that marks the start of this handles
 
 363          * contribution to a memento.
 
 365         protected abstract char getHandleMementoDelimiter();
 
 369         public IJavaModel getJavaModel() {
 
 370                 IJavaElement current = this;
 
 372                         if (current instanceof IJavaModel) return (IJavaModel) current;
 
 373                 } while ((current = current.getParent()) != null);
 
 380         public IJavaProject getJavaProject() {
 
 381                 IJavaElement current = this;
 
 383                         if (current instanceof IJavaProject) return (IJavaProject) current;
 
 384                 } while ((current = current.getParent()) != null);
 
 388          * Returns the occurrence count of the handle.
 
 390         protected int getOccurrenceCount() {
 
 391                 return fOccurrenceCount;
 
 396         public IOpenable getOpenable() {
 
 397                 return this.getOpenableParent();        
 
 400          * Return the first instance of IOpenable in the parent
 
 401          * hierarchy of this element.
 
 403          * <p>Subclasses that are not IOpenable's must override this method.
 
 405         public IOpenable getOpenableParent() {
 
 407                 return (IOpenable)fParent;
 
 412         public IJavaElement getParent() {
 
 417          * Returns the element that is located at the given source position
 
 418          * in this element.  This is a helper method for <code>ICompilationUnit#getElementAt</code>,
 
 419          * and only works on compilation units and types. The position given is
 
 420          * known to be within this element's source range already, and if no finer
 
 421          * grained element is found at the position, this element is returned.
 
 423         protected IJavaElement getSourceElementAt(int position) throws JavaModelException {
 
 424                 if (this instanceof ISourceReference) {
 
 425                         IJavaElement[] children = getChildren();
 
 427                         for (i = 0; i < children.length; i++) {
 
 428                                 IJavaElement aChild = children[i];
 
 429                                 if (aChild instanceof SourceRefElement) {
 
 430                                         SourceRefElement child = (SourceRefElement) children[i];
 
 431                                         ISourceRange range = child.getSourceRange();
 
 432                                         if (position < range.getOffset() + range.getLength() && position >= range.getOffset()) {
 
 433                                                 if (child instanceof IParent) {
 
 434                                                         return child.getSourceElementAt(position);
 
 443                         Assert.isTrue(false);
 
 448          * Returns the SourceMapper facility for this element, or
 
 449          * <code>null</code> if this element does not have a
 
 452 //      public SourceMapper getSourceMapper() {
 
 453 //              return ((JavaElement)getParent()).getSourceMapper();
 
 457          * Returns the hash code for this Java element. By default,
 
 458          * the hash code for an element is a combination of its name
 
 459          * and parent's hash code. Elements with other requirements must
 
 460          * override this method.
 
 462         public int hashCode() {
 
 463                 if (fParent == null) return super.hashCode();
 
 464                 return Util.combineHashCodes(fName.hashCode(), fParent.hashCode());
 
 467          * Returns true if this element is an ancestor of the given element,
 
 470         protected boolean isAncestorOf(IJavaElement e) {
 
 471                 IJavaElement parent= e.getParent();
 
 472                 while (parent != null && !parent.equals(this)) {
 
 473                         parent= parent.getParent();
 
 475                 return parent != null;
 
 481         public boolean isReadOnly() {
 
 487         public boolean isStructureKnown() throws JavaModelException {
 
 488                 return ((JavaElementInfo)getElementInfo()).isStructureKnown();
 
 491          * Creates and returns and not present exception for this element.
 
 493         protected JavaModelException newNotPresentException() {
 
 494                 return new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST, this));
 
 497          * Opens this element and all parents that are not already open.
 
 499          * @exception JavaModelException this element is not present or accessible
 
 501         protected void openHierarchy() throws JavaModelException {
 
 502                 if (this instanceof IOpenable) {
 
 503                         ((Openable) this).openWhenClosed(null);
 
 505                         Openable openableParent = (Openable)getOpenableParent();
 
 506                         if (openableParent != null) {
 
 507                                 JavaElementInfo openableParentInfo = (JavaElementInfo) JavaModelManager.getJavaModelManager().getInfo((IJavaElement) openableParent);
 
 508                                 if (openableParentInfo == null) {
 
 509                                         openableParent.openWhenClosed(null);
 
 511                                         throw newNotPresentException();
 
 517          * This element has just been opened.  Do any necessary setup.
 
 519         protected void opening(Object info) {
 
 523         public String readableName() {
 
 524                 return this.getElementName();
 
 527          * Removes all cached info from the Java Model, including all children,
 
 528          * but does not close this element.
 
 530         protected void removeInfo() {
 
 531                 Object info = JavaModelManager.getJavaModelManager().peekAtInfo(this);
 
 533                         if (this instanceof IParent) {
 
 534                                 IJavaElement[] children = ((JavaElementInfo)info).getChildren();
 
 535                                 for (int i = 0, size = children.length; i < size; ++i) {
 
 536                                         JavaElement child = (JavaElement) children[i];
 
 540                         JavaModelManager.getJavaModelManager().removeInfo(this);
 
 544          * Returns a copy of this element rooted at the given project.
 
 546         public abstract IJavaElement rootedAt(IJavaProject project);
 
 548          * Runs a Java Model Operation
 
 550         public static void runOperation(JavaModelOperation operation, IProgressMonitor monitor) throws JavaModelException {
 
 552                         if (operation.isReadOnly() || ResourcesPlugin.getWorkspace().isTreeLocked()) {
 
 553                                 operation.run(monitor);
 
 555                                 // use IWorkspace.run(...) to ensure that a build will be done in autobuild mode
 
 556                                 ResourcesPlugin.getWorkspace().run(operation, monitor);
 
 558                 } catch (CoreException ce) {
 
 559                         if (ce instanceof JavaModelException) {
 
 560                                 throw (JavaModelException)ce;
 
 562                                 if (ce.getStatus().getCode() == IResourceStatus.OPERATION_FAILED) {
 
 563                                         Throwable e= ce.getStatus().getException();
 
 564                                         if (e instanceof JavaModelException) {
 
 565                                                 throw (JavaModelException) e;
 
 568                                 throw new JavaModelException(ce);
 
 573          * Sets the occurrence count of the handle.
 
 575         protected void setOccurrenceCount(int count) {
 
 576                 fOccurrenceCount = count;
 
 578         protected String tabString(int tab) {
 
 579                 StringBuffer buffer = new StringBuffer();
 
 580                 for (int i = tab; i > 0; i--)
 
 581                         buffer.append("  "); //$NON-NLS-1$
 
 582                 return buffer.toString();
 
 587         public String toDebugString() {
 
 588                 StringBuffer buffer = new StringBuffer();
 
 589                 this.toStringInfo(0, buffer, NO_INFO);
 
 590                 return buffer.toString();
 
 595         public String toString() {
 
 596                 StringBuffer buffer = new StringBuffer();
 
 598                 return buffer.toString();
 
 603         protected void toString(int tab, StringBuffer buffer) {
 
 604         //      Object info = this.toStringInfo(tab, buffer);
 
 607                         this.toStringAncestors(buffer);
 
 609                 this.toStringChildren(tab, buffer, info);
 
 614         public String toStringWithAncestors() {
 
 615                 StringBuffer buffer = new StringBuffer();
 
 616                 this.toStringInfo(0, buffer, NO_INFO);
 
 617                 this.toStringAncestors(buffer);
 
 618                 return buffer.toString();
 
 623         protected void toStringAncestors(StringBuffer buffer) {
 
 624                 JavaElement parent = (JavaElement)this.getParent();
 
 625                 if (parent != null && parent.getParent() != null) {
 
 626                         buffer.append(" [in "); //$NON-NLS-1$
 
 627                         parent.toStringInfo(0, buffer, NO_INFO);
 
 628                         parent.toStringAncestors(buffer);
 
 629                         buffer.append("]"); //$NON-NLS-1$
 
 635         protected void toStringChildren(int tab, StringBuffer buffer, Object info) {
 
 636                 if (info == null || !(info instanceof JavaElementInfo)) return;
 
 637                 IJavaElement[] children = ((JavaElementInfo)info).getChildren();
 
 638                 for (int i = 0; i < children.length; i++) {
 
 639                         buffer.append("\n"); //$NON-NLS-1$
 
 640                         ((JavaElement)children[i]).toString(tab + 1, buffer);
 
 646 //      public Object toStringInfo(int tab, StringBuffer buffer) {
 
 647 //              Object info = JavaModelManager.getJavaModelManager().peekAtInfo(this);
 
 648 //              this.toStringInfo(tab, buffer, info);
 
 654         protected void toStringInfo(int tab, StringBuffer buffer, Object info) {
 
 655                 buffer.append(this.tabString(tab));
 
 656                 buffer.append(getElementName());
 
 658                         buffer.append(" (not open)"); //$NON-NLS-1$