blob: d4fc66a9785e16fad245ee159f078159e0cc1f1b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2004 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Nico Seessle - bug 51332
*******************************************************************************/
package org.eclipse.ant.internal.ui.editor.outline;
import java.io.File;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import org.apache.tools.ant.AntTypeDefinition;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.ComponentHelper;
import org.apache.tools.ant.Location;
import org.apache.tools.ant.Project;
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.Type;
import org.eclipse.ant.internal.ui.editor.model.AntDefiningTaskNode;
import org.eclipse.ant.internal.ui.editor.model.AntElementNode;
import org.eclipse.ant.internal.ui.editor.model.AntImportNode;
import org.eclipse.ant.internal.ui.editor.model.AntProjectNode;
import org.eclipse.ant.internal.ui.editor.model.AntPropertyNode;
import org.eclipse.ant.internal.ui.editor.model.AntTargetNode;
import org.eclipse.ant.internal.ui.editor.model.AntTaskNode;
import org.eclipse.ant.internal.ui.editor.model.IAntModelConstants;
import org.eclipse.ant.internal.ui.editor.text.XMLReconcilingStrategy;
import org.eclipse.ant.internal.ui.editor.utils.ProjectHelper;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Preferences;
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.reconciler.DirtyRegion;
import org.xml.sax.Attributes;
import org.xml.sax.SAXParseException;
public class AntModel {
private static ClassLoader fgClassLoader;
private static int fgInstanceCount= 0;
private XMLCore fCore;
private IDocument fDocument;
private IProblemRequestor fProblemRequestor;
private LocationProvider fLocationProvider;
private AntProjectNode fProjectNode;
private AntTargetNode fCurrentTargetNode;
private AntElementNode fLastNode;
private AntElementNode fNodeBeingResolved;
private AntTargetNode fIncrementalTarget= null;
private boolean fReplaceHasOccurred= false;
private int fRemoveLengthOfReplace= 0;
private DirtyRegion fDirtyRegion= null;
/**
* Stack of still open elements.
* <P>
* On top of the stack is the innermost element.
*/
private Stack fStillOpenElements = new Stack();
private Map fTaskToNode= new HashMap();
private List fTaskNodes= new ArrayList();
private Map fEntityNameToPath;
private final Object fDirtyLock= new Object();
private boolean fIsDirty= true;
private IDocumentListener fListener;
private File fEditedFile= null;
private AntEditorMarkerUpdater fMarkerUpdater= null;
private Set fNamesOfOldDefiningNodes;
private Preferences.IPropertyChangeListener fPropertyChangeListener= new Preferences.IPropertyChangeListener() {
public void propertyChange(Preferences.PropertyChangeEvent event) {
fgClassLoader= null;
AntDefiningTaskNode.setJavaClassPath();
fIsDirty= true;
reconcile(null);
updateMarkers();
}
};
public AntModel(XMLCore core, IDocument document, IProblemRequestor problemRequestor, LocationProvider locationProvider) {
fCore= core;
fDocument= document;
fProblemRequestor= problemRequestor;
fMarkerUpdater= new AntEditorMarkerUpdater();
fMarkerUpdater.setModel(this);
fLocationProvider= locationProvider;
AntCorePlugin.getPlugin().getPluginPreferences().addPropertyChangeListener(fPropertyChangeListener);
AntDefiningTaskNode.setJavaClassPath();
fgInstanceCount++;
}
public void install() {
fListener= new IDocumentListener() {
public void documentAboutToBeChanged(DocumentEvent event) {
synchronized (fDirtyLock) {
fIsDirty= true;
}
}
public void documentChanged(DocumentEvent event) {}
};
fDocument.addDocumentListener(fListener);
}
public void dispose() {
synchronized (this) {
if (fDocument != null) {
fDocument.removeDocumentListener(fListener);
}
fDocument= null;
fCore= null;
ProjectHelper.setAntModel(null);
}
AntCorePlugin.getPlugin().getPluginPreferences().removePropertyChangeListener(fPropertyChangeListener);
fgInstanceCount--;
if (fgInstanceCount == 0) {
fgClassLoader= null;
}
if (getProjectNode() != null) {
//cleanup the introspection helpers that may have been
//generated
getProjectNode().getProject().fireBuildFinished(null);
}
}
public void reconcile(DirtyRegion region) {
//TODO turn off incremental as it is deferred to post 3.0
region= null;
fDirtyRegion= region;
synchronized (fDirtyLock) {
if (!fIsDirty) {
return;
}
if (fReplaceHasOccurred && region != null) {
//this is the removed part of a replace
//the insert region will be along shortly
fRemoveLengthOfReplace= region.getLength();
fReplaceHasOccurred= false;
return;
}
fIsDirty= false;
}
synchronized (this) {
if (fCore == null) {
// disposed
notifyAll();
return;
}
if (fDocument == null) {
fProjectNode= null;
} else {
reset(region);
if (fDocument.get().trim().length() != 0) {
parseDocument(fDocument, region);
}
fRemoveLengthOfReplace= 0;
fDirtyRegion= null;
reconcileTaskAndTypes();
}
fCore.notifyDocumentModelListeners(new DocumentModelChangeEvent(this));
notifyAll();
}
}
private void reset(DirtyRegion region) {
//TODO this could be better for incremental parsing
//cleaning up the task to node map (do when a target is reset)
fCurrentTargetNode= null;
if (region == null ) {
fStillOpenElements= new Stack();
fTaskToNode= new HashMap();
fTaskNodes= new ArrayList();
fNodeBeingResolved= null;
fLastNode= null;
}
}
public AntElementNode[] getRootElements() {
possiblyWaitForReconcile();
if (fProjectNode == null) {
return new AntElementNode[0];
}
return new AntElementNode[] {fProjectNode};
}
private void parseDocument(IDocument input, DirtyRegion region) {
boolean parsed= true;
if (input.getLength() == 0) {
fProjectNode= null;
parsed= false;
return;
}
ClassLoader parsingClassLoader= getClassLoader();
ClassLoader originalClassLoader= Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(parsingClassLoader);
boolean incremental= false;
Project project= null;
try {
String textToParse= null;
ProjectHelper projectHelper= null;
if (region == null || fProjectNode == null) { //full parse
if (fProjectNode == null || !fProjectNode.hasChildren()) {
fProjectNode= null;
project = new AntModelProject();
projectHelper= prepareForFullParse(project, parsingClassLoader);
textToParse= input.get(); //the entire document
} else {
project= fProjectNode.getProject();
projectHelper= (ProjectHelper)project.getReference("ant.projectHelper"); //$NON-NLS-1$
textToParse= prepareForFullIncremental(input);
}
} else { //incremental
project= fProjectNode.getProject();
textToParse= prepareForIncrementalParse(project, region, input);
if (textToParse == null) {
parsed= false;
return;
}
incremental= true;
projectHelper= (ProjectHelper)project.getReference("ant.projectHelper"); //$NON-NLS-1$
}
beginReporting();
Map 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 {
Thread.currentThread().setContextClassLoader(originalClassLoader);
if (parsed) {
if (incremental) {
updateAfterIncrementalChange(region, true);
}
resolveBuildfile();
endReporting();
project.fireBuildFinished(null); //cleanup (IntrospectionHelper)
fIncrementalTarget= null;
}
}
}
private void updateAfterIncrementalChange(DirtyRegion region, boolean updateProjectLength) {
if (fProjectNode == null) {
return;
}
int editAdjustment= determineEditAdjustment(region);
if (editAdjustment == 0) {
return;
}
if (updateProjectLength) { //edit within the project
fProjectNode.setLength(fProjectNode.getLength() + editAdjustment);
} else {
fProjectNode.setOffset(fProjectNode.getOffset() + editAdjustment);
}
if ((fIncrementalTarget != null || !updateProjectLength) && fProjectNode.hasChildren()) {
List children= fProjectNode.getChildNodes();
int index= children.indexOf(fIncrementalTarget) + 1;
updateNodesForIncrementalParse(editAdjustment, children, index);
}
}
private void updateNodesForIncrementalParse(int editAdjustment, List children, int index) {
AntElementNode node;
for (int i = index; i < children.size(); i++) {
node= (AntElementNode)children.get(i);
node.setOffset(node.getOffset() + editAdjustment);
if (node.hasChildren()) {
updateNodesForIncrementalParse(editAdjustment, node.getChildNodes(), 0);
}
}
}
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= ""; //$NON-NLS-1$
if (file != null) {
filePath= file.getAbsolutePath();
}
project.setUserProperty("ant.file", filePath); //$NON-NLS-1$
ProjectHelper projectHelper= new ProjectHelper(this);
projectHelper.setBuildFile(file);
project.addReference("ant.projectHelper", projectHelper); //$NON-NLS-1$
return projectHelper;
}
private String prepareForIncrementalParse(Project project, DirtyRegion region, IDocument input) {
String textToParse= null;
AntElementNode node= fProjectNode.getNode(region.getOffset());
if (node == null) {
if (fProjectNode.getLength() > 0) {
//outside of any element
if (region.getOffset() < fProjectNode.getOffset()) {
updateAfterIncrementalChange(region, false);
}
return null;
}
//nodes don't know their lengths due to parsing error --> full parse
textToParse = prepareForFullIncremental(input);
return textToParse;
}
while (node != null && !(node instanceof AntTargetNode)) {
node= node.getParentNode();
}
if (node == null) { //no enclosing target node found
if (region.getText() != null && region.getText().trim().length() == 0) {
return null; //no need to parse for whitespace additions
}
textToParse= prepareForFullIncremental(input);
} else {
fIncrementalTarget= (AntTargetNode)node;
if (fIncrementalTarget.hasChildren()) {
Collection nodes= fTaskToNode.values();
nodes.removeAll(fIncrementalTarget.getDescendents());
}
markHierarchy(node, XMLProblem.NO_PROBLEM);
StringBuffer temp = createIncrementalContents(project);
fIncrementalTarget.reset();
try {
int editAdjustment = determineEditAdjustment(region) + 1;
String targetString= input.get(node.getOffset() - 1, node.getLength() + editAdjustment);
temp.append(targetString);
temp.append("\n</project>"); //$NON-NLS-1$
textToParse= temp.toString();
} catch (BadLocationException e) {
textToParse= input.get();
}
}
return textToParse;
}
private String prepareForFullIncremental(IDocument input) {
String textToParse= input.get();
fProjectNode.reset();
fTaskToNode= new HashMap();
fTaskNodes= new ArrayList();
return textToParse;
}
private StringBuffer createIncrementalContents(Project project) {
int offset= fIncrementalTarget.getOffset();
int line= getLine(offset) - 1;
StringBuffer temp= new StringBuffer("<project");//$NON-NLS-1$
String defltTarget= project.getDefaultTarget();
if (defltTarget != null) {
temp.append(" default=\""); //$NON-NLS-1$
temp.append(defltTarget);
temp.append("\""); //$NON-NLS-1$
}
temp.append(">"); //$NON-NLS-1$
while (line > 0) {
temp.append("\n"); //$NON-NLS-1$
line--;
}
return temp;
}
private int determineEditAdjustment(DirtyRegion region) {
int editAdjustment= 0;
if (region.getType().equals(DirtyRegion.INSERT)) {
editAdjustment+= region.getLength() - fRemoveLengthOfReplace;
} else {
editAdjustment-= region.getLength();
}
return editAdjustment;
}
private void initializeProject(Project project, ClassLoader loader) {
project.init();
setTasks(project, loader);
setTypes(project, loader);
}
private void setTasks(Project project, ClassLoader loader) {
List tasks = AntCorePlugin.getPlugin().getPreferences().getTasks();
for (Iterator iterator = tasks.iterator(); iterator.hasNext();) {
org.eclipse.ant.core.Task task = (org.eclipse.ant.core.Task) iterator.next();
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 types = AntCorePlugin.getPlugin().getPreferences().getTypes();
for (Iterator iterator = types.iterator(); iterator.hasNext();) {
Type type = (Type) iterator.next();
AntTypeDefinition def = new AntTypeDefinition();
def.setName(type.getTypeName());
def.setClassName(type.getClassName());
def.setClassLoader(loader);
ComponentHelper.getComponentHelper(project).addDataTypeDefinition(def);
}
}
private void resolveBuildfile() {
Collection nodeCopy= new ArrayList(fTaskNodes.size());
nodeCopy.addAll(fTaskNodes);
Iterator iter= nodeCopy.iterator();
while (iter.hasNext()) {
AntTaskNode node = (AntTaskNode) iter.next();
fNodeBeingResolved= node;
if (node.configure(false)) {
//resolve any new elements that may have been added
resolveBuildfile();
}
}
fNodeBeingResolved= null;
checkTargets();
}
/**
* Check that we have a default target defined and that the
* target dependencies exist.
*/
private void checkTargets() {
if (fProjectNode == null) {
return;
}
String defaultTargetName= fProjectNode.getProject().getDefaultTarget();
if (defaultTargetName == null || fProjectNode.getProject().getTargets().get(defaultTargetName) == null) {
//no default target
String message;
if (defaultTargetName == null) {
message= AntOutlineMessages.getString("AntModel.0"); //$NON-NLS-1$
} else {
message= MessageFormat.format(AntOutlineMessages.getString("AntModel.43"), new String[]{defaultTargetName}); //$NON-NLS-1$
}
IProblem problem= createProblem(message, fProjectNode.getOffset(), fProjectNode.getSelectionLength(), XMLProblem.SEVERITY_ERROR);
acceptProblem(problem);
markHierarchy(fProjectNode, XMLProblem.SEVERITY_ERROR);
}
if (!fProjectNode.hasChildren()) {
return;
}
List children= fProjectNode.getChildNodes();
Iterator iter= children.iterator();
while (iter.hasNext()) {
AntElementNode node = (AntElementNode) iter.next();
if (node instanceof AntTargetNode) {
String missing= ((AntTargetNode)node).checkDependencies();
if (missing != null) {
String message= MessageFormat.format(AntOutlineMessages.getString("AntModel.44"), new String[]{missing}); //$NON-NLS-1$
IProblem problem= createProblem(message, node.getOffset(), node.getSelectionLength(), XMLProblem.SEVERITY_ERROR);
acceptProblem(problem);
markHierarchy(node, XMLProblem.SEVERITY_ERROR);
}
}
}
}
public void handleBuildException(BuildException e, AntElementNode node, int severity) {
try {
if (node != null) {
markHierarchy(node, severity);
}
Location location= e.getLocation();
int line= 0;
int originalOffset= 0;
int nonWhitespaceOffset= 0;
int length= 0;
if (location == Location.UNKNOWN_LOCATION && node != null) {
nonWhitespaceOffset= node.getOffset();
length= node.getLength();
} else {
line= location.getLineNumber();
if (line == 0) {
if (getProjectNode() != null) {
length= getProjectNode().getSelectionLength();
nonWhitespaceOffset= getProjectNode().getOffset();
if (severity == XMLProblem.SEVERITY_ERROR) {
getProjectNode().setProblemSeverity(XMLProblem.NO_PROBLEM);
}
} else {
return;
}
} else {
if (node == null) {
originalOffset= getOffset(line, 1);
nonWhitespaceOffset= originalOffset;
try {
nonWhitespaceOffset= getNonWhitespaceOffset(line, 1);
} catch (BadLocationException be) {
}
length= getLastCharColumn(line) - (nonWhitespaceOffset - originalOffset);
} else {
nonWhitespaceOffset= node.getOffset();
length= node.getLength();
}
}
}
notifyProblemRequestor(e, nonWhitespaceOffset, length, severity);
} catch (BadLocationException e1) {
}
}
public void handleBuildException(BuildException e, AntElementNode node) {
handleBuildException(e, node, XMLProblem.SEVERITY_ERROR);
}
public File getEditedFile() {
if (fLocationProvider != null && fEditedFile == null) {
fEditedFile= fLocationProvider.getLocation().toFile();
}
return fEditedFile;
}
private void markHierarchy(AntElementNode openElement, int severity) {
while (openElement != null) {
openElement.setProblemSeverity(severity);
openElement= openElement.getParentNode();
}
}
public LocationProvider getLocationProvider() {
return fLocationProvider;
}
public void addTarget(Target newTarget, int line, int column) {
if (fIncrementalTarget != null) {
fCurrentTargetNode= fIncrementalTarget;
fCurrentTargetNode.setTarget(newTarget);
fStillOpenElements.push(fCurrentTargetNode);
} else {
AntTargetNode targetNode= new AntTargetNode(newTarget);
fProjectNode.addChildNode(targetNode);
fCurrentTargetNode= targetNode;
fStillOpenElements.push(targetNode);
computeOffset(targetNode, line, column);
if (fNodeBeingResolved instanceof AntImportNode) {
targetNode.setImportNode(fNodeBeingResolved);
}
}
}
public void addProject(Project project, int line, int column) {
if (fIncrementalTarget != null) {
return;
}
fProjectNode= new AntProjectNode((AntModelProject)project, this);
fStillOpenElements.push(fProjectNode);
computeOffset(fProjectNode, line, column);
}
public void addTask(Task newTask, Task parentTask, Attributes attributes, int line, int column) {
AntTaskNode taskNode= null;
if (parentTask == null) {
taskNode= newTaskNode(newTask, attributes);
if (fCurrentTargetNode == null) {
fProjectNode.addChildNode(taskNode);
} else {
fCurrentTargetNode.addChildNode(taskNode);
if (taskNode.isExternal()) {
fCurrentTargetNode.setExternal(true);
fCurrentTargetNode.setFilePath(taskNode.getFilePath());
}
}
} else {
taskNode= newNotWellKnownTaskNode(newTask, attributes);
((AntTaskNode)fTaskToNode.get(parentTask)).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
int index= fTaskNodes.indexOf(fNodeBeingResolved) + 1;
fTaskNodes.add(index, taskNode);
} else {
fTaskNodes.add(taskNode);
}
}
public void addEntity(String entityName, String entityPath) {
if (fEntityNameToPath == null) {
fEntityNameToPath= new HashMap();
}
fEntityNameToPath.put(entityName, entityPath);
}
private AntTaskNode newTaskNode(Task newTask, Attributes attributes) {
AntTaskNode newNode= null;
String taskName= newTask.getTaskName();
if (isPropertySettingTask(taskName)) { //$NON-NLS-1$
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$
String name = attributes.getValue(IAntModelConstants.ATTR_NAME);
newNode= new AntDefiningTaskNode(newTask, name);
} 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, IAntModelConstants.ATTR_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("delete")) { //$NON-NLS-1$
String label = "delete "; //$NON-NLS-1$
String file = attributes.getValue(IAntModelConstants.ATTR_FILE);
if(file != null) {
label+= file;
} else {
file = attributes.getValue(IAntModelConstants.ATTR_DIR);
if(file != null) {
label+= file;
}
}
newNode= new AntTaskNode(newTask, label);
} else if(taskName.equalsIgnoreCase("import")) { //$NON-NLS-1$
newNode= new AntTaskNode(newTask, generateLabel(taskName, attributes, IAntModelConstants.ATTR_FILE)); //$NON-NLS-1$
} else {
newNode = newNotWellKnownTaskNode(newTask, attributes);
}
String taskFileName= newTask.getLocation().getFileName();
boolean external= isTaskExternal(taskFileName);
newNode.setExternal(external);
if (external) {
newNode.setFilePath(taskFileName);
}
return newNode;
}
/**
* @param taskName the name of the task to check
* @return whether or not a task with this name sets properties
*/
public static 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$
}
private boolean isTaskExternal(String taskFileName) {
File taskFile= new File(taskFileName);
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);
}
return newNode;
}
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 (element.isExternal()) {
return;
}
try {
int offset;
String prefix= "<"; //$NON-NLS-1$
if (column <= 0) {
offset= getOffset(line, 0);
int lastCharColumn= getLastCharColumn(line);
offset= computeOffsetUsingPrefix(element, line, offset, prefix, lastCharColumn);
} else {
offset= getOffset(line, column);
offset= computeOffsetUsingPrefix(element, line, offset, prefix, column);
}
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 computeOffsetUsingPrefix(AntElementNode element, int line, int offset, String prefix, int column) throws BadLocationException {
String lineText= fDocument.get(fDocument.getLineOffset(line - 1), column);
int lastIndex= lineText.indexOf(prefix + element.getName());
if (lastIndex > -1) {
offset= getOffset(line, lastIndex + 1);
} else {
return computeOffsetUsingPrefix(element, line - 1, offset, prefix, getLastCharColumn(line - 1));
}
return offset;
}
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;
}
private 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;
}
public void setCurrentElementLength(int lineNumber, int column) {
fLastNode= (AntElementNode)fStillOpenElements.pop();
if (fLastNode == fCurrentTargetNode) {
fCurrentTargetNode= null; //the current target element has been closed
}
computeLength(fLastNode, lineNumber, column);
}
public void acceptProblem(IProblem problem) {
if (fProblemRequestor != null) {
fProblemRequestor.acceptProblem(problem);
fMarkerUpdater.acceptProblem(problem);
}
}
protected IFile getFile() {
IPath location= fLocationProvider.getLocation();
if (location == null) {
return null;
}
IFile[] files= ResourcesPlugin.getWorkspace().getRoot().findFilesForLocation(location);
if (files.length > 0) {
return files[0];
}
return null;
}
private void beginReporting() {
if (fProblemRequestor != null) {
fProblemRequestor.beginReporting();
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 XMLProblem(message, severity, offset, length, getLine(offset));
}
protected void notifyProblemRequestor(Exception exception, AntElementNode element, int severity) {
IProblem problem= createProblem(exception, element.getOffset(), element.getLength(), severity);
acceptProblem(problem);
element.associatedProblem(problem);
}
protected void notifyProblemRequestor(Exception exception, int offset, int length, int severity) {
if (fProblemRequestor != null) {
IProblem problem= createProblem(exception, offset, length, severity);
acceptProblem(problem);
}
}
public void warning(Exception exception) {
notifyProblemRequestor(exception, (AntElementNode)fStillOpenElements.pop(), XMLProblem.SEVERITY_WARNING);
}
public void error(Exception exception) {
AntElementNode node= null;
if (fStillOpenElements.empty()) {
if (exception instanceof SAXParseException) {
node= createProblemElement((SAXParseException)exception);
}
} else {
node= (AntElementNode)fStillOpenElements.peek();
markHierarchy(node, XMLProblem.SEVERITY_ERROR);
}
notifyProblemRequestor(exception, node, XMLProblem.SEVERITY_ERROR);
}
public void errorFromElementText(Exception exception, int start, int count) {
AntElementNode node= fLastNode;
if (node == null) {
if (!fStillOpenElements.empty()) {
node= (AntElementNode)fStillOpenElements.peek();
}
}
if (node == null) {
return;
}
computeEndLocationForErrorNode(node, start, count);
notifyProblemRequestor(exception, start, count, XMLProblem.SEVERITY_ERROR);
markHierarchy(fLastNode, XMLProblem.SEVERITY_ERROR);
}
public void errorFromElement(Exception exception, AntElementNode node, int lineNumber, int column) {
if (node == null) {
if (!fStillOpenElements.empty()) {
node= (AntElementNode)fStillOpenElements.peek();
} else {
node= fLastNode;
}
}
computeEndLocationForErrorNode(node, lineNumber, column);
notifyProblemRequestor(exception, node, XMLProblem.SEVERITY_ERROR);
markHierarchy(node, XMLProblem.SEVERITY_ERROR);
}
private AntElementNode createProblemElement(SAXParseException exception) {
int lineNumber= exception.getLineNumber();
StringBuffer message= new StringBuffer(exception.getMessage());
if (lineNumber != -1){
message.append(AntOutlineMessages.getString("AntModel.1") + lineNumber); //$NON-NLS-1$
}
AntElementNode errorNode= new AntElementNode(message.toString());
errorNode.setFilePath(exception.getSystemId());
errorNode.setProblemSeverity(XMLProblem.SEVERITY_ERROR);
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(AntElementNode 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) {
}
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
}
}
public void fatalError(Exception exception) {
if (fStillOpenElements.isEmpty()) {
//TODO do we need to handle this better
return;
}
AntElementNode node= (AntElementNode)fStillOpenElements.peek();
markHierarchy(node, XMLProblem.SEVERITY_ERROR);
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(XMLProblem.SEVERITY_ERROR);
} else {
node= createProblemElement(parseException);
}
} catch (BadLocationException be) {
node= createProblemElement(parseException);
}
}
}
notifyProblemRequestor(exception, node, XMLProblem.SEVERITY_FATAL_ERROR);
while (node.getParentNode() != null) {
AntElementNode parentNode= node.getParentNode();
if (parentNode.getLength() == -1) {
parentNode.setLength(node.getOffset() - parentNode.getOffset() + node.getLength());
}
node= parentNode;
}
if (fIncrementalTarget != null) { //update the targets length for the edit
int editAdjustment= determineEditAdjustment(fDirtyRegion);
fIncrementalTarget.setLength(fIncrementalTarget.getLength() + editAdjustment);
AntElementNode startingNode= null;
while(fStillOpenElements.peek() != fIncrementalTarget) {
startingNode= (AntElementNode)fStillOpenElements.pop();
if (startingNode.getLength() > -1) {
startingNode.setLength(startingNode.getLength() + editAdjustment);
}
}
fStillOpenElements.pop(); //get rid of the incremental target
if (startingNode != null && fIncrementalTarget.hasChildren()) {
List children= fIncrementalTarget.getChildNodes();
int index= children.indexOf(startingNode);
updateNodesForIncrementalParse(editAdjustment, children, index);
}
}
}
public AntElementNode getOpenElement() {
if (fStillOpenElements.isEmpty()) {
return null;
}
return (AntElementNode)fStillOpenElements.peek();
}
public String getEntityPath(String entityName) {
if (fEntityNameToPath != null) {
return (String)fEntityNameToPath.get(entityName);
}
return null;
}
public String getEntityName(String path) {
if (fEntityNameToPath != null) {
Iterator itr= fEntityNameToPath.keySet().iterator();
String entityPath;
String name;
while (itr.hasNext()) {
name= (String) itr.next();
entityPath= (String) fEntityNameToPath.get(name);
if (entityPath.equals(path)) {
return name;
}
}
}
return null;
}
public String getPropertyValue(String propertyName) {
AntProjectNode projectNode= getProjectNode();
if (projectNode == null) {
return null;
}
return projectNode.getProject().getProperty(propertyName);
}
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 AntElementNode getReferenceNode(String text) {
Object reference= getReferenceObject(text);
if (reference == null) {
return null;
}
Collection nodes= fTaskToNode.keySet();
Iterator iter= nodes.iterator();
while (iter.hasNext()) {
Object original = iter.next();
Object object= original;
if (object instanceof UnknownElement) {
UnknownElement element= (UnknownElement) object;
object= element.getRealThing();
if (object == null) {
continue;
}
}
if (object == reference) {
return (AntElementNode)fTaskToNode.get(original);
}
}
return null;
}
private ClassLoader getClassLoader() {
if (fgClassLoader == null) {
fgClassLoader= AntCorePlugin.getPlugin().getNewClassLoader(true);
}
return fgClassLoader;
}
public String getTargetDescription(String targetRename) {
AntProjectNode projectNode= getProjectNode();
if (projectNode == null) {
return null;
}
Project project= projectNode.getProject();
Map targets= project.getTargets();
Target target= (Target)targets.get(targetRename);
if (target != null) {
return target.getDescription();
}
return null;
}
public AntProjectNode getProjectNode(boolean waitForReconcile) {
if (waitForReconcile) {
possiblyWaitForReconcile();
}
return fProjectNode;
}
public AntProjectNode getProjectNode() {
return getProjectNode(true);
}
private void possiblyWaitForReconcile() {
synchronized (fDirtyLock) {
if (!fIsDirty) {
return;
}
}
synchronized (this) {
//wait for the reconcile from the edit
//or if that fails, timeout
try {
wait(XMLReconcilingStrategy.DELAY * 10);
} catch (InterruptedException e) {
}
}
}
public void setReplaceHasOccurred() {
fReplaceHasOccurred= true;
}
public void updateMarkers() {
possiblyWaitForReconcile();
fMarkerUpdater.updateMarkers();
}
public AntElementNode getNode(int offset, boolean waitForReconcile) {
if (getProjectNode(waitForReconcile) != null) {
return getProjectNode(waitForReconcile).getNode(offset);
}
return null;
}
/**
* The Ant model has been reconciled for the first time with the contents displayed in the Ant editor.
* Since problem marker creation has been added after many buildfiles have been created (or if the file has been
* created outside of Eclipse) we need to update the markers to match the problems.
*/
public void updateForInitialReconcile() {
fMarkerUpdater.updateMarkers();
}
/**
* Provides the set of names of the defining nodes that existed from the previous
* parse of the build file.
*/
public void setNamesOfOldDefiningNodes(Set set) {
fNamesOfOldDefiningNodes= set;
}
/**
* Removes any type definitions that no longer exist in the buildfile
*/
private void reconcileTaskAndTypes() {
if (fNamesOfOldDefiningNodes == null) {
return;
}
Iterator iter= fNamesOfOldDefiningNodes.iterator();
while (iter.hasNext()) {
String nodeLabel = (String) iter.next();
if (fProjectNode.getDefininingTaskNode(nodeLabel) == null) {
ComponentHelper helper= ComponentHelper.getComponentHelper(fProjectNode.getProject());
helper.getAntTypeTable().remove(nodeLabel);
iter.remove();
}
}
}
}