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 net.sourceforge.phpdt.core.IJavaElement;
14 import net.sourceforge.phpdt.core.IJavaModel;
15 import net.sourceforge.phpdt.core.IJavaModelStatusConstants;
16 import net.sourceforge.phpdt.core.IOpenable;
17 import net.sourceforge.phpdt.core.IParent;
18 import net.sourceforge.phpdt.core.JavaModelException;
19 import net.sourceforge.phpdt.internal.corext.Assert;
21 import org.eclipse.core.resources.IResourceStatus;
22 import org.eclipse.core.resources.ResourcesPlugin;
23 import org.eclipse.core.runtime.CoreException;
24 import org.eclipse.core.runtime.IProgressMonitor;
25 import org.eclipse.core.runtime.Path;
26 import org.eclipse.core.runtime.PlatformObject;
29 * Root of Java element handle hierarchy.
33 public abstract class JavaElement extends PlatformObject implements IJavaElement {
35 public static final char JEM_JAVAPROJECT= '=';
36 public static final char JEM_PACKAGEFRAGMENTROOT= Path.SEPARATOR;
37 public static final char JEM_PACKAGEFRAGMENT= '<';
38 public static final char JEM_FIELD= '^';
39 public static final char JEM_METHOD= '~';
40 public static final char JEM_INITIALIZER= '|';
41 public static final char JEM_COMPILATIONUNIT= '{';
42 public static final char JEM_CLASSFILE= '(';
43 public static final char JEM_TYPE= '[';
44 public static final char JEM_PACKAGEDECLARATION= '%';
45 public static final char JEM_IMPORTDECLARATION= '#';
48 * A count to uniquely identify this element in the case
49 * that a duplicate named element exists. For example, if
50 * there are two fields in a compilation unit with the
51 * same name, the occurrence count is used to distinguish
52 * them. The occurrence count starts at 1 (thus the first
53 * occurrence is occurrence 1, not occurrence 0).
55 protected int fOccurrenceCount = 1;
59 * This element's type - one of the constants defined
60 * in IJavaLanguageElementTypes.
62 protected int fLEType = 0;
65 * This element's parent, or <code>null</code> if this
66 * element does not have a parent.
68 protected IJavaElement fParent;
71 * This element's name, or an empty <code>String</code> if this
72 * element does not have a name.
74 protected String fName;
76 protected static final Object NO_INFO = new Object();
79 * Constructs a handle for a java element of the specified type, with
80 * the given parent element and name.
82 * @param type - one of the constants defined in IJavaLanguageElement
84 * @exception IllegalArgumentException if the type is not one of the valid
85 * Java element type constants
88 protected JavaElement(int type, IJavaElement parent, String name) throws IllegalArgumentException {
89 if (type < JAVA_MODEL || type > IMPORT_DECLARATION) {
90 throw new IllegalArgumentException(Util.bind("element.invalidType")); //$NON-NLS-1$
99 // public void close() throws JavaModelException {
100 // Object info = JavaModelManager.getJavaModelManager().peekAtInfo(this);
101 // if (info != null) {
102 // boolean wasVerbose = false;
104 // if (JavaModelManager.VERBOSE) {
105 // System.out.println("CLOSING Element ("+ Thread.currentThread()+"): " + this.toStringWithAncestors()); //$NON-NLS-1$//$NON-NLS-2$
106 // wasVerbose = true;
107 // JavaModelManager.VERBOSE = false;
109 // if (this instanceof IParent) {
110 // IJavaElement[] children = ((JavaElementInfo) info).getChildren();
111 // for (int i = 0, size = children.length; i < size; ++i) {
112 // JavaElement child = (JavaElement) children[i];
117 // JavaModelManager.getJavaModelManager().removeInfo(this);
119 // System.out.println("-> Package cache size = " + JavaModelManager.getJavaModelManager().cache.pkgSize()); //$NON-NLS-1$
120 // System.out.println("-> Openable cache filling ratio = " + JavaModelManager.getJavaModelManager().cache.openableFillingRatio() + "%"); //$NON-NLS-1$//$NON-NLS-2$
123 // JavaModelManager.VERBOSE = wasVerbose;
128 * This element is being closed. Do any necessary cleanup.
130 protected void closing(Object info) throws JavaModelException {
133 * Returns true if this handle represents the same Java element
134 * as the given handle. By default, two handles represent the same
135 * element if they are identical or if they represent the same type
136 * of element, have equal names, parents, and occurrence counts.
138 * <p>If a subclass has other requirements for equality, this method
139 * must be overridden.
143 public boolean equals(Object o) {
145 if (this == o) return true;
147 // Java model parent is null
148 if (fParent == null) return super.equals(o);
150 if (o instanceof JavaElement) {
151 JavaElement other = (JavaElement) o;
152 if (fLEType != other.fLEType) return false;
154 return fName.equals(other.fName) &&
155 fParent.equals(other.fParent) &&
156 fOccurrenceCount == other.fOccurrenceCount;
161 * Returns true if this <code>JavaElement</code> is equivalent to the given
162 * <code>IDOMNode</code>.
164 // protected boolean equalsDOMNode(IDOMNode node) throws JavaModelException {
170 // public boolean exists() {
175 // } catch (JavaModelException e) {
181 * Returns the <code>IDOMNode</code> that corresponds to this <code>JavaElement</code>
182 * or <code>null</code> if there is no corresponding node.
184 // public IDOMNode findNode(IDOMCompilationUnit dom) {
185 // int type = getElementType();
186 // if (type == IJavaElement.COMPILATION_UNIT ||
187 // type == IJavaElement.FIELD ||
188 // type == IJavaElement.IMPORT_DECLARATION ||
189 // type == IJavaElement.INITIALIZER ||
190 // type == IJavaElement.METHOD ||
191 // type == IJavaElement.PACKAGE_DECLARATION ||
192 // type == IJavaElement.TYPE) {
193 // ArrayList path = new ArrayList();
194 // IJavaElement element = this;
195 // while (element != null && element.getElementType() != IJavaElement.COMPILATION_UNIT) {
196 // if (element.getElementType() != IJavaElement.IMPORT_CONTAINER) {
197 // // the DOM does not have import containers, so skip them
198 // path.add(0, element);
200 // element = element.getParent();
202 // if (path.size() == 0) {
204 // if (equalsDOMNode(dom)) {
209 // } catch(JavaModelException e) {
213 // return ((JavaElement) path.get(0)).followPath(path, 0, dom.getFirstChild());
220 // protected IDOMNode followPath(ArrayList path, int position, IDOMNode node) {
223 // if (equalsDOMNode(node)) {
224 // if (position == (path.size() - 1)) {
227 // if (node.getFirstChild() != null) {
229 // return ((JavaElement)path.get(position)).followPath(path, position, node.getFirstChild());
234 // } else if (node.getNextNode() != null) {
235 // return followPath(path, position, node.getNextNode());
239 // } catch (JavaModelException e) {
247 public IJavaElement getAncestor(int ancestorType) {
249 IJavaElement element = this;
250 while (element != null) {
251 if (element.getElementType() == ancestorType) return element;
252 element= element.getParent();
259 // public IJavaElement[] getChildren() throws JavaModelException {
260 // return ((JavaElementInfo)getElementInfo()).getChildren();
263 * Returns a collection of (immediate) children of this node of the
266 * @param type - one of constants defined by IJavaLanguageElementTypes
268 // public ArrayList getChildrenOfType(int type) throws JavaModelException {
269 // IJavaElement[] children = getChildren();
270 // int size = children.length;
271 // ArrayList list = new ArrayList(size);
272 // for (int i = 0; i < size; ++i) {
273 // JavaElement elt = (JavaElement)children[i];
274 // if (elt.getElementType() == type) {
283 // public IClassFile getClassFile() {
289 // public ICompilationUnit getCompilationUnit() {
293 * Returns the info for this handle.
294 * If this element is not already open, it and all of its parents are opened.
295 * Does not return null.
296 * NOTE: BinaryType infos are NJOT rooted under JavaElementInfo.
297 * @exception JavaModelException if the element is not present or not accessible
299 // public Object getElementInfo() throws JavaModelException {
301 // // workaround to ensure parent project resolved classpath is available to avoid triggering initializers
302 // // while the JavaModelManager lock is acquired (can cause deadlocks in clients)
303 // IJavaProject project = getJavaProject();
304 // if (project != null && !project.isOpen()) {
305 // // TODO: need to revisit, since deadlock could still occur if perProjectInfo is removed concurrent before entering the lock
307 // project.getResolvedClasspath(true); // trigger all possible container/variable initialization outside the model lock
308 // } catch (JavaModelException e) {
309 // // project is not accessible or is not a java project
313 // // element info creation is done inside a lock on the JavaModelManager
314 // JavaModelManager manager;
315 // synchronized(manager = JavaModelManager.getJavaModelManager()){
316 // Object info = manager.getInfo(this);
317 // if (info == null) {
319 // info= manager.getInfo(this);
320 // if (info == null) {
321 // throw newNotPresentException();
330 public String getElementName() {
336 public int getElementType() {
342 public String getHandleIdentifier() {
343 return getHandleMemento();
346 * @see JavaElement#getHandleMemento()
348 public String getHandleMemento(){
349 StringBuffer buff= new StringBuffer(((JavaElement)getParent()).getHandleMemento());
350 buff.append(getHandleMementoDelimiter());
351 buff.append(getElementName());
352 return buff.toString();
355 * Returns the <code>char</code> that marks the start of this handles
356 * contribution to a memento.
358 protected abstract char getHandleMementoDelimiter();
362 public IJavaModel getJavaModel() {
363 IJavaElement current = this;
365 if (current instanceof IJavaModel) return (IJavaModel) current;
366 } while ((current = current.getParent()) != null);
371 // * @see IJavaElement
373 // public IJavaProject getJavaProject() {
374 // IJavaElement current = this;
376 // if (current instanceof IJavaProject) return (IJavaProject) current;
377 // } while ((current = current.getParent()) != null);
381 * Returns the occurrence count of the handle.
383 protected int getOccurrenceCount() {
384 return fOccurrenceCount;
389 public IOpenable getOpenable() {
390 return this.getOpenableParent();
393 * Return the first instance of IOpenable in the parent
394 * hierarchy of this element.
396 * <p>Subclasses that are not IOpenable's must override this method.
398 public IOpenable getOpenableParent() {
400 return (IOpenable)fParent;
405 public IJavaElement getParent() {
410 * Returns the element that is located at the given source position
411 * in this element. This is a helper method for <code>ICompilationUnit#getElementAt</code>,
412 * and only works on compilation units and types. The position given is
413 * known to be within this element's source range already, and if no finer
414 * grained element is found at the position, this element is returned.
416 // protected IJavaElement getSourceElementAt(int position) throws JavaModelException {
417 // if (this instanceof ISourceReference) {
418 // IJavaElement[] children = getChildren();
420 // for (i = 0; i < children.length; i++) {
421 // IJavaElement aChild = children[i];
422 // if (aChild instanceof SourceRefElement) {
423 // SourceRefElement child = (SourceRefElement) children[i];
424 // ISourceRange range = child.getSourceRange();
425 // if (position < range.getOffset() + range.getLength() && position >= range.getOffset()) {
426 // if (child instanceof IParent) {
427 // return child.getSourceElementAt(position);
435 // // should not happen
436 // Assert.isTrue(false);
441 * Returns the SourceMapper facility for this element, or
442 * <code>null</code> if this element does not have a
445 // public SourceMapper getSourceMapper() {
446 // return ((JavaElement)getParent()).getSourceMapper();
450 * Returns the hash code for this Java element. By default,
451 * the hash code for an element is a combination of its name
452 * and parent's hash code. Elements with other requirements must
453 * override this method.
455 public int hashCode() {
456 if (fParent == null) return super.hashCode();
457 return Util.combineHashCodes(fName.hashCode(), fParent.hashCode());
460 * Returns true if this element is an ancestor of the given element,
463 protected boolean isAncestorOf(IJavaElement e) {
464 IJavaElement parent= e.getParent();
465 while (parent != null && !parent.equals(this)) {
466 parent= parent.getParent();
468 return parent != null;
474 public boolean isReadOnly() {
480 // public boolean isStructureKnown() throws JavaModelException {
481 // return ((JavaElementInfo)getElementInfo()).isStructureKnown();
484 // * Creates and returns and not present exception for this element.
486 // protected JavaModelException newNotPresentException() {
487 // return new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST, this));
490 // * Opens this element and all parents that are not already open.
492 // * @exception JavaModelException this element is not present or accessible
494 // protected void openHierarchy() throws JavaModelException {
495 // if (this instanceof IOpenable) {
496 // ((Openable) this).openWhenClosed(null);
498 // Openable openableParent = (Openable)getOpenableParent();
499 // if (openableParent != null) {
500 // JavaElementInfo openableParentInfo = (JavaElementInfo) JavaModelManager.getJavaModelManager().getInfo((IJavaElement) openableParent);
501 // if (openableParentInfo == null) {
502 // openableParent.openWhenClosed(null);
504 // throw newNotPresentException();
510 * This element has just been opened. Do any necessary setup.
512 protected void opening(Object info) {
516 public String readableName() {
517 return this.getElementName();
520 * Removes all cached info from the Java Model, including all children,
521 * but does not close this element.
523 // protected void removeInfo() {
524 // Object info = JavaModelManager.getJavaModelManager().peekAtInfo(this);
525 // if (info != null) {
526 // if (this instanceof IParent) {
527 // IJavaElement[] children = ((JavaElementInfo)info).getChildren();
528 // for (int i = 0, size = children.length; i < size; ++i) {
529 // JavaElement child = (JavaElement) children[i];
530 // child.removeInfo();
533 // JavaModelManager.getJavaModelManager().removeInfo(this);
537 // * Returns a copy of this element rooted at the given project.
539 // public abstract IJavaElement rootedAt(IJavaProject project);
541 // * Runs a Java Model Operation
543 // public static void runOperation(JavaModelOperation operation, IProgressMonitor monitor) throws JavaModelException {
545 // if (operation.isReadOnly() || ResourcesPlugin.getWorkspace().isTreeLocked()) {
546 // operation.run(monitor);
548 // // use IWorkspace.run(...) to ensure that a build will be done in autobuild mode
549 // ResourcesPlugin.getWorkspace().run(operation, monitor);
551 // } catch (CoreException ce) {
552 // if (ce instanceof JavaModelException) {
553 // throw (JavaModelException)ce;
555 // if (ce.getStatus().getCode() == IResourceStatus.OPERATION_FAILED) {
556 // Throwable e= ce.getStatus().getException();
557 // if (e instanceof JavaModelException) {
558 // throw (JavaModelException) e;
561 // throw new JavaModelException(ce);
566 * Sets the occurrence count of the handle.
568 protected void setOccurrenceCount(int count) {
569 fOccurrenceCount = count;
571 protected String tabString(int tab) {
572 StringBuffer buffer = new StringBuffer();
573 for (int i = tab; i > 0; i--)
574 buffer.append(" "); //$NON-NLS-1$
575 return buffer.toString();
580 public String toDebugString() {
581 StringBuffer buffer = new StringBuffer();
582 this.toStringInfo(0, buffer, NO_INFO);
583 return buffer.toString();
588 public String toString() {
589 StringBuffer buffer = new StringBuffer();
591 return buffer.toString();
596 protected void toString(int tab, StringBuffer buffer) {
597 // Object info = this.toStringInfo(tab, buffer);
600 this.toStringAncestors(buffer);
602 this.toStringChildren(tab, buffer, info);
607 public String toStringWithAncestors() {
608 StringBuffer buffer = new StringBuffer();
609 this.toStringInfo(0, buffer, NO_INFO);
610 this.toStringAncestors(buffer);
611 return buffer.toString();
616 protected void toStringAncestors(StringBuffer buffer) {
617 JavaElement parent = (JavaElement)this.getParent();
618 if (parent != null && parent.getParent() != null) {
619 buffer.append(" [in "); //$NON-NLS-1$
620 parent.toStringInfo(0, buffer, NO_INFO);
621 parent.toStringAncestors(buffer);
622 buffer.append("]"); //$NON-NLS-1$
628 protected void toStringChildren(int tab, StringBuffer buffer, Object info) {
629 if (info == null || !(info instanceof JavaElementInfo)) return;
630 IJavaElement[] children = ((JavaElementInfo)info).getChildren();
631 for (int i = 0; i < children.length; i++) {
632 buffer.append("\n"); //$NON-NLS-1$
633 ((JavaElement)children[i]).toString(tab + 1, buffer);
639 // public Object toStringInfo(int tab, StringBuffer buffer) {
640 // Object info = JavaModelManager.getJavaModelManager().peekAtInfo(this);
641 // this.toStringInfo(tab, buffer, info);
647 protected void toStringInfo(int tab, StringBuffer buffer, Object info) {
648 buffer.append(this.tabString(tab));
649 buffer.append(getElementName());
651 buffer.append(" (not open)"); //$NON-NLS-1$