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 implements IJavaElement {
54 public static final char JEM_JAVAPROJECT= '=';
55 public static final char JEM_PACKAGEFRAGMENTROOT= Path.SEPARATOR;
56 public static final char JEM_PACKAGEFRAGMENT= '<';
57 public static final char JEM_FIELD= '^';
58 public static final char JEM_METHOD= '~';
59 public static final char JEM_INITIALIZER= '|';
60 public static final char JEM_COMPILATIONUNIT= '{';
61 public static final char JEM_CLASSFILE= '(';
62 public static final char JEM_TYPE= '[';
63 public static final char JEM_PACKAGEDECLARATION= '%';
64 public static final char JEM_IMPORTDECLARATION= '#';
67 * A count to uniquely identify this element in the case
68 * that a duplicate named element exists. For example, if
69 * there are two fields in a compilation unit with the
70 * same name, the occurrence count is used to distinguish
71 * them. The occurrence count starts at 1 (thus the first
72 * occurrence is occurrence 1, not occurrence 0).
74 protected int occurrenceCount = 1;
78 * This element's type - one of the constants defined
79 * in IJavaLanguageElementTypes.
81 //protected int fLEType = 0;
84 * This element's parent, or <code>null</code> if this
85 * element does not have a parent.
87 protected IJavaElement parent;
90 * This element's name, or an empty <code>String</code> if this
91 * element 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
99 * the given parent element and name.
101 * @param parent The parent of java element
102 * @param name The name of java element
104 * @exception IllegalArgumentException if the type is not one of the valid
105 * Java element type constants
108 protected JavaElement(JavaElement parent, String name) throws IllegalArgumentException {
109 this.parent = parent;
115 public void close() throws JavaModelException {
116 JavaModelManager.getJavaModelManager().removeInfoAndChildren(this);
119 * This element is being closed. Do any necessary cleanup.
121 protected abstract void closing(Object info) throws JavaModelException;
123 * Returns a new element info for this element.
125 protected abstract Object createElementInfo();
127 * Returns true if this handle represents the same Java element
128 * as the given handle. By default, two handles represent the same
129 * element if they are identical or if they represent the same type
130 * of element, have equal names, parents, and occurrence counts.
132 * <p>If a subclass has other requirements for equality, this method
133 * must be overridden.
137 public boolean equals(Object o) {
139 if (this == o) return true;
141 // Java model parent is null
142 if (this.parent == null) return super.equals(o);
144 // assume instanceof check is done in subclass
145 JavaElement other = (JavaElement) o;
146 return this.occurrenceCount == other.occurrenceCount &&
147 this.name.equals(other.name) &&
148 this.parent.equals(other.parent);
151 * Returns true if this <code>JavaElement</code> is equivalent to the given
152 * <code>IDOMNode</code>.
154 protected boolean equalsDOMNode(IDOMNode node) throws JavaModelException {
160 public boolean exists() {
165 } catch (JavaModelException e) {
171 * Returns the <code>IDOMNode</code> that corresponds to this <code>JavaElement</code>
172 * or <code>null</code> if there is no corresponding node.
174 public IDOMNode findNode(IDOMCompilationUnit dom) {
175 int type = getElementType();
176 if (type == IJavaElement.COMPILATION_UNIT ||
177 type == IJavaElement.FIELD ||
178 type == IJavaElement.IMPORT_DECLARATION ||
179 type == IJavaElement.INITIALIZER ||
180 type == IJavaElement.METHOD ||
181 type == IJavaElement.PACKAGE_DECLARATION ||
182 type == IJavaElement.TYPE) {
183 ArrayList path = new ArrayList();
184 IJavaElement element = this;
185 while (element != null && element.getElementType() != IJavaElement.COMPILATION_UNIT) {
186 if (element.getElementType() != IJavaElement.IMPORT_CONTAINER) {
187 // the DOM does not have import containers, so skip them
188 path.add(0, element);
190 element = element.getParent();
192 if (path.size() == 0) {
194 if (equalsDOMNode(dom)) {
199 } catch(JavaModelException e) {
203 return ((JavaElement) path.get(0)).followPath(path, 0, dom.getFirstChild());
210 protected IDOMNode followPath(ArrayList path, int position, IDOMNode node) {
213 if (equalsDOMNode(node)) {
214 if (position == (path.size() - 1)) {
217 if (node.getFirstChild() != null) {
219 return ((JavaElement)path.get(position)).followPath(path, position, node.getFirstChild());
224 } else if (node.getNextNode() != null) {
225 return followPath(path, position, node.getNextNode());
229 } catch (JavaModelException e) {
237 public IJavaElement getAncestor(int ancestorType) {
239 IJavaElement element = this;
240 while (element != null) {
241 if (element.getElementType() == ancestorType) return element;
242 element= element.getParent();
247 * Generates the element infos for this element, its ancestors (if they are not opened) and its children (if it is an Openable).
248 * Puts the newly created element info in the given map.
250 protected abstract void generateInfos(Object info, HashMap newElements, IProgressMonitor pm) throws JavaModelException;
255 public IJavaElement[] getChildren() throws JavaModelException {
256 return ((JavaElementInfo)getElementInfo()).getChildren();
259 * Returns a collection of (immediate) children of this node of the
262 * @param type - one of constants defined by IJavaLanguageElementTypes
264 public ArrayList getChildrenOfType(int type) throws JavaModelException {
265 IJavaElement[] children = getChildren();
266 int size = children.length;
267 ArrayList list = new ArrayList(size);
268 for (int i = 0; i < size; ++i) {
269 JavaElement elt = (JavaElement)children[i];
270 if (elt.getElementType() == type) {
279 // public IClassFile getClassFile() {
285 public ICompilationUnit getCompilationUnit() {
289 * Returns the info for this handle.
290 * If this element is not already open, it and all of its parents are opened.
291 * Does not return null.
292 * NOTE: BinaryType infos are NOT rooted under JavaElementInfo.
293 * @exception JavaModelException if the element is not present or not accessible
295 public Object getElementInfo() throws JavaModelException {
296 return getElementInfo(null);
299 * Returns the info for this handle.
300 * If this element is not already open, it and all of its parents are opened.
301 * Does not return null.
302 * NOTE: BinaryType infos are NOT rooted under JavaElementInfo.
303 * @exception JavaModelException if the element is not present or not accessible
305 public Object getElementInfo(IProgressMonitor monitor) throws JavaModelException {
307 JavaModelManager manager = JavaModelManager.getJavaModelManager();
308 Object info = manager.getInfo(this);
309 if (info != null) return info;
310 return openWhenClosed(createElementInfo(), monitor);
315 public String getElementName() {
322 public String getHandleIdentifier() {
323 return getHandleMemento();
326 * @see JavaElement#getHandleMemento()
328 public String getHandleMemento(){
329 StringBuffer buff= new StringBuffer(((JavaElement)getParent()).getHandleMemento());
330 buff.append(getHandleMementoDelimiter());
331 buff.append(getElementName());
332 return buff.toString();
335 * Returns the <code>char</code> that marks the start of this handles
336 * contribution to a memento.
338 protected abstract char getHandleMementoDelimiter();
342 public IJavaModel getJavaModel() {
343 IJavaElement current = this;
345 if (current instanceof IJavaModel) return (IJavaModel) current;
346 } while ((current = current.getParent()) != null);
353 public IJavaProject getJavaProject() {
354 IJavaElement current = this;
356 if (current instanceof IJavaProject) return (IJavaProject) current;
357 } while ((current = current.getParent()) != null);
361 * Returns the occurrence count of the handle.
363 protected int getOccurrenceCount() {
364 return occurrenceCount;
369 public IOpenable getOpenable() {
370 return this.getOpenableParent();
373 * Return the first instance of IOpenable in the parent
374 * hierarchy of this element.
376 * <p>Subclasses that are not IOpenable's must override this method.
378 public IOpenable getOpenableParent() {
380 return (IOpenable)parent;
385 public IJavaElement getParent() {
390 * @see IJavaElement#getPrimaryElement()
392 public IJavaElement getPrimaryElement() {
393 return getPrimaryElement(true);
396 * Returns the primary element. If checkOwner, and the cu owner is primary,
397 * return this element.
399 public IJavaElement getPrimaryElement(boolean checkOwner) {
403 * Returns the element that is located at the given source position
404 * in this element. This is a helper method for <code>ICompilationUnit#getElementAt</code>,
405 * and only works on compilation units and types. The position given is
406 * known to be within this element's source range already, and if no finer
407 * grained element is found at the position, this element is returned.
409 protected IJavaElement getSourceElementAt(int position) throws JavaModelException {
410 if (this instanceof ISourceReference) {
411 IJavaElement[] children = getChildren();
413 for (i = 0; i < children.length; i++) {
414 IJavaElement aChild = children[i];
415 if (aChild instanceof SourceRefElement) {
416 SourceRefElement child = (SourceRefElement) children[i];
417 ISourceRange range = child.getSourceRange();
418 if (position < range.getOffset() + range.getLength() && position >= range.getOffset()) {
419 if (child instanceof IParent) {
420 return child.getSourceElementAt(position);
429 Assert.isTrue(false);
434 * Returns the SourceMapper facility for this element, or
435 * <code>null</code> if this element does not have a
438 // public SourceMapper getSourceMapper() {
439 // return ((JavaElement)getParent()).getSourceMapper();
442 * @see org.eclipse.jdt.core.IJavaElement#getSchedulingRule()
444 public ISchedulingRule getSchedulingRule() {
445 IResource resource = getResource();
446 if (resource == null) {
447 class NoResourceSchedulingRule implements ISchedulingRule {
449 public NoResourceSchedulingRule(IPath path) {
452 public boolean contains(ISchedulingRule rule) {
453 if (rule instanceof NoResourceSchedulingRule) {
454 return this.path.isPrefixOf(((NoResourceSchedulingRule)rule).path);
459 public boolean isConflicting(ISchedulingRule rule) {
460 if (rule instanceof NoResourceSchedulingRule) {
461 IPath otherPath = ((NoResourceSchedulingRule)rule).path;
462 return this.path.isPrefixOf(otherPath) || otherPath.isPrefixOf(this.path);
468 return new NoResourceSchedulingRule(getPath());
476 public boolean hasChildren() throws JavaModelException {
477 // if I am not open, return true to avoid opening (case of a Java project, a compilation unit or a class file).
478 // also see https://bugs.eclipse.org/bugs/show_bug.cgi?id=52474
479 Object elementInfo = JavaModelManager.getJavaModelManager().getInfo(this);
480 if (elementInfo instanceof JavaElementInfo) {
481 return ((JavaElementInfo)elementInfo).getChildren().length > 0;
488 * Returns the hash code for this Java element. By default,
489 * the hash code for an element is a combination of its name
490 * and parent's hash code. Elements with other requirements must
491 * override this method.
493 public int hashCode() {
494 if (this.parent == null) return super.hashCode();
495 return Util.combineHashCodes(this.name.hashCode(), this.parent.hashCode());
499 * Returns true if this element is an ancestor of the given element,
502 protected boolean isAncestorOf(IJavaElement e) {
503 IJavaElement parent= e.getParent();
504 while (parent != null && !parent.equals(this)) {
505 parent= parent.getParent();
507 return parent != null;
513 public boolean isReadOnly() {
519 public boolean isStructureKnown() throws JavaModelException {
520 return ((JavaElementInfo)getElementInfo()).isStructureKnown();
523 * Creates and returns and not present exception for this element.
525 protected JavaModelException newNotPresentException() {
526 return new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST, this));
529 * Opens this element and all parents that are not already open.
531 * @exception JavaModelException this element is not present or accessible
533 // protected void openHierarchy() throws JavaModelException {
534 // if (this instanceof IOpenable) {
535 // ((Openable) this).openWhenClosed(null);
537 // Openable openableParent = (Openable)getOpenableParent();
538 // if (openableParent != null) {
539 // JavaElementInfo openableParentInfo = (JavaElementInfo) JavaModelManager.getJavaModelManager().getInfo((IJavaElement) openableParent);
540 // if (openableParentInfo == null) {
541 // openableParent.openWhenClosed(null);
543 // throw newNotPresentException();
549 * This element has just been opened. Do any necessary setup.
551 protected void opening(Object info) {
554 * Opens an <code>Openable</code> that is known to be closed (no check for <code>isOpen()</code>).
555 * Returns the created element info.
557 protected Object openWhenClosed(Object info, IProgressMonitor monitor) throws JavaModelException {
558 JavaModelManager manager = JavaModelManager.getJavaModelManager();
559 boolean hadTemporaryCache = manager.hasTemporaryCache();
561 HashMap newElements = manager.getTemporaryCache();
562 generateInfos(info, newElements, monitor);
564 info = newElements.get(this);
566 if (info == null) { // a source ref element could not be opened
567 // close any buffer that was opened for the openable parent
568 Iterator iterator = newElements.keySet().iterator();
569 while (iterator.hasNext()) {
570 IJavaElement element = (IJavaElement)iterator.next();
571 if (element instanceof Openable) {
572 ((Openable)element).closeBuffer();
575 throw newNotPresentException();
577 if (!hadTemporaryCache) {
578 manager.putInfos(this, newElements);
581 if (!hadTemporaryCache) {
582 manager.resetTemporaryCache();
589 public String readableName() {
590 return this.getElementName();
593 * Removes all cached info from the Java Model, including all children,
594 * but does not close this element.
596 // protected void removeInfo() {
597 // Object info = JavaModelManager.getJavaModelManager().peekAtInfo(this);
598 // if (info != null) {
599 // if (this instanceof IParent) {
600 // IJavaElement[] children = ((JavaElementInfo)info).getChildren();
601 // for (int i = 0, size = children.length; i < size; ++i) {
602 // JavaElement child = (JavaElement) children[i];
603 // child.removeInfo();
606 // JavaModelManager.getJavaModelManager().removeInfo(this);
610 // * Returns a copy of this element rooted at the given project.
612 // public abstract IJavaElement rootedAt(IJavaProject project);
614 * Runs a Java Model Operation
616 public static void runOperation(JavaModelOperation operation, IProgressMonitor monitor) throws JavaModelException {
618 if (operation.isReadOnly() || ResourcesPlugin.getWorkspace().isTreeLocked()) {
619 operation.run(monitor);
621 // use IWorkspace.run(...) to ensure that a build will be done in autobuild mode
622 ResourcesPlugin.getWorkspace().run(operation, monitor);
624 } catch (CoreException ce) {
625 if (ce instanceof JavaModelException) {
626 throw (JavaModelException)ce;
628 if (ce.getStatus().getCode() == IResourceStatus.OPERATION_FAILED) {
629 Throwable e= ce.getStatus().getException();
630 if (e instanceof JavaModelException) {
631 throw (JavaModelException) e;
634 throw new JavaModelException(ce);
639 * Sets the occurrence count of the handle.
641 protected void setOccurrenceCount(int count) {
642 occurrenceCount = count;
644 protected String tabString(int tab) {
645 StringBuffer buffer = new StringBuffer();
646 for (int i = tab; i > 0; i--)
647 buffer.append(" "); //$NON-NLS-1$
648 return buffer.toString();
653 public String toDebugString() {
654 StringBuffer buffer = new StringBuffer();
655 this.toStringInfo(0, buffer, NO_INFO);
656 return buffer.toString();
661 public String toString() {
662 StringBuffer buffer = new StringBuffer();
664 return buffer.toString();
669 protected void toString(int tab, StringBuffer buffer) {
670 // Object info = this.toStringInfo(tab, buffer);
673 this.toStringAncestors(buffer);
675 this.toStringChildren(tab, buffer, info);
680 public String toStringWithAncestors() {
681 StringBuffer buffer = new StringBuffer();
682 this.toStringInfo(0, buffer, NO_INFO);
683 this.toStringAncestors(buffer);
684 return buffer.toString();
689 protected void toStringAncestors(StringBuffer buffer) {
690 JavaElement parent = (JavaElement)this.getParent();
691 if (parent != null && parent.getParent() != null) {
692 buffer.append(" [in "); //$NON-NLS-1$
693 parent.toStringInfo(0, buffer, NO_INFO);
694 parent.toStringAncestors(buffer);
695 buffer.append("]"); //$NON-NLS-1$
701 protected void toStringChildren(int tab, StringBuffer buffer, Object info) {
702 if (info == null || !(info instanceof JavaElementInfo)) return;
703 IJavaElement[] children = ((JavaElementInfo)info).getChildren();
704 for (int i = 0; i < children.length; i++) {
705 buffer.append("\n"); //$NON-NLS-1$
706 ((JavaElement)children[i]).toString(tab + 1, buffer);
712 // public Object toStringInfo(int tab, StringBuffer buffer) {
713 // Object info = JavaModelManager.getJavaModelManager().peekAtInfo(this);
714 // this.toStringInfo(tab, buffer, info);
720 protected void toStringInfo(int tab, StringBuffer buffer, Object info) {
721 buffer.append(this.tabString(tab));
722 buffer.append(getElementName());
724 buffer.append(" (not open)"); //$NON-NLS-1$