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;
34 //import net.sourceforge.phpdt.internal.corext.Assert;
35 import org.eclipse.core.runtime.Assert;
37 import org.eclipse.core.resources.IResource;
38 import org.eclipse.core.resources.IResourceStatus;
39 import org.eclipse.core.resources.ResourcesPlugin;
40 import org.eclipse.core.runtime.CoreException;
41 import org.eclipse.core.runtime.IAdaptable;
42 import org.eclipse.core.runtime.IPath;
43 import org.eclipse.core.runtime.IProgressMonitor;
44 import org.eclipse.core.runtime.Path;
45 import org.eclipse.core.runtime.PlatformObject;
46 import org.eclipse.core.runtime.jobs.ISchedulingRule;
49 * Root of Java element handle hierarchy.
53 public abstract class JavaElement extends PlatformObject implements
55 public static final char JEM_ESCAPE = '\\';
57 public static final char JEM_JAVAPROJECT = '=';
59 public static final char JEM_PACKAGEFRAGMENTROOT = Path.SEPARATOR;
61 public static final char JEM_PACKAGEFRAGMENT = '<';
63 public static final char JEM_FIELD = '^';
65 public static final char JEM_METHOD = '~';
67 public static final char JEM_INITIALIZER = '|';
69 public static final char JEM_COMPILATIONUNIT = '{';
71 // public static final char JEM_CLASSFILE = '(';
72 public static final char JEM_TYPE = '[';
74 public static final char JEM_PACKAGEDECLARATION = '%';
76 public static final char JEM_IMPORTDECLARATION = '#';
78 public static final char JEM_COUNT = '!';
80 public static final char JEM_LOCALVARIABLE = '@';
83 * A count to uniquely identify this element in the case that a duplicate
84 * named element exists. For example, if there are two fields in a
85 * compilation unit with the same name, the occurrence count is used to
86 * distinguish them. The occurrence count starts at 1 (thus the first
87 * occurrence is occurrence 1, not occurrence 0).
89 protected int occurrenceCount = 1;
92 * This element's type - one of the constants defined in
93 * IJavaLanguageElementTypes.
95 // protected int fLEType = 0;
97 * This element's parent, or <code>null</code> if this element does not
100 protected JavaElement parent;
103 * This element's name, or an empty <code>String</code> if this element
104 * does not have a name.
106 protected String name;
108 protected static final Object NO_INFO = new Object();
111 * Constructs a handle for a java element with the given parent element and
115 * The parent of java element
117 * The name of java element
119 * @exception IllegalArgumentException
120 * if the type is not one of the valid Java element type
124 protected JavaElement(JavaElement parent, String name)
125 throws IllegalArgumentException {
126 this.parent = parent;
133 public void close() throws JavaModelException {
134 JavaModelManager.getJavaModelManager().removeInfoAndChildren(this);
138 * This element is being closed. Do any necessary cleanup.
140 protected abstract void closing(Object info) throws JavaModelException;
143 * Returns a new element info for this element.
145 protected abstract Object createElementInfo();
148 * Returns true if this handle represents the same Java element as the given
149 * handle. By default, two handles represent the same element if they are
150 * identical or if they represent the same type of element, have equal
151 * names, parents, and occurrence counts.
154 * If a subclass has other requirements for equality, this method must be
159 public boolean equals(Object o) {
164 // Java model parent is null
165 if (this.parent == null)
166 return super.equals(o);
167 if (o instanceof JavaElement) {
168 // assume instanceof check is done in subclass
169 JavaElement other = (JavaElement) o;
170 return this.occurrenceCount == other.occurrenceCount
171 && this.name.equals(other.name)
172 && this.parent.equals(other.parent);
178 * Returns true if this <code>JavaElement</code> is equivalent to the
179 * given <code>IDOMNode</code>.
181 protected boolean equalsDOMNode(IDOMNode node) throws JavaModelException {
185 protected void escapeMementoName(StringBuffer buffer, String mementoName) {
186 for (int i = 0, length = mementoName.length(); i < length; i++) {
187 char character = mementoName.charAt(i);
191 case JEM_JAVAPROJECT:
192 case JEM_PACKAGEFRAGMENTROOT:
193 case JEM_PACKAGEFRAGMENT:
196 case JEM_INITIALIZER:
197 case JEM_COMPILATIONUNIT:
198 // case JEM_CLASSFILE:
200 case JEM_PACKAGEDECLARATION:
201 case JEM_IMPORTDECLARATION:
202 case JEM_LOCALVARIABLE:
203 buffer.append(JEM_ESCAPE);
205 buffer.append(character);
212 public boolean exists() {
217 } catch (JavaModelException e) {
223 * Returns the <code>IDOMNode</code> that corresponds to this
224 * <code>JavaElement</code> or <code>null</code> if there is no
225 * corresponding node.
227 public IDOMNode findNode(IDOMCompilationUnit dom) {
228 int type = getElementType();
229 if (type == IJavaElement.COMPILATION_UNIT || type == IJavaElement.FIELD
230 || type == IJavaElement.IMPORT_DECLARATION
231 || type == IJavaElement.INITIALIZER
232 || type == IJavaElement.METHOD
233 || type == IJavaElement.PACKAGE_DECLARATION
234 || type == IJavaElement.TYPE) {
235 ArrayList path = new ArrayList();
236 IJavaElement element = this;
237 while (element != null
238 && element.getElementType() != IJavaElement.COMPILATION_UNIT) {
239 if (element.getElementType() != IJavaElement.IMPORT_CONTAINER) {
240 // the DOM does not have import containers, so skip them
241 path.add(0, element);
243 element = element.getParent();
245 if (path.size() == 0) {
247 if (equalsDOMNode(dom)) {
252 } catch (JavaModelException e) {
256 return ((JavaElement) path.get(0)).followPath(path, 0, dom
265 protected IDOMNode followPath(ArrayList path, int position, IDOMNode node) {
268 if (equalsDOMNode(node)) {
269 if (position == (path.size() - 1)) {
272 if (node.getFirstChild() != null) {
274 return ((JavaElement) path.get(position)).followPath(
275 path, position, node.getFirstChild());
280 } else if (node.getNextNode() != null) {
281 return followPath(path, position, node.getNextNode());
285 } catch (JavaModelException e) {
294 public IJavaElement getAncestor(int ancestorType) {
296 IJavaElement element = this;
297 while (element != null) {
298 if (element.getElementType() == ancestorType)
300 element = element.getParent();
306 * Generates the element infos for this element, its ancestors (if they are
307 * not opened) and its children (if it is an Openable). Puts the newly
308 * created element info in the given map.
310 protected abstract void generateInfos(Object info, HashMap newElements,
311 IProgressMonitor pm) throws JavaModelException;
316 public IJavaElement[] getChildren() throws JavaModelException {
317 return ((JavaElementInfo) getElementInfo()).getChildren();
321 * Returns a collection of (immediate) children of this node of the
325 * one of constants defined by IJavaLanguageElementTypes
327 public ArrayList getChildrenOfType(int type) throws JavaModelException {
328 IJavaElement[] children = getChildren();
329 int size = children.length;
330 ArrayList list = new ArrayList(size);
331 for (int i = 0; i < size; ++i) {
332 JavaElement elt = (JavaElement) children[i];
333 if (elt.getElementType() == type) {
343 // public IClassFile getClassFile() {
349 public ICompilationUnit getCompilationUnit() {
354 * Returns the info for this handle. If this element is not already open, it
355 * and all of its parents are opened. Does not return null. NOTE: BinaryType
356 * infos are NOT rooted under JavaElementInfo.
358 * @exception JavaModelException
359 * if the element is not present or not accessible
361 public Object getElementInfo() throws JavaModelException {
362 return getElementInfo(null);
366 * Returns the info for this handle. If this element is not already open, it
367 * and all of its parents are opened. Does not return null. NOTE: BinaryType
368 * infos are NOT rooted under JavaElementInfo.
370 * @exception JavaModelException
371 * if the element is not present or not accessible
373 public Object getElementInfo(IProgressMonitor monitor)
374 throws JavaModelException {
376 JavaModelManager manager = JavaModelManager.getJavaModelManager();
377 Object info = manager.getInfo(this);
380 return openWhenClosed(createElementInfo(), monitor);
386 public String getElementName() {
391 * Creates a Java element handle from the given memento. The given token is
392 * the current delimiter indicating the type of the next token(s). The given
393 * working copy owner is used only for compilation unit handles.
395 public abstract IJavaElement getHandleFromMemento(String token,
396 MementoTokenizer memento, WorkingCopyOwner owner);
399 * Creates a Java element handle from the given memento. The given working
400 * copy owner is used only for compilation unit handles.
402 public IJavaElement getHandleFromMemento(MementoTokenizer memento,
403 WorkingCopyOwner owner) {
404 if (!memento.hasMoreTokens())
406 String token = memento.nextToken();
407 return getHandleFromMemento(token, memento, owner);
411 * Update the occurence count of the receiver and creates a Java element
412 * handle from the given memento. The given working copy owner is used only
413 * for compilation unit handles.
415 public IJavaElement getHandleUpdatingCountFromMemento(
416 MementoTokenizer memento, WorkingCopyOwner owner) {
417 this.occurrenceCount = Integer.parseInt(memento.nextToken());
418 if (!memento.hasMoreTokens())
420 String token = memento.nextToken();
421 return getHandleFromMemento(token, memento, owner);
427 public String getHandleIdentifier() {
428 return getHandleMemento();
432 * @see JavaElement#getHandleMemento()
434 public String getHandleMemento() {
435 StringBuffer buff = new StringBuffer(((JavaElement) getParent())
436 .getHandleMemento());
437 buff.append(getHandleMementoDelimiter());
438 escapeMementoName(buff, getElementName());
439 if (this.occurrenceCount > 1) {
440 buff.append(JEM_COUNT);
441 buff.append(this.occurrenceCount);
443 return buff.toString();
447 * Returns the <code>char</code> that marks the start of this handles
448 * contribution to a memento.
452 * Returns the <code>char</code> that marks the start of this handles
453 * contribution to a memento.
455 protected abstract char getHandleMementoDelimiter();
460 public IJavaModel getJavaModel() {
461 IJavaElement current = this;
463 if (current instanceof IJavaModel)
464 return (IJavaModel) current;
465 } while ((current = current.getParent()) != null);
472 public IJavaProject getJavaProject() {
473 IJavaElement current = this;
475 if (current instanceof IJavaProject)
476 return (IJavaProject) current;
477 } while ((current = current.getParent()) != null);
482 * Returns the occurrence count of the handle.
484 protected int getOccurrenceCount() {
485 return occurrenceCount;
491 public IOpenable getOpenable() {
492 return this.getOpenableParent();
496 * Return the first instance of IOpenable in the parent hierarchy of this
500 * Subclasses that are not IOpenable's must override this method.
502 public IOpenable getOpenableParent() {
504 return (IOpenable) parent;
510 public IJavaElement getParent() {
515 * @see IJavaElement#getPrimaryElement()
517 public IJavaElement getPrimaryElement() {
518 return getPrimaryElement(true);
522 * Returns the primary element. If checkOwner, and the cu owner is primary,
523 * return this element.
525 public IJavaElement getPrimaryElement(boolean checkOwner) {
530 * Returns the element that is located at the given source position in this
531 * element. This is a helper method for
532 * <code>ICompilationUnit#getElementAt</code>, and only works on
533 * compilation units and types. The position given is known to be within
534 * this element's source range already, and if no finer grained element is
535 * found at the position, this element is returned.
537 protected IJavaElement getSourceElementAt(int position)
538 throws JavaModelException {
539 if (this instanceof ISourceReference) {
540 IJavaElement[] children = getChildren();
542 for (i = 0; i < children.length; i++) {
543 IJavaElement aChild = children[i];
545 if (aChild instanceof SourceRefElement) {
546 SourceRefElement child = (SourceRefElement) children[i];
547 ISourceRange range = child.getSourceRange();
548 // if (child.name.equals("stopObject")||range==null ||
549 // range.getOffset()<=0) {
550 // System.out.println(child.name);
552 if (position < range.getOffset() + range.getLength()
553 && position >= range.getOffset()) {
554 if (child instanceof IParent) {
555 return child.getSourceElementAt(position);
564 Assert.isTrue(false);
570 * Returns the SourceMapper facility for this element, or <code>null</code>
571 * if this element does not have a SourceMapper.
573 // public SourceMapper getSourceMapper() {
574 // return ((JavaElement)getParent()).getSourceMapper();
579 * @see net.sourceforge.phpdt.core.IJavaElement#getSchedulingRule()
581 public ISchedulingRule getSchedulingRule() {
582 IResource resource = getResource();
583 if (resource == null) {
584 class NoResourceSchedulingRule implements ISchedulingRule {
587 public NoResourceSchedulingRule(IPath path) {
591 public boolean contains(ISchedulingRule rule) {
592 if (rule instanceof NoResourceSchedulingRule) {
594 .isPrefixOf(((NoResourceSchedulingRule) rule).path);
600 public boolean isConflicting(ISchedulingRule rule) {
601 if (rule instanceof NoResourceSchedulingRule) {
602 IPath otherPath = ((NoResourceSchedulingRule) rule).path;
603 return this.path.isPrefixOf(otherPath)
604 || otherPath.isPrefixOf(this.path);
610 return new NoResourceSchedulingRule(getPath());
619 public boolean hasChildren() throws JavaModelException {
620 // if I am not open, return true to avoid opening (case of a Java
621 // project, a compilation unit or a class file).
622 // also see https://bugs.eclipse.org/bugs/show_bug.cgi?id=52474
623 Object elementInfo = JavaModelManager.getJavaModelManager().getInfo(
625 if (elementInfo instanceof JavaElementInfo) {
626 return ((JavaElementInfo) elementInfo).getChildren().length > 0;
633 * Returns the hash code for this Java element. By default, the hash code
634 * for an element is a combination of its name and parent's hash code.
635 * Elements with other requirements must override this method.
637 public int hashCode() {
638 if (this.parent == null)
639 return super.hashCode();
640 return Util.combineHashCodes(this.name.hashCode(), this.parent
645 * Returns true if this element is an ancestor of the given element,
648 public boolean isAncestorOf(IJavaElement e) {
649 IJavaElement parentElement = e.getParent();
650 while (parentElement != null && !parentElement.equals(this)) {
651 parentElement = parentElement.getParent();
653 return parentElement != null;
659 public boolean isReadOnly() {
666 public boolean isStructureKnown() throws JavaModelException {
667 return ((JavaElementInfo) getElementInfo()).isStructureKnown();
671 * Creates and returns and not present exception for this element.
673 protected JavaModelException newNotPresentException() {
674 return new JavaModelException(new JavaModelStatus(
675 IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST, this));
679 * Opens this element and all parents that are not already open.
681 * @exception JavaModelException
682 * this element is not present or accessible
684 // protected void openHierarchy() throws JavaModelException {
685 // if (this instanceof IOpenable) {
686 // ((Openable) this).openWhenClosed(null);
688 // Openable openableParent = (Openable)getOpenableParent();
689 // if (openableParent != null) {
690 // JavaElementInfo openableParentInfo = (JavaElementInfo)
691 // JavaModelManager.getJavaModelManager().getInfo((IJavaElement)
693 // if (openableParentInfo == null) {
694 // openableParent.openWhenClosed(null);
696 // throw newNotPresentException();
702 * This element has just been opened. Do any necessary setup.
704 protected void opening(Object info) {
708 * Opens an <code> Openable </code> that is known to be closed (no check for
709 * <code> isOpen() </code> ). Returns the created element info.
711 protected Object openWhenClosed(Object info, IProgressMonitor monitor)
712 throws JavaModelException {
713 JavaModelManager manager = JavaModelManager.getJavaModelManager();
714 boolean hadTemporaryCache = manager.hasTemporaryCache();
716 HashMap newElements = manager.getTemporaryCache();
717 generateInfos(info, newElements, monitor);
719 info = newElements.get(this);
721 if (info == null) { // a source ref element could not be opened
722 // close any buffer that was opened for the openable parent
723 Iterator iterator = newElements.keySet().iterator();
724 while (iterator.hasNext()) {
725 IJavaElement element = (IJavaElement) iterator.next();
726 if (element instanceof Openable) {
727 ((Openable) element).closeBuffer();
730 throw newNotPresentException();
732 if (!hadTemporaryCache) {
733 manager.putInfos(this, newElements);
736 if (!hadTemporaryCache) {
737 manager.resetTemporaryCache();
745 public String readableName() {
746 return this.getElementName();
750 * Removes all cached info from the Java Model, including all children, but
751 * does not close this element.
753 // protected void removeInfo() {
754 // Object info = JavaModelManager.getJavaModelManager().peekAtInfo(this);
755 // if (info != null) {
756 // if (this instanceof IParent) {
757 // IJavaElement[] children = ((JavaElementInfo)info).getChildren();
758 // for (int i = 0, size = children.length; i < size; ++i) {
759 // JavaElement child = (JavaElement) children[i];
760 // child.removeInfo();
763 // JavaModelManager.getJavaModelManager().removeInfo(this);
767 // * Returns a copy of this element rooted at the given project.
769 // public abstract IJavaElement rootedAt(IJavaProject project);
771 * Runs a Java Model Operation
773 public static void runOperation(JavaModelOperation operation,
774 IProgressMonitor monitor) throws JavaModelException {
776 if (operation.isReadOnly()
777 || ResourcesPlugin.getWorkspace().isTreeLocked()) {
778 operation.run(monitor);
780 // use IWorkspace.run(...) to ensure that a build will be done
782 ResourcesPlugin.getWorkspace().run(operation, monitor);
784 } catch (CoreException ce) {
785 if (ce instanceof JavaModelException) {
786 throw (JavaModelException) ce;
788 if (ce.getStatus().getCode() == IResourceStatus.OPERATION_FAILED) {
789 Throwable e = ce.getStatus().getException();
790 if (e instanceof JavaModelException) {
791 throw (JavaModelException) e;
794 throw new JavaModelException(ce);
800 * Sets the occurrence count of the handle.
802 protected void setOccurrenceCount(int count) {
803 occurrenceCount = count;
806 protected String tabString(int tab) {
807 StringBuffer buffer = new StringBuffer();
808 for (int i = tab; i > 0; i--)
809 buffer.append(" "); //$NON-NLS-1$
810 return buffer.toString();
816 public String toDebugString() {
817 StringBuffer buffer = new StringBuffer();
818 this.toStringInfo(0, buffer, NO_INFO);
819 return buffer.toString();
825 public String toString() {
826 StringBuffer buffer = new StringBuffer();
828 return buffer.toString();
834 protected void toStringName(StringBuffer buffer) {
835 buffer.append(getElementName());
836 if (this.occurrenceCount > 1) {
837 buffer.append("#"); //$NON-NLS-1$
838 buffer.append(this.occurrenceCount);
845 protected void toString(int tab, StringBuffer buffer) {
846 // Object info = this.toStringInfo(tab, buffer);
849 this.toStringAncestors(buffer);
851 this.toStringChildren(tab, buffer, info);
857 public String toStringWithAncestors() {
858 StringBuffer buffer = new StringBuffer();
859 this.toStringInfo(0, buffer, NO_INFO);
860 this.toStringAncestors(buffer);
861 return buffer.toString();
867 protected void toStringAncestors(StringBuffer buffer) {
868 JavaElement parent = (JavaElement) this.getParent();
869 if (parent != null && parent.getParent() != null) {
870 buffer.append(" [in "); //$NON-NLS-1$
871 parent.toStringInfo(0, buffer, NO_INFO);
872 parent.toStringAncestors(buffer);
873 buffer.append("]"); //$NON-NLS-1$
880 protected void toStringChildren(int tab, StringBuffer buffer, Object info) {
881 if (info == null || !(info instanceof JavaElementInfo))
883 IJavaElement[] children = ((JavaElementInfo) info).getChildren();
884 for (int i = 0; i < children.length; i++) {
885 buffer.append("\n"); //$NON-NLS-1$
886 ((JavaElement) children[i]).toString(tab + 1, buffer);
893 // public Object toStringInfo(int tab, StringBuffer buffer) {
894 // Object info = JavaModelManager.getJavaModelManager().peekAtInfo(this);
895 // this.toStringInfo(tab, buffer, info);
901 protected void toStringInfo(int tab, StringBuffer buffer, Object info) {
902 buffer.append(this.tabString(tab));
903 buffer.append(getElementName());
905 buffer.append(" (not open)"); //$NON-NLS-1$