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$