/* * Created on Jan 13, 2005 * */ package org.rubypeople.rdt.internal.core; import java.util.HashMap; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.jruby.ast.Node; import org.rubypeople.rdt.core.IBuffer; import org.rubypeople.rdt.core.IOpenable; import org.rubypeople.rdt.core.IRubyElement; import org.rubypeople.rdt.core.IRubyScript; import org.rubypeople.rdt.core.ISourceRange; import org.rubypeople.rdt.core.ISourceReference; import org.rubypeople.rdt.core.RubyModelException; import org.rubypeople.rdt.core.WorkingCopyOwner; import org.rubypeople.rdt.internal.core.util.DOMFinder; import org.rubypeople.rdt.internal.core.util.MementoTokenizer; /** * @author cawilliams * */ public abstract class SourceRefElement extends RubyElement implements ISourceReference { /* * 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). */ public int occurrenceCount = 1; /** * @param name */ public SourceRefElement(RubyElement parent) { super(parent); } /* * (non-Javadoc) * * @see org.rubypeople.rdt.core.IMember#getRubyScript() */ public IRubyScript getRubyScript() { return ((RubyElement) getParent()).getRubyScript(); } /** * Returns the <code>ASTNode</code> that corresponds to this <code>RubyElement</code> * or <code>null</code> if there is no corresponding node. */ public Node findNode(Node ast) { DOMFinder finder = new DOMFinder(ast, this); try { return finder.search(); } catch (RubyModelException e) { // receiver doesn't exist return null; } } /** * Return the first instance of IOpenable in the hierarchy of this type * (going up the hierarchy from this type); */ public IOpenable getOpenableParent() { IRubyElement current = getParent(); while (current != null) { if (current instanceof IOpenable) { return (IOpenable) current; } current = current.getParent(); } return null; } /** * Elements within compilation units and class files have no * corresponding resource. * * @see IRubyElement */ public IResource getCorrespondingResource() throws RubyModelException { if (!exists()) throw newNotPresentException(); return null; } /** * @see IRubyElement */ public boolean isStructureKnown() throws RubyModelException { // structure is always known inside an openable return true; } /** * @throws RubyModelException * @see ISourceReference */ public String getSource() throws RubyModelException { IOpenable openable = getOpenableParent(); IBuffer buffer = openable.getBuffer(); if (buffer == null) { return null; } ISourceRange range = getSourceRange(); int offset = range.getOffset(); int length = range.getLength(); if (offset == -1 || length == 0) { return null; } try { return buffer.getText(offset, length); } catch (RuntimeException e) { return null; } } /** * @see ISourceReference */ public ISourceRange getSourceRange() throws RubyModelException { SourceRefElementInfo info = (SourceRefElementInfo) getElementInfo(); return info.getSourceRange(); } /* * @see IRubyElement */ public IPath getPath() { return this.getParent().getPath(); } public IResource getResource() { return this.getParent().getResource(); } /** * @see IRubyElement */ public IResource getUnderlyingResource() throws RubyModelException { if (!exists()) throw newNotPresentException(); return getParent().getUnderlyingResource(); } /** * This element is being closed. Do any necessary cleanup. */ protected void closing(Object info) throws RubyModelException { // Do any necessary cleanup } /* * @see RubyElement#generateInfos */ protected void generateInfos(Object info, HashMap newElements, IProgressMonitor pm) throws RubyModelException { Openable openableParent = (Openable) getOpenableParent(); if (openableParent == null) return; RubyElementInfo openableParentInfo = (RubyElementInfo) RubyModelManager.getRubyModelManager().getInfo(openableParent); if (openableParentInfo == null) { openableParent.generateInfos(openableParent.createElementInfo(), newElements, pm); } } /** * Returns a new element info for this element. */ protected Object createElementInfo() { return null; // not used for source ref elements } public boolean equals(Object o) { if (!(o instanceof SourceRefElement)) return false; return this.occurrenceCount == ((SourceRefElement) o).occurrenceCount && super.equals(o); } /* * Update the occurence count of the receiver and creates a Ruby element handle from the given memento. * The given working copy owner is used only for compilation unit handles. */ public IRubyElement getHandleUpdatingCountFromMemento(MementoTokenizer memento, WorkingCopyOwner owner) { if (!memento.hasMoreTokens()) return this; this.occurrenceCount = Integer.parseInt(memento.nextToken()); if (!memento.hasMoreTokens()) return this; String token = memento.nextToken(); return getHandleFromMemento(token, memento, owner); } /* * @see RubyElement */ public IRubyElement getHandleFromMemento(String token, MementoTokenizer memento, WorkingCopyOwner workingCopyOwner) { switch (token.charAt(0)) { case JEM_COUNT: return getHandleUpdatingCountFromMemento(memento, workingCopyOwner); } return this; } protected void getHandleMemento(StringBuffer buff) { super.getHandleMemento(buff); if (this.occurrenceCount > 1) { buff.append(JEM_COUNT); buff.append(this.occurrenceCount); } } }