/******************************************************************************* * Copyright (c) 2000, 2004 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.erlide.engine.internal.model.root; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Set; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.PlatformObject; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.erlide.engine.ErlangEngine; import org.erlide.engine.internal.model.cache.ErlModelCache; import org.erlide.engine.model.ErlElementKind; import org.erlide.engine.model.ErlModelException; import org.erlide.engine.model.IErlElement; import org.erlide.engine.model.IErlElementVisitor; import org.erlide.engine.model.IParent; import org.erlide.engine.model.root.IOpenable; import com.google.common.base.Objects; import com.google.common.collect.Lists; /** * Root of Erlang element handle hierarchy. * * @see IErlElement */ public abstract class ErlElement extends PlatformObject implements IErlElement, IParent, Cloneable { /** * This element's parent, or <code>null</code> if this element does not have a parent. */ private final IParent fParent; private final List<IErlElement> fChildren = Lists.newArrayList(); /** * 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 Erlang element with the given parent element and name. * * @param parent * The parent of Erlang element * @param name * The name of Erlang element * * @throws IllegalArgumentException * if the type is not one of the valid Erlang element type constants * */ protected ErlElement(final IParent parent, final String name) { fParent = parent; fName = name; } /** * This element is being closed. Do any necessary cleanup. * * @throws ErlModelException */ protected void closing(final Object info) throws ErlModelException { for (final IErlElement e : getChildren()) { if (e instanceof ErlElement) { final ErlElement ee = (ErlElement) e; ee.closing(info); } } } /** * Returns true if this handle represents the same Erlang 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 */ @Override public boolean equals(final Object o) { if (this == o) { return true; } if (o == null) { return false; } // Erlang model parent is null if (fParent == null) { return super.equals(o); } if (o instanceof ErlElement) { // WHY OH WHY?!?!?!? This was a tough // bug (jc) // assume instanceof check is done in subclass final ErlElement other = (ErlElement) o; return fName.equals(other.fName) && fParent.equals(other.fParent); } return false; } /** * @see IErlElement */ @Override public boolean exists() { return true; } /** * @see IErlElement */ @Override public IErlElement getAncestorOfKind(final ErlElementKind kind) { IErlElement element = this; while (true) { if (element.getKind() == kind) { return element; } final IParent parent = element.getParent(); if (parent instanceof IErlElement) { element = (IErlElement) parent; } else { break; } } return null; } /** * @see IErlElement */ @Override public String getName() { return fName; } /** * 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; } /** * @see IErlElement */ @Override public IParent getParent() { return fParent; } static class NoResourceSchedulingRule implements ISchedulingRule { public IPath fPath; public NoResourceSchedulingRule(final IPath path) { fPath = path; } @Override public boolean contains(final ISchedulingRule rule) { if (rule instanceof NoResourceSchedulingRule) { return fPath.isPrefixOf(((NoResourceSchedulingRule) rule).fPath); } return false; } @Override public boolean isConflicting(final ISchedulingRule rule) { if (rule instanceof NoResourceSchedulingRule) { final IPath otherPath = ((NoResourceSchedulingRule) rule).fPath; return fPath.isPrefixOf(otherPath) || otherPath.isPrefixOf(fPath); } return false; } } /** * @see IParent */ @Override public boolean hasChildren() { synchronized (getModelLock()) { return !internalGetChildren().isEmpty(); } } @Override public boolean hasChildrenOfKind(final ErlElementKind... kinds) { synchronized (getModelLock()) { for (final ErlElementKind kind : kinds) { for (final IErlElement child : internalGetChildren()) { if (child.getKind() == kind) { return true; } } } } return false; } /** * Returns the hash code for this Erlang 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. */ @Override public int hashCode() { if (fParent == null) { return super.hashCode(); } return Objects.hashCode(fName, fParent); } /** * @see IErlElement */ @Override public boolean isReadOnly() { return false; } /** */ public String readableName() { return getName(); } protected String tabString(final int tab) { return " "; // final StringBuilder buffer = new StringBuilder(); // for (int i = tab; i > 0; i--) { // buffer.append(" "); //$NON-NLS-1$ // } // return buffer.toString(); } /** * Debugging purposes */ public String toDebugString() { final StringBuilder buffer = new StringBuilder(); this.toStringInfo(0, buffer, NO_INFO); return buffer.toString(); } @Override public String toString() { final StringBuilder buffer = new StringBuilder(); toString(0, buffer); return buffer.toString(); } /** * Debugging purposes */ protected void toString(final int tab, final StringBuilder buffer) { final Object info = this.toStringInfo(tab, buffer); if (tab == 0) { toStringAncestors(buffer); } toStringChildren(tab, buffer, info); } /** * Debugging purposes */ @Override public String toStringWithAncestors() { final StringBuilder buffer = new StringBuilder(); this.toStringInfo(0, buffer, NO_INFO); toStringAncestors(buffer); return buffer.toString(); } /** * Debugging purposes */ protected void toStringAncestors(final StringBuilder buffer) { final IParent parent = getParent(); if (parent != null) { if (parent instanceof ErlElement) { final ErlElement parentElement = (ErlElement) parent; buffer.append("[> "); //$NON-NLS-1$ parentElement.toStringInfo(0, buffer, NO_INFO); parentElement.toStringAncestors(buffer); } buffer.append("] "); //$NON-NLS-1$ } } /** * Debugging purposes */ protected void toStringChildren(final int tab, final StringBuilder buffer, final Object info) { if (!(info instanceof ErlElement)) { return; } if (getChildCount() > 0) { buffer.append("{"); int i = 0; try { for (final IErlElement element : getChildren()) { ((ErlElement) element).toString(tab + 1, buffer); buffer.append(","); //$NON-NLS-1$ if (++i > 3) { buffer.append("..."); break; } } } catch (final ErlModelException e) { } buffer.deleteCharAt(buffer.length() - 1); buffer.append("}"); } } /** * Debugging purposes */ public Object toStringInfo(final int tab, final StringBuilder buffer) { this.toStringInfo(tab, buffer, this); return this; } /** * Debugging purposes */ protected void toStringInfo(final int tab, final StringBuilder buffer, final Object info) { buffer.append(tabString(tab)); buffer.append(getName()); if (info == null) { buffer.append(" (not open)"); //$NON-NLS-1$ } } /** * Is the structure of this element known * * @see IErlElement#isStructureKnown() */ protected boolean structureKnown = false; @Override public void clearCaches() { } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public List<IErlElement> getChildren() throws ErlModelException { synchronized (getModelLock()) { return Collections .unmodifiableList(Lists.newArrayList(internalGetChildren())); } } public List<IErlElement> internalGetChildren() { return fChildren; } @Override public int getChildCount() { synchronized (getModelLock()) { return internalGetChildren().size(); } } /** * Returns a collection of (immediate) children of this node of the specified type. * * @param type * - one of the constants defined by IErlElement */ @Override public List<IErlElement> getChildrenOfKind(final ErlElementKind... kinds) throws ErlModelException { final List<IErlElement> result = Lists.newArrayList(); synchronized (getModelLock()) { for (final ErlElementKind kind : kinds) { for (final IErlElement element : internalGetChildren()) { if (element.getKind() == kind) { result.add(element); } } } } return result; } @Override public IErlElement getChildNamed(final String name) { return getChildNamed(this, name); } @Override public IErlElement getChildWithResource(final IResource rsrc) { return getChildWithResource(this, rsrc); } /** * Returns <code>true</code> if this child is in my children collection */ protected boolean includesChild(final IErlElement child) { synchronized (getModelLock()) { return internalGetChildren().contains(child); } } /** * @see IErlElement#isStructureKnown() */ @Override public boolean isStructureKnown() { return structureKnown; } @Override public void removeChild(final IErlElement child) { synchronized (getModelLock()) { clearCaches(); fChildren.remove(child); } } @Override public void addChild(final IErlElement child) { synchronized (getModelLock()) { clearCaches(); fChildren.add(child); } } @Override public void setChildren(final Collection<? extends IErlElement> children) { synchronized (getModelLock()) { clearCaches(); fChildren.clear(); if (children != null) { fChildren.addAll(children); } } } public void setStructureKnown(final boolean newStructureKnown) { structureKnown = newStructureKnown; } @Override public void resourceChanged(final IResourceDelta delta) { setStructureKnown(false); } private static IErlElement getChildNamed(final ErlElement parent, final String name) { synchronized (parent.getModelLock()) { for (final IErlElement child : parent.internalGetChildren()) { if (child.getName().equals(name)) { return child; } } } return null; } protected Object getModelLock() { return ErlangEngine.getInstance().getModel().getModelLock(); } private static IErlElement getChildWithResource(final ErlElement parent, final IResource rsrc) { synchronized (parent.getModelLock()) { for (final IErlElement child : parent.internalGetChildren()) { if (rsrc.equals(child.getResource())) { return child; } } } return null; } @Override public final void accept(final IErlElementVisitor visitor, final Set<AcceptFlags> flags, final ErlElementKind leafKind) throws ErlModelException { synchronized (getModelLock()) { internalAccept(visitor, flags, leafKind); } } private final void internalAccept(final IErlElementVisitor visitor, final Set<AcceptFlags> flags, final ErlElementKind leafKind) throws ErlModelException { if (getKind() == leafKind) { visitor.visit(this); } else { boolean visitChildren = true; if (!flags.contains(AcceptFlags.LEAFS_ONLY) && !flags.contains(AcceptFlags.CHILDREN_FIRST)) { visitChildren = visitor.visit(this); } if (visitChildren) { for (final IErlElement child : internalGetChildren()) { child.accept(visitor, flags, leafKind); } } if (!flags.contains(AcceptFlags.LEAFS_ONLY) && flags.contains(AcceptFlags.CHILDREN_FIRST)) { visitor.visit(this); } } } /** * Return my corresponding resource. Overridden in IErlModule, IErlFolder and * IErlProject */ @Override public IResource getCorrespondingResource() { return null; } @Override public IResource getResource() { if (fParent instanceof IErlElement) { final IErlElement parentElement = (IErlElement) fParent; return parentElement.getResource(); } return null; } @Override public String getFilePath() { return null; } @Override public void dispose() { // if (!LOCAL_CHILDREN) { // getModel().setChildrenOf(this, null); // } } protected ErlModelCache getModelCache() { return ErlModelCache.getDefault(); } }