--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package net.sourceforge.phpdt.internal.core;
+
+import net.sourceforge.phpdt.core.IJavaElement;
+import net.sourceforge.phpdt.core.IJavaModel;
+import net.sourceforge.phpdt.core.IJavaModelStatusConstants;
+import net.sourceforge.phpdt.core.IOpenable;
+import net.sourceforge.phpdt.core.IParent;
+import net.sourceforge.phpdt.core.JavaModelException;
+import net.sourceforge.phpdt.internal.corext.Assert;
+
+import org.eclipse.core.resources.IResourceStatus;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.PlatformObject;
+
+/**
+ * Root of Java element handle hierarchy.
+ *
+ * @see IJavaElement
+ */
+public abstract class JavaElement extends PlatformObject implements IJavaElement {
+
+ public static final char JEM_JAVAPROJECT= '=';
+ public static final char JEM_PACKAGEFRAGMENTROOT= Path.SEPARATOR;
+ public static final char JEM_PACKAGEFRAGMENT= '<';
+ public static final char JEM_FIELD= '^';
+ public static final char JEM_METHOD= '~';
+ public static final char JEM_INITIALIZER= '|';
+ public static final char JEM_COMPILATIONUNIT= '{';
+ public static final char JEM_CLASSFILE= '(';
+ public static final char JEM_TYPE= '[';
+ public static final char JEM_PACKAGEDECLARATION= '%';
+ public static final char JEM_IMPORTDECLARATION= '#';
+
+ /**
+ * A count to uniquely identify this element in the case
+ * that a duplicate named element exists. For example, if
+ * there are two fields in a compilation unit with the
+ * same name, the occurrence count is used to distinguish
+ * them. The occurrence count starts at 1 (thus the first
+ * occurrence is occurrence 1, not occurrence 0).
+ */
+ protected int fOccurrenceCount = 1;
+
+
+ /**
+ * This element's type - one of the constants defined
+ * in IJavaLanguageElementTypes.
+ */
+ protected int fLEType = 0;
+
+ /**
+ * This element's parent, or <code>null</code> if this
+ * element does not have a parent.
+ */
+ protected IJavaElement fParent;
+
+ /**
+ * This element's name, or an empty <code>String</code> if this
+ * element does not have a name.
+ */
+ protected String fName;
+
+ protected static final Object NO_INFO = new Object();
+
+ /**
+ * Constructs a handle for a java element of the specified type, with
+ * the given parent element and name.
+ *
+ * @param type - one of the constants defined in IJavaLanguageElement
+ *
+ * @exception IllegalArgumentException if the type is not one of the valid
+ * Java element type constants
+ *
+ */
+ protected JavaElement(int type, IJavaElement parent, String name) throws IllegalArgumentException {
+ if (type < JAVA_MODEL || type > IMPORT_DECLARATION) {
+ throw new IllegalArgumentException(Util.bind("element.invalidType")); //$NON-NLS-1$
+ }
+ fLEType= type;
+ fParent= parent;
+ fName= name;
+ }
+ /**
+ * @see IOpenable
+ */
+// public void close() throws JavaModelException {
+// Object info = JavaModelManager.getJavaModelManager().peekAtInfo(this);
+// if (info != null) {
+// boolean wasVerbose = false;
+// try {
+// if (JavaModelManager.VERBOSE) {
+// System.out.println("CLOSING Element ("+ Thread.currentThread()+"): " + this.toStringWithAncestors()); //$NON-NLS-1$//$NON-NLS-2$
+// wasVerbose = true;
+// JavaModelManager.VERBOSE = false;
+// }
+// if (this instanceof IParent) {
+// IJavaElement[] children = ((JavaElementInfo) info).getChildren();
+// for (int i = 0, size = children.length; i < size; ++i) {
+// JavaElement child = (JavaElement) children[i];
+// child.close();
+// }
+// }
+// closing(info);
+// JavaModelManager.getJavaModelManager().removeInfo(this);
+// if (wasVerbose) {
+// System.out.println("-> Package cache size = " + JavaModelManager.getJavaModelManager().cache.pkgSize()); //$NON-NLS-1$
+// System.out.println("-> Openable cache filling ratio = " + JavaModelManager.getJavaModelManager().cache.openableFillingRatio() + "%"); //$NON-NLS-1$//$NON-NLS-2$
+// }
+// } finally {
+// JavaModelManager.VERBOSE = wasVerbose;
+// }
+// }
+// }
+ /**
+ * This element is being closed. Do any necessary cleanup.
+ */
+ protected void closing(Object info) throws JavaModelException {
+ }
+ /**
+ * Returns true if this handle represents the same Java element
+ * as the given handle. By default, two handles represent the same
+ * element if they are identical or if they represent the same type
+ * of element, have equal names, parents, and occurrence counts.
+ *
+ * <p>If a subclass has other requirements for equality, this method
+ * must be overridden.
+ *
+ * @see Object#equals
+ */
+ public boolean equals(Object o) {
+
+ if (this == o) return true;
+
+ // Java model parent is null
+ if (fParent == null) return super.equals(o);
+
+ if (o instanceof JavaElement) {
+ JavaElement other = (JavaElement) o;
+ if (fLEType != other.fLEType) return false;
+
+ return fName.equals(other.fName) &&
+ fParent.equals(other.fParent) &&
+ fOccurrenceCount == other.fOccurrenceCount;
+ }
+ return false;
+ }
+ /**
+ * Returns true if this <code>JavaElement</code> is equivalent to the given
+ * <code>IDOMNode</code>.
+ */
+// protected boolean equalsDOMNode(IDOMNode node) throws JavaModelException {
+// return false;
+// }
+ /**
+ * @see IJavaElement
+ */
+// public boolean exists() {
+//
+// try {
+// getElementInfo();
+// return true;
+// } catch (JavaModelException e) {
+// }
+// return false;
+// }
+
+ /**
+ * Returns the <code>IDOMNode</code> that corresponds to this <code>JavaElement</code>
+ * or <code>null</code> if there is no corresponding node.
+ */
+// public IDOMNode findNode(IDOMCompilationUnit dom) {
+// int type = getElementType();
+// if (type == IJavaElement.COMPILATION_UNIT ||
+// type == IJavaElement.FIELD ||
+// type == IJavaElement.IMPORT_DECLARATION ||
+// type == IJavaElement.INITIALIZER ||
+// type == IJavaElement.METHOD ||
+// type == IJavaElement.PACKAGE_DECLARATION ||
+// type == IJavaElement.TYPE) {
+// ArrayList path = new ArrayList();
+// IJavaElement element = this;
+// while (element != null && element.getElementType() != IJavaElement.COMPILATION_UNIT) {
+// if (element.getElementType() != IJavaElement.IMPORT_CONTAINER) {
+// // the DOM does not have import containers, so skip them
+// path.add(0, element);
+// }
+// element = element.getParent();
+// }
+// if (path.size() == 0) {
+// try {
+// if (equalsDOMNode(dom)) {
+// return dom;
+// } else {
+// return null;
+// }
+// } catch(JavaModelException e) {
+// return null;
+// }
+// }
+// return ((JavaElement) path.get(0)).followPath(path, 0, dom.getFirstChild());
+// } else {
+// return null;
+// }
+// }
+// /**
+// */
+// protected IDOMNode followPath(ArrayList path, int position, IDOMNode node) {
+//
+// try {
+// if (equalsDOMNode(node)) {
+// if (position == (path.size() - 1)) {
+// return node;
+// } else {
+// if (node.getFirstChild() != null) {
+// position++;
+// return ((JavaElement)path.get(position)).followPath(path, position, node.getFirstChild());
+// } else {
+// return null;
+// }
+// }
+// } else if (node.getNextNode() != null) {
+// return followPath(path, position, node.getNextNode());
+// } else {
+// return null;
+// }
+// } catch (JavaModelException e) {
+// return null;
+// }
+//
+// }
+ /**
+ * @see IJavaElement
+ */
+ public IJavaElement getAncestor(int ancestorType) {
+
+ IJavaElement element = this;
+ while (element != null) {
+ if (element.getElementType() == ancestorType) return element;
+ element= element.getParent();
+ }
+ return null;
+ }
+ /**
+ * @see IParent
+ */
+// public IJavaElement[] getChildren() throws JavaModelException {
+// return ((JavaElementInfo)getElementInfo()).getChildren();
+// }
+ /**
+ * Returns a collection of (immediate) children of this node of the
+ * specified type.
+ *
+ * @param type - one of constants defined by IJavaLanguageElementTypes
+ */
+// public ArrayList getChildrenOfType(int type) throws JavaModelException {
+// IJavaElement[] children = getChildren();
+// int size = children.length;
+// ArrayList list = new ArrayList(size);
+// for (int i = 0; i < size; ++i) {
+// JavaElement elt = (JavaElement)children[i];
+// if (elt.getElementType() == type) {
+// list.add(elt);
+// }
+// }
+// return list;
+// }
+ /**
+ * @see IMember
+ */
+// public IClassFile getClassFile() {
+// return null;
+// }
+// /**
+// * @see IMember
+// */
+// public ICompilationUnit getCompilationUnit() {
+// return null;
+// }
+ /**
+ * Returns the info for this handle.
+ * If this element is not already open, it and all of its parents are opened.
+ * Does not return null.
+ * NOTE: BinaryType infos are NJOT rooted under JavaElementInfo.
+ * @exception JavaModelException if the element is not present or not accessible
+ */
+// public Object getElementInfo() throws JavaModelException {
+//
+// // workaround to ensure parent project resolved classpath is available to avoid triggering initializers
+// // while the JavaModelManager lock is acquired (can cause deadlocks in clients)
+// IJavaProject project = getJavaProject();
+// if (project != null && !project.isOpen()) {
+// // TODO: need to revisit, since deadlock could still occur if perProjectInfo is removed concurrent before entering the lock
+// try {
+// project.getResolvedClasspath(true); // trigger all possible container/variable initialization outside the model lock
+// } catch (JavaModelException e) {
+// // project is not accessible or is not a java project
+// }
+// }
+//
+// // element info creation is done inside a lock on the JavaModelManager
+// JavaModelManager manager;
+// synchronized(manager = JavaModelManager.getJavaModelManager()){
+// Object info = manager.getInfo(this);
+// if (info == null) {
+// openHierarchy();
+// info= manager.getInfo(this);
+// if (info == null) {
+// throw newNotPresentException();
+// }
+// }
+// return info;
+// }
+// }
+ /**
+ * @see IAdaptable
+ */
+ public String getElementName() {
+ return fName;
+ }
+ /**
+ * @see IJavaElement
+ */
+ public int getElementType() {
+ return fLEType;
+ }
+ /**
+ * @see IJavaElement
+ */
+ public String getHandleIdentifier() {
+ return getHandleMemento();
+ }
+ /**
+ * @see JavaElement#getHandleMemento()
+ */
+ public String getHandleMemento(){
+ StringBuffer buff= new StringBuffer(((JavaElement)getParent()).getHandleMemento());
+ buff.append(getHandleMementoDelimiter());
+ buff.append(getElementName());
+ return buff.toString();
+ }
+ /**
+ * Returns the <code>char</code> that marks the start of this handles
+ * contribution to a memento.
+ */
+ protected abstract char getHandleMementoDelimiter();
+ /**
+ * @see IJavaElement
+ */
+ public IJavaModel getJavaModel() {
+ IJavaElement current = this;
+ do {
+ if (current instanceof IJavaModel) return (IJavaModel) current;
+ } while ((current = current.getParent()) != null);
+ return null;
+ }
+//
+// /**
+// * @see IJavaElement
+// */
+// public IJavaProject getJavaProject() {
+// IJavaElement current = this;
+// do {
+// if (current instanceof IJavaProject) return (IJavaProject) current;
+// } while ((current = current.getParent()) != null);
+// return null;
+// }
+ /**
+ * Returns the occurrence count of the handle.
+ */
+ protected int getOccurrenceCount() {
+ return fOccurrenceCount;
+ }
+ /*
+ * @see IJavaElement
+ */
+ public IOpenable getOpenable() {
+ return this.getOpenableParent();
+ }
+ /**
+ * Return the first instance of IOpenable in the parent
+ * hierarchy of this element.
+ *
+ * <p>Subclasses that are not IOpenable's must override this method.
+ */
+ public IOpenable getOpenableParent() {
+
+ return (IOpenable)fParent;
+ }
+ /**
+ * @see IJavaElement
+ */
+ public IJavaElement getParent() {
+ return fParent;
+ }
+
+ /**
+ * Returns the element that is located at the given source position
+ * in this element. This is a helper method for <code>ICompilationUnit#getElementAt</code>,
+ * and only works on compilation units and types. The position given is
+ * known to be within this element's source range already, and if no finer
+ * grained element is found at the position, this element is returned.
+ */
+// protected IJavaElement getSourceElementAt(int position) throws JavaModelException {
+// if (this instanceof ISourceReference) {
+// IJavaElement[] children = getChildren();
+// int i;
+// for (i = 0; i < children.length; i++) {
+// IJavaElement aChild = children[i];
+// if (aChild instanceof SourceRefElement) {
+// SourceRefElement child = (SourceRefElement) children[i];
+// ISourceRange range = child.getSourceRange();
+// if (position < range.getOffset() + range.getLength() && position >= range.getOffset()) {
+// if (child instanceof IParent) {
+// return child.getSourceElementAt(position);
+// } else {
+// return child;
+// }
+// }
+// }
+// }
+// } else {
+// // should not happen
+// Assert.isTrue(false);
+// }
+// return this;
+// }
+ /**
+ * Returns the SourceMapper facility for this element, or
+ * <code>null</code> if this element does not have a
+ * SourceMapper.
+ */
+// public SourceMapper getSourceMapper() {
+// return ((JavaElement)getParent()).getSourceMapper();
+// }
+
+ /**
+ * Returns the hash code for this Java element. By default,
+ * the hash code for an element is a combination of its name
+ * and parent's hash code. Elements with other requirements must
+ * override this method.
+ */
+ public int hashCode() {
+ if (fParent == null) return super.hashCode();
+ return Util.combineHashCodes(fName.hashCode(), fParent.hashCode());
+ }
+ /**
+ * Returns true if this element is an ancestor of the given element,
+ * otherwise false.
+ */
+ protected boolean isAncestorOf(IJavaElement e) {
+ IJavaElement parent= e.getParent();
+ while (parent != null && !parent.equals(this)) {
+ parent= parent.getParent();
+ }
+ return parent != null;
+ }
+
+ /**
+ * @see IJavaElement
+ */
+ public boolean isReadOnly() {
+ return false;
+ }
+ /**
+ * @see IJavaElement
+ */
+// public boolean isStructureKnown() throws JavaModelException {
+// return ((JavaElementInfo)getElementInfo()).isStructureKnown();
+// }
+// /**
+// * Creates and returns and not present exception for this element.
+// */
+// protected JavaModelException newNotPresentException() {
+// return new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST, this));
+// }
+// /**
+// * Opens this element and all parents that are not already open.
+// *
+// * @exception JavaModelException this element is not present or accessible
+// */
+// protected void openHierarchy() throws JavaModelException {
+// if (this instanceof IOpenable) {
+// ((Openable) this).openWhenClosed(null);
+// } else {
+// Openable openableParent = (Openable)getOpenableParent();
+// if (openableParent != null) {
+// JavaElementInfo openableParentInfo = (JavaElementInfo) JavaModelManager.getJavaModelManager().getInfo((IJavaElement) openableParent);
+// if (openableParentInfo == null) {
+// openableParent.openWhenClosed(null);
+// } else {
+// throw newNotPresentException();
+// }
+// }
+// }
+// }
+ /**
+ * This element has just been opened. Do any necessary setup.
+ */
+ protected void opening(Object info) {
+ }
+ /**
+ */
+ public String readableName() {
+ return this.getElementName();
+ }
+ /**
+ * Removes all cached info from the Java Model, including all children,
+ * but does not close this element.
+ */
+// protected void removeInfo() {
+// Object info = JavaModelManager.getJavaModelManager().peekAtInfo(this);
+// if (info != null) {
+// if (this instanceof IParent) {
+// IJavaElement[] children = ((JavaElementInfo)info).getChildren();
+// for (int i = 0, size = children.length; i < size; ++i) {
+// JavaElement child = (JavaElement) children[i];
+// child.removeInfo();
+// }
+// }
+// JavaModelManager.getJavaModelManager().removeInfo(this);
+// }
+// }
+// /**
+// * Returns a copy of this element rooted at the given project.
+// */
+// public abstract IJavaElement rootedAt(IJavaProject project);
+// /**
+// * Runs a Java Model Operation
+// */
+// public static void runOperation(JavaModelOperation operation, IProgressMonitor monitor) throws JavaModelException {
+// try {
+// if (operation.isReadOnly() || ResourcesPlugin.getWorkspace().isTreeLocked()) {
+// operation.run(monitor);
+// } else {
+// // use IWorkspace.run(...) to ensure that a build will be done in autobuild mode
+// ResourcesPlugin.getWorkspace().run(operation, monitor);
+// }
+// } catch (CoreException ce) {
+// if (ce instanceof JavaModelException) {
+// throw (JavaModelException)ce;
+// } else {
+// if (ce.getStatus().getCode() == IResourceStatus.OPERATION_FAILED) {
+// Throwable e= ce.getStatus().getException();
+// if (e instanceof JavaModelException) {
+// throw (JavaModelException) e;
+// }
+// }
+// throw new JavaModelException(ce);
+// }
+// }
+// }
+ /**
+ * Sets the occurrence count of the handle.
+ */
+ protected void setOccurrenceCount(int count) {
+ fOccurrenceCount = count;
+ }
+ protected String tabString(int tab) {
+ StringBuffer buffer = new StringBuffer();
+ for (int i = tab; i > 0; i--)
+ buffer.append(" "); //$NON-NLS-1$
+ return buffer.toString();
+ }
+ /**
+ * Debugging purposes
+ */
+ public String toDebugString() {
+ StringBuffer buffer = new StringBuffer();
+ this.toStringInfo(0, buffer, NO_INFO);
+ return buffer.toString();
+ }
+ /**
+ * Debugging purposes
+ */
+ public String toString() {
+ StringBuffer buffer = new StringBuffer();
+ toString(0, buffer);
+ return buffer.toString();
+ }
+ /**
+ * Debugging purposes
+ */
+ protected void toString(int tab, StringBuffer buffer) {
+ // Object info = this.toStringInfo(tab, buffer);
+ Object info = null;
+ if (tab == 0) {
+ this.toStringAncestors(buffer);
+ }
+ this.toStringChildren(tab, buffer, info);
+ }
+ /**
+ * Debugging purposes
+ */
+ public String toStringWithAncestors() {
+ StringBuffer buffer = new StringBuffer();
+ this.toStringInfo(0, buffer, NO_INFO);
+ this.toStringAncestors(buffer);
+ return buffer.toString();
+ }
+ /**
+ * Debugging purposes
+ */
+ protected void toStringAncestors(StringBuffer buffer) {
+ JavaElement parent = (JavaElement)this.getParent();
+ if (parent != null && parent.getParent() != null) {
+ buffer.append(" [in "); //$NON-NLS-1$
+ parent.toStringInfo(0, buffer, NO_INFO);
+ parent.toStringAncestors(buffer);
+ buffer.append("]"); //$NON-NLS-1$
+ }
+ }
+ /**
+ * Debugging purposes
+ */
+ protected void toStringChildren(int tab, StringBuffer buffer, Object info) {
+ if (info == null || !(info instanceof JavaElementInfo)) return;
+ IJavaElement[] children = ((JavaElementInfo)info).getChildren();
+ for (int i = 0; i < children.length; i++) {
+ buffer.append("\n"); //$NON-NLS-1$
+ ((JavaElement)children[i]).toString(tab + 1, buffer);
+ }
+ }
+ /**
+ * Debugging purposes
+ */
+// public Object toStringInfo(int tab, StringBuffer buffer) {
+// Object info = JavaModelManager.getJavaModelManager().peekAtInfo(this);
+// this.toStringInfo(tab, buffer, info);
+// return info;
+// }
+ /**
+ * Debugging purposes
+ */
+ protected void toStringInfo(int tab, StringBuffer buffer, Object info) {
+ buffer.append(this.tabString(tab));
+ buffer.append(getElementName());
+ if (info == null) {
+ buffer.append(" (not open)"); //$NON-NLS-1$
+ }
+ }
+}