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.WorkingCopyOwner;
29 import net.sourceforge.phpdt.core.jdom.IDOMCompilationUnit;
30 import net.sourceforge.phpdt.core.jdom.IDOMNode;
31 import net.sourceforge.phpdt.internal.core.util.MementoTokenizer;
32 import net.sourceforge.phpdt.internal.core.util.Util;
33 import net.sourceforge.phpdt.internal.corext.Assert;
35 import org.eclipse.core.resources.IResource;
36 import org.eclipse.core.resources.IResourceStatus;
37 import org.eclipse.core.resources.ResourcesPlugin;
38 import org.eclipse.core.runtime.CoreException;
39 import org.eclipse.core.runtime.IAdaptable;
40 import org.eclipse.core.runtime.IPath;
41 import org.eclipse.core.runtime.IProgressMonitor;
42 import org.eclipse.core.runtime.Path;
43 import org.eclipse.core.runtime.PlatformObject;
44 import org.eclipse.core.runtime.jobs.ISchedulingRule;
47 * Root of Java element handle hierarchy.
51 public abstract class JavaElement extends PlatformObject
54 public static final char JEM_ESCAPE = '\\';
55 public static final char JEM_JAVAPROJECT = '=';
56 public static final char JEM_PACKAGEFRAGMENTROOT = Path.SEPARATOR;
57 public static final char JEM_PACKAGEFRAGMENT = '<';
58 public static final char JEM_FIELD = '^';
59 public static final char JEM_METHOD = '~';
60 public static final char JEM_INITIALIZER = '|';
61 public static final char JEM_COMPILATIONUNIT = '{';
62 // public static final char JEM_CLASSFILE = '(';
63 public static final char JEM_TYPE = '[';
64 public static final char JEM_PACKAGEDECLARATION = '%';
65 public static final char JEM_IMPORTDECLARATION = '#';
66 public static final char JEM_COUNT = '!';
67 public static final char JEM_LOCALVARIABLE = '@';
70 * A count to uniquely identify this element in the case that a duplicate
71 * named element exists. For example, if there are two fields in a
72 * compilation unit with the same name, the occurrence count is used to
73 * distinguish them. The occurrence count starts at 1 (thus the first
74 * occurrence is occurrence 1, not occurrence 0).
76 protected int occurrenceCount = 1;
79 * This element's type - one of the constants defined in
80 * IJavaLanguageElementTypes.
82 //protected int fLEType = 0;
84 * This element's parent, or <code>null</code> if this
85 * element does not have a parent.
87 protected JavaElement parent;
90 * This element's name, or an empty <code>String</code> if this element
91 * does not have a name.
93 protected String name;
95 protected static final Object NO_INFO = new Object();
98 * Constructs a handle for a java element with the given parent element and
102 * The parent of java element
104 * The name of java element
106 * @exception IllegalArgumentException
107 * if the type is not one of the valid Java element type
111 protected JavaElement(JavaElement parent, String name)
112 throws IllegalArgumentException {
113 this.parent = parent;
119 public void close() throws JavaModelException {
120 JavaModelManager.getJavaModelManager().removeInfoAndChildren(this);
123 * This element is being closed. Do any necessary cleanup.
125 protected abstract void closing(Object info) throws JavaModelException;
127 * Returns a new element info for this element.
129 protected abstract Object createElementInfo();
131 * Returns true if this handle represents the same Java element as the given
132 * handle. By default, two handles represent the same element if they are
133 * identical or if they represent the same type of element, have equal
134 * names, parents, and occurrence counts.
137 * If a subclass has other requirements for equality, this method must be
142 public boolean equals(Object o) {
147 // Java model parent is null
148 if (this.parent == null)
149 return super.equals(o);
150 if (o instanceof JavaElement) {
151 // assume instanceof check is done in subclass
152 JavaElement other = (JavaElement) o;
153 return this.occurrenceCount == other.occurrenceCount
154 && this.name.equals(other.name)
155 && this.parent.equals(other.parent);
160 * Returns true if this <code>JavaElement</code> is equivalent to the
161 * given <code>IDOMNode</code>.
163 protected boolean equalsDOMNode(IDOMNode node) throws JavaModelException {
166 protected void escapeMementoName(StringBuffer buffer, String mementoName) {
167 for (int i = 0, length = mementoName.length(); i < length; i++) {
168 char character = mementoName.charAt(i);
172 case JEM_JAVAPROJECT:
173 case JEM_PACKAGEFRAGMENTROOT:
174 case JEM_PACKAGEFRAGMENT:
177 case JEM_INITIALIZER:
178 case JEM_COMPILATIONUNIT:
179 // case JEM_CLASSFILE:
181 case JEM_PACKAGEDECLARATION:
182 case JEM_IMPORTDECLARATION:
183 case JEM_LOCALVARIABLE:
184 buffer.append(JEM_ESCAPE);
186 buffer.append(character);
193 public boolean exists() {
198 } catch (JavaModelException e) {
204 * Returns the <code>IDOMNode</code> that corresponds to this
205 * <code>JavaElement</code> or <code>null</code> if there is no
206 * corresponding node.
208 public IDOMNode findNode(IDOMCompilationUnit dom) {
209 int type = getElementType();
210 if (type == IJavaElement.COMPILATION_UNIT || type == IJavaElement.FIELD
211 || type == IJavaElement.IMPORT_DECLARATION
212 || type == IJavaElement.INITIALIZER
213 || type == IJavaElement.METHOD
214 || type == IJavaElement.PACKAGE_DECLARATION
215 || type == IJavaElement.TYPE) {
216 ArrayList path = new ArrayList();
217 IJavaElement element = this;
218 while (element != null
219 && element.getElementType() != IJavaElement.COMPILATION_UNIT) {
220 if (element.getElementType() != IJavaElement.IMPORT_CONTAINER) {
221 // the DOM does not have import containers, so skip them
222 path.add(0, element);
224 element = element.getParent();
226 if (path.size() == 0) {
228 if (equalsDOMNode(dom)) {
233 } catch (JavaModelException e) {
237 return ((JavaElement) path.get(0)).followPath(path, 0, dom
245 protected IDOMNode followPath(ArrayList path, int position, IDOMNode node) {
248 if (equalsDOMNode(node)) {
249 if (position == (path.size() - 1)) {
252 if (node.getFirstChild() != null) {
254 return ((JavaElement) path.get(position)).followPath(
255 path, position, node.getFirstChild());
260 } else if (node.getNextNode() != null) {
261 return followPath(path, position, node.getNextNode());
265 } catch (JavaModelException e) {
273 public IJavaElement getAncestor(int ancestorType) {
275 IJavaElement element = this;
276 while (element != null) {
277 if (element.getElementType() == ancestorType)
279 element = element.getParent();
284 * Generates the element infos for this element, its ancestors (if they are
285 * not opened) and its children (if it is an Openable). Puts the newly
286 * created element info in the given map.
288 protected abstract void generateInfos(Object info, HashMap newElements,
289 IProgressMonitor pm) throws JavaModelException;
294 public IJavaElement[] getChildren() throws JavaModelException {
295 return ((JavaElementInfo) getElementInfo()).getChildren();
298 * Returns a collection of (immediate) children of this node of the
302 * one of constants defined by IJavaLanguageElementTypes
304 public ArrayList getChildrenOfType(int type) throws JavaModelException {
305 IJavaElement[] children = getChildren();
306 int size = children.length;
307 ArrayList list = new ArrayList(size);
308 for (int i = 0; i < size; ++i) {
309 JavaElement elt = (JavaElement) children[i];
310 if (elt.getElementType() == type) {
319 // public IClassFile getClassFile() {
325 public ICompilationUnit getCompilationUnit() {
329 * Returns the info for this handle. If this element is not already open, it
330 * and all of its parents are opened. Does not return null. NOTE: BinaryType
331 * infos are NOT rooted under JavaElementInfo.
333 * @exception JavaModelException
334 * if the element is not present or not accessible
336 public Object getElementInfo() throws JavaModelException {
337 return getElementInfo(null);
340 * Returns the info for this handle. If this element is not already open, it
341 * and all of its parents are opened. Does not return null. NOTE: BinaryType
342 * infos are NOT rooted under JavaElementInfo.
344 * @exception JavaModelException
345 * if the element is not present or not accessible
347 public Object getElementInfo(IProgressMonitor monitor)
348 throws JavaModelException {
350 JavaModelManager manager = JavaModelManager.getJavaModelManager();
351 Object info = manager.getInfo(this);
354 return openWhenClosed(createElementInfo(), monitor);
359 public String getElementName() {
364 * Creates a Java element handle from the given memento.
365 * The given token is the current delimiter indicating the type of the next token(s).
366 * The given working copy owner is used only for compilation unit handles.
368 public abstract IJavaElement getHandleFromMemento(String token, MementoTokenizer memento, WorkingCopyOwner owner);
370 * Creates a Java element handle from the given memento.
371 * The given working copy owner is used only for compilation unit handles.
373 public IJavaElement getHandleFromMemento(MementoTokenizer memento, WorkingCopyOwner owner) {
374 if (!memento.hasMoreTokens()) return this;
375 String token = memento.nextToken();
376 return getHandleFromMemento(token, memento, owner);
379 * Update the occurence count of the receiver and creates a Java element handle from the given memento.
380 * The given working copy owner is used only for compilation unit handles.
382 public IJavaElement getHandleUpdatingCountFromMemento(MementoTokenizer memento, WorkingCopyOwner owner) {
383 this.occurrenceCount = Integer.parseInt(memento.nextToken());
384 if (!memento.hasMoreTokens()) return this;
385 String token = memento.nextToken();
386 return getHandleFromMemento(token, memento, owner);
391 public String getHandleIdentifier() {
392 return getHandleMemento();
395 * @see JavaElement#getHandleMemento()
397 public String getHandleMemento(){
398 StringBuffer buff= new StringBuffer(((JavaElement)getParent()).getHandleMemento());
399 buff.append(getHandleMementoDelimiter());
400 escapeMementoName(buff, getElementName());
401 if (this.occurrenceCount > 1) {
402 buff.append(JEM_COUNT);
403 buff.append(this.occurrenceCount);
405 return buff.toString();
408 * Returns the <code>char</code> that marks the start of this handles
409 * contribution to a memento.
413 * Returns the <code>char</code> that marks the start of this handles
414 * contribution to a memento.
416 protected abstract char getHandleMementoDelimiter();
420 public IJavaModel getJavaModel() {
421 IJavaElement current = this;
423 if (current instanceof IJavaModel)
424 return (IJavaModel) current;
425 } while ((current = current.getParent()) != null);
432 public IJavaProject getJavaProject() {
433 IJavaElement current = this;
435 if (current instanceof IJavaProject)
436 return (IJavaProject) current;
437 } while ((current = current.getParent()) != null);
441 * Returns the occurrence count of the handle.
443 protected int getOccurrenceCount() {
444 return occurrenceCount;
449 public IOpenable getOpenable() {
450 return this.getOpenableParent();
453 * Return the first instance of IOpenable in the parent hierarchy of this
457 * Subclasses that are not IOpenable's must override this method.
459 public IOpenable getOpenableParent() {
461 return (IOpenable) parent;
467 public IJavaElement getParent() {
472 * @see IJavaElement#getPrimaryElement()
474 public IJavaElement getPrimaryElement() {
475 return getPrimaryElement(true);
478 * Returns the primary element. If checkOwner, and the cu owner is primary,
479 * return this element.
481 public IJavaElement getPrimaryElement(boolean checkOwner) {
485 * Returns the element that is located at the given source position in this
486 * element. This is a helper method for
487 * <code>ICompilationUnit#getElementAt</code>, and only works on
488 * compilation units and types. The position given is known to be within
489 * this element's source range already, and if no finer grained element is
490 * found at the position, this element is returned.
492 protected IJavaElement getSourceElementAt(int position)
493 throws JavaModelException {
494 if (this instanceof ISourceReference) {
495 IJavaElement[] children = getChildren();
497 for (i = 0; i < children.length; i++) {
498 IJavaElement aChild = children[i];
500 if (aChild instanceof SourceRefElement) {
501 SourceRefElement child = (SourceRefElement) children[i];
502 ISourceRange range = child.getSourceRange();
503 // if (child.name.equals("stopObject")||range==null || range.getOffset()<=0) {
504 // System.out.println(child.name);
506 if (position < range.getOffset() + range.getLength()
507 && position >= range.getOffset()) {
508 if (child instanceof IParent) {
509 return child.getSourceElementAt(position);
518 Assert.isTrue(false);
523 * Returns the SourceMapper facility for this element, or <code>null</code>
524 * if this element does not have a SourceMapper.
526 // public SourceMapper getSourceMapper() {
527 // return ((JavaElement)getParent()).getSourceMapper();
532 * @see net.sourceforge.phpdt.core.IJavaElement#getSchedulingRule()
534 public ISchedulingRule getSchedulingRule() {
535 IResource resource = getResource();
536 if (resource == null) {
537 class NoResourceSchedulingRule implements ISchedulingRule {
539 public NoResourceSchedulingRule(IPath path) {
542 public boolean contains(ISchedulingRule rule) {
543 if (rule instanceof NoResourceSchedulingRule) {
545 .isPrefixOf(((NoResourceSchedulingRule) rule).path);
550 public boolean isConflicting(ISchedulingRule rule) {
551 if (rule instanceof NoResourceSchedulingRule) {
552 IPath otherPath = ((NoResourceSchedulingRule) rule).path;
553 return this.path.isPrefixOf(otherPath)
554 || otherPath.isPrefixOf(this.path);
560 return new NoResourceSchedulingRule(getPath());
568 public boolean hasChildren() throws JavaModelException {
569 // if I am not open, return true to avoid opening (case of a Java
570 // project, a compilation unit or a class file).
571 // also see https://bugs.eclipse.org/bugs/show_bug.cgi?id=52474
572 Object elementInfo = JavaModelManager.getJavaModelManager().getInfo(
574 if (elementInfo instanceof JavaElementInfo) {
575 return ((JavaElementInfo) elementInfo).getChildren().length > 0;
582 * Returns the hash code for this Java element. By default, the hash code
583 * for an element is a combination of its name and parent's hash code.
584 * Elements with other requirements must override this method.
586 public int hashCode() {
587 if (this.parent == null)
588 return super.hashCode();
589 return Util.combineHashCodes(this.name.hashCode(), this.parent
593 * Returns true if this element is an ancestor of the given element,
596 public boolean isAncestorOf(IJavaElement e) {
597 IJavaElement parentElement= e.getParent();
598 while (parentElement != null && !parentElement.equals(this)) {
599 parentElement= parentElement.getParent();
601 return parentElement != null;
607 public boolean isReadOnly() {
613 public boolean isStructureKnown() throws JavaModelException {
614 return ((JavaElementInfo) getElementInfo()).isStructureKnown();
617 * Creates and returns and not present exception for this element.
619 protected JavaModelException newNotPresentException() {
620 return new JavaModelException(new JavaModelStatus(
621 IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST, this));
624 * Opens this element and all parents that are not already open.
626 * @exception JavaModelException
627 * this element is not present or accessible
629 // protected void openHierarchy() throws JavaModelException {
630 // if (this instanceof IOpenable) {
631 // ((Openable) this).openWhenClosed(null);
633 // Openable openableParent = (Openable)getOpenableParent();
634 // if (openableParent != null) {
635 // JavaElementInfo openableParentInfo = (JavaElementInfo)
636 // JavaModelManager.getJavaModelManager().getInfo((IJavaElement)
638 // if (openableParentInfo == null) {
639 // openableParent.openWhenClosed(null);
641 // throw newNotPresentException();
647 * This element has just been opened. Do any necessary setup.
649 protected void opening(Object info) {
652 * Opens an <code> Openable </code> that is known to be closed (no check for
653 * <code> isOpen() </code> ). Returns the created element info.
655 protected Object openWhenClosed(Object info, IProgressMonitor monitor)
656 throws JavaModelException {
657 JavaModelManager manager = JavaModelManager.getJavaModelManager();
658 boolean hadTemporaryCache = manager.hasTemporaryCache();
660 HashMap newElements = manager.getTemporaryCache();
661 generateInfos(info, newElements, monitor);
663 info = newElements.get(this);
665 if (info == null) { // a source ref element could not be opened
666 // close any buffer that was opened for the openable parent
667 Iterator iterator = newElements.keySet().iterator();
668 while (iterator.hasNext()) {
669 IJavaElement element = (IJavaElement) iterator.next();
670 if (element instanceof Openable) {
671 ((Openable) element).closeBuffer();
674 throw newNotPresentException();
676 if (!hadTemporaryCache) {
677 manager.putInfos(this, newElements);
680 if (!hadTemporaryCache) {
681 manager.resetTemporaryCache();
688 public String readableName() {
689 return this.getElementName();
692 * Removes all cached info from the Java Model, including all children, but
693 * does not close this element.
695 // protected void removeInfo() {
696 // Object info = JavaModelManager.getJavaModelManager().peekAtInfo(this);
697 // if (info != null) {
698 // if (this instanceof IParent) {
699 // IJavaElement[] children = ((JavaElementInfo)info).getChildren();
700 // for (int i = 0, size = children.length; i < size; ++i) {
701 // JavaElement child = (JavaElement) children[i];
702 // child.removeInfo();
705 // JavaModelManager.getJavaModelManager().removeInfo(this);
709 // * Returns a copy of this element rooted at the given project.
711 // public abstract IJavaElement rootedAt(IJavaProject project);
713 * Runs a Java Model Operation
715 public static void runOperation(JavaModelOperation operation,
716 IProgressMonitor monitor) throws JavaModelException {
718 if (operation.isReadOnly()
719 || ResourcesPlugin.getWorkspace().isTreeLocked()) {
720 operation.run(monitor);
722 // use IWorkspace.run(...) to ensure that a build will be done
724 ResourcesPlugin.getWorkspace().run(operation, monitor);
726 } catch (CoreException ce) {
727 if (ce instanceof JavaModelException) {
728 throw (JavaModelException) ce;
730 if (ce.getStatus().getCode() == IResourceStatus.OPERATION_FAILED) {
731 Throwable e = ce.getStatus().getException();
732 if (e instanceof JavaModelException) {
733 throw (JavaModelException) e;
736 throw new JavaModelException(ce);
741 * Sets the occurrence count of the handle.
743 protected void setOccurrenceCount(int count) {
744 occurrenceCount = count;
746 protected String tabString(int tab) {
747 StringBuffer buffer = new StringBuffer();
748 for (int i = tab; i > 0; i--)
749 buffer.append(" "); //$NON-NLS-1$
750 return buffer.toString();
755 public String toDebugString() {
756 StringBuffer buffer = new StringBuffer();
757 this.toStringInfo(0, buffer, NO_INFO);
758 return buffer.toString();
763 public String toString() {
764 StringBuffer buffer = new StringBuffer();
766 return buffer.toString();
771 protected void toStringName(StringBuffer buffer) {
772 buffer.append(getElementName());
773 if (this.occurrenceCount > 1) {
774 buffer.append("#"); //$NON-NLS-1$
775 buffer.append(this.occurrenceCount);
781 protected void toString(int tab, StringBuffer buffer) {
782 // Object info = this.toStringInfo(tab, buffer);
785 this.toStringAncestors(buffer);
787 this.toStringChildren(tab, buffer, info);
792 public String toStringWithAncestors() {
793 StringBuffer buffer = new StringBuffer();
794 this.toStringInfo(0, buffer, NO_INFO);
795 this.toStringAncestors(buffer);
796 return buffer.toString();
801 protected void toStringAncestors(StringBuffer buffer) {
802 JavaElement parent = (JavaElement) this.getParent();
803 if (parent != null && parent.getParent() != null) {
804 buffer.append(" [in "); //$NON-NLS-1$
805 parent.toStringInfo(0, buffer, NO_INFO);
806 parent.toStringAncestors(buffer);
807 buffer.append("]"); //$NON-NLS-1$
813 protected void toStringChildren(int tab, StringBuffer buffer, Object info) {
814 if (info == null || !(info instanceof JavaElementInfo))
816 IJavaElement[] children = ((JavaElementInfo) info).getChildren();
817 for (int i = 0; i < children.length; i++) {
818 buffer.append("\n"); //$NON-NLS-1$
819 ((JavaElement) children[i]).toString(tab + 1, buffer);
825 // public Object toStringInfo(int tab, StringBuffer buffer) {
826 // Object info = JavaModelManager.getJavaModelManager().peekAtInfo(this);
827 // this.toStringInfo(tab, buffer, info);
833 protected void toStringInfo(int tab, StringBuffer buffer, Object info) {
834 buffer.append(this.tabString(tab));
835 buffer.append(getElementName());
837 buffer.append(" (not open)"); //$NON-NLS-1$