| /******************************************************************************* |
| * Copyright (c) 2002, 2013 GEBIT Gesellschaft fuer EDV-Beratung |
| * und Informatik-Technologien mbH, |
| * Berlin, Duesseldorf, Frankfurt (Germany) 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: |
| * GEBIT Gesellschaft fuer EDV-Beratung und Informatik-Technologien mbH - initial API and implementation |
| * IBM Corporation - bug fixes |
| * John-Mason P. Shackelford (john-mason.shackelford@pearson.com) - bug 49445 |
| *******************************************************************************/ |
| |
| package org.eclipse.ant.internal.ui.model; |
| |
| import java.io.File; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.ant.internal.core.IAntCoreConstants; |
| import org.eclipse.ant.internal.ui.AntImageDescriptor; |
| import org.eclipse.ant.internal.ui.AntUIImages; |
| import org.eclipse.ant.internal.ui.AntUtil; |
| import org.eclipse.ant.internal.ui.IAntUIConstants; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.runtime.IAdaptable; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.jface.resource.ImageDescriptor; |
| import org.eclipse.jface.text.IRegion; |
| import org.eclipse.swt.graphics.Image; |
| |
| import com.ibm.icu.text.MessageFormat; |
| |
| /** |
| * General representation of an Ant buildfile element. |
| * |
| */ |
| public class AntElementNode implements IAdaptable, IAntElement { |
| |
| /** |
| * The offset of the corresponding source. |
| * |
| * @see #getOffset() |
| */ |
| protected int fOffset = -1; |
| |
| /** |
| * The length of the corresponding source. |
| * |
| * @see #getLength() |
| */ |
| protected int fLength = -1; |
| |
| /** |
| * The length of the source to select for this node |
| */ |
| protected int fSelectionLength; |
| |
| /** |
| * The parent node. |
| */ |
| protected AntElementNode fParent; |
| |
| /** |
| * The import node that "imported" this element |
| */ |
| private AntElementNode fImportNode; |
| |
| /** |
| * The child nodes. |
| */ |
| protected List<IAntElement> fChildNodes = null; |
| |
| /** |
| * The (tag-)name of the element. |
| */ |
| protected String fName; |
| |
| /** |
| * Whether this element has been generated as part of an element hierarchy this has problems. This is the severity of the problem. |
| * |
| * @see XMLProblem#NO_PROBLEM |
| * @see XMLProblem#SEVERITY_ERROR |
| * @see XMLProblem#SEVERITY_WARNING |
| * @see XMLProblem#SEVERITY_FATAL_ERROR |
| */ |
| private int fProblemSeverity = AntModelProblem.NO_PROBLEM; |
| |
| private String fProblemMessage = null; |
| |
| /** |
| * The absolute file system path of the file this element is defined within. |
| */ |
| private String fFilePath; |
| |
| /** |
| * Whether this element has been generated from an external entity definition |
| */ |
| private boolean fIsExternal = false; |
| |
| /** |
| * The unique (in the corresponding element tree) path of this element. |
| */ |
| private String fElementPath; |
| |
| /** |
| * The (not necessarily unique) identifier of this element. |
| */ |
| private String fElementIdentifier; |
| |
| /** |
| * The problem associated with this node. May be <code>null</code>. |
| */ |
| private IProblem fProblem; |
| |
| /** |
| * The unique index of this element in it's parents child collection |
| */ |
| private int fIndex = 0; |
| |
| /** |
| * Only used when opening an import element to indicate the location in the imported file |
| */ |
| private int fLine; |
| private int fColumn; |
| |
| /** |
| * Creates an instance with the specified name. |
| */ |
| public AntElementNode(String aName) { |
| fName = aName; |
| } |
| |
| public AntElementNode() { |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ant.internal.ui.model.IAntElement#getName() |
| */ |
| @Override |
| public String getName() { |
| return fName; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ant.internal.ui.model.IAntElement#getLabel() |
| */ |
| @Override |
| public String getLabel() { |
| return getName(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ant.internal.ui.model.IAntElement#getChildNodes() |
| */ |
| @Override |
| public List<IAntElement> getChildNodes() { |
| return fChildNodes; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ant.internal.ui.model.IAntElement#getParentNode() |
| */ |
| @Override |
| public IAntElement getParentNode() { |
| return fParent; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ant.internal.ui.model.IAntElement#getProjectNode() |
| */ |
| @Override |
| public AntProjectNode getProjectNode() { |
| IAntElement projectParent = getParentNode(); |
| while (projectParent != null && !(projectParent instanceof AntProjectNode)) { |
| projectParent = projectParent.getParentNode(); |
| } |
| return (AntProjectNode) projectParent; |
| } |
| |
| /** |
| * Adds the specified element as a child. |
| * <P> |
| * The specified element will have this assigned as its parent. |
| */ |
| public void addChildNode(AntElementNode childElement) { |
| childElement.setParent(this); |
| synchronized (this) { |
| if (fChildNodes == null) { |
| fChildNodes = new ArrayList<IAntElement>(); |
| } |
| fChildNodes.add(childElement); |
| childElement.setIndex(fChildNodes.size() - 1); |
| } |
| } |
| |
| private void setIndex(int index) { |
| fIndex = index; |
| } |
| |
| protected void setParent(AntElementNode node) { |
| fParent = node; |
| } |
| |
| /** |
| * Sets the absolute file system path of the file this element is defined within. |
| */ |
| public void setFilePath(String path) { |
| if (path == null) { |
| return; |
| } |
| URL url = null; |
| try { |
| url = new URL(path); |
| } |
| catch (MalformedURLException e) { |
| fFilePath = path; |
| return; |
| } |
| fFilePath = new Path(new File(url.getPath()).getAbsolutePath()).toString(); |
| } |
| |
| /** |
| * Returns the absolute file system path of the file this element is defined within. Only relevant for nodes that are external |
| * |
| * @see #isExternal() |
| */ |
| public String getFilePath() { |
| return fFilePath; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ant.internal.ui.model.IAntElement#getOffset() |
| */ |
| @Override |
| public int getOffset() { |
| return fOffset; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ant.internal.ui.model.IAntElement#setOffset(int) |
| */ |
| @Override |
| public void setOffset(int anOffset) { |
| fOffset = anOffset; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ant.internal.ui.model.IAntElement#getLength() |
| */ |
| @Override |
| public int getLength() { |
| return fLength; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ant.internal.ui.model.IAntElement#setLength(int) |
| */ |
| @Override |
| public void setLength(int aLength) { |
| fLength = aLength; |
| if (fProblem != null && fProblem instanceof AntModelProblem) { |
| ((AntModelProblem) fProblem).setLength(aLength); |
| } |
| } |
| |
| /** |
| * Returns a string representation of this element. |
| */ |
| @Override |
| public String toString() { |
| return "Ant Element Node: " + getLabel() + " Offset: " + getOffset() + " Length: " + getLength(); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ant.internal.ui.model.IAntElement#isErrorNode() |
| */ |
| @Override |
| public boolean isErrorNode() { |
| return fProblemSeverity == AntModelProblem.SEVERITY_ERROR || fProblemSeverity == AntModelProblem.SEVERITY_FATAL_ERROR; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ant.internal.ui.model.IAntElement#isWarningNode() |
| */ |
| @Override |
| public boolean isWarningNode() { |
| return fProblemSeverity == AntModelProblem.SEVERITY_WARNING; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ant.internal.ui.model.IAntElement#setProblemSeverity(int) |
| */ |
| @Override |
| public void setProblemSeverity(int severity) { |
| fProblemSeverity = severity; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ant.internal.ui.model.IAntElement#isExternal() |
| */ |
| @Override |
| public boolean isExternal() { |
| return fIsExternal; |
| } |
| |
| /** |
| * Sets whether this xml element is defined in an external entity. |
| */ |
| public void setExternal(boolean isExternal) { |
| fIsExternal = isExternal; |
| } |
| |
| /** |
| * Returns a unique string representation of this element. The format of the string is not specified. |
| * |
| * @return the string representation |
| */ |
| @Override |
| public String getElementPath() { |
| if (fElementPath == null) { |
| StringBuffer buffer = new StringBuffer(); |
| String buildFileName = getProjectNode().getBuildFileName(); |
| if (buildFileName != null) { |
| buffer.append(buildFileName); |
| } |
| buffer.append(getParentNode() != null ? getParentNode().getElementPath() : IAntCoreConstants.EMPTY_STRING); |
| buffer.append('/'); |
| buffer.append(getElementIdentifier()); |
| buffer.append('['); |
| buffer.append(fIndex); |
| buffer.append(']'); |
| |
| fElementPath = buffer.toString(); |
| } |
| return fElementPath; |
| } |
| |
| private String getElementIdentifier() { |
| if (fElementIdentifier == null) { |
| StringBuffer buffer = escape(new StringBuffer(getName() != null ? getName() : IAntCoreConstants.EMPTY_STRING), '\\', "$/[]\\"); //$NON-NLS-1$ |
| buffer.append('$'); |
| buffer.append(escape(new StringBuffer(getLabel() != null ? getLabel() : IAntCoreConstants.EMPTY_STRING), '\\', "$/[]\\").toString()); //$NON-NLS-1$ |
| |
| fElementIdentifier = buffer.toString(); |
| } |
| return fElementIdentifier; |
| } |
| |
| private StringBuffer escape(StringBuffer sb, char esc, String special) { |
| for (int i = 0; i < sb.length(); i++) { |
| if (special.indexOf(sb.charAt(i)) >= 0) { |
| sb.insert(i++, esc); |
| } |
| } |
| |
| return sb; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see java.lang.Object#equals(java.lang.Object) |
| */ |
| @Override |
| public boolean equals(Object o2) { |
| Object o1 = this; |
| |
| if (o1 == o2) { |
| return true; |
| } |
| if (o2 == null) { |
| return false; |
| } |
| if (!(o1 instanceof AntElementNode || o2 instanceof AntElementNode)) { |
| return o2.equals(o1); |
| } |
| if (!(o1 instanceof AntElementNode && o2 instanceof AntElementNode)) { |
| return false; |
| } |
| |
| AntElementNode e1 = (AntElementNode) o1; |
| AntElementNode e2 = (AntElementNode) o2; |
| |
| return e1.getElementPath().equals(e2.getElementPath()); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see java.lang.Object#hashCode() |
| */ |
| @Override |
| public int hashCode() { |
| return getElementPath().hashCode(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ant.internal.ui.model.IAntElement#getSelectionLength() |
| */ |
| @Override |
| public int getSelectionLength() { |
| return fSelectionLength; |
| } |
| |
| public void setSelectionLength(int selectionLength) { |
| this.fSelectionLength = selectionLength; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ant.internal.ui.model.IAntElement#getNode(int) |
| */ |
| @Override |
| public AntElementNode getNode(int sourceOffset) { |
| synchronized (this) { |
| if (fChildNodes != null) { |
| for (IAntElement node : fChildNodes) { |
| AntElementNode containingNode = node.getNode(sourceOffset); |
| if (containingNode != null) { |
| return containingNode; |
| } |
| } |
| } |
| } |
| if (fLength == -1 && fOffset <= sourceOffset && !isExternal()) { // this is still an open element |
| return this; |
| } |
| if (fOffset <= sourceOffset && sourceOffset <= (fOffset + fLength - 2)) { |
| return this; |
| } |
| |
| return null; |
| } |
| |
| public Image getImage() { |
| int flags = 0; |
| |
| if (isErrorNode()) { |
| flags = flags | AntImageDescriptor.HAS_ERRORS; |
| } else if (isWarningNode()) { |
| flags = flags | AntImageDescriptor.HAS_WARNINGS; |
| } |
| if (fImportNode != null || isExternal()) { |
| flags = flags | AntImageDescriptor.IMPORTED; |
| } |
| ImageDescriptor base = getBaseImageDescriptor(); |
| return AntUIImages.getImage(new AntImageDescriptor(base, flags)); |
| } |
| |
| protected ImageDescriptor getBaseImageDescriptor() { |
| return AntUIImages.getImageDescriptor(IAntUIConstants.IMG_TASK_PROPOSAL); |
| } |
| |
| protected IAntModel getAntModel() { |
| IAntElement parentNode = getParentNode(); |
| while (!(parentNode instanceof AntProjectNode)) { |
| parentNode = parentNode.getParentNode(); |
| } |
| if (parentNode instanceof AntProjectNode) { |
| return ((AntProjectNode) parentNode).getAntModel(); |
| } |
| return null; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ant.internal.ui.model.IAntElement#setProblem(org.eclipse.ant.internal.ui.model.IProblem) |
| */ |
| @Override |
| public void setProblem(IProblem problem) { |
| fProblem = problem; |
| } |
| |
| /** |
| * @return |
| */ |
| public IProblem getProblem() { |
| return fProblem; |
| } |
| |
| protected void appendEntityName(StringBuffer displayName) { |
| String path = getFilePath(); |
| |
| if (getImportNode() != null) { |
| displayName.append(MessageFormat.format(AntModelMessages.AntElementNode_9, new Object[] { getImportNode().getLabel() })); |
| } else { |
| String entityName = getAntModel().getEntityName(path); |
| displayName.append(MessageFormat.format(AntModelMessages.AntElementNode_9, new Object[] { entityName })); |
| } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ant.internal.ui.model.IAntElement#getImportNode() |
| */ |
| @Override |
| public AntElementNode getImportNode() { |
| return fImportNode; |
| } |
| |
| public void setImportNode(AntElementNode importNode) { |
| fImportNode = importNode; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ant.internal.ui.model.IAntElement#hasChildren() |
| */ |
| @Override |
| public boolean hasChildren() { |
| if (fChildNodes == null) { |
| return false; |
| } |
| return !fChildNodes.isEmpty(); |
| } |
| |
| public void reset() { |
| fChildNodes = null; |
| } |
| |
| public void setExternalInfo(int line, int column) { |
| fLine = line; |
| fColumn = column; |
| } |
| |
| public int[] getExternalInfo() { |
| return new int[] { fLine, fColumn }; |
| } |
| |
| /** |
| * Return the resource that contains the definition of this Ant node. |
| * |
| * @return The resource that contains the definition of this ant node or <code>null</code> if that resource could not be determined (a buildfile |
| * that is external to the workspace). |
| */ |
| public IFile getIFile() { |
| if (isExternal()) { |
| return AntUtil.getFileForLocation(fFilePath, null); |
| } |
| return getBuildFileResource(); |
| } |
| |
| /** |
| * Return the resource that is the main build file for this Ant node. |
| * |
| * @return The resource that is the main buildfile for this ant node or <code>null</code> if that resource could not be determined (a buildfile |
| * that is external to the workspace). |
| */ |
| public IFile getBuildFileResource() { |
| LocationProvider locationProvider = getAntModel().getLocationProvider(); |
| return locationProvider.getFile(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class) |
| */ |
| @Override |
| public Object getAdapter(Class adapter) { |
| return Platform.getAdapterManager().getAdapter(this, adapter); |
| } |
| |
| /** |
| * Returns whether this node is a structural node that should be shown in the buildfile outline. For example, an AntCommentNode would return |
| * <code>false</code> |
| * |
| * @return whether this node is a structural node that should be shown in the buildfile outline |
| */ |
| public boolean isStructuralNode() { |
| return true; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ant.internal.ui.model.IAntElement#collapseProjection() |
| */ |
| @Override |
| public boolean collapseProjection() { |
| return false; |
| } |
| |
| public void dispose() { |
| getAntModel().dispose(); |
| } |
| |
| /** |
| * Returns the name or path of the element referenced at the offset within the declaration of this node or <code>null</code> if no element is |
| * referenced at the offset |
| * |
| * @param offset |
| * The offset within the declaration of this node |
| * @return <code>null</code> or the name or path of the referenced element |
| */ |
| public String getReferencedElement(int offset) { |
| return null; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ant.internal.ui.model.IAntElement#getProblemMessage() |
| */ |
| @Override |
| public String getProblemMessage() { |
| return fProblemMessage; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ant.internal.ui.model.IAntElement#setProblemMessage(java.lang.String) |
| */ |
| @Override |
| public void setProblemMessage(String problemMessage) { |
| fProblemMessage = problemMessage; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ant.internal.ui.model.IAntElement#containsOccurrence(java.lang.String) |
| */ |
| @Override |
| public boolean containsOccurrence(String identifier) { |
| return false; |
| } |
| |
| /** |
| * Returns the identifier to use for matching occurrences in the Ant editor. |
| * |
| * @return the occurrences identifier for this node |
| */ |
| public String getOccurrencesIdentifier() { |
| return getLabel(); |
| } |
| |
| /** |
| * Returns whether the supplied region can be considered as an area in this node containing a reference. |
| * |
| * @param region |
| * the area to consider for finding a reference |
| * @return whether a reference could exist in this node from the supplied region |
| */ |
| public boolean isRegionPotentialReference(IRegion region) { |
| return region.getOffset() >= fOffset; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ant.internal.ui.model.IAntElement#computeIdentifierOffsets(java.lang.String) |
| */ |
| @Override |
| public List<Integer> computeIdentifierOffsets(String identifier) { |
| return null; |
| } |
| |
| /** |
| * Returns whether the supplied region is from within this node's declaration identifier area |
| * |
| * @param region |
| * The region to check |
| * @return whether the region is from within this node and is the declaration of a reference. |
| */ |
| public boolean isFromDeclaration(IRegion region) { |
| return false; |
| } |
| |
| protected boolean checkReferenceRegion(IRegion region, String textToSearch, String attributeName) { |
| int attributeOffset = textToSearch.indexOf(attributeName); |
| while (attributeOffset > 0 && !Character.isWhitespace(textToSearch.charAt(attributeOffset - 1))) { |
| attributeOffset = textToSearch.indexOf(attributeName, attributeOffset + 1); |
| } |
| if (attributeOffset != -1) { |
| attributeOffset += attributeName.length(); |
| int attributeOffsetEnd = textToSearch.indexOf('"', attributeOffset); |
| attributeOffsetEnd = textToSearch.indexOf('"', attributeOffsetEnd + 1); |
| return region.getOffset() >= getOffset() + attributeOffset |
| && (region.getOffset() + region.getLength()) <= getOffset() + attributeOffsetEnd; |
| } |
| return false; |
| } |
| } |