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;
14 import java.util.HashMap;
15 import java.util.Iterator;
17 import net.sourceforge.phpdt.core.ICompilationUnit;
18 import net.sourceforge.phpdt.core.IJavaElement;
19 import net.sourceforge.phpdt.core.IJavaModel;
20 import net.sourceforge.phpdt.core.IJavaModelStatusConstants;
21 import net.sourceforge.phpdt.core.IJavaProject;
22 import net.sourceforge.phpdt.core.IMember;
23 import net.sourceforge.phpdt.core.IOpenable;
24 import net.sourceforge.phpdt.core.IParent;
25 import net.sourceforge.phpdt.core.ISourceRange;
26 import net.sourceforge.phpdt.core.ISourceReference;
27 import net.sourceforge.phpdt.core.JavaModelException;
28 import net.sourceforge.phpdt.core.jdom.IDOMCompilationUnit;
29 import net.sourceforge.phpdt.core.jdom.IDOMNode;
30 import net.sourceforge.phpdt.internal.corext.Assert;
32 import org.eclipse.core.resources.IResource;
33 import org.eclipse.core.resources.IResourceStatus;
34 import org.eclipse.core.resources.ResourcesPlugin;
35 import org.eclipse.core.runtime.CoreException;
36 import org.eclipse.core.runtime.IAdaptable;
37 import org.eclipse.core.runtime.IPath;
38 import org.eclipse.core.runtime.IProgressMonitor;
39 import org.eclipse.core.runtime.Path;
40 import org.eclipse.core.runtime.PlatformObject;
41 import org.eclipse.core.runtime.jobs.ISchedulingRule;
42 import net.sourceforge.phpdt.internal.core.JavaElementInfo;
44 import net.sourceforge.phpdt.internal.core.JavaModelManager;
45 import net.sourceforge.phpdt.internal.core.util.Util;
48 * Root of Java element handle hierarchy.
52 public abstract class JavaElement extends PlatformObject
56 public static final char JEM_JAVAPROJECT = '=';
57 public static final char JEM_PACKAGEFRAGMENTROOT = Path.SEPARATOR;
58 public static final char JEM_PACKAGEFRAGMENT = '<';
59 public static final char JEM_FIELD = '^';
60 public static final char JEM_METHOD = '~';
61 public static final char JEM_INITIALIZER = '|';
62 public static final char JEM_COMPILATIONUNIT = '{';
63 public static final char JEM_CLASSFILE = '(';
64 public static final char JEM_TYPE = '[';
65 public static final char JEM_PACKAGEDECLARATION = '%';
66 public static final char JEM_IMPORTDECLARATION = '#';
69 * A count to uniquely identify this element in the case that a duplicate
70 * named element exists. For example, if there are two fields in a
71 * compilation unit with the same name, the occurrence count is used to
72 * distinguish them. The occurrence count starts at 1 (thus the first
73 * occurrence is occurrence 1, not occurrence 0).
75 protected int occurrenceCount = 1;
78 * This element's type - one of the constants defined in
79 * IJavaLanguageElementTypes.
81 //protected int fLEType = 0;
83 * This element's parent, or <code>null</code> if this element does not
86 protected IJavaElement parent;
89 * This element's name, or an empty <code>String</code> if this element
90 * does not have a name.
92 protected String name;
94 protected static final Object NO_INFO = new Object();
97 * Constructs a handle for a java element with the given parent element and
101 * The parent of java element
103 * The name of java element
105 * @exception IllegalArgumentException
106 * if the type is not one of the valid Java element type
110 protected JavaElement(JavaElement parent, String name)
111 throws IllegalArgumentException {
112 this.parent = parent;
118 public void close() throws JavaModelException {
119 JavaModelManager.getJavaModelManager().removeInfoAndChildren(this);
122 * This element is being closed. Do any necessary cleanup.
124 protected abstract void closing(Object info) throws JavaModelException;
126 * Returns a new element info for this element.
128 protected abstract Object createElementInfo();
130 * Returns true if this handle represents the same Java element as the given
131 * handle. By default, two handles represent the same element if they are
132 * identical or if they represent the same type of element, have equal
133 * names, parents, and occurrence counts.
136 * If a subclass has other requirements for equality, this method must be
141 public boolean equals(Object o) {
146 // Java model parent is null
147 if (this.parent == null)
148 return super.equals(o);
149 if (o instanceof JavaElement) {
150 // assume instanceof check is done in subclass
151 JavaElement other = (JavaElement) o;
152 return this.occurrenceCount == other.occurrenceCount
153 && this.name.equals(other.name)
154 && this.parent.equals(other.parent);
159 * Returns true if this <code>JavaElement</code> is equivalent to the
160 * given <code>IDOMNode</code>.
162 protected boolean equalsDOMNode(IDOMNode node) throws JavaModelException {
168 public boolean exists() {
173 } catch (JavaModelException e) {
179 * Returns the <code>IDOMNode</code> that corresponds to this
180 * <code>JavaElement</code> or <code>null</code> if there is no
181 * corresponding node.
183 public IDOMNode findNode(IDOMCompilationUnit dom) {
184 int type = getElementType();
185 if (type == IJavaElement.COMPILATION_UNIT || type == IJavaElement.FIELD
186 || type == IJavaElement.IMPORT_DECLARATION
187 || type == IJavaElement.INITIALIZER
188 || type == IJavaElement.METHOD
189 || type == IJavaElement.PACKAGE_DECLARATION
190 || type == IJavaElement.TYPE) {
191 ArrayList path = new ArrayList();
192 IJavaElement element = this;
193 while (element != null
194 && element.getElementType() != IJavaElement.COMPILATION_UNIT) {
195 if (element.getElementType() != IJavaElement.IMPORT_CONTAINER) {
196 // the DOM does not have import containers, so skip them
197 path.add(0, element);
199 element = element.getParent();
201 if (path.size() == 0) {
203 if (equalsDOMNode(dom)) {
208 } catch (JavaModelException e) {
212 return ((JavaElement) path.get(0)).followPath(path, 0, dom
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(
230 path, position, node.getFirstChild());
235 } else if (node.getNextNode() != null) {
236 return followPath(path, position, node.getNextNode());
240 } catch (JavaModelException e) {
248 public IJavaElement getAncestor(int ancestorType) {
250 IJavaElement element = this;
251 while (element != null) {
252 if (element.getElementType() == ancestorType)
254 element = element.getParent();
259 * Generates the element infos for this element, its ancestors (if they are
260 * not opened) and its children (if it is an Openable). Puts the newly
261 * created element info in the given map.
263 protected abstract void generateInfos(Object info, HashMap newElements,
264 IProgressMonitor pm) throws JavaModelException;
269 public IJavaElement[] getChildren() throws JavaModelException {
270 return ((JavaElementInfo) getElementInfo()).getChildren();
273 * Returns a collection of (immediate) children of this node of the
277 * one of constants defined by IJavaLanguageElementTypes
279 public ArrayList getChildrenOfType(int type) throws JavaModelException {
280 IJavaElement[] children = getChildren();
281 int size = children.length;
282 ArrayList list = new ArrayList(size);
283 for (int i = 0; i < size; ++i) {
284 JavaElement elt = (JavaElement) children[i];
285 if (elt.getElementType() == type) {
294 // public IClassFile getClassFile() {
300 public ICompilationUnit getCompilationUnit() {
304 * Returns the info for this handle. If this element is not already open, it
305 * and all of its parents are opened. Does not return null. NOTE: BinaryType
306 * infos are NOT rooted under JavaElementInfo.
308 * @exception JavaModelException
309 * if the element is not present or not accessible
311 public Object getElementInfo() throws JavaModelException {
312 return getElementInfo(null);
315 * Returns the info for this handle. If this element is not already open, it
316 * and all of its parents are opened. Does not return null. NOTE: BinaryType
317 * infos are NOT rooted under JavaElementInfo.
319 * @exception JavaModelException
320 * if the element is not present or not accessible
322 public Object getElementInfo(IProgressMonitor monitor)
323 throws JavaModelException {
325 JavaModelManager manager = JavaModelManager.getJavaModelManager();
326 Object info = manager.getInfo(this);
329 return openWhenClosed(createElementInfo(), monitor);
334 public String getElementName() {
341 public String getHandleIdentifier() {
342 return getHandleMemento();
345 * @see JavaElement#getHandleMemento()
347 public String getHandleMemento() {
348 StringBuffer buff = new StringBuffer(((JavaElement) getParent())
349 .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)
366 return (IJavaModel) current;
367 } while ((current = current.getParent()) != null);
374 public IJavaProject getJavaProject() {
375 IJavaElement current = this;
377 if (current instanceof IJavaProject)
378 return (IJavaProject) current;
379 } while ((current = current.getParent()) != null);
383 * Returns the occurrence count of the handle.
385 protected int getOccurrenceCount() {
386 return occurrenceCount;
391 public IOpenable getOpenable() {
392 return this.getOpenableParent();
395 * Return the first instance of IOpenable in the parent hierarchy of this
399 * Subclasses that are not IOpenable's must override this method.
401 public IOpenable getOpenableParent() {
403 return (IOpenable) parent;
408 public IJavaElement getParent() {
413 * @see IJavaElement#getPrimaryElement()
415 public IJavaElement getPrimaryElement() {
416 return getPrimaryElement(true);
419 * Returns the primary element. If checkOwner, and the cu owner is primary,
420 * return this element.
422 public IJavaElement getPrimaryElement(boolean checkOwner) {
426 * Returns the element that is located at the given source position in this
427 * element. This is a helper method for
428 * <code>ICompilationUnit#getElementAt</code>, and only works on
429 * compilation units and types. The position given is known to be within
430 * this element's source range already, and if no finer grained element is
431 * found at the position, this element is returned.
433 protected IJavaElement getSourceElementAt(int position)
434 throws JavaModelException {
435 if (this instanceof ISourceReference) {
436 IJavaElement[] children = getChildren();
438 for (i = 0; i < children.length; i++) {
439 IJavaElement aChild = children[i];
440 if (aChild instanceof SourceRefElement) {
441 SourceRefElement child = (SourceRefElement) children[i];
442 ISourceRange range = child.getSourceRange();
443 if (position < range.getOffset() + range.getLength()
444 && position >= range.getOffset()) {
445 if (child instanceof IParent) {
446 return child.getSourceElementAt(position);
455 Assert.isTrue(false);
460 * Returns the SourceMapper facility for this element, or <code>null</code>
461 * if this element does not have a SourceMapper.
463 // public SourceMapper getSourceMapper() {
464 // return ((JavaElement)getParent()).getSourceMapper();
469 * @see org.eclipse.jdt.core.IJavaElement#getSchedulingRule()
471 public ISchedulingRule getSchedulingRule() {
472 IResource resource = getResource();
473 if (resource == null) {
474 class NoResourceSchedulingRule implements ISchedulingRule {
476 public NoResourceSchedulingRule(IPath path) {
479 public boolean contains(ISchedulingRule rule) {
480 if (rule instanceof NoResourceSchedulingRule) {
482 .isPrefixOf(((NoResourceSchedulingRule) rule).path);
487 public boolean isConflicting(ISchedulingRule rule) {
488 if (rule instanceof NoResourceSchedulingRule) {
489 IPath otherPath = ((NoResourceSchedulingRule) rule).path;
490 return this.path.isPrefixOf(otherPath)
491 || otherPath.isPrefixOf(this.path);
497 return new NoResourceSchedulingRule(getPath());
505 public boolean hasChildren() throws JavaModelException {
506 // if I am not open, return true to avoid opening (case of a Java
507 // project, a compilation unit or a class file).
508 // also see https://bugs.eclipse.org/bugs/show_bug.cgi?id=52474
509 Object elementInfo = JavaModelManager.getJavaModelManager().getInfo(
511 if (elementInfo instanceof JavaElementInfo) {
512 return ((JavaElementInfo) elementInfo).getChildren().length > 0;
519 * Returns the hash code for this Java element. By default, the hash code
520 * for an element is a combination of its name and parent's hash code.
521 * Elements with other requirements must override this method.
523 public int hashCode() {
524 if (this.parent == null)
525 return super.hashCode();
526 return Util.combineHashCodes(this.name.hashCode(), this.parent
531 * Returns true if this element is an ancestor of the given element,
534 protected boolean isAncestorOf(IJavaElement e) {
535 IJavaElement parent = e.getParent();
536 while (parent != null && !parent.equals(this)) {
537 parent = parent.getParent();
539 return parent != null;
545 public boolean isReadOnly() {
551 public boolean isStructureKnown() throws JavaModelException {
552 return ((JavaElementInfo) getElementInfo()).isStructureKnown();
555 * Creates and returns and not present exception for this element.
557 protected JavaModelException newNotPresentException() {
558 return new JavaModelException(new JavaModelStatus(
559 IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST, this));
562 * Opens this element and all parents that are not already open.
564 * @exception JavaModelException
565 * this element is not present or accessible
567 // protected void openHierarchy() throws JavaModelException {
568 // if (this instanceof IOpenable) {
569 // ((Openable) this).openWhenClosed(null);
571 // Openable openableParent = (Openable)getOpenableParent();
572 // if (openableParent != null) {
573 // JavaElementInfo openableParentInfo = (JavaElementInfo)
574 // JavaModelManager.getJavaModelManager().getInfo((IJavaElement)
576 // if (openableParentInfo == null) {
577 // openableParent.openWhenClosed(null);
579 // throw newNotPresentException();
585 * This element has just been opened. Do any necessary setup.
587 protected void opening(Object info) {
590 * Opens an <code> Openable </code> that is known to be closed (no check for
591 * <code> isOpen() </code> ). Returns the created element info.
593 protected Object openWhenClosed(Object info, IProgressMonitor monitor)
594 throws JavaModelException {
595 JavaModelManager manager = JavaModelManager.getJavaModelManager();
596 boolean hadTemporaryCache = manager.hasTemporaryCache();
598 HashMap newElements = manager.getTemporaryCache();
599 generateInfos(info, newElements, monitor);
601 info = newElements.get(this);
603 if (info == null) { // a source ref element could not be opened
604 // close any buffer that was opened for the openable parent
605 Iterator iterator = newElements.keySet().iterator();
606 while (iterator.hasNext()) {
607 IJavaElement element = (IJavaElement) iterator.next();
608 if (element instanceof Openable) {
609 ((Openable) element).closeBuffer();
612 throw newNotPresentException();
614 if (!hadTemporaryCache) {
615 manager.putInfos(this, newElements);
618 if (!hadTemporaryCache) {
619 manager.resetTemporaryCache();
626 public String readableName() {
627 return this.getElementName();
630 * Removes all cached info from the Java Model, including all children, but
631 * does not close this element.
633 // protected void removeInfo() {
634 // Object info = JavaModelManager.getJavaModelManager().peekAtInfo(this);
635 // if (info != null) {
636 // if (this instanceof IParent) {
637 // IJavaElement[] children = ((JavaElementInfo)info).getChildren();
638 // for (int i = 0, size = children.length; i < size; ++i) {
639 // JavaElement child = (JavaElement) children[i];
640 // child.removeInfo();
643 // JavaModelManager.getJavaModelManager().removeInfo(this);
647 // * Returns a copy of this element rooted at the given project.
649 // public abstract IJavaElement rootedAt(IJavaProject project);
651 * Runs a Java Model Operation
653 public static void runOperation(JavaModelOperation operation,
654 IProgressMonitor monitor) throws JavaModelException {
656 if (operation.isReadOnly()
657 || ResourcesPlugin.getWorkspace().isTreeLocked()) {
658 operation.run(monitor);
660 // use IWorkspace.run(...) to ensure that a build will be done
662 ResourcesPlugin.getWorkspace().run(operation, monitor);
664 } catch (CoreException ce) {
665 if (ce instanceof JavaModelException) {
666 throw (JavaModelException) ce;
668 if (ce.getStatus().getCode() == IResourceStatus.OPERATION_FAILED) {
669 Throwable e = ce.getStatus().getException();
670 if (e instanceof JavaModelException) {
671 throw (JavaModelException) e;
674 throw new JavaModelException(ce);
679 * Sets the occurrence count of the handle.
681 protected void setOccurrenceCount(int count) {
682 occurrenceCount = count;
684 protected String tabString(int tab) {
685 StringBuffer buffer = new StringBuffer();
686 for (int i = tab; i > 0; i--)
687 buffer.append(" "); //$NON-NLS-1$
688 return buffer.toString();
693 public String toDebugString() {
694 StringBuffer buffer = new StringBuffer();
695 this.toStringInfo(0, buffer, NO_INFO);
696 return buffer.toString();
701 public String toString() {
702 StringBuffer buffer = new StringBuffer();
704 return buffer.toString();
709 protected void toStringName(StringBuffer buffer) {
710 buffer.append(getElementName());
711 if (this.occurrenceCount > 1) {
712 buffer.append("#"); //$NON-NLS-1$
713 buffer.append(this.occurrenceCount);
719 protected void toString(int tab, StringBuffer buffer) {
720 // Object info = this.toStringInfo(tab, buffer);
723 this.toStringAncestors(buffer);
725 this.toStringChildren(tab, buffer, info);
730 public String toStringWithAncestors() {
731 StringBuffer buffer = new StringBuffer();
732 this.toStringInfo(0, buffer, NO_INFO);
733 this.toStringAncestors(buffer);
734 return buffer.toString();
739 protected void toStringAncestors(StringBuffer buffer) {
740 JavaElement parent = (JavaElement) this.getParent();
741 if (parent != null && parent.getParent() != null) {
742 buffer.append(" [in "); //$NON-NLS-1$
743 parent.toStringInfo(0, buffer, NO_INFO);
744 parent.toStringAncestors(buffer);
745 buffer.append("]"); //$NON-NLS-1$
751 protected void toStringChildren(int tab, StringBuffer buffer, Object info) {
752 if (info == null || !(info instanceof JavaElementInfo))
754 IJavaElement[] children = ((JavaElementInfo) info).getChildren();
755 for (int i = 0; i < children.length; i++) {
756 buffer.append("\n"); //$NON-NLS-1$
757 ((JavaElement) children[i]).toString(tab + 1, buffer);
763 // public Object toStringInfo(int tab, StringBuffer buffer) {
764 // Object info = JavaModelManager.getJavaModelManager().peekAtInfo(this);
765 // this.toStringInfo(tab, buffer, info);
771 protected void toStringInfo(int tab, StringBuffer buffer, Object info) {
772 buffer.append(this.tabString(tab));
773 buffer.append(getElementName());
775 buffer.append(" (not open)"); //$NON-NLS-1$