blob: 397a1ebe504afe20c7486a5c362e1cd7f77d5c35 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2002, 2015 GEBIT Gesellschaft fuer EDV-Beratung
* und Informatik-Technologien mbH,
* Berlin, Duesseldorf, Frankfurt (Germany) and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* 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
* Ericsson AB, Hamdan Msheik - Bug 389564
* Ericsson AB, Julian Enoch - Bug 389564
* David North - Bug 475839
*******************************************************************************/
package org.eclipse.ant.internal.ui.model;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.MessageFormat;
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.AntUIPlugin;
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.FileLocator;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.URIUtil;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.text.IRegion;
import org.eclipse.swt.graphics.Image;
/**
* 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() {
}
@Override
public String getName() {
return fName;
}
@Override
public String getLabel() {
return getName();
}
@Override
public List<IAntElement> getChildNodes() {
return fChildNodes;
}
@Override
public IAntElement getParentNode() {
return fParent;
}
@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<>();
}
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;
}
try {
URL fileURL = FileLocator.toFileURL(url);
// To be worked in 4.6 via Bug 476266
if (IAntCoreConstants.FILE.equals(fileURL.getProtocol())) {
fFilePath = new Path((URIUtil.toFile(URIUtil.toURI(fileURL))).getAbsolutePath()).toString();
}
}
catch (URISyntaxException e) {
AntUIPlugin.log(e);
}
catch (IOException e) {
AntUIPlugin.log(e);
}
}
/**
* 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;
}
@Override
public int getOffset() {
return fOffset;
}
@Override
public void setOffset(int anOffset) {
fOffset = anOffset;
}
@Override
public int getLength() {
return fLength;
}
@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$
}
@Override
public boolean isErrorNode() {
return fProblemSeverity == AntModelProblem.SEVERITY_ERROR || fProblemSeverity == AntModelProblem.SEVERITY_FATAL_ERROR;
}
@Override
public boolean isWarningNode() {
return fProblemSeverity == AntModelProblem.SEVERITY_WARNING;
}
@Override
public void setProblemSeverity(int severity) {
fProblemSeverity = severity;
}
@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) {
StringBuilder buffer = new StringBuilder();
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;
}
@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());
}
@Override
public int hashCode() {
return getElementPath().hashCode();
}
@Override
public int getSelectionLength() {
return fSelectionLength;
}
public void setSelectionLength(int selectionLength) {
this.fSelectionLength = selectionLength;
}
@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;
}
@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 }));
}
}
@Override
public AntElementNode getImportNode() {
return fImportNode;
}
public void setImportNode(AntElementNode importNode) {
fImportNode = importNode;
}
@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();
}
@Override
public <T> T getAdapter(Class<T> 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;
}
@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;
}
@Override
public String getProblemMessage() {
return fProblemMessage;
}
@Override
public void setProblemMessage(String problemMessage) {
fProblemMessage = problemMessage;
}
@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;
}
@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;
}
}