/*******************************************************************************
* 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.IOpenable;
import net.sourceforge.phpdt.core.JavaModelException;
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 null
if this
* element does not have a parent.
*/
protected IJavaElement fParent;
/**
* This element's name, or an empty String
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.
*
*
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 JavaElement
is equivalent to the given
* IDOMNode
.
*/
// 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 IDOMNode
that corresponds to this JavaElement
* or null
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 char
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.
*
*
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 ICompilationUnit#getElementAt
,
* 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
* null
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$
}
}
}