blob: 77dc33eac52eb961003589a18ed7d4b994b865a7 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2015 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
* Nico Seessle - bug 51332
*******************************************************************************/
package org.eclipse.ant.internal.ui.model;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.Stack;
import java.util.regex.Pattern;
import org.apache.tools.ant.AntTypeDefinition;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.ComponentHelper;
import org.apache.tools.ant.IntrospectionHelper;
import org.apache.tools.ant.Location;
import org.apache.tools.ant.Main;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.ProjectHelperRepository;
import org.apache.tools.ant.RuntimeConfigurable;
import org.apache.tools.ant.Target;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.TaskAdapter;
import org.apache.tools.ant.UnknownElement;
import org.eclipse.ant.core.AntCorePlugin;
import org.eclipse.ant.core.AntCorePreferences;
import org.eclipse.ant.core.AntSecurityException;
import org.eclipse.ant.core.Property;
import org.eclipse.ant.core.Type;
import org.eclipse.ant.internal.core.AntClassLoader;
import org.eclipse.ant.internal.core.AntCoreUtil;
import org.eclipse.ant.internal.core.AntSecurityManager;
import org.eclipse.ant.internal.core.IAntCoreConstants;
import org.eclipse.ant.internal.ui.AntUIPlugin;
import org.eclipse.ant.internal.ui.AntUtil;
import org.eclipse.ant.internal.ui.editor.DecayCodeCompletionDataStructuresThread;
import org.eclipse.ant.internal.ui.editor.outline.AntEditorMarkerUpdater;
import org.eclipse.ant.internal.ui.editor.utils.ProjectHelper;
import org.eclipse.ant.internal.ui.preferences.AntEditorPreferenceConstants;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.core.runtime.content.IContentDescription;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener;
import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.core.variables.IStringVariableManager;
import org.eclipse.core.variables.VariablesPlugin;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.text.ISynchronizable;
import org.xml.sax.Attributes;
import org.xml.sax.SAXParseException;
import com.ibm.icu.text.MessageFormat;
public class AntModel implements IAntModel {
private static ClassLoader fgClassLoader;
private static int fgInstanceCount = 0;
private static Object loaderLock = new Object();
private IDocument fDocument;
private IProblemRequestor fProblemRequestor;
private LocationProvider fLocationProvider;
private AntProjectNode fProjectNode;
private AntTargetNode fCurrentTargetNode;
private AntElementNode fLastNode;
private AntElementNode fNodeBeingResolved;
private int fNodeBeingResolvedIndex = -1;
private String fEncoding = null;
private Map<String, String> fEntityNameToPath;
/**
* Stack of still open elements.
* <P>
* On top of the stack is the innermost element.
*/
private Stack<AntElementNode> fStillOpenElements = new Stack<AntElementNode>();
private Map<Task, AntTaskNode> fTaskToNode = new HashMap<Task, AntTaskNode>();
private List<AntTaskNode> fTaskNodes = new ArrayList<AntTaskNode>();
private final Object fDirtyLock = new Object();
private boolean fIsDirty = true;
private File fEditedFile = null;
private ClassLoader fLocalClassLoader = null;
private boolean fHasLexicalInfo = true;
private boolean fHasPositionInfo = true;
private boolean fHasTaskInfo = true;
private IDocumentListener fListener;
private AntEditorMarkerUpdater fMarkerUpdater = null;
private List<AntElementNode> fNonStructuralNodes = new ArrayList<AntElementNode>(1);
private IPreferenceChangeListener fCoreListener = new IPreferenceChangeListener() {
@Override
public void preferenceChange(PreferenceChangeEvent event) {
if (IAntCoreConstants.PREFERENCE_CLASSPATH_CHANGED.equals(event.getKey())) {
if (Boolean.parseBoolean((String) event.getNewValue()) == true) {
reconcileForPropertyChange(true);
}
}
}
};
private IPreferenceChangeListener fUIListener = new IPreferenceChangeListener() {
@Override
public void preferenceChange(PreferenceChangeEvent event) {
String property = event.getKey();
if (property.equals(AntEditorPreferenceConstants.PROBLEM)) {
IEclipsePreferences node = InstanceScope.INSTANCE.getNode(AntUIPlugin.PI_ANTUI);
if (node != null) {
node.removePreferenceChangeListener(fUIListener);
reconcileForPropertyChange(false);
node.addPreferenceChangeListener(fUIListener);
}
} else if (property.equals(AntEditorPreferenceConstants.CODEASSIST_USER_DEFINED_TASKS)) {
reconcileForPropertyChange(false);
} else if (property.equals(AntEditorPreferenceConstants.BUILDFILE_NAMES_TO_IGNORE)
|| property.equals(AntEditorPreferenceConstants.BUILDFILE_IGNORE_ALL)) {
fReportingProblemsCurrent = false;
reconcileForPropertyChange(false);
}
}
};
private Map<String, String> fProperties = null;
private List<String> fPropertyFiles = null;
private Map<String, String> fDefinersToText;
private Map<String, String> fPreviousDefinersToText;
private Map<String, List<String>> fDefinerNodeIdentifierToDefinedTasks;
private Map<String, AntDefiningTaskNode> fTaskNameToDefiningNode;
private Map<String, String> fCurrentNodeIdentifiers;
private boolean fReportingProblemsCurrent = false;
private boolean fDoNotReportProblems = false;
private boolean fShouldReconcile = true;
private HashMap<String, String> fNamespacePrefixMappings;
public AntModel(IDocument document, IProblemRequestor problemRequestor, LocationProvider locationProvider) {
init(document, problemRequestor, locationProvider);
fMarkerUpdater = new AntEditorMarkerUpdater();
fMarkerUpdater.setModel(this);
IEclipsePreferences node = InstanceScope.INSTANCE.getNode(AntCorePlugin.PI_ANTCORE);
if (node != null) {
node.addPreferenceChangeListener(fCoreListener);
}
node = InstanceScope.INSTANCE.getNode(AntUIPlugin.PI_ANTUI);
if (node != null) {
node.addPreferenceChangeListener(fUIListener);
}
}
public AntModel(IDocument document, IProblemRequestor problemRequestor, LocationProvider locationProvider, boolean resolveLexicalInfo, boolean resolvePositionInfo, boolean resolveTaskInfo) {
init(document, problemRequestor, locationProvider);
fHasLexicalInfo = resolveLexicalInfo;
fHasPositionInfo = resolvePositionInfo;
fHasTaskInfo = resolveTaskInfo;
}
private void init(IDocument document, IProblemRequestor problemRequestor, LocationProvider locationProvider) {
fDocument = document;
fProblemRequestor = problemRequestor;
fLocationProvider = locationProvider;
if (fgInstanceCount == 0) {
// no other models are open to ensure that the classpath is up to date wrt the
// Ant preferences and start listening for breakpoint changes
AntDefiningTaskNode.setJavaClassPath();
AntModelCore.getDefault().startBreakpointListening();
}
fgInstanceCount++;
DecayCodeCompletionDataStructuresThread.cancel();
ProjectHelper helper = getProjectHelper();
if (helper == null) {
ProjectHelperRepository.getInstance().registerProjectHelper(ProjectHelper.class);
}
computeEncoding();
}
/**
* Searches the collection of registered {@link org.apache.tools.ant.ProjectHelper}s to see if we have one registered already.
*
* @return the {@link ProjectHelper} from our implementation of <code>null</code> if we have not registered one yet
* @since 3.7
* @see ProjectHelperRepository
*/
ProjectHelper getProjectHelper() {
Iterator<org.apache.tools.ant.ProjectHelper> helpers = ProjectHelperRepository.getInstance().getHelpers();
while (helpers.hasNext()) {
org.apache.tools.ant.ProjectHelper helper = helpers.next();
if (helper instanceof ProjectHelper) {
return (ProjectHelper) helper;
}
}
return null;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ant.internal.ui.model.IAntModel#dispose()
*/
@Override
public void dispose() {
synchronized (getLockObject()) {
if (fDocument != null && fListener != null) {
fDocument.removeDocumentListener(fListener);
}
fDocument = null;
fLocationProvider = null;
ProjectHelper.setAntModel(null);
}
IEclipsePreferences node = InstanceScope.INSTANCE.getNode(AntCorePlugin.PI_ANTCORE);
if (node != null) {
node.removePreferenceChangeListener(fCoreListener);
}
node = InstanceScope.INSTANCE.getNode(AntUIPlugin.PI_ANTUI);
if (node != null) {
node.removePreferenceChangeListener(fUIListener);
}
fgInstanceCount--;
if (fgInstanceCount == 0) {
fgClassLoader = null;
DecayCodeCompletionDataStructuresThread.getDefault().start();
AntModelCore.getDefault().stopBreakpointListening();
cleanup();
}
}
private Object getLockObject() {
if (fDocument instanceof ISynchronizable) {
Object lock = ((ISynchronizable) fDocument).getLockObject();
if (lock != null) {
return lock;
}
}
return this;
}
private void cleanup() {
AntProjectNode projectNode = getProjectNode();
if (projectNode != null) {
// cleanup the introspection helpers that may have been generated
IntrospectionHelper.clearCache();
projectNode.getProject().fireBuildFinished(null);
}
fEncoding = null;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ant.internal.ui.model.IAntModel#reconcile()
*/
@Override
public void reconcile() {
synchronized (fDirtyLock) {
if (!fShouldReconcile || !fIsDirty) {
return;
}
fIsDirty = false;
}
synchronized (getLockObject()) {
if (fLocationProvider == null) {
// disposed
return;
}
if (fDocument == null) {
fProjectNode = null;
} else {
reset();
parseDocument(fDocument);
reconcileTaskAndTypes();
}
AntModelCore.getDefault().notifyAntModelListeners(new AntModelChangeEvent(this));
}
}
private void reset() {
fCurrentTargetNode = null;
fStillOpenElements = new Stack<AntElementNode>();
fTaskToNode = new HashMap<Task, AntTaskNode>();
fTaskNodes = new ArrayList<AntTaskNode>();
fNodeBeingResolved = null;
fNodeBeingResolvedIndex = -1;
fLastNode = null;
fCurrentNodeIdentifiers = null;
fNamespacePrefixMappings = null;
fNonStructuralNodes = new ArrayList<AntElementNode>(1);
if (fDefinersToText != null) {
fPreviousDefinersToText = new HashMap<String, String>(fDefinersToText);
fDefinersToText = null;
}
}
private void parseDocument(IDocument input) {
boolean parsed = true;
if (input.getLength() == 0) {
fProjectNode = null;
parsed = false;
return;
}
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
ClassLoader parsingClassLoader = getClassLoader(originalClassLoader);
Thread.currentThread().setContextClassLoader(parsingClassLoader);
Project project = null;
try {
ProjectHelper projectHelper = null;
String textToParse = input.get();
if (fProjectNode == null || !fProjectNode.hasChildren()) {
fProjectNode = null;
project = new AntModelProject();
projectHelper = prepareForFullParse(project, parsingClassLoader);
} else {
project = fProjectNode.getProject();
projectHelper = (ProjectHelper) project.getReference("ant.projectHelper"); //$NON-NLS-1$
projectHelper.setBuildFile(getEditedFile());
prepareForFullIncremental();
}
beginReporting();
Map<String, Object> references = project.getReferences();
references.remove("ant.parsing.context"); //$NON-NLS-1$
ProjectHelper.setAntModel(this);
projectHelper.parse(project, textToParse);
}
catch (BuildException e) {
handleBuildException(e, null);
}
finally {
if (parsed) {
SecurityManager origSM = System.getSecurityManager();
processAntHome(true);
try {
// set a security manager to disallow system exit and system property setting
System.setSecurityManager(new AntSecurityManager(origSM, Thread.currentThread(), false));
resolveBuildfile();
endReporting();
}
catch (AntSecurityException e) {
// do nothing
}
finally {
Thread.currentThread().setContextClassLoader(originalClassLoader);
getClassLoader(null);
System.setSecurityManager(origSM);
project.fireBuildFinished(null); // cleanup (IntrospectionHelper)
}
}
}
}
private ProjectHelper prepareForFullParse(Project project, ClassLoader parsingClassLoader) {
initializeProject(project, parsingClassLoader);
// Ant's parsing facilities always works on a file, therefore we need
// to determine the actual location of the file. Though the file
// contents will not be parsed. We parse the passed document string
File file = getEditedFile();
String filePath = IAntCoreConstants.EMPTY_STRING;
if (file != null) {
filePath = file.getAbsolutePath();
}
project.setUserProperty("ant.file", filePath); //$NON-NLS-1$
project.setUserProperty("ant.version", Main.getAntVersion()); //$NON-NLS-1$
ProjectHelper projectHelper = getProjectHelper();
ProjectHelper.setAntModel(this);
projectHelper.setBuildFile(file);
project.addReference("ant.projectHelper", projectHelper); //$NON-NLS-1$
return projectHelper;
}
private void prepareForFullIncremental() {
fProjectNode.reset();
fTaskToNode = new HashMap<Task, AntTaskNode>();
fTaskNodes = new ArrayList<AntTaskNode>();
}
private void initializeProject(Project project, ClassLoader loader) {
try {
processAntHome(false);
}
catch (AntSecurityException ex) {
// do nothing - Ant home can not be set from this thread
}
project.init();
setProperties(project);
setTasks(project, loader);
setTypes(project, loader);
}
private void setTasks(Project project, ClassLoader loader) {
List<org.eclipse.ant.core.Task> tasks = AntCorePlugin.getPlugin().getPreferences().getTasks();
for (org.eclipse.ant.core.Task task : tasks) {
AntTypeDefinition def = new AntTypeDefinition();
def.setName(task.getTaskName());
def.setClassName(task.getClassName());
def.setClassLoader(loader);
def.setAdaptToClass(Task.class);
def.setAdapterClass(TaskAdapter.class);
ComponentHelper.getComponentHelper(project).addDataTypeDefinition(def);
}
}
private void setTypes(Project project, ClassLoader loader) {
List<Type> types = AntCorePlugin.getPlugin().getPreferences().getTypes();
for (Type type : types) {
AntTypeDefinition def = new AntTypeDefinition();
def.setName(type.getTypeName());
def.setClassName(type.getClassName());
def.setClassLoader(loader);
ComponentHelper.getComponentHelper(project).addDataTypeDefinition(def);
}
}
private void setProperties(Project project) {
setBuiltInProperties(project);
setExtraProperties(project);
setGlobalProperties(project);
loadExtraPropertyFiles(project);
loadPropertyFiles(project);
}
private void setExtraProperties(Project project) {
if (fProperties != null) {
Pattern pattern = Pattern.compile("\\$\\{.*_prompt.*\\}"); //$NON-NLS-1$
IStringVariableManager manager = VariablesPlugin.getDefault().getStringVariableManager();
for (Iterator<String> iter = fProperties.keySet().iterator(); iter.hasNext();) {
String name = iter.next();
String value = fProperties.get(name);
if (!pattern.matcher(value).find()) {
try {
value = manager.performStringSubstitution(value);
}
catch (CoreException e) {
// do nothing
}
}
if (value != null) {
project.setUserProperty(name, value);
}
}
}
}
private void loadExtraPropertyFiles(Project project) {
if (fPropertyFiles != null) {
try {
List<Properties> allProperties = AntCoreUtil.loadPropertyFiles(fPropertyFiles, project.getUserProperty("basedir"), getEditedFile().getAbsolutePath()); //$NON-NLS-1$
setPropertiesFromFiles(project, allProperties);
}
catch (IOException e1) {
AntUIPlugin.log(e1);
}
}
}
/**
* Load all properties from the files
*/
private void loadPropertyFiles(Project project) {
List<String> fileNames = Arrays.asList(AntCorePlugin.getPlugin().getPreferences().getCustomPropertyFiles());
try {
List<Properties> allProperties = AntCoreUtil.loadPropertyFiles(fileNames, project.getUserProperty("basedir"), getEditedFile().getAbsolutePath()); //$NON-NLS-1$
setPropertiesFromFiles(project, allProperties);
}
catch (IOException e1) {
AntUIPlugin.log(e1);
}
}
private void setPropertiesFromFiles(Project project, List<Properties> allProperties) {
for (Properties props : allProperties) {
Enumeration<?> propertyNames = props.propertyNames();
while (propertyNames.hasMoreElements()) {
String name = (String) propertyNames.nextElement();
// do not override extra local properties with the global settings
if (project.getUserProperty(name) == null) {
project.setUserProperty(name, props.getProperty(name));
}
}
}
}
private void setBuiltInProperties(Project project) {
// set processAntHome for other properties set as system properties
project.setUserProperty("ant.file", getEditedFile().getAbsolutePath()); //$NON-NLS-1$
project.setUserProperty("ant.version", Main.getAntVersion()); //$NON-NLS-1$
}
private void processAntHome(boolean finished) {
AntCorePreferences prefs = AntCorePlugin.getPlugin().getPreferences();
String antHome = prefs.getAntHome();
if (finished || antHome == null) {
System.getProperties().remove("ant.home"); //$NON-NLS-1$
System.getProperties().remove("ant.library.dir"); //$NON-NLS-1$
} else {
System.setProperty("ant.home", antHome); //$NON-NLS-1$
File antLibDir = new File(antHome, "lib"); //$NON-NLS-1$
System.setProperty("ant.library.dir", antLibDir.getAbsolutePath()); //$NON-NLS-1$
}
}
private void setGlobalProperties(Project project) {
List<Property> properties = AntCorePlugin.getPlugin().getPreferences().getProperties();
if (properties != null) {
for (Iterator<Property> iter = properties.iterator(); iter.hasNext();) {
Property property = iter.next();
String value = property.getValue(true);
if (value != null) {
project.setUserProperty(property.getName(), value);
}
}
}
}
private void resolveBuildfile() {
Collection<AntTaskNode> nodeCopy = new ArrayList<AntTaskNode>(fTaskNodes);
Iterator<AntTaskNode> iter = nodeCopy.iterator();
while (iter.hasNext()) {
AntTaskNode node = iter.next();
fNodeBeingResolved = node;
fNodeBeingResolvedIndex = -1;
if (node.configure(false)) {
// resolve any new elements that may have been added
resolveBuildfile();
}
}
fNodeBeingResolved = null;
fNodeBeingResolvedIndex = -1;
checkTargets();
}
/**
* Check that if a default target is specified it exists and that the target dependencies exist.
*/
private void checkTargets() {
if (fProjectNode == null || doNotReportProblems()) {
return;
}
String defaultTargetName = fProjectNode.getDefaultTargetName();
if (defaultTargetName != null && fProjectNode.getProject().getTargets().get(defaultTargetName) == null) {
// no default target when one specified (default target does not have to be specified)
String message = MessageFormat.format(AntModelMessages.AntModel_43, new Object[] { defaultTargetName });
IProblem problem = createProblem(message, fProjectNode.getOffset(), fProjectNode.getSelectionLength(), AntModelProblem.SEVERITY_ERROR);
acceptProblem(problem);
markHierarchy(fProjectNode, AntModelProblem.SEVERITY_ERROR, message);
}
if (!fProjectNode.hasChildren()) {
return;
}
List<IAntElement> children = fProjectNode.getChildNodes();
boolean checkCircularDependencies = true;
for (IAntElement node : children) {
IAntElement originalNode = node;
if (node instanceof AntTargetNode) {
if (checkCircularDependencies) {
checkCircularDependencies = false;
checkCircularDependencies(node);
}
checkMissingDependencies(node, originalNode);
}
}
}
/**
* method assumes sender has checked whether to report problems
*/
private void checkMissingDependencies(IAntElement node, IAntElement originalNode) {
String missing = ((AntTargetNode) node).checkDependencies();
if (missing != null) {
String message = MessageFormat.format(AntModelMessages.AntModel_44, new Object[] { missing });
IAntElement importNode = node.getImportNode();
if (importNode != null) {
node = importNode;
}
IProblem problem = createProblem(message, node.getOffset(), node.getSelectionLength(), AntModelProblem.SEVERITY_ERROR);
acceptProblem(problem);
markHierarchy(originalNode, AntModelProblem.SEVERITY_ERROR, message);
}
}
private boolean doNotReportProblems() {
if (fReportingProblemsCurrent) {
return fDoNotReportProblems;
}
fReportingProblemsCurrent = true;
fDoNotReportProblems = false;
if (AntUIPlugin.getDefault().getCombinedPreferenceStore().getBoolean(AntEditorPreferenceConstants.BUILDFILE_IGNORE_ALL)) {
fDoNotReportProblems = true;
return fDoNotReportProblems;
}
String buildFileNames = AntUIPlugin.getDefault().getCombinedPreferenceStore().getString(AntEditorPreferenceConstants.BUILDFILE_NAMES_TO_IGNORE);
if (buildFileNames.length() > 0) {
String[] names = AntUtil.parseString(buildFileNames, ","); //$NON-NLS-1$
String editedFileName = getEditedFile().getName();
for (int i = 0; i < names.length; i++) {
String string = names[i];
if (string.trim().equals(editedFileName)) {
fDoNotReportProblems = true;
return fDoNotReportProblems;
}
}
}
return fDoNotReportProblems;
}
/**
* method assumes sendor has checked whether to report problems
*/
private void checkCircularDependencies(IAntElement node) {
Target target = ((AntTargetNode) node).getTarget();
String name = target.getName();
if (name == null) {
return;
}
try {
target.getProject().topoSort(name, target.getProject().getTargets());
}
catch (BuildException be) {
// possible circular dependency
String message = be.getMessage();
if (message.startsWith("Circular")) { //$NON-NLS-1$ //we do our own checking for missing dependencies
IProblem problem = createProblem(message, node.getProjectNode().getOffset(), node.getProjectNode().getSelectionLength(), AntModelProblem.SEVERITY_ERROR);
acceptProblem(problem);
markHierarchy(node.getProjectNode(), AntModelProblem.SEVERITY_ERROR, message);
}
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ant.internal.ui.model.IAntModel#handleBuildException(org.apache.tools.ant.BuildException,
* org.eclipse.ant.internal.ui.model.AntElementNode, int)
*/
@Override
public void handleBuildException(BuildException e, AntElementNode node, int severity) {
try {
if (node != null) {
markHierarchy(node, severity, e.getMessage());
}
Location location = e.getLocation();
int line = 0;
int originalOffset = 0;
int nonWhitespaceOffset = 0;
int length = 0;
if (location == Location.UNKNOWN_LOCATION && node != null) {
if (node.getImportNode() != null) {
node = node.getImportNode();
}
nonWhitespaceOffset = node.getOffset();
length = node.getLength();
} else {
line = location.getLineNumber();
if (line == 0) {
AntProjectNode projectNode = getProjectNode();
if (projectNode != null) {
length = projectNode.getSelectionLength();
nonWhitespaceOffset = projectNode.getOffset();
if (severity == AntModelProblem.SEVERITY_ERROR) {
projectNode.setProblemSeverity(AntModelProblem.NO_PROBLEM);
projectNode.setProblemMessage(null);
}
} else {
return;
}
} else {
if (node == null) {
originalOffset = getOffset(line, 1);
nonWhitespaceOffset = originalOffset;
try {
nonWhitespaceOffset = getNonWhitespaceOffset(line, 1);
}
catch (BadLocationException be) {
// do nothing
}
length = getLastCharColumn(line) - (nonWhitespaceOffset - originalOffset);
} else {
if (node.getImportNode() != null) {
node = node.getImportNode();
}
nonWhitespaceOffset = node.getOffset();
length = node.getLength();
}
}
}
notifyProblemRequestor(e, nonWhitespaceOffset, length, severity);
}
catch (BadLocationException e1) {
// do nothing
}
}
public void handleBuildException(BuildException e, AntElementNode node) {
handleBuildException(e, node, AntModelProblem.SEVERITY_ERROR);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ant.internal.ui.model.IAntModel#getEditedFile()
*/
@Override
public File getEditedFile() {
if (fLocationProvider != null && fEditedFile == null) {
fEditedFile = fLocationProvider.getLocation().toFile();
}
return fEditedFile;
}
private void markHierarchy(IAntElement openElement, int severity, String message) {
if (doNotReportProblems()) {
return;
}
while (openElement != null) {
openElement.setProblemSeverity(severity);
openElement.setProblemMessage(message);
openElement = openElement.getParentNode();
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ant.internal.ui.model.IAntModel#getLocationProvider()
*/
@Override
public LocationProvider getLocationProvider() {
return fLocationProvider;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ant.internal.ui.model.IAntModel#addTarget(org.apache.tools.ant.Target, int, int)
*/
@Override
public void addTarget(Target newTarget, int line, int column) {
AntTargetNode targetNode = AntTargetNode.newAntTargetNode(newTarget);
fProjectNode.addChildNode(targetNode);
fCurrentTargetNode = targetNode;
fStillOpenElements.push(targetNode);
if (fNodeBeingResolved instanceof AntImportNode) {
targetNode.setImportNode(fNodeBeingResolved);
targetNode.setExternal(true);
targetNode.setFilePath(newTarget.getLocation().getFileName());
} else {
String targetFileName = newTarget.getLocation().getFileName();
boolean external = isNodeExternal(targetFileName);
targetNode.setExternal(external);
if (external) {
targetNode.setFilePath(targetFileName);
}
}
computeOffset(targetNode, line, column);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ant.internal.ui.model.IAntModel#addProject(org.apache.tools.ant.Project, int, int)
*/
@Override
public void addProject(Project project, int line, int column) {
fProjectNode = new AntProjectNode((AntModelProject) project, this);
fStillOpenElements.push(fProjectNode);
computeOffset(fProjectNode, line, column);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ant.internal.ui.model.IAntModel#addDTD(java.lang.String, int, int)
*/
@Override
public void addDTD(String name, int line, int column) {
AntDTDNode node = new AntDTDNode(name);
fStillOpenElements.push(node);
int offset = -1;
try {
if (column <= 0) {
offset = getOffset(line, 0);
int lastCharColumn = getLastCharColumn(line);
offset = computeOffsetUsingPrefix(line, offset, "<!DOCTYPE", lastCharColumn); //$NON-NLS-1$
} else {
offset = getOffset(line, column);
}
}
catch (BadLocationException e) {
AntUIPlugin.log(e);
}
node.setOffset(offset);
fNonStructuralNodes.add(node);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ant.internal.ui.model.IAntModel#addTask(org.apache.tools.ant.Task, org.apache.tools.ant.Task, org.xml.sax.Attributes, int,
* int)
*/
@Override
public void addTask(Task newTask, Task parentTask, Attributes attributes, int line, int column) {
if (!canGetTaskInfo()) {
// need to add top level tasks so imports are executed even when
// the model is not interested in task level resolution
Target owningTarget = newTask.getOwningTarget();
String name = owningTarget.getName();
if (name == null || name.length() != 0) {
// not the top level implicit target
return;
}
}
AntTaskNode taskNode = null;
if (parentTask == null) {
taskNode = newTaskNode(newTask, attributes);
if (fCurrentTargetNode == null) {
fProjectNode.addChildNode(taskNode);
} else {
fCurrentTargetNode.addChildNode(taskNode);
}
} else {
taskNode = newNotWellKnownTaskNode(newTask, attributes);
AntTaskNode parentNode = fTaskToNode.get(parentTask);
parentNode.addChildNode(taskNode);
}
fTaskToNode.put(newTask, taskNode);
fStillOpenElements.push(taskNode);
computeOffset(taskNode, line, column);
if (fNodeBeingResolved instanceof AntImportNode) {
taskNode.setImportNode(fNodeBeingResolved);
// place the node in the collection right after the import node
if (fNodeBeingResolvedIndex == -1) {
fNodeBeingResolvedIndex = fTaskNodes.indexOf(fNodeBeingResolved);
}
fNodeBeingResolvedIndex++;
fTaskNodes.add(fNodeBeingResolvedIndex, taskNode);
} else {
fTaskNodes.add(taskNode);
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ant.internal.ui.model.IAntModel#addEntity(java.lang.String, java.lang.String)
*/
@Override
public void addEntity(String entityName, String entityPath) {
if (fEntityNameToPath == null) {
fEntityNameToPath = new HashMap<String, String>();
}
fEntityNameToPath.put(entityName, entityPath);
}
private AntTaskNode newTaskNode(Task newTask, Attributes attributes) {
AntTaskNode newNode = null;
String taskName = newTask.getTaskName();
if (newTask instanceof UnknownElement) {
// attempt to handle namespaces
taskName = ((UnknownElement) newTask).getTag();
}
if (isPropertySettingTask(taskName)) {
newNode = new AntPropertyNode(newTask, attributes);
} else if (taskName.equalsIgnoreCase("import")) { //$NON-NLS-1$
newNode = new AntImportNode(newTask, attributes);
} else if (taskName.equalsIgnoreCase("macrodef") //$NON-NLS-1$
|| taskName.equalsIgnoreCase("presetdef") //$NON-NLS-1$
|| taskName.equalsIgnoreCase("typedef") //$NON-NLS-1$
|| taskName.equalsIgnoreCase("taskdef")) { //$NON-NLS-1$
newNode = new AntDefiningTaskNode(newTask, attributes);
} else if (taskName.equalsIgnoreCase("antcall")) { //$NON-NLS-1$
newNode = new AntTaskNode(newTask, generateLabel(taskName, attributes, IAntModelConstants.ATTR_TARGET));
} else if (taskName.equalsIgnoreCase("mkdir")) { //$NON-NLS-1$
newNode = new AntTaskNode(newTask, generateLabel(taskName, attributes, IAntCoreConstants.DIR));
} else if (taskName.equalsIgnoreCase("copy")) { //$NON-NLS-1$
newNode = new AntTaskNode(newTask, generateLabel(taskName, attributes, IAntModelConstants.ATTR_DESTFILE));
} else if (taskName.equalsIgnoreCase("tar") //$NON-NLS-1$
|| taskName.equalsIgnoreCase("jar") //$NON-NLS-1$
|| taskName.equalsIgnoreCase("war") //$NON-NLS-1$
|| taskName.equalsIgnoreCase("zip")) { //$NON-NLS-1$
newNode = new AntTaskNode(newTask, generateLabel(newTask.getTaskName(), attributes, IAntModelConstants.ATTR_DESTFILE));
} else if (taskName.equalsIgnoreCase("untar") //$NON-NLS-1$
|| taskName.equalsIgnoreCase("unjar") //$NON-NLS-1$
|| taskName.equalsIgnoreCase("unwar") //$NON-NLS-1$
|| taskName.equalsIgnoreCase("gunzip") //$NON-NLS-1$
|| taskName.equalsIgnoreCase("bunzip2") //$NON-NLS-1$
|| taskName.equalsIgnoreCase("unzip")) { //$NON-NLS-1$
newNode = new AntTaskNode(newTask, generateLabel(newTask.getTaskName(), attributes, IAntModelConstants.ATTR_SRC));
} else if (taskName.equalsIgnoreCase("gzip") //$NON-NLS-1$
|| taskName.equalsIgnoreCase("bzip2")) { //$NON-NLS-1$
newNode = new AntTaskNode(newTask, generateLabel(newTask.getTaskName(), attributes, IAntModelConstants.ATTR_ZIPFILE));
} else if (taskName.equalsIgnoreCase("exec")) { //$NON-NLS-1$
String label = "exec "; //$NON-NLS-1$
String command = attributes.getValue(IAntModelConstants.ATTR_COMMAND);
if (command != null) {
label += command;
}
command = attributes.getValue(IAntModelConstants.ATTR_EXECUTABLE);
if (command != null) {
label += command;
}
newNode = new AntTaskNode(newTask, label);
} else if (taskName.equalsIgnoreCase("ant")) { //$NON-NLS-1$
newNode = new AntAntNode(newTask, attributes);
} else if (taskName.equalsIgnoreCase("delete")) { //$NON-NLS-1$
String label = "delete "; //$NON-NLS-1$
String file = attributes.getValue(IAntCoreConstants.FILE);
if (file != null) {
label += file;
} else {
file = attributes.getValue(IAntCoreConstants.DIR);
if (file != null) {
label += file;
}
}
newNode = new AntTaskNode(newTask, label);
} else if (IAntCoreConstants.AUGMENT.equals(taskName)) {
newNode = new AntAugmentTaskNode(newTask, generateLabel(newTask.getTaskName(), attributes, IAntCoreConstants.ID));
} else {
newNode = newNotWellKnownTaskNode(newTask, attributes);
}
setExternalInformation(newTask, newNode);
return newNode;
}
/**
* @param taskName
* the name of the task to check
* @return whether or not a task with this name sets properties
*/
private boolean isPropertySettingTask(String taskName) {
return taskName.equalsIgnoreCase("property") //$NON-NLS-1$
|| taskName.equalsIgnoreCase("available") //$NON-NLS-1$
|| taskName.equalsIgnoreCase("basename") //$NON-NLS-1$
|| taskName.equalsIgnoreCase("condition") //$NON-NLS-1$
|| taskName.equalsIgnoreCase("dirname") //$NON-NLS-1$
|| taskName.equalsIgnoreCase("loadfile") //$NON-NLS-1$
|| taskName.equalsIgnoreCase("pathconvert") //$NON-NLS-1$
|| taskName.equalsIgnoreCase("uptodate") //$NON-NLS-1$
|| taskName.equalsIgnoreCase("xmlproperty") //$NON-NLS-1$
|| taskName.equalsIgnoreCase("loadproperties"); //$NON-NLS-1$
}
private boolean isNodeExternal(String fileName) {
File taskFile = new File(fileName);
return !taskFile.equals(getEditedFile());
}
private AntTaskNode newNotWellKnownTaskNode(Task newTask, Attributes attributes) {
AntTaskNode newNode = new AntTaskNode(newTask);
String id = attributes.getValue("id"); //$NON-NLS-1$
if (id != null) {
newNode.setId(id);
}
String taskName = newTask.getTaskName();
if ("attribute".equals(taskName) || "element".equals(taskName)) { //$NON-NLS-1$ //$NON-NLS-2$
String name = attributes.getValue(IAntCoreConstants.NAME);
if (name != null) {
newNode.setBaseLabel(name);
}
}
setExternalInformation(newTask, newNode);
return newNode;
}
private void setExternalInformation(Task newTask, AntTaskNode newNode) {
String taskFileName = newTask.getLocation().getFileName();
boolean external = isNodeExternal(taskFileName);
newNode.setExternal(external);
if (external) {
newNode.setFilePath(taskFileName);
}
}
private String generateLabel(String taskName, Attributes attributes, String attributeName) {
StringBuffer label = new StringBuffer(taskName);
String srcFile = attributes.getValue(attributeName);
if (srcFile != null) {
label.append(' ');
label.append(srcFile);
}
return label.toString();
}
private void computeLength(AntElementNode element, int line, int column) {
if (element.isExternal()) {
element.setExternalInfo(line, column);
return;
}
try {
int length;
int offset;
if (column <= 0) {
column = getLastCharColumn(line);
String lineText = fDocument.get(fDocument.getLineOffset(line - 1), column);
StringBuffer searchString = new StringBuffer("</"); //$NON-NLS-1$
searchString.append(element.getName());
searchString.append('>');
int index = lineText.indexOf(searchString.toString());
if (index == -1) {
index = lineText.indexOf("/>"); //$NON-NLS-1$
if (index == -1) {
index = column; // set to the end of line
} else {
index = index + 3;
}
} else {
index = index + searchString.length() + 1;
}
offset = getOffset(line, index);
} else {
offset = getOffset(line, column);
}
length = offset - element.getOffset();
element.setLength(length);
}
catch (BadLocationException e) {
// ignore as the parser may be out of sync with the document during reconciliation
}
}
private void computeOffset(AntElementNode element, int line, int column) {
if (!canGetPositionInfo()) {
return;
}
if (element.isExternal()) {
element.setExternalInfo(line - 1, column);
return;
}
try {
String prefix = "<" + element.getName(); //$NON-NLS-1$
int offset = computeOffset(line, column, prefix);
element.setOffset(offset + 1);
element.setSelectionLength(element.getName().length());
}
catch (BadLocationException e) {
// ignore as the parser may be out of sync with the document during reconciliation
}
}
private int computeOffset(int line, int column, String prefix) throws BadLocationException {
int offset;
if (column <= 0) {
offset = getOffset(line, 0);
int lastCharColumn = getLastCharColumn(line);
offset = computeOffsetUsingPrefix(line, offset, prefix, lastCharColumn);
} else {
column = column - 1;
offset = getOffset(line, column);
offset = computeOffsetUsingPrefix(line, offset, prefix, column);
}
return offset;
}
private int computeOffsetUsingPrefix(int line, int offset, String prefix, int column) throws BadLocationException {
String lineText = fDocument.get(fDocument.getLineOffset(line - 1), column);
int lastIndex = lineText.indexOf(prefix);
if (lastIndex > -1) {
offset = getOffset(line, lastIndex + 1);
} else {
return computeOffsetUsingPrefix(line - 1, offset, prefix, getLastCharColumn(line - 1));
}
return offset;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ant.internal.ui.model.IAntModel#getOffset(int, int)
*/
@Override
public int getOffset(int line, int column) throws BadLocationException {
return fDocument.getLineOffset(line - 1) + column - 1;
}
private int getNonWhitespaceOffset(int line, int column) throws BadLocationException {
int offset = fDocument.getLineOffset(line - 1) + column - 1;
while (Character.isWhitespace(fDocument.getChar(offset))) {
offset++;
}
return offset;
}
public int getLine(int offset) {
try {
return fDocument.getLineOfOffset(offset) + 1;
}
catch (BadLocationException be) {
return -1;
}
}
private int getLastCharColumn(int line) throws BadLocationException {
String lineDelimiter = fDocument.getLineDelimiter(line - 1);
int lineDelimiterLength = lineDelimiter != null ? lineDelimiter.length() : 0;
return fDocument.getLineLength(line - 1) - lineDelimiterLength;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ant.internal.ui.model.IAntModel#setCurrentElementLength(int, int)
*/
@Override
public void setCurrentElementLength(int lineNumber, int column) {
fLastNode = fStillOpenElements.pop();
if (fLastNode == fCurrentTargetNode) {
fCurrentTargetNode = null; // the current target element has been closed
}
if (canGetPositionInfo()) {
computeLength(fLastNode, lineNumber, column);
}
}
private void acceptProblem(IProblem problem) {
if (fProblemRequestor != null) {
fProblemRequestor.acceptProblem(problem);
}
if (fMarkerUpdater != null) {
fMarkerUpdater.acceptProblem(problem);
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ant.internal.ui.model.IAntModel#getFile()
*/
@Override
public IFile getFile() {
IPath location = fLocationProvider.getLocation();
if (location == null) {
return null;
}
IFile[] files = ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(location.toFile().toURI());
if (files.length > 0) {
return files[0];
}
return null;
}
private void beginReporting() {
if (fProblemRequestor != null) {
fProblemRequestor.beginReporting();
}
if (fMarkerUpdater != null) {
fMarkerUpdater.beginReporting();
}
}
private void endReporting() {
if (fProblemRequestor != null) {
fProblemRequestor.endReporting();
}
}
private IProblem createProblem(Exception exception, int offset, int length, int severity) {
return createProblem(exception.getMessage(), offset, length, severity);
}
private IProblem createProblem(String message, int offset, int length, int severity) {
return new AntModelProblem(message, severity, offset, length, getLine(offset));
}
private void notifyProblemRequestor(Exception exception, IAntElement element, int severity) {
if (doNotReportProblems()) {
return;
}
IAntElement importNode = element.getImportNode();
if (importNode != null) {
element = importNode;
}
IProblem problem = createProblem(exception, element.getOffset(), element.getLength(), severity);
acceptProblem(problem);
element.setProblem(problem);
}
private void notifyProblemRequestor(Exception exception, int offset, int length, int severity) {
if (doNotReportProblems()) {
return;
}
if (fProblemRequestor != null) {
IProblem problem = createProblem(exception, offset, length, severity);
acceptProblem(problem);
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ant.internal.ui.model.IAntModel#warning(java.lang.Exception)
*/
@Override
public void warning(Exception exception) {
handleError(exception, AntModelProblem.SEVERITY_WARNING);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ant.internal.ui.model.IAntModel#error(java.lang.Exception)
*/
@Override
public void error(Exception exception) {
handleError(exception, AntModelProblem.SEVERITY_ERROR);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ant.internal.ui.model.IAntModel#errorFromElementText(java.lang.Exception, int, int)
*/
@Override
public void errorFromElementText(Exception exception, int start, int count) {
AntElementNode node = fLastNode;
if (node == null) {
if (!fStillOpenElements.empty()) {
node = fStillOpenElements.peek();
}
}
if (node == null) {
return;
}
computeEndLocationForErrorNode(node, start, count);
notifyProblemRequestor(exception, start, count, AntModelProblem.SEVERITY_ERROR);
markHierarchy(fLastNode, AntModelProblem.SEVERITY_ERROR, exception.getMessage());
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ant.internal.ui.model.IAntModel#errorFromElement(java.lang.Exception, org.eclipse.ant.internal.ui.model.AntElementNode, int,
* int)
*/
@Override
public void errorFromElement(Exception exception, AntElementNode node, int lineNumber, int column) {
if (node == null) {
if (!fStillOpenElements.empty()) {
node = fStillOpenElements.peek();
} else {
node = fLastNode;
}
}
computeEndLocationForErrorNode(node, lineNumber, column);
notifyProblemRequestor(exception, node, AntModelProblem.SEVERITY_ERROR);
markHierarchy(node, AntModelProblem.SEVERITY_ERROR, exception.getMessage());
}
private AntElementNode createProblemElement(SAXParseException exception) {
int lineNumber = exception.getLineNumber();
StringBuffer message = new StringBuffer(exception.getMessage());
if (lineNumber != -1) {
message.append(AntModelMessages.AntModel_1 + lineNumber);
}
AntElementNode errorNode = new AntElementNode(message.toString());
errorNode.setFilePath(exception.getSystemId());
errorNode.setProblemSeverity(AntModelProblem.SEVERITY_ERROR);
errorNode.setProblemMessage(exception.getMessage());
computeErrorLocation(errorNode, exception);
return errorNode;
}
private void computeErrorLocation(AntElementNode element, SAXParseException exception) {
if (element.isExternal()) {
return;
}
int line = exception.getLineNumber();
int startColumn = exception.getColumnNumber();
computeEndLocationForErrorNode(element, line, startColumn);
}
private void computeEndLocationForErrorNode(IAntElement element, int line, int startColumn) {
try {
if (line <= 0) {
line = 1;
}
int endColumn;
if (startColumn <= 0) {
if (element.getOffset() > -1) {
startColumn = element.getOffset() + 1;
} else {
startColumn = 1;
}
endColumn = getLastCharColumn(line) + 1;
} else {
if (startColumn > 1) {
--startColumn;
}
endColumn = startColumn;
if (startColumn <= getLastCharColumn(line)) {
++endColumn;
}
}
int correction = 0;
if (element.getOffset() == -1) {
int originalOffset = getOffset(line, startColumn);
int nonWhitespaceOffset = originalOffset;
try {
nonWhitespaceOffset = getNonWhitespaceOffset(line, startColumn);
}
catch (BadLocationException be) {
// do nothing
}
element.setOffset(nonWhitespaceOffset);
correction = nonWhitespaceOffset - originalOffset;
}
if (endColumn - startColumn == 0) {
int offset = getOffset(line, startColumn);
element.setLength(offset - element.getOffset() - correction);
} else {
element.setLength(endColumn - startColumn - correction);
}
}
catch (BadLocationException e) {
// ignore as the parser may be out of sync with the document during reconciliation
}
}
private void handleError(Exception exception, int severity) {
IAntElement node = null;
if (fStillOpenElements.isEmpty()) {
if (exception instanceof SAXParseException) {
node = createProblemElement((SAXParseException) exception);
}
} else {
node = fStillOpenElements.peek();
}
if (node == null) {
return;
}
markHierarchy(node, severity, exception.getMessage());
if (exception instanceof SAXParseException) {
SAXParseException parseException = (SAXParseException) exception;
if (node.getOffset() == -1) {
computeEndLocationForErrorNode(node, parseException.getLineNumber() - 1, parseException.getColumnNumber());
} else {
int lineNumber = parseException.getLineNumber();
int columnNumber = parseException.getColumnNumber();
if (columnNumber == -1) {
columnNumber = 1;
}
try {
AntElementNode childNode = node.getNode(getNonWhitespaceOffset(lineNumber, columnNumber) + 1);
if (childNode != null && childNode != node) {
node = childNode;
node.setProblemSeverity(severity);
node.setProblemMessage(exception.getMessage());
} else {
node = createProblemElement(parseException);
}
}
catch (BadLocationException be) {
node = createProblemElement(parseException);
}
}
}
notifyProblemRequestor(exception, node, severity);
if (node != null) {
while (node.getParentNode() != null) {
IAntElement parentNode = node.getParentNode();
if (parentNode.getLength() == -1) {
parentNode.setLength(node.getOffset() - parentNode.getOffset() + node.getLength());
}
node = parentNode;
}
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ant.internal.ui.model.IAntModel#fatalError(java.lang.Exception)
*/
@Override
public void fatalError(Exception exception) {
handleError(exception, AntModelProblem.SEVERITY_FATAL_ERROR);
}
public AntElementNode getOpenElement() {
if (fStillOpenElements.isEmpty()) {
return null;
}
return fStillOpenElements.peek();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ant.internal.ui.model.IAntModel#getEntityName(java.lang.String)
*/
@Override
public String getEntityName(String path) {
if (fEntityNameToPath != null) {
Iterator<String> itr = fEntityNameToPath.keySet().iterator();
String entityPath;
String name;
while (itr.hasNext()) {
name = itr.next();
entityPath = fEntityNameToPath.get(name);
if (entityPath.equals(path)) {
return name;
}
}
}
return null;
}
private ClassLoader getClassLoader(ClassLoader contextClassLoader) {
synchronized (loaderLock) {
if (fLocalClassLoader != null) {
((AntClassLoader) fLocalClassLoader).setPluginContextClassloader(contextClassLoader);
return fLocalClassLoader;
}
if (fgClassLoader == null) {
fgClassLoader = AntCorePlugin.getPlugin().getNewClassLoader(true);
}
if (fgClassLoader instanceof AntClassLoader) {
((AntClassLoader) fgClassLoader).setPluginContextClassloader(contextClassLoader);
}
return fgClassLoader;
}
}
public String getTargetDescription(String targetName) {
AntTargetNode target = getTargetNode(targetName);
if (target != null) {
return target.getTarget().getDescription();
}
return null;
}
public AntTargetNode getTargetNode(String targetName) {
AntProjectNode projectNode = getProjectNode();
if (projectNode == null) {
return null;
}
if (projectNode.hasChildren()) {
List<IAntElement> possibleTargets = projectNode.getChildNodes();
for (IAntElement node : possibleTargets) {
if (node instanceof AntTargetNode) {
AntTargetNode targetNode = (AntTargetNode) node;
if (targetName.equalsIgnoreCase(targetNode.getTarget().getName())) {
return targetNode;
}
}
}
}
return null;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ant.internal.ui.model.IAntModel#getProjectNode(boolean)
*/
@Override
public AntProjectNode getProjectNode(boolean doReconcile) {
if (doReconcile) {
synchronized (getLockObject()) { // ensure to wait for any current synchronization
reconcile();
}
}
return fProjectNode;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ant.internal.ui.model.IAntModel#getProjectNode()
*/
@Override
public AntProjectNode getProjectNode() {
return getProjectNode(true);
}
public AntElementNode getNode(int offset, boolean waitForReconcile) {
if (getProjectNode(waitForReconcile) != null) {
return getProjectNode(waitForReconcile).getNode(offset);
}
return null;
}
/**
* Removes any type definitions that no longer exist in the buildfile
*/
private void reconcileTaskAndTypes() {
if (fCurrentNodeIdentifiers == null || fDefinerNodeIdentifierToDefinedTasks == null) {
return;
}
Iterator<String> iter = fDefinerNodeIdentifierToDefinedTasks.keySet().iterator();
ComponentHelper helper = ComponentHelper.getComponentHelper(fProjectNode.getProject());
while (iter.hasNext()) {
String key = iter.next();
if (fCurrentNodeIdentifiers.get(key) == null) {
removeDefinerTasks(key, helper.getAntTypeTable());
}
}
}
protected void removeDefinerTasks(String definerIdentifier, Hashtable<String, AntTypeDefinition> typeTable) {
if (fDefinerNodeIdentifierToDefinedTasks == null) {
return;
}
List<String> tasks = fDefinerNodeIdentifierToDefinedTasks.get(definerIdentifier);
if (tasks == null) {
return;
}
Iterator<String> iterator = tasks.iterator();
while (iterator.hasNext()) {
typeTable.remove(iterator.next());
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ant.internal.ui.model.IAntModel#addComment(int, int, int)
*/
@Override
public void addComment(int lineNumber, int columnNumber, int length) {
AntCommentNode commentNode = new AntCommentNode();
int offset = -1;
try {
offset = computeOffset(lineNumber, columnNumber, "-->"); //$NON-NLS-1$
}
catch (BadLocationException e) {
commentNode.setExternal(true);
commentNode.setExternalInfo(lineNumber, columnNumber);
offset = length - 1;
}
commentNode.setOffset(offset - length);
commentNode.setLength(length);
fNonStructuralNodes.add(commentNode);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ant.internal.ui.model.IAntModel#needsTaskResolution()
*/
@Override
public boolean canGetTaskInfo() {
return fHasTaskInfo;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ant.internal.ui.model.IAntModel#needsLexicalResolution()
*/
@Override
public boolean canGetLexicalInfo() {
return fHasLexicalInfo;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ant.internal.ui.model.IAntModel#setClassLoader(java.net.URLClassLoader)
*/
@Override
public void setClassLoader(URLClassLoader loader) {
AntDefiningTaskNode.setJavaClassPath(loader.getURLs());
fLocalClassLoader = loader;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ant.internal.ui.model.IAntModel#needsPositionResolution()
*/
@Override
public boolean canGetPositionInfo() {
return fHasPositionInfo;
}
public String getPath(String text, int offset) {
if (fEntityNameToPath != null) {
String path = fEntityNameToPath.get(text);
if (path != null) {
return path;
}
}
AntElementNode node = getNode(offset, true);
if (node != null) {
return node.getReferencedElement(offset);
}
return null;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ant.internal.ui.model.IAntModel#getText(int, int)
*/
@Override
public String getText(int offset, int length) {
try {
return fDocument.get(offset, length);
}
catch (BadLocationException e) {
// do nothing
}
return null;
}
private IAntElement findPropertyNode(String text, List<IAntElement> children) {
for (IAntElement element : children) {
if (element instanceof AntPropertyNode) {
if (((AntPropertyNode) element).getProperty(text) != null) {
return element;
}
} else if (element.hasChildren()) {
IAntElement found = findPropertyNode(text, element.getChildNodes());
if (found != null) {
return found;
}
}
}
return null;
}
public IAntElement getPropertyNode(String text) {
AntProjectNode node = getProjectNode();
if (node == null || !node.hasChildren()) {
return null;
}
return findPropertyNode(text, node.getChildNodes());
}
public List<AntElementNode> getNonStructuralNodes() {
return fNonStructuralNodes;
}
public void updateForInitialReconcile() {
fMarkerUpdater.updateMarkers();
fShouldReconcile = AntUIPlugin.getDefault().getPreferenceStore().getBoolean(AntEditorPreferenceConstants.EDITOR_RECONCILE);
}
public void updateMarkers() {
boolean temp = fShouldReconcile;
try {
fShouldReconcile = true;
reconcile();
fMarkerUpdater.updateMarkers();
}
finally {
fShouldReconcile = temp;
}
}
public AntElementNode getReferenceNode(String text) {
Object reference = getReferenceObject(text);
if (reference == null) {
return null;
}
Set<Task> nodes = fTaskToNode.keySet();
Iterator<Task> iter = nodes.iterator();
while (iter.hasNext()) {
Task task = iter.next();
Task tmptask = task;
if (tmptask instanceof UnknownElement) {
UnknownElement element = (UnknownElement) tmptask;
RuntimeConfigurable wrapper = element.getWrapper();
Map<String, Object> attributes = wrapper.getAttributeMap();
String id = (String) attributes.get("id"); //$NON-NLS-1$
if (text.equals(id)) {
return fTaskToNode.get(task);
}
}
}
return null;
}
public Object getReferenceObject(String refId) {
AntProjectNode projectNode = getProjectNode();
if (projectNode == null) {
return null;
}
try {
Project project = projectNode.getProject();
Object ref = project.getReference(refId);
return ref;
}
catch (BuildException be) {
handleBuildException(be, null);
}
return null;
}
public String getPropertyValue(String propertyName) {
AntProjectNode projectNode = getProjectNode();
if (projectNode == null) {
return null;
}
return projectNode.getProject().getProperty(propertyName);
}
/**
* Only called if the AntModel is associated with an AntEditor
*/
public void install() {
fListener = new IDocumentListener() {
@Override
public void documentAboutToBeChanged(DocumentEvent event) {
synchronized (fDirtyLock) {
fIsDirty = true;
}
}
@Override
public void documentChanged(DocumentEvent event) {
// do nothing
}
};
fDocument.addDocumentListener(fListener);
}
private void reconcileForPropertyChange(boolean classpathChanged) {
if (classpathChanged) {
fProjectNode = null; // need to reset tasks, types and properties
fgClassLoader = null;
AntDefiningTaskNode.setJavaClassPath();
ProjectHelper.reset();
}
fIsDirty = true;
reconcile();
AntModelCore.getDefault().notifyAntModelListeners(new AntModelChangeEvent(this, true));
fMarkerUpdater.updateMarkers();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ant.internal.ui.model.IAntModel#setProperties(java.util.Map)
*/
@Override
public void setProperties(Map<String, String> properties) {
fProperties = properties;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ant.internal.ui.model.IAntModel#setPropertyFiles(java.util.List)
*/
@Override
public void setPropertyFiles(String[] propertyFiles) {
if (propertyFiles != null) {
fPropertyFiles = Arrays.asList(propertyFiles);
}
}
@Override
public void setDefiningTaskNodeText(AntDefiningTaskNode node) {
if (fDefinersToText == null) {
fDefinersToText = new HashMap<String, String>();
fCurrentNodeIdentifiers = new HashMap<String, String>();
}
String nodeIdentifier = node.getIdentifier();
String nodeText = null;
if (fPreviousDefinersToText != null) {
nodeText = fPreviousDefinersToText.get(nodeIdentifier);
}
String newNodeText = getText(node.getOffset(), node.getLength());
if (nodeText != null) {
if (nodeText.equals(newNodeText)) {
node.setNeedsToBeConfigured(false);
// update the data structures for the new node as the offset may have changed.
List<String> tasks = fDefinerNodeIdentifierToDefinedTasks.get(nodeIdentifier);
if (tasks != null) {
for (Iterator<String> iter = tasks.iterator(); iter.hasNext();) {
String taskName = iter.next();
fTaskNameToDefiningNode.put(taskName, node);
}
}
}
}
if (newNodeText != null) {
fDefinersToText.put(nodeIdentifier, newNodeText);
}
fCurrentNodeIdentifiers.put(nodeIdentifier, nodeIdentifier);
}
protected void removeDefiningTaskNodeInfo(AntDefiningTaskNode node) {
Object identifier = node.getIdentifier();
if (identifier != null && fCurrentNodeIdentifiers != null) {
fCurrentNodeIdentifiers.remove(identifier);
fDefinersToText.remove(identifier);
}
}
protected void addDefinedTasks(List<String> newTasks, AntDefiningTaskNode node) {
if (fTaskNameToDefiningNode == null) {
fTaskNameToDefiningNode = new HashMap<String, AntDefiningTaskNode>();
fDefinerNodeIdentifierToDefinedTasks = new HashMap<String, List<String>>();
}
String identifier = node.getIdentifier();
if (identifier == null) {
return;
}
if (newTasks.isEmpty() && fCurrentNodeIdentifiers != null) {
fCurrentNodeIdentifiers.remove(identifier);
}
fDefinerNodeIdentifierToDefinedTasks.put(identifier, newTasks);
Iterator<String> iter = newTasks.iterator();
while (iter.hasNext()) {
String name = iter.next();
fTaskNameToDefiningNode.put(name, node);
}
}
public AntDefiningTaskNode getDefininingTaskNode(String nodeName) {
if (fTaskNameToDefiningNode != null) {
AntDefiningTaskNode node = fTaskNameToDefiningNode.get(nodeName);
if (node == null) {
nodeName = getNamespaceCorrectName(nodeName);
node = fTaskNameToDefiningNode.get(nodeName);
}
return node;
}
return null;
}
public String getNamespaceCorrectName(String nodeName) {
String prefix = org.apache.tools.ant.ProjectHelper.extractUriFromComponentName(nodeName);
String uri = getPrefixMapping(prefix);
nodeName = org.apache.tools.ant.ProjectHelper.genComponentName(uri, org.apache.tools.ant.ProjectHelper.extractNameFromComponentName(nodeName));
return nodeName;
}
public String getUserNamespaceCorrectName(String nodeName) {
String prefix = org.apache.tools.ant.ProjectHelper.extractUriFromComponentName(nodeName);
if (prefix.length() > 0) {
String uri = getUserPrefixMapping(prefix);
nodeName = org.apache.tools.ant.ProjectHelper.genComponentName(uri, org.apache.tools.ant.ProjectHelper.extractNameFromComponentName(nodeName));
}
return nodeName;
}
public AntTaskNode getMacroDefAttributeNode(String macroDefAttributeName) {
if (fTaskNameToDefiningNode == null) {
return null;
}
for (AntDefiningTaskNode definingNode : fTaskNameToDefiningNode.values()) {
List<IAntElement> attributes = definingNode.getChildNodes();
if (attributes != null) {
for (IAntElement element : attributes) {
if (macroDefAttributeName.equals(element.getLabel())) {
return (AntTaskNode) element;
}
}
}
}
return null;
}
/**
* Sets whether the AntModel should reconcile if it become dirty. If set to reconcile, a reconcile is triggered if the model is dirty.
*
* @param shouldReconcile
*/
public void setShouldReconcile(boolean shouldReconcile) {
fShouldReconcile = shouldReconcile;
if (fShouldReconcile) {
reconcile();
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ant.internal.ui.model.IAntModel#addPrefixMapping(java.lang.String, java.lang.String)
*/
@Override
public void addPrefixMapping(String prefix, String uri) {
if (fNamespacePrefixMappings == null) {
fNamespacePrefixMappings = new HashMap<String, String>();
}
fNamespacePrefixMappings.put(prefix, uri);
}
private String getPrefixMapping(String prefix) {
if (fNamespacePrefixMappings != null) {
return fNamespacePrefixMappings.get(prefix);
}
return null;
}
private String getUserPrefixMapping(String prefix) {
if (fNamespacePrefixMappings != null) {
Set<Entry<String, String>> entrySet = fNamespacePrefixMappings.entrySet();
Iterator<Entry<String, String>> entries = entrySet.iterator();
while (entries.hasNext()) {
Map.Entry<String, String> entry = entries.next();
if (entry.getValue().equals(prefix)) {
return entry.getKey();
}
}
}
return null;
}
/**
* Compute the encoding for the backing build file
*
* @since 3.7
*/
void computeEncoding() {
try {
IFile file = getFile();
if (file != null) {
fEncoding = getFile().getCharset(true);
return;
}
}
catch (CoreException e) {
// do nothing. default to UTF-8
}
// try the file buffer manager - likely an external file
IPath path = getLocationProvider().getLocation();
if (path != null) {
File buildfile = path.toFile();
try (FileReader reader = new FileReader(buildfile)) {
QualifiedName[] options = new QualifiedName[] { IContentDescription.CHARSET };
IContentDescription desc = Platform.getContentTypeManager().getDescriptionFor(reader, buildfile.getName(), options);
if (desc != null) {
fEncoding = desc.getCharset();
return;
}
}
catch (IOException ioe) {
// do nothing
}
}
fEncoding = IAntCoreConstants.UTF_8;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ant.internal.ui.model.IAntModel#getEncoding()
*/
@Override
public String getEncoding() {
return fEncoding;
}
}