/******************************************************************************* * Copyright © 2000, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * *******************************************************************************/ package org.eclipse.edt.ide.core.internal.model; import java.util.ArrayList; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.PlatformObject; import org.eclipse.edt.ide.core.model.EGLModelException; import org.eclipse.edt.ide.core.model.IClassFile; import org.eclipse.edt.ide.core.model.IEGLElement; import org.eclipse.edt.ide.core.model.IEGLFile; import org.eclipse.edt.ide.core.model.IEGLModel; import org.eclipse.edt.ide.core.model.IEGLModelStatusConstants; import org.eclipse.edt.ide.core.model.IEGLProject; import org.eclipse.edt.ide.core.model.IMember; import org.eclipse.edt.ide.core.model.IOpenable; import org.eclipse.edt.ide.core.model.IParent; import org.eclipse.edt.ide.core.model.ISourceRange; import org.eclipse.edt.ide.core.model.ISourceReference; /** * @author twilson * created Jul 24, 2003 */ public abstract class EGLElement extends PlatformObject implements IEGLElement { public static final char EGLM_EGLPROJECT= '='; public static final char EGLM_PACKAGEFRAGMENTROOT= Path.SEPARATOR; public static final char EGLM_PACKAGEFRAGMENT= '<'; public static final char EGLM_FIELD= '^'; public static final char EGLM_FUNCTION= '~'; public static final char EGLM_INITIALIZER= '|'; public static final char EGLM_EGLFILE= '{'; public static final char EGLM_CLASSFILE= '('; public static final char EGLM_PART= '['; public static final char EGLM_PACKAGEDECLARATION= '%'; public static final char EGLM_IMPORTDECLARATION= '\''; public static final char EGLM_PROPERTYBLOCK= '!'; public static final char EGLM_PROPERTY= '>'; public static final char EGLM_USE= '*'; protected static final EGLElement[] NO_ELEMENTS = new EGLElement[0]; /** * A count to uniquely identify this element in the case * that a duplicate named element exists. For example, if * there are two fields in a compilation unit with the * same name, the occurrence count is used to distinguish * them. The occurrence count starts at 1 (thus the first * occurrence is occurrence 1, not occurrence 0). */ protected int fOccurrenceCount = 1; /** * This element's type - one of the constants defined * in IEGLLanguageElementTypes. */ protected int fLEType = 0; /** * This element's parent, or <code>null</code> if this * element does not have a parent. */ protected IEGLElement fParent; /** * This element's name, or an empty <code>String</code> if this * element does not have a name. */ protected String fName; protected static final Object NO_INFO = new Object(); /** * Constructs a handle for a java element of the specified type, with * the given parent element and name. * * @param type - one of the constants defined in IEGLLanguageElement * * @exception IllegalArgumentException if the type is not one of the valid * EGL element type constants * */ protected EGLElement(int type, IEGLElement parent, String name) throws IllegalArgumentException { if (type < EGL_MODEL || type > PROPERTY) { throw new IllegalArgumentException(EGLModelResources.elementInvalidType); } fLEType= type; fParent= parent; fName= name; } /** * @see IOpenable */ public void close() throws EGLModelException { Object info = EGLModelManager.getEGLModelManager().peekAtInfo(this); if (info != null) { boolean wasVerbose = false; try { if (EGLModelManager.VERBOSE) { System.out.println("CLOSING Element ("+ Thread.currentThread()+"): " + this.toStringWithAncestors()); //$NON-NLS-1$//$NON-NLS-2$ wasVerbose = true; EGLModelManager.VERBOSE = false; } if (this instanceof IParent) { IEGLElement[] children = ((EGLElementInfo) info).getChildren(); for (int i = 0, size = children.length; i < size; ++i) { EGLElement child = (EGLElement) children[i]; child.close(); } } closing(info); EGLModelManager.getEGLModelManager().removeInfo(this); if (wasVerbose) { System.out.println("-> Package cache size = " + EGLModelManager.getEGLModelManager().cache.pkgSize()); //$NON-NLS-1$ System.out.println("-> Openable cache filling ratio = " + EGLModelManager.getEGLModelManager().cache.openableFillingRatio() + "%"); //$NON-NLS-1$//$NON-NLS-2$ } } finally { EGLModelManager.VERBOSE = wasVerbose; } } } /** * This element is being closed. Do any necessary cleanup. */ protected void closing(Object info) throws EGLModelException { } /** * Returns true if this handle represents the same EGL element * as the given handle. By default, two handles represent the same * element if they are identical or if they represent the same type * of element, have equal names, parents, and occurrence counts. * * <p>If a subclass has other requirements for equality, this method * must be overridden. * * @see Object#equals */ public boolean equals(Object o) { if (this == o) return true; // EGL model parent is null if (fParent == null) return super.equals(o); if (o instanceof EGLElement) { EGLElement other = (EGLElement) o; if (fLEType != other.fLEType) return false; return fOccurrenceCount == other.fOccurrenceCount && fName.equals(other.fName) && fParent.equals(other.fParent); } return false; } /* (non-EGLdoc) * @see com.ibm.etools.egl.internal.model.core.IEGLElement#exists() */ public boolean exists() { try { getElementInfo(); return true; } catch (EGLModelException e) { } return false; } /* (non-EGLdoc) * @see com.ibm.etools.egl.internal.model.core.IEGLElement#getAncestor(int) */ public IEGLElement getAncestor(int ancestorType) { IEGLElement element = this; while (element != null) { if (element.getElementType() == ancestorType) return element; element= element.getParent(); } return null; } /** * @see IParent */ public IEGLElement[] getChildren() throws EGLModelException { return ((EGLElementInfo)getElementInfo()).getChildren(); } /** * Returns a collection of (immediate) children of this node of the * specified type. * * @param type - one of constants defined by IEGLLanguageElementTypes */ public ArrayList getChildrenOfType(int type) throws EGLModelException { IEGLElement[] children = getChildren(); int size = children.length; ArrayList list = new ArrayList(size); for (int i = 0; i < size; ++i) { EGLElement elt = (EGLElement)children[i]; if (elt.getElementType() == type) { list.add(elt); } } return list; } /* (non-EGLdoc) * @see com.ibm.etools.egl.internal.model.core.IEGLElement#getCorrespondingResource() */ abstract public IResource getCorrespondingResource() throws EGLModelException; /** * Returns the info for this handle. * If this element is not already open, it and all of its parents are opened. * Does not return null. * @exception EGLModelException if the element is not present or not accessible */ public Object getElementInfo() throws EGLModelException { // workaround to ensure parent project resolved classpath is available to avoid triggering initializers // while the EGLModelManager lock is acquired (can cause deadlocks in clients) IEGLProject project = getEGLProject(); if (project != null && !project.isOpen()) { // TODO: need to revisit, since deadlock could still occur if perProjectInfo is removed concurrent before entering the lock try { project.getResolvedEGLPath(true); // trigger all possible container/variable initialization outside the model lock } catch (EGLModelException e) { // project is not accessible or is not a java project } } // element info creation is done inside a lock on the EGLModelManager EGLModelManager manager; synchronized(manager = EGLModelManager.getEGLModelManager()){ Object info = manager.getInfo(this); if (info == null) { openHierarchy(); info= manager.getInfo(this); if (info == null) { throw newNotPresentException(); } } return info; } } /** * @see IAdaptable */ public String getElementName() { return fName; } /** * @see IEGLElement */ public int getElementType() { return fLEType; } /** * @see IEGLElement */ public String getHandleIdentifier() { return getHandleMemento(); } /** * @see EGLElement#getHandleMemento() */ public String getHandleMemento(){ StringBuffer buff= new StringBuffer(((EGLElement)getParent()).getHandleMemento()); buff.append(getHandleMementoDelimiter()); buff.append(getElementName()); return buff.toString(); } /** * @see IMember */ public IEGLFile getEGLFile() { return null; } /** * @see IMember */ public IClassFile getClassFile() { return null; } /** * Returns the <code>char</code> that marks the start of this handles * contribution to a memento. */ protected abstract char getHandleMementoDelimiter(); /* (non-EGLdoc) * @see com.ibm.etools.egl.internal.model.core.IEGLElement#getEGLModel() */ public IEGLModel getEGLModel() { IEGLElement current = this; do { if (current instanceof IEGLModel) return (IEGLModel) current; } while ((current = current.getParent()) != null); return null; } /* (non-EGLdoc) * @see com.ibm.etools.egl.internal.model.core.IEGLElement#getEGLProject() */ public IEGLProject getEGLProject() { IEGLElement current = this; do { if (current instanceof IEGLProject) return (IEGLProject) current; } while ((current = current.getParent()) != null); return null; } /** * Returns the occurrence count of the handle. */ protected int getOccurrenceCount() { return fOccurrenceCount; } /** * @see IEGLElement */ public IEGLElement getParent() { return fParent; } /* * @see IEGLElement */ public IOpenable getOpenable() { return this.getOpenableParent(); } /** * Return the first instance of IOpenable in the parent * hierarchy of this element. * * <p>Subclasses that are not IOpenable's must override this method. */ public IOpenable getOpenableParent() { return (IOpenable)fParent; } /** * Returns the element that is located at the given source position * in this element. This is a helper method for <code>ICompilationUnit#getElementAt</code>, * and only works on compilation units and types. The position given is * known to be within this element's source range already, and if no finer * grained element is found at the position, this element is returned. */ protected IEGLElement getSourceElementAt(int position) throws EGLModelException { if (this instanceof ISourceReference) { IEGLElement[] children = getChildren(); int i; for (i = 0; i < children.length; i++) { IEGLElement aChild = children[i]; if (aChild instanceof SourceRefElement) { SourceRefElement child = (SourceRefElement) children[i]; ISourceRange range = child.getSourceRange(); if (position < range.getOffset() + range.getLength() && position >= range.getOffset()) { if (child instanceof IParent) { return child.getSourceElementAt(position); } else { return child; } } } } } else { // should not happen Assert.isTrue(false); } return this; } /** * Returns the hash code for this EGL element. By default, * the hash code for an element is a combination of its name * and parent's hash code. Elements with other requirements must * override this method. */ public int hashCode() { if (fParent == null) return super.hashCode(); return Util.combineHashCodes(fName.hashCode(), fParent.hashCode()); } /** * Returns true if this element is an ancestor of the given element, * otherwise false. */ protected boolean isAncestorOf(IEGLElement e) { IEGLElement parent= e.getParent(); while (parent != null && !parent.equals(this)) { parent= parent.getParent(); } return parent != null; } /* (non-EGLdoc) * @see com.ibm.etools.egl.internal.model.core.IEGLElement#getPath() */ abstract public IPath getPath(); /* (non-EGLdoc) * @see com.ibm.etools.egl.internal.model.core.IEGLElement#getResource() */ abstract public IResource getResource(); /* (non-EGLdoc) * @see com.ibm.etools.egl.internal.model.core.IEGLElement#getUnderlyingResource() */ abstract public IResource getUnderlyingResource() throws EGLModelException; /* (non-EGLdoc) * @see com.ibm.etools.egl.internal.model.core.IEGLElement#isReadOnly() */ public boolean isReadOnly() { return false; } /** * @see IEGLElement */ public boolean isStructureKnown() throws EGLModelException { return ((EGLElementInfo)getElementInfo()).isStructureKnown(); } /** * Creates and returns and not present exception for this element. */ protected EGLModelException newNotPresentException() { return new EGLModelException(new EGLModelStatus(IEGLModelStatusConstants.ELEMENT_DOES_NOT_EXIST, this)); } /** * Opens this element and all parents that are not already open. * * @exception EGLModelException this element is not present or accessible */ protected void openHierarchy() throws EGLModelException { if (this instanceof IOpenable) { ((Openable) this).openWhenClosed(null); } else { Openable openableParent = (Openable)getOpenableParent(); if (openableParent != null) { EGLElementInfo openableParentInfo = (EGLElementInfo) EGLModelManager.getEGLModelManager().getInfo((IEGLElement) openableParent); if (openableParentInfo == null) { openableParent.openWhenClosed(null); } else { throw newNotPresentException(); } } } } /** * This element has just been opened. Do any necessary setup. */ protected void opening(Object info) { } /** */ public String readableName() { return this.getElementName(); } /** * Removes all cached info from the EGL Model, including all children, * but does not close this element. */ protected void removeInfo() { Object info = EGLModelManager.getEGLModelManager().peekAtInfo(this); if (info != null) { if (this instanceof IParent) { IEGLElement[] children = ((EGLElementInfo)info).getChildren(); for (int i = 0, size = children.length; i < size; ++i) { EGLElement child = (EGLElement) children[i]; child.removeInfo(); } } EGLModelManager.getEGLModelManager().removeInfo(this); } } /** * Sets the occurrence count of the handle. */ protected void setOccurrenceCount(int count) { fOccurrenceCount = count; } protected String tabString(int tab) { StringBuffer buffer = new StringBuffer(); for (int i = tab; i > 0; i--) buffer.append(" "); //$NON-NLS-1$ return buffer.toString(); } /** * Debugging purposes */ public String toDebugString() { StringBuffer buffer = new StringBuffer(); this.toStringInfo(0, buffer, NO_INFO); return buffer.toString(); } /** * Debugging purposes */ public String toString() { StringBuffer buffer = new StringBuffer(); toString(0, buffer); return buffer.toString(); } /** * Debugging purposes */ protected void toString(int tab, StringBuffer buffer) { Object info = this.toStringInfo(tab, buffer); if (tab == 0) { this.toStringAncestors(buffer); } this.toStringChildren(tab, buffer, info); } /** * Debugging purposes */ public String toStringWithAncestors() { StringBuffer buffer = new StringBuffer(); this.toStringInfo(0, buffer, NO_INFO); this.toStringAncestors(buffer); return buffer.toString(); } /** * Debugging purposes */ protected void toStringAncestors(StringBuffer buffer) { EGLElement parent = (EGLElement)this.getParent(); if (parent != null && parent.getParent() != null) { buffer.append(" [in "); //$NON-NLS-1$ parent.toStringInfo(0, buffer, NO_INFO); parent.toStringAncestors(buffer); buffer.append("]"); //$NON-NLS-1$ } } /** * Debugging purposes */ protected void toStringChildren(int tab, StringBuffer buffer, Object info) { if (info == null || !(info instanceof EGLElementInfo)) return; IEGLElement[] children = ((EGLElementInfo)info).getChildren(); for (int i = 0; i < children.length; i++) { buffer.append("\n"); //$NON-NLS-1$ ((EGLElement)children[i]).toString(tab + 1, buffer); } } /** * Debugging purposes */ public Object toStringInfo(int tab, StringBuffer buffer) { Object info = EGLModelManager.getEGLModelManager().peekAtInfo(this); this.toStringInfo(tab, buffer, info); return info; } /** * Debugging purposes */ protected void toStringInfo(int tab, StringBuffer buffer, Object info) { buffer.append(this.tabString(tab)); buffer.append(getElementName()); if (info == null) { buffer.append(" (not open)"); //$NON-NLS-1$ } } /** * Returns a copy of this element rooted at the given project. */ public abstract IEGLElement rootedAt(IEGLProject project); /** * Returns the SourceMapper facility for this element, or * <code>null</code> if this element does not have a * SourceMapper. */ public SourceMapper getSourceMapper() { return ((EGLElement)getParent()).getSourceMapper(); } }