1 /*******************************************************************************
2  * Copyright (c) 2000, 2019 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     Nico Seessle - bug 51332
14  *     Alexander Blaas (arctis Softwaretechnologie GmbH) - bug 412809
15  *******************************************************************************/
16 
17 package org.eclipse.ant.internal.ui.model;
18 
19 import java.io.File;
20 import java.io.FileReader;
21 import java.io.IOException;
22 import java.net.URLClassLoader;
23 import java.text.MessageFormat;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collection;
27 import java.util.Enumeration;
28 import java.util.HashMap;
29 import java.util.Hashtable;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Map.Entry;
34 import java.util.Properties;
35 import java.util.Set;
36 import java.util.Stack;
37 import java.util.regex.Pattern;
38 
39 import org.apache.tools.ant.AntTypeDefinition;
40 import org.apache.tools.ant.BuildException;
41 import org.apache.tools.ant.ComponentHelper;
42 import org.apache.tools.ant.IntrospectionHelper;
43 import org.apache.tools.ant.Location;
44 import org.apache.tools.ant.Main;
45 import org.apache.tools.ant.Project;
46 import org.apache.tools.ant.ProjectHelperRepository;
47 import org.apache.tools.ant.RuntimeConfigurable;
48 import org.apache.tools.ant.Target;
49 import org.apache.tools.ant.Task;
50 import org.apache.tools.ant.TaskAdapter;
51 import org.apache.tools.ant.UnknownElement;
52 import org.eclipse.ant.core.AntCorePlugin;
53 import org.eclipse.ant.core.AntCorePreferences;
54 import org.eclipse.ant.core.AntSecurityException;
55 import org.eclipse.ant.core.Property;
56 import org.eclipse.ant.core.Type;
57 import org.eclipse.ant.internal.core.AntClassLoader;
58 import org.eclipse.ant.internal.core.AntCoreUtil;
59 import org.eclipse.ant.internal.core.AntSecurityManager;
60 import org.eclipse.ant.internal.core.IAntCoreConstants;
61 import org.eclipse.ant.internal.ui.AntUIPlugin;
62 import org.eclipse.ant.internal.ui.AntUtil;
63 import org.eclipse.ant.internal.ui.editor.DecayCodeCompletionDataStructuresThread;
64 import org.eclipse.ant.internal.ui.editor.outline.AntEditorMarkerUpdater;
65 import org.eclipse.ant.internal.ui.editor.utils.ProjectHelper;
66 import org.eclipse.ant.internal.ui.preferences.AntEditorPreferenceConstants;
67 import org.eclipse.core.resources.IFile;
68 import org.eclipse.core.resources.ResourcesPlugin;
69 import org.eclipse.core.runtime.CoreException;
70 import org.eclipse.core.runtime.IPath;
71 import org.eclipse.core.runtime.Platform;
72 import org.eclipse.core.runtime.QualifiedName;
73 import org.eclipse.core.runtime.content.IContentDescription;
74 import org.eclipse.core.runtime.preferences.IEclipsePreferences;
75 import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener;
76 import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent;
77 import org.eclipse.core.runtime.preferences.InstanceScope;
78 import org.eclipse.core.variables.IStringVariableManager;
79 import org.eclipse.core.variables.VariablesPlugin;
80 import org.eclipse.jface.text.BadLocationException;
81 import org.eclipse.jface.text.DocumentEvent;
82 import org.eclipse.jface.text.IDocument;
83 import org.eclipse.jface.text.IDocumentListener;
84 import org.eclipse.jface.text.ISynchronizable;
85 import org.xml.sax.Attributes;
86 import org.xml.sax.SAXParseException;
87 
88 public class AntModel implements IAntModel {
89 
90 	private static ClassLoader fgClassLoader;
91 	private static int fgInstanceCount = 0;
92 	private static Object loaderLock = new Object();
93 
94 	private IDocument fDocument;
95 	private IProblemRequestor fProblemRequestor;
96 	private LocationProvider fLocationProvider;
97 
98 	private AntProjectNode fProjectNode;
99 	private AntTargetNode fCurrentTargetNode;
100 	private AntElementNode fLastNode;
101 	private AntElementNode fNodeBeingResolved;
102 	private int fNodeBeingResolvedIndex = -1;
103 	private String fEncoding = null;
104 
105 	private Map<String, String> fEntityNameToPath;
106 
107 	/**
108 	 * Stack of still open elements.
109 	 * <P>
110 	 * On top of the stack is the innermost element.
111 	 */
112 	private Stack<AntElementNode> fStillOpenElements = new Stack<>();
113 
114 	private Map<Task, AntTaskNode> fTaskToNode = new HashMap<>();
115 
116 	private List<AntTaskNode> fTaskNodes = new ArrayList<>();
117 
118 	private final Object fDirtyLock = new Object();
119 	private boolean fIsDirty = true;
120 	private File fEditedFile = null;
121 
122 	private ClassLoader fLocalClassLoader = null;
123 
124 	private boolean fHasLexicalInfo = true;
125 	private boolean fHasPositionInfo = true;
126 	private boolean fHasTaskInfo = true;
127 
128 	private IDocumentListener fListener;
129 	private AntEditorMarkerUpdater fMarkerUpdater = null;
130 	private List<AntElementNode> fNonStructuralNodes = new ArrayList<>(1);
131 
132 	private IPreferenceChangeListener fCoreListener = event -> {
133 		if (IAntCoreConstants.PREFERENCE_CLASSPATH_CHANGED.equals(event.getKey())) {
134 			if (Boolean.parseBoolean((String) event.getNewValue()) == true) {
135 				reconcileForPropertyChange(true);
136 			}
137 		}
138 	};
139 	private IPreferenceChangeListener fUIListener = new IPreferenceChangeListener() {
140 		@Override
141 		public void preferenceChange(PreferenceChangeEvent event) {
142 			String property = event.getKey();
143 			if (property.equals(AntEditorPreferenceConstants.PROBLEM)) {
144 				IEclipsePreferences node = InstanceScope.INSTANCE.getNode(AntUIPlugin.PI_ANTUI);
145 				if (node != null) {
146 					node.removePreferenceChangeListener(fUIListener);
147 					reconcileForPropertyChange(false);
148 					node.addPreferenceChangeListener(fUIListener);
149 				}
150 			} else if (property.equals(AntEditorPreferenceConstants.CODEASSIST_USER_DEFINED_TASKS)) {
151 				reconcileForPropertyChange(false);
152 			} else if (property.equals(AntEditorPreferenceConstants.BUILDFILE_NAMES_TO_IGNORE)
153 					|| property.equals(AntEditorPreferenceConstants.BUILDFILE_IGNORE_ALL)) {
154 				fReportingProblemsCurrent = false;
155 				reconcileForPropertyChange(false);
156 			}
157 		}
158 	};
159 
160 	private Map<String, String> fProperties = null;
161 	private List<String> fPropertyFiles = null;
162 
163 	private Map<String, String> fDefinersToText;
164 	private Map<String, String> fPreviousDefinersToText;
165 	private Map<String, List<String>> fDefinerNodeIdentifierToDefinedTasks;
166 	private Map<String, AntDefiningTaskNode> fTaskNameToDefiningNode;
167 	private Map<String, String> fCurrentNodeIdentifiers;
168 
169 	private boolean fReportingProblemsCurrent = false;
170 	private boolean fDoNotReportProblems = false;
171 	private boolean fShouldReconcile = true;
172 	private HashMap<String, String> fNamespacePrefixMappings;
173 
AntModel(IDocument document, IProblemRequestor problemRequestor, LocationProvider locationProvider)174 	public AntModel(IDocument document, IProblemRequestor problemRequestor, LocationProvider locationProvider) {
175 		init(document, problemRequestor, locationProvider);
176 
177 		fMarkerUpdater = new AntEditorMarkerUpdater();
178 		fMarkerUpdater.setModel(this);
179 
180 		IEclipsePreferences node = InstanceScope.INSTANCE.getNode(AntCorePlugin.PI_ANTCORE);
181 		if (node != null) {
182 			node.addPreferenceChangeListener(fCoreListener);
183 		}
184 
185 		node = InstanceScope.INSTANCE.getNode(AntUIPlugin.PI_ANTUI);
186 		if (node != null) {
187 			node.addPreferenceChangeListener(fUIListener);
188 		}
189 	}
190 
AntModel(IDocument document, IProblemRequestor problemRequestor, LocationProvider locationProvider, boolean resolveLexicalInfo, boolean resolvePositionInfo, boolean resolveTaskInfo)191 	public AntModel(IDocument document, IProblemRequestor problemRequestor, LocationProvider locationProvider, boolean resolveLexicalInfo, boolean resolvePositionInfo, boolean resolveTaskInfo) {
192 		init(document, problemRequestor, locationProvider);
193 		fHasLexicalInfo = resolveLexicalInfo;
194 		fHasPositionInfo = resolvePositionInfo;
195 		fHasTaskInfo = resolveTaskInfo;
196 	}
197 
init(IDocument document, IProblemRequestor problemRequestor, LocationProvider locationProvider)198 	private void init(IDocument document, IProblemRequestor problemRequestor, LocationProvider locationProvider) {
199 		fDocument = document;
200 		fProblemRequestor = problemRequestor;
201 		fLocationProvider = locationProvider;
202 		if (fgInstanceCount == 0) {
203 			// no other models are open to ensure that the classpath is up to date wrt the
204 			// Ant preferences and start listening for breakpoint changes
205 			AntDefiningTaskNode.setJavaClassPath();
206 			AntModelCore.getDefault().startBreakpointListening();
207 		}
208 		fgInstanceCount++;
209 		DecayCodeCompletionDataStructuresThread.cancel();
210 		ProjectHelper helper = getProjectHelper();
211 		if (helper == null) {
212 			ProjectHelperRepository.getInstance().registerProjectHelper(ProjectHelper.class);
213 		}
214 		computeEncoding();
215 	}
216 
217 	/**
218 	 * Searches the collection of registered {@link org.apache.tools.ant.ProjectHelper}s to see if we have one registered already.
219 	 *
220 	 * @return the {@link ProjectHelper} from our implementation of <code>null</code> if we have not registered one yet
221 	 * @since 3.7
222 	 * @see ProjectHelperRepository
223 	 */
getProjectHelper()224 	ProjectHelper getProjectHelper() {
225 		Iterator<org.apache.tools.ant.ProjectHelper> helpers = ProjectHelperRepository.getInstance().getHelpers();
226 		while (helpers.hasNext()) {
227 			org.apache.tools.ant.ProjectHelper helper = helpers.next();
228 			if (helper instanceof ProjectHelper) {
229 				return (ProjectHelper) helper;
230 			}
231 		}
232 		return null;
233 	}
234 
235 	@Override
dispose()236 	public void dispose() {
237 		synchronized (getLockObject()) {
238 			if (fDocument != null && fListener != null) {
239 				fDocument.removeDocumentListener(fListener);
240 			}
241 			fDocument = null;
242 			fLocationProvider = null;
243 			ProjectHelper.setAntModel(null);
244 		}
245 
246 		IEclipsePreferences node = InstanceScope.INSTANCE.getNode(AntCorePlugin.PI_ANTCORE);
247 		if (node != null) {
248 			node.removePreferenceChangeListener(fCoreListener);
249 		}
250 
251 		node = InstanceScope.INSTANCE.getNode(AntUIPlugin.PI_ANTUI);
252 		if (node != null) {
253 			node.removePreferenceChangeListener(fUIListener);
254 		}
255 		fgInstanceCount--;
256 		if (fgInstanceCount == 0) {
257 			fgClassLoader = null;
258 			DecayCodeCompletionDataStructuresThread.getDefault().start();
259 			AntModelCore.getDefault().stopBreakpointListening();
260 			cleanup();
261 		}
262 	}
263 
getLockObject()264 	private Object getLockObject() {
265 		if (fDocument instanceof ISynchronizable) {
266 			Object lock = ((ISynchronizable) fDocument).getLockObject();
267 			if (lock != null) {
268 				return lock;
269 			}
270 		}
271 		return this;
272 	}
273 
cleanup()274 	private void cleanup() {
275 		AntProjectNode projectNode = getProjectNode();
276 		if (projectNode != null) {
277 			// cleanup the introspection helpers that may have been generated
278 			IntrospectionHelper.clearCache();
279 			projectNode.getProject().fireBuildFinished(null);
280 		}
281 		fEncoding = null;
282 	}
283 
284 	@Override
reconcile()285 	public void reconcile() {
286 		synchronized (fDirtyLock) {
287 			if (!fShouldReconcile || !fIsDirty) {
288 				return;
289 			}
290 			fIsDirty = false;
291 		}
292 
293 		synchronized (getLockObject()) {
294 			if (fLocationProvider == null) {
295 				// disposed
296 				return;
297 			}
298 
299 			if (fDocument == null) {
300 				fProjectNode = null;
301 			} else {
302 				reset();
303 				parseDocument(fDocument);
304 				reconcileTaskAndTypes();
305 			}
306 			AntModelCore.getDefault().notifyAntModelListeners(new AntModelChangeEvent(this));
307 		}
308 	}
309 
reset()310 	private void reset() {
311 		fCurrentTargetNode = null;
312 		fStillOpenElements = new Stack<>();
313 		fTaskToNode = new HashMap<>();
314 		fTaskNodes = new ArrayList<>();
315 		fNodeBeingResolved = null;
316 		fNodeBeingResolvedIndex = -1;
317 		fLastNode = null;
318 		fCurrentNodeIdentifiers = null;
319 		fNamespacePrefixMappings = null;
320 
321 		fNonStructuralNodes = new ArrayList<>(1);
322 		if (fDefinersToText != null) {
323 			fPreviousDefinersToText = new HashMap<>(fDefinersToText);
324 			fDefinersToText = null;
325 		}
326 	}
327 
parseDocument(IDocument input)328 	private void parseDocument(IDocument input) {
329 		boolean parsed = true;
330 		if (input.getLength() == 0) {
331 			fProjectNode = null;
332 			parsed = false;
333 			return;
334 		}
335 		ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
336 		ClassLoader parsingClassLoader = getClassLoader(originalClassLoader);
337 		Thread.currentThread().setContextClassLoader(parsingClassLoader);
338 		Project project = null;
339 		try {
340 			ProjectHelper projectHelper = null;
341 			String textToParse = input.get();
342 			if (fProjectNode == null || !fProjectNode.hasChildren()) {
343 				fProjectNode = null;
344 				project = new AntModelProject();
345 				projectHelper = prepareForFullParse(project, parsingClassLoader);
346 			} else {
347 				project = fProjectNode.getProject();
348 				projectHelper = (ProjectHelper) project.getReference("ant.projectHelper"); //$NON-NLS-1$
349 				projectHelper.setBuildFile(getEditedFile());
350 				prepareForFullIncremental();
351 			}
352 			beginReporting();
353 			Map<String, Object> references = project.getReferences();
354 			references.remove("ant.parsing.context"); //$NON-NLS-1$
355 			ProjectHelper.setAntModel(this);
356 			projectHelper.parse(project, textToParse);
357 
358 		}
359 		catch (BuildException e) {
360 			handleBuildException(e, null);
361 		}
362 		finally {
363 			if (parsed) {
364 				SecurityManager origSM = System.getSecurityManager();
365 				processAntHome(true);
366 				try {
367 					// set a security manager to disallow system exit and system property setting
368 					System.setSecurityManager(new AntSecurityManager(origSM, Thread.currentThread(), false));
369 					resolveBuildfile();
370 					endReporting();
371 					// clear the additional property-holder(s) to avoid potential memory leaks
372 					ProjectHelper.clearAdditionalPropertyHolders();
373 				}
374 				catch (AntSecurityException e) {
375 					// do nothing
376 				}
377 				finally {
378 					Thread.currentThread().setContextClassLoader(originalClassLoader);
379 					getClassLoader(null);
380 					System.setSecurityManager(origSM);
381 					project.fireBuildFinished(null); // cleanup (IntrospectionHelper)
382 				}
383 			}
384 		}
385 	}
386 
prepareForFullParse(Project project, ClassLoader parsingClassLoader)387 	private ProjectHelper prepareForFullParse(Project project, ClassLoader parsingClassLoader) {
388 		initializeProject(project, parsingClassLoader);
389 		// Ant's parsing facilities always works on a file, therefore we need
390 		// to determine the actual location of the file. Though the file
391 		// contents will not be parsed. We parse the passed document string
392 		File file = getEditedFile();
393 		String filePath = IAntCoreConstants.EMPTY_STRING;
394 		if (file != null) {
395 			filePath = file.getAbsolutePath();
396 		}
397 		project.setUserProperty("ant.file", filePath); //$NON-NLS-1$
398 		project.setUserProperty("ant.version", Main.getAntVersion()); //$NON-NLS-1$
399 
400 		ProjectHelper projectHelper = getProjectHelper();
401 		ProjectHelper.setAntModel(this);
402 		projectHelper.setBuildFile(file);
403 		project.addReference("ant.projectHelper", projectHelper); //$NON-NLS-1$
404 		return projectHelper;
405 	}
406 
prepareForFullIncremental()407 	private void prepareForFullIncremental() {
408 		fProjectNode.reset();
409 		fTaskToNode = new HashMap<>();
410 		fTaskNodes = new ArrayList<>();
411 	}
412 
initializeProject(Project project, ClassLoader loader)413 	private void initializeProject(Project project, ClassLoader loader) {
414 		try {
415 			processAntHome(false);
416 		}
417 		catch (AntSecurityException ex) {
418 			// do nothing - Ant home can not be set from this thread
419 		}
420 		project.init();
421 		setProperties(project);
422 		setTasks(project, loader);
423 		setTypes(project, loader);
424 	}
425 
setTasks(Project project, ClassLoader loader)426 	private void setTasks(Project project, ClassLoader loader) {
427 		List<org.eclipse.ant.core.Task> tasks = AntCorePlugin.getPlugin().getPreferences().getTasks();
428 		for (org.eclipse.ant.core.Task task : tasks) {
429 			AntTypeDefinition def = new AntTypeDefinition();
430 			def.setName(task.getTaskName());
431 			def.setClassName(task.getClassName());
432 			def.setClassLoader(loader);
433 			def.setAdaptToClass(Task.class);
434 			def.setAdapterClass(TaskAdapter.class);
435 			ComponentHelper.getComponentHelper(project).addDataTypeDefinition(def);
436 		}
437 	}
438 
setTypes(Project project, ClassLoader loader)439 	private void setTypes(Project project, ClassLoader loader) {
440 		List<Type> types = AntCorePlugin.getPlugin().getPreferences().getTypes();
441 		for (Type type : types) {
442 			AntTypeDefinition def = new AntTypeDefinition();
443 			def.setName(type.getTypeName());
444 			def.setClassName(type.getClassName());
445 			def.setClassLoader(loader);
446 			ComponentHelper.getComponentHelper(project).addDataTypeDefinition(def);
447 		}
448 	}
449 
setProperties(Project project)450 	private void setProperties(Project project) {
451 		setBuiltInProperties(project);
452 		setExtraProperties(project);
453 		setGlobalProperties(project);
454 		loadExtraPropertyFiles(project);
455 		loadPropertyFiles(project);
456 	}
457 
setExtraProperties(Project project)458 	private void setExtraProperties(Project project) {
459 		if (fProperties != null) {
460 			Pattern pattern = Pattern.compile("\\$\\{.*_prompt.*\\}"); //$NON-NLS-1$
461 			IStringVariableManager manager = VariablesPlugin.getDefault().getStringVariableManager();
462 			for (String name : fProperties.keySet()) {
463 				String value = fProperties.get(name);
464 				if (!pattern.matcher(value).find()) {
465 					try {
466 						value = manager.performStringSubstitution(value);
467 					}
468 					catch (CoreException e) {
469 						// do nothing
470 					}
471 				}
472 				if (value != null) {
473 					project.setUserProperty(name, value);
474 				}
475 			}
476 		}
477 	}
478 
loadExtraPropertyFiles(Project project)479 	private void loadExtraPropertyFiles(Project project) {
480 		if (fPropertyFiles != null) {
481 			try {
482 				List<Properties> allProperties = AntCoreUtil.loadPropertyFiles(fPropertyFiles, project.getUserProperty("basedir"), getEditedFile().getAbsolutePath()); //$NON-NLS-1$
483 				setPropertiesFromFiles(project, allProperties);
484 			}
485 			catch (IOException e1) {
486 				AntUIPlugin.log(e1);
487 			}
488 		}
489 	}
490 
491 	/**
492 	 * Load all properties from the files
493 	 */
loadPropertyFiles(Project project)494 	private void loadPropertyFiles(Project project) {
495 		List<String> fileNames = Arrays.asList(AntCorePlugin.getPlugin().getPreferences().getCustomPropertyFiles());
496 		try {
497 			List<Properties> allProperties = AntCoreUtil.loadPropertyFiles(fileNames, project.getUserProperty("basedir"), getEditedFile().getAbsolutePath()); //$NON-NLS-1$
498 			setPropertiesFromFiles(project, allProperties);
499 		}
500 		catch (IOException e1) {
501 			AntUIPlugin.log(e1);
502 		}
503 	}
504 
setPropertiesFromFiles(Project project, List<Properties> allProperties)505 	private void setPropertiesFromFiles(Project project, List<Properties> allProperties) {
506 		for (Properties props : allProperties) {
507 			Enumeration<?> propertyNames = props.propertyNames();
508 			while (propertyNames.hasMoreElements()) {
509 				String name = (String) propertyNames.nextElement();
510 				// do not override extra local properties with the global settings
511 				if (project.getUserProperty(name) == null) {
512 					project.setUserProperty(name, props.getProperty(name));
513 				}
514 			}
515 		}
516 	}
517 
setBuiltInProperties(Project project)518 	private void setBuiltInProperties(Project project) {
519 		// set processAntHome for other properties set as system properties
520 		project.setUserProperty("ant.file", getEditedFile().getAbsolutePath()); //$NON-NLS-1$
521 		project.setUserProperty("ant.version", Main.getAntVersion()); //$NON-NLS-1$
522 	}
523 
processAntHome(boolean finished)524 	private void processAntHome(boolean finished) {
525 		AntCorePreferences prefs = AntCorePlugin.getPlugin().getPreferences();
526 		String antHome = prefs.getAntHome();
527 		if (finished || antHome == null) {
528 			System.getProperties().remove("ant.home"); //$NON-NLS-1$
529 			System.getProperties().remove("ant.library.dir"); //$NON-NLS-1$
530 		} else {
531 			System.setProperty("ant.home", antHome); //$NON-NLS-1$
532 			File antLibDir = new File(antHome, "lib"); //$NON-NLS-1$
533 			System.setProperty("ant.library.dir", antLibDir.getAbsolutePath()); //$NON-NLS-1$
534 		}
535 	}
536 
setGlobalProperties(Project project)537 	private void setGlobalProperties(Project project) {
538 		List<Property> properties = AntCorePlugin.getPlugin().getPreferences().getProperties();
539 		if (properties != null) {
540 			for (Property property : properties) {
541 				String value = property.getValue(true);
542 				if (value != null) {
543 					project.setUserProperty(property.getName(), value);
544 				}
545 			}
546 		}
547 	}
548 
resolveBuildfile()549 	private void resolveBuildfile() {
550 		Collection<AntTaskNode> nodeCopy = new ArrayList<>(fTaskNodes);
551 		Iterator<AntTaskNode> iter = nodeCopy.iterator();
552 		while (iter.hasNext()) {
553 			AntTaskNode node = iter.next();
554 			fNodeBeingResolved = node;
555 			fNodeBeingResolvedIndex = -1;
556 			if (node.configure(false)) {
557 				// resolve any new elements that may have been added
558 				resolveBuildfile();
559 			}
560 		}
561 		fNodeBeingResolved = null;
562 		fNodeBeingResolvedIndex = -1;
563 		checkTargets();
564 	}
565 
566 	/**
567 	 * Check that if a default target is specified it exists and that the target dependencies exist.
568 	 */
checkTargets()569 	private void checkTargets() {
570 		if (fProjectNode == null || doNotReportProblems()) {
571 			return;
572 		}
573 		String defaultTargetName = fProjectNode.getDefaultTargetName();
574 		if (defaultTargetName != null && fProjectNode.getProject().getTargets().get(defaultTargetName) == null) {
575 			// no default target when one specified (default target does not have to be specified)
576 			String message = MessageFormat.format(AntModelMessages.AntModel_43, new Object[] { defaultTargetName });
577 			IProblem problem = createProblem(message, fProjectNode.getOffset(), fProjectNode.getSelectionLength(), AntModelProblem.SEVERITY_ERROR);
578 			acceptProblem(problem);
579 			markHierarchy(fProjectNode, AntModelProblem.SEVERITY_ERROR, message);
580 		}
581 		if (!fProjectNode.hasChildren()) {
582 			return;
583 		}
584 		List<IAntElement> children = fProjectNode.getChildNodes();
585 		boolean checkCircularDependencies = true;
586 		for (IAntElement node : children) {
587 			IAntElement originalNode = node;
588 			if (node instanceof AntTargetNode) {
589 				if (checkCircularDependencies) {
590 					checkCircularDependencies = false;
591 					checkCircularDependencies(node);
592 				}
593 				checkMissingDependencies(node, originalNode);
594 			}
595 		}
596 	}
597 
598 	/**
599 	 * method assumes sender has checked whether to report problems
600 	 */
checkMissingDependencies(IAntElement node, IAntElement originalNode)601 	private void checkMissingDependencies(IAntElement node, IAntElement originalNode) {
602 		String missing = ((AntTargetNode) node).checkDependencies();
603 		if (missing != null) {
604 			String message = MessageFormat.format(AntModelMessages.AntModel_44, new Object[] { missing });
605 			IAntElement importNode = node.getImportNode();
606 			if (importNode != null) {
607 				node = importNode;
608 			}
609 			IProblem problem = createProblem(message, node.getOffset(), node.getSelectionLength(), AntModelProblem.SEVERITY_ERROR);
610 			acceptProblem(problem);
611 			markHierarchy(originalNode, AntModelProblem.SEVERITY_ERROR, message);
612 		}
613 	}
614 
doNotReportProblems()615 	private boolean doNotReportProblems() {
616 		if (fReportingProblemsCurrent) {
617 			return fDoNotReportProblems;
618 		}
619 
620 		fReportingProblemsCurrent = true;
621 		fDoNotReportProblems = false;
622 
623 		if (AntUIPlugin.getDefault().getCombinedPreferenceStore().getBoolean(AntEditorPreferenceConstants.BUILDFILE_IGNORE_ALL)) {
624 			fDoNotReportProblems = true;
625 			return fDoNotReportProblems;
626 		}
627 		String buildFileNames = AntUIPlugin.getDefault().getCombinedPreferenceStore().getString(AntEditorPreferenceConstants.BUILDFILE_NAMES_TO_IGNORE);
628 		if (buildFileNames.length() > 0) {
629 			String editedFileName = getEditedFile().getName();
630 			for (String fileName : AntUtil.parseString(buildFileNames, ",")) { //$NON-NLS-1$
631 				if (fileName.trim().equals(editedFileName)) {
632 					fDoNotReportProblems = true;
633 					return fDoNotReportProblems;
634 				}
635 			}
636 		}
637 
638 		return fDoNotReportProblems;
639 	}
640 
641 	/**
642 	 * method assumes sendor has checked whether to report problems
643 	 */
checkCircularDependencies(IAntElement node)644 	private void checkCircularDependencies(IAntElement node) {
645 		Target target = ((AntTargetNode) node).getTarget();
646 		String name = target.getName();
647 		if (name == null) {
648 			return;
649 		}
650 		try {
651 			target.getProject().topoSort(name, target.getProject().getTargets());
652 		}
653 		catch (BuildException be) {
654 			// possible circular dependency
655 			String message = be.getMessage();
656 			if (message.startsWith("Circular")) { //$NON-NLS-1$ //we do our own checking for missing dependencies
657 				IProblem problem = createProblem(message, node.getProjectNode().getOffset(), node.getProjectNode().getSelectionLength(), AntModelProblem.SEVERITY_ERROR);
658 				acceptProblem(problem);
659 				markHierarchy(node.getProjectNode(), AntModelProblem.SEVERITY_ERROR, message);
660 			}
661 		}
662 	}
663 
664 	@Override
handleBuildException(BuildException e, AntElementNode node, int severity)665 	public void handleBuildException(BuildException e, AntElementNode node, int severity) {
666 		try {
667 			if (node != null) {
668 				markHierarchy(node, severity, e.getMessage());
669 			}
670 			Location location = e.getLocation();
671 			int line = 0;
672 			int originalOffset = 0;
673 			int nonWhitespaceOffset = 0;
674 			int length = 0;
675 			if (location == Location.UNKNOWN_LOCATION && node != null) {
676 				if (node.getImportNode() != null) {
677 					node = node.getImportNode();
678 				}
679 				nonWhitespaceOffset = node.getOffset();
680 				length = node.getLength();
681 			} else {
682 				line = location.getLineNumber();
683 				if (line == 0) {
684 					AntProjectNode projectNode = getProjectNode();
685 					if (projectNode != null) {
686 						length = projectNode.getSelectionLength();
687 						nonWhitespaceOffset = projectNode.getOffset();
688 						if (severity == AntModelProblem.SEVERITY_ERROR) {
689 							projectNode.setProblemSeverity(AntModelProblem.NO_PROBLEM);
690 							projectNode.setProblemMessage(null);
691 						}
692 					} else {
693 						return;
694 					}
695 				} else {
696 					if (node == null) {
697 						originalOffset = getOffset(line, 1);
698 						nonWhitespaceOffset = originalOffset;
699 						try {
700 							nonWhitespaceOffset = getNonWhitespaceOffset(line, 1);
701 						}
702 						catch (BadLocationException be) {
703 							// do nothing
704 						}
705 						length = getLastCharColumn(line) - (nonWhitespaceOffset - originalOffset);
706 					} else {
707 						if (node.getImportNode() != null) {
708 							node = node.getImportNode();
709 						}
710 						nonWhitespaceOffset = node.getOffset();
711 						length = node.getLength();
712 					}
713 				}
714 			}
715 			notifyProblemRequestor(e, nonWhitespaceOffset, length, severity);
716 		}
717 		catch (BadLocationException e1) {
718 			// do nothing
719 		}
720 	}
721 
handleBuildException(BuildException e, AntElementNode node)722 	public void handleBuildException(BuildException e, AntElementNode node) {
723 		handleBuildException(e, node, AntModelProblem.SEVERITY_ERROR);
724 	}
725 
726 	@Override
getEditedFile()727 	public File getEditedFile() {
728 		if (fLocationProvider != null && fEditedFile == null) {
729 			fEditedFile = fLocationProvider.getLocation().toFile();
730 		}
731 		return fEditedFile;
732 	}
733 
markHierarchy(IAntElement openElement, int severity, String message)734 	private void markHierarchy(IAntElement openElement, int severity, String message) {
735 		if (doNotReportProblems()) {
736 			return;
737 		}
738 		while (openElement != null) {
739 			openElement.setProblemSeverity(severity);
740 			openElement.setProblemMessage(message);
741 			openElement = openElement.getParentNode();
742 		}
743 	}
744 
745 	@Override
getLocationProvider()746 	public LocationProvider getLocationProvider() {
747 		return fLocationProvider;
748 	}
749 
750 	@Override
addTarget(Target newTarget, int line, int column)751 	public void addTarget(Target newTarget, int line, int column) {
752 		AntTargetNode targetNode = AntTargetNode.newAntTargetNode(newTarget);
753 		fProjectNode.addChildNode(targetNode);
754 		fCurrentTargetNode = targetNode;
755 		fStillOpenElements.push(targetNode);
756 		if (fNodeBeingResolved instanceof AntImportNode) {
757 			targetNode.setImportNode(fNodeBeingResolved);
758 			targetNode.setExternal(true);
759 			targetNode.setFilePath(newTarget.getLocation().getFileName());
760 		} else {
761 			String targetFileName = newTarget.getLocation().getFileName();
762 			boolean external = isNodeExternal(targetFileName);
763 			targetNode.setExternal(external);
764 			if (external) {
765 				targetNode.setFilePath(targetFileName);
766 			}
767 		}
768 		computeOffset(targetNode, line, column);
769 	}
770 
771 	@Override
addProject(Project project, int line, int column)772 	public void addProject(Project project, int line, int column) {
773 		fProjectNode = new AntProjectNode((AntModelProject) project, this);
774 		fStillOpenElements.push(fProjectNode);
775 		computeOffset(fProjectNode, line, column);
776 	}
777 
778 	@Override
addDTD(String name, int line, int column)779 	public void addDTD(String name, int line, int column) {
780 		AntDTDNode node = new AntDTDNode(name);
781 		fStillOpenElements.push(node);
782 		int offset = -1;
783 		try {
784 			if (column <= 0) {
785 				offset = getOffset(line, 0);
786 				int lastCharColumn = getLastCharColumn(line);
787 				offset = computeOffsetUsingPrefix(line, offset, "<!DOCTYPE", lastCharColumn); //$NON-NLS-1$
788 			} else {
789 				offset = getOffset(line, column);
790 			}
791 		}
792 		catch (BadLocationException e) {
793 			AntUIPlugin.log(e);
794 		}
795 		node.setOffset(offset);
796 		fNonStructuralNodes.add(node);
797 	}
798 
799 	@Override
addTask(Task newTask, Task parentTask, Attributes attributes, int line, int column)800 	public void addTask(Task newTask, Task parentTask, Attributes attributes, int line, int column) {
801 		if (!canGetTaskInfo()) {
802 			// need to add top level tasks so imports are executed even when
803 			// the model is not interested in task level resolution
804 			Target owningTarget = newTask.getOwningTarget();
805 			String name = owningTarget.getName();
806 			if (name == null || name.length() != 0) {
807 				// not the top level implicit target
808 				return;
809 			}
810 		}
811 		AntTaskNode taskNode = null;
812 		if (parentTask == null) {
813 			taskNode = newTaskNode(newTask, attributes);
814 			if (fCurrentTargetNode == null) {
815 				fProjectNode.addChildNode(taskNode);
816 			} else {
817 				fCurrentTargetNode.addChildNode(taskNode);
818 			}
819 		} else {
820 			taskNode = newNotWellKnownTaskNode(newTask, attributes);
821 			AntTaskNode parentNode = fTaskToNode.get(parentTask);
822 			parentNode.addChildNode(taskNode);
823 		}
824 		fTaskToNode.put(newTask, taskNode);
825 
826 		fStillOpenElements.push(taskNode);
827 		computeOffset(taskNode, line, column);
828 		if (fNodeBeingResolved instanceof AntImportNode) {
829 			taskNode.setImportNode(fNodeBeingResolved);
830 			// place the node in the collection right after the import node
831 			if (fNodeBeingResolvedIndex == -1) {
832 				fNodeBeingResolvedIndex = fTaskNodes.indexOf(fNodeBeingResolved);
833 			}
834 			fNodeBeingResolvedIndex++;
835 			fTaskNodes.add(fNodeBeingResolvedIndex, taskNode);
836 		} else {
837 			fTaskNodes.add(taskNode);
838 		}
839 	}
840 
841 	@Override
addEntity(String entityName, String entityPath)842 	public void addEntity(String entityName, String entityPath) {
843 		if (fEntityNameToPath == null) {
844 			fEntityNameToPath = new HashMap<>();
845 		}
846 		fEntityNameToPath.put(entityName, entityPath);
847 	}
848 
newTaskNode(Task newTask, Attributes attributes)849 	private AntTaskNode newTaskNode(Task newTask, Attributes attributes) {
850 		AntTaskNode newNode = null;
851 		String taskName = newTask.getTaskName();
852 		if (newTask instanceof UnknownElement) {
853 			// attempt to handle namespaces
854 			taskName = ((UnknownElement) newTask).getTag();
855 		}
856 
857 		if (isPropertySettingTask(taskName)) {
858 			newNode = new AntPropertyNode(newTask, attributes);
859 		} else if (taskName.equalsIgnoreCase("import")) { //$NON-NLS-1$
860 			newNode = new AntImportNode(newTask, attributes);
861 		} else if (taskName.equalsIgnoreCase("include")) { //$NON-NLS-1$
862 			newNode = new AntIncludeNode(newTask, attributes);
863 		} else if (taskName.equalsIgnoreCase("macrodef") //$NON-NLS-1$
864 				|| taskName.equalsIgnoreCase("presetdef") //$NON-NLS-1$
865 				|| taskName.equalsIgnoreCase("typedef") //$NON-NLS-1$
866 				|| taskName.equalsIgnoreCase("taskdef")) { //$NON-NLS-1$
867 			newNode = new AntDefiningTaskNode(newTask, attributes);
868 		} else if (taskName.equalsIgnoreCase("antcall")) { //$NON-NLS-1$
869 			newNode = new AntTaskNode(newTask, generateLabel(taskName, attributes, IAntModelConstants.ATTR_TARGET));
870 		} else if (taskName.equalsIgnoreCase("mkdir")) { //$NON-NLS-1$
871 			newNode = new AntTaskNode(newTask, generateLabel(taskName, attributes, IAntCoreConstants.DIR));
872 		} else if (taskName.equalsIgnoreCase("copy")) { //$NON-NLS-1$
873 			newNode = new AntTaskNode(newTask, generateLabel(taskName, attributes, IAntModelConstants.ATTR_DESTFILE));
874 		} else if (taskName.equalsIgnoreCase("tar") //$NON-NLS-1$
875 				|| taskName.equalsIgnoreCase("jar") //$NON-NLS-1$
876 				|| taskName.equalsIgnoreCase("war") //$NON-NLS-1$
877 				|| taskName.equalsIgnoreCase("zip")) { //$NON-NLS-1$
878 			newNode = new AntTaskNode(newTask, generateLabel(newTask.getTaskName(), attributes, IAntModelConstants.ATTR_DESTFILE));
879 		} else if (taskName.equalsIgnoreCase("untar") //$NON-NLS-1$
880 				|| taskName.equalsIgnoreCase("unjar") //$NON-NLS-1$
881 				|| taskName.equalsIgnoreCase("unwar") //$NON-NLS-1$
882 				|| taskName.equalsIgnoreCase("gunzip") //$NON-NLS-1$
883 				|| taskName.equalsIgnoreCase("bunzip2") //$NON-NLS-1$
884 				|| taskName.equalsIgnoreCase("unzip")) { //$NON-NLS-1$
885 			newNode = new AntTaskNode(newTask, generateLabel(newTask.getTaskName(), attributes, IAntModelConstants.ATTR_SRC));
886 		} else if (taskName.equalsIgnoreCase("gzip") //$NON-NLS-1$
887 				|| taskName.equalsIgnoreCase("bzip2")) { //$NON-NLS-1$
888 			newNode = new AntTaskNode(newTask, generateLabel(newTask.getTaskName(), attributes, IAntModelConstants.ATTR_ZIPFILE));
889 		} else if (taskName.equalsIgnoreCase("exec")) { //$NON-NLS-1$
890 			String label = "exec "; //$NON-NLS-1$
891 			String command = attributes.getValue(IAntModelConstants.ATTR_COMMAND);
892 			if (command != null) {
893 				label += command;
894 			}
895 			command = attributes.getValue(IAntModelConstants.ATTR_EXECUTABLE);
896 			if (command != null) {
897 				label += command;
898 			}
899 			newNode = new AntTaskNode(newTask, label);
900 		} else if (taskName.equalsIgnoreCase("ant")) { //$NON-NLS-1$
901 			newNode = new AntAntNode(newTask, attributes);
902 		} else if (taskName.equalsIgnoreCase("delete")) { //$NON-NLS-1$
903 			String label = "delete "; //$NON-NLS-1$
904 			String file = attributes.getValue(IAntCoreConstants.FILE);
905 			if (file != null) {
906 				label += file;
907 			} else {
908 				file = attributes.getValue(IAntCoreConstants.DIR);
909 				if (file != null) {
910 					label += file;
911 				}
912 			}
913 			newNode = new AntTaskNode(newTask, label);
914 		} else if (IAntCoreConstants.AUGMENT.equals(taskName)) {
915 			newNode = new AntAugmentTaskNode(newTask, generateLabel(newTask.getTaskName(), attributes, IAntCoreConstants.ID));
916 		} else {
917 			newNode = newNotWellKnownTaskNode(newTask, attributes);
918 		}
919 		setExternalInformation(newTask, newNode);
920 		return newNode;
921 	}
922 
923 	/**
924 	 * @param taskName
925 	 *            the name of the task to check
926 	 * @return whether or not a task with this name sets properties
927 	 */
isPropertySettingTask(String taskName)928 	private boolean isPropertySettingTask(String taskName) {
929 		return taskName.equalsIgnoreCase("property") //$NON-NLS-1$
930 				|| taskName.equalsIgnoreCase("available") //$NON-NLS-1$
931 				|| taskName.equalsIgnoreCase("basename") //$NON-NLS-1$
932 				|| taskName.equalsIgnoreCase("condition") //$NON-NLS-1$
933 				|| taskName.equalsIgnoreCase("dirname") //$NON-NLS-1$
934 				|| taskName.equalsIgnoreCase("loadfile") //$NON-NLS-1$
935 				|| taskName.equalsIgnoreCase("pathconvert") //$NON-NLS-1$
936 				|| taskName.equalsIgnoreCase("uptodate") //$NON-NLS-1$
937 				|| taskName.equalsIgnoreCase("xmlproperty") //$NON-NLS-1$
938 				|| taskName.equalsIgnoreCase("loadproperties"); //$NON-NLS-1$
939 	}
940 
isNodeExternal(String fileName)941 	private boolean isNodeExternal(String fileName) {
942 		File taskFile = new File(fileName);
943 		return !taskFile.equals(getEditedFile());
944 	}
945 
newNotWellKnownTaskNode(Task newTask, Attributes attributes)946 	private AntTaskNode newNotWellKnownTaskNode(Task newTask, Attributes attributes) {
947 		AntTaskNode newNode = new AntTaskNode(newTask);
948 		String id = attributes.getValue("id"); //$NON-NLS-1$
949 		if (id != null) {
950 			newNode.setId(id);
951 		}
952 		String taskName = newTask.getTaskName();
953 		if ("attribute".equals(taskName) || "element".equals(taskName)) { //$NON-NLS-1$ //$NON-NLS-2$
954 			String name = attributes.getValue(IAntCoreConstants.NAME);
955 			if (name != null) {
956 				newNode.setBaseLabel(name);
957 			}
958 		}
959 
960 		setExternalInformation(newTask, newNode);
961 		return newNode;
962 	}
963 
setExternalInformation(Task newTask, AntTaskNode newNode)964 	private void setExternalInformation(Task newTask, AntTaskNode newNode) {
965 		String taskFileName = newTask.getLocation().getFileName();
966 		boolean external = isNodeExternal(taskFileName);
967 		newNode.setExternal(external);
968 		if (external) {
969 			newNode.setFilePath(taskFileName);
970 		}
971 	}
972 
generateLabel(String taskName, Attributes attributes, String attributeName)973 	private String generateLabel(String taskName, Attributes attributes, String attributeName) {
974 		StringBuilder label = new StringBuilder(taskName);
975 		String srcFile = attributes.getValue(attributeName);
976 		if (srcFile != null) {
977 			label.append(' ');
978 			label.append(srcFile);
979 		}
980 		return label.toString();
981 	}
982 
computeLength(AntElementNode element, int line, int column)983 	private void computeLength(AntElementNode element, int line, int column) {
984 		if (element.isExternal()) {
985 			element.setExternalInfo(line, column);
986 			return;
987 		}
988 		try {
989 			int length;
990 			int offset;
991 			if (column <= 0) {
992 				column = getLastCharColumn(line);
993 				String lineText = fDocument.get(fDocument.getLineOffset(line - 1), column);
994 				StringBuilder searchString = new StringBuilder("</"); //$NON-NLS-1$
995 				searchString.append(element.getName());
996 				searchString.append('>');
997 				int index = lineText.indexOf(searchString.toString());
998 				if (index == -1) {
999 					index = lineText.indexOf("/>"); //$NON-NLS-1$
1000 					if (index == -1) {
1001 						index = column; // set to the end of line
1002 					} else {
1003 						index = index + 3;
1004 					}
1005 				} else {
1006 					index = index + searchString.length() + 1;
1007 				}
1008 				offset = getOffset(line, index);
1009 			} else {
1010 				offset = getOffset(line, column);
1011 			}
1012 
1013 			length = offset - element.getOffset();
1014 			element.setLength(length);
1015 		}
1016 		catch (BadLocationException e) {
1017 			// ignore as the parser may be out of sync with the document during reconciliation
1018 		}
1019 	}
1020 
computeOffset(AntElementNode element, int line, int column)1021 	private void computeOffset(AntElementNode element, int line, int column) {
1022 		if (!canGetPositionInfo()) {
1023 			return;
1024 		}
1025 		if (element.isExternal()) {
1026 			element.setExternalInfo(line - 1, column);
1027 			return;
1028 		}
1029 		try {
1030 			String prefix = "<" + element.getName(); //$NON-NLS-1$
1031 			int offset = computeOffset(line, column, prefix);
1032 			element.setOffset(offset + 1);
1033 			element.setSelectionLength(element.getName().length());
1034 		}
1035 		catch (BadLocationException e) {
1036 			// ignore as the parser may be out of sync with the document during reconciliation
1037 		}
1038 	}
1039 
computeOffset(int line, int column, String prefix)1040 	private int computeOffset(int line, int column, String prefix) throws BadLocationException {
1041 		int offset;
1042 		if (column <= 0) {
1043 			offset = getOffset(line, 0);
1044 			int lastCharColumn = getLastCharColumn(line);
1045 			offset = computeOffsetUsingPrefix(line, offset, prefix, lastCharColumn);
1046 		} else {
1047 			column = column - 1;
1048 			offset = getOffset(line, column);
1049 			offset = computeOffsetUsingPrefix(line, offset, prefix, column);
1050 		}
1051 		return offset;
1052 	}
1053 
computeOffsetUsingPrefix(int line, int offset, String prefix, int column)1054 	private int computeOffsetUsingPrefix(int line, int offset, String prefix, int column) throws BadLocationException {
1055 		String lineText = fDocument.get(fDocument.getLineOffset(line - 1), column);
1056 		int lastIndex = lineText.indexOf(prefix);
1057 		if (lastIndex > -1) {
1058 			offset = getOffset(line, lastIndex + 1);
1059 		} else {
1060 			return computeOffsetUsingPrefix(line - 1, offset, prefix, getLastCharColumn(line - 1));
1061 		}
1062 		return offset;
1063 	}
1064 
1065 	@Override
getOffset(int line, int column)1066 	public int getOffset(int line, int column) throws BadLocationException {
1067 		return fDocument.getLineOffset(line - 1) + column - 1;
1068 	}
1069 
getNonWhitespaceOffset(int line, int column)1070 	private int getNonWhitespaceOffset(int line, int column) throws BadLocationException {
1071 		int offset = fDocument.getLineOffset(line - 1) + column - 1;
1072 		while (Character.isWhitespace(fDocument.getChar(offset))) {
1073 			offset++;
1074 		}
1075 		return offset;
1076 	}
1077 
getLine(int offset)1078 	public int getLine(int offset) {
1079 		try {
1080 			return fDocument.getLineOfOffset(offset) + 1;
1081 		}
1082 		catch (BadLocationException be) {
1083 			return -1;
1084 		}
1085 	}
1086 
getLastCharColumn(int line)1087 	private int getLastCharColumn(int line) throws BadLocationException {
1088 		String lineDelimiter = fDocument.getLineDelimiter(line - 1);
1089 		int lineDelimiterLength = lineDelimiter != null ? lineDelimiter.length() : 0;
1090 		return fDocument.getLineLength(line - 1) - lineDelimiterLength;
1091 	}
1092 
1093 	@Override
setCurrentElementLength(int lineNumber, int column)1094 	public void setCurrentElementLength(int lineNumber, int column) {
1095 		fLastNode = fStillOpenElements.pop();
1096 		if (fLastNode == fCurrentTargetNode) {
1097 			fCurrentTargetNode = null; // the current target element has been closed
1098 		}
1099 		if (canGetPositionInfo()) {
1100 			computeLength(fLastNode, lineNumber, column);
1101 		}
1102 	}
1103 
acceptProblem(IProblem problem)1104 	private void acceptProblem(IProblem problem) {
1105 		if (fProblemRequestor != null) {
1106 			fProblemRequestor.acceptProblem(problem);
1107 		}
1108 		if (fMarkerUpdater != null) {
1109 			fMarkerUpdater.acceptProblem(problem);
1110 		}
1111 	}
1112 
1113 	@Override
getFile()1114 	public IFile getFile() {
1115 		IPath location = fLocationProvider.getLocation();
1116 		if (location == null) {
1117 			return null;
1118 		}
1119 		IFile[] files = ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(location.toFile().toURI());
1120 		if (files.length > 0) {
1121 			return files[0];
1122 		}
1123 		return null;
1124 	}
1125 
beginReporting()1126 	private void beginReporting() {
1127 		if (fProblemRequestor != null) {
1128 			fProblemRequestor.beginReporting();
1129 		}
1130 		if (fMarkerUpdater != null) {
1131 			fMarkerUpdater.beginReporting();
1132 		}
1133 	}
1134 
endReporting()1135 	private void endReporting() {
1136 		if (fProblemRequestor != null) {
1137 			fProblemRequestor.endReporting();
1138 		}
1139 	}
1140 
createProblem(Exception exception, int offset, int length, int severity)1141 	private IProblem createProblem(Exception exception, int offset, int length, int severity) {
1142 		return createProblem(exception.getMessage(), offset, length, severity);
1143 	}
1144 
createProblem(String message, int offset, int length, int severity)1145 	private IProblem createProblem(String message, int offset, int length, int severity) {
1146 		return new AntModelProblem(message, severity, offset, length, getLine(offset));
1147 	}
1148 
notifyProblemRequestor(Exception exception, IAntElement element, int severity)1149 	private void notifyProblemRequestor(Exception exception, IAntElement element, int severity) {
1150 		if (doNotReportProblems()) {
1151 			return;
1152 		}
1153 		IAntElement importNode = element.getImportNode();
1154 		if (importNode != null) {
1155 			element = importNode;
1156 		}
1157 		IProblem problem = createProblem(exception, element.getOffset(), element.getLength(), severity);
1158 		acceptProblem(problem);
1159 		element.setProblem(problem);
1160 	}
1161 
notifyProblemRequestor(Exception exception, int offset, int length, int severity)1162 	private void notifyProblemRequestor(Exception exception, int offset, int length, int severity) {
1163 		if (doNotReportProblems()) {
1164 			return;
1165 		}
1166 		if (fProblemRequestor != null) {
1167 			IProblem problem = createProblem(exception, offset, length, severity);
1168 			acceptProblem(problem);
1169 		}
1170 	}
1171 
1172 	@Override
warning(Exception exception)1173 	public void warning(Exception exception) {
1174 		handleError(exception, AntModelProblem.SEVERITY_WARNING);
1175 	}
1176 
1177 	@Override
error(Exception exception)1178 	public void error(Exception exception) {
1179 		handleError(exception, AntModelProblem.SEVERITY_ERROR);
1180 	}
1181 
1182 	@Override
errorFromElementText(Exception exception, int start, int count)1183 	public void errorFromElementText(Exception exception, int start, int count) {
1184 		AntElementNode node = fLastNode;
1185 		if (node == null) {
1186 			if (!fStillOpenElements.empty()) {
1187 				node = fStillOpenElements.peek();
1188 			}
1189 		}
1190 		if (node == null) {
1191 			return;
1192 		}
1193 		computeEndLocationForErrorNode(node, start, count);
1194 		notifyProblemRequestor(exception, start, count, AntModelProblem.SEVERITY_ERROR);
1195 		markHierarchy(fLastNode, AntModelProblem.SEVERITY_ERROR, exception.getMessage());
1196 	}
1197 
1198 	@Override
errorFromElement(Exception exception, AntElementNode node, int lineNumber, int column)1199 	public void errorFromElement(Exception exception, AntElementNode node, int lineNumber, int column) {
1200 		if (node == null) {
1201 			if (!fStillOpenElements.empty()) {
1202 				node = fStillOpenElements.peek();
1203 			} else {
1204 				node = fLastNode;
1205 			}
1206 		}
1207 		computeEndLocationForErrorNode(node, lineNumber, column);
1208 		notifyProblemRequestor(exception, node, AntModelProblem.SEVERITY_ERROR);
1209 		markHierarchy(node, AntModelProblem.SEVERITY_ERROR, exception.getMessage());
1210 	}
1211 
createProblemElement(SAXParseException exception)1212 	private AntElementNode createProblemElement(SAXParseException exception) {
1213 		int lineNumber = exception.getLineNumber();
1214 		StringBuilder message = new StringBuilder(exception.getMessage());
1215 		if (lineNumber != -1) {
1216 			message.append(AntModelMessages.AntModel_1 + lineNumber);
1217 		}
1218 
1219 		AntElementNode errorNode = new AntElementNode(message.toString());
1220 		errorNode.setFilePath(exception.getSystemId());
1221 		errorNode.setProblemSeverity(AntModelProblem.SEVERITY_ERROR);
1222 		errorNode.setProblemMessage(exception.getMessage());
1223 		computeErrorLocation(errorNode, exception);
1224 		return errorNode;
1225 	}
1226 
computeErrorLocation(AntElementNode element, SAXParseException exception)1227 	private void computeErrorLocation(AntElementNode element, SAXParseException exception) {
1228 		if (element.isExternal()) {
1229 			return;
1230 		}
1231 
1232 		int line = exception.getLineNumber();
1233 		int startColumn = exception.getColumnNumber();
1234 		computeEndLocationForErrorNode(element, line, startColumn);
1235 	}
1236 
computeEndLocationForErrorNode(IAntElement element, int line, int startColumn)1237 	private void computeEndLocationForErrorNode(IAntElement element, int line, int startColumn) {
1238 		try {
1239 			if (line <= 0) {
1240 				line = 1;
1241 			}
1242 			int endColumn;
1243 			if (startColumn <= 0) {
1244 				if (element.getOffset() > -1) {
1245 					startColumn = element.getOffset() + 1;
1246 				} else {
1247 					startColumn = 1;
1248 				}
1249 				endColumn = getLastCharColumn(line) + 1;
1250 			} else {
1251 				if (startColumn > 1) {
1252 					--startColumn;
1253 				}
1254 
1255 				endColumn = startColumn;
1256 				if (startColumn <= getLastCharColumn(line)) {
1257 					++endColumn;
1258 				}
1259 			}
1260 
1261 			int correction = 0;
1262 			if (element.getOffset() == -1) {
1263 				int originalOffset = getOffset(line, startColumn);
1264 				int nonWhitespaceOffset = originalOffset;
1265 				try {
1266 					nonWhitespaceOffset = getNonWhitespaceOffset(line, startColumn);
1267 				}
1268 				catch (BadLocationException be) {
1269 					// do nothing
1270 				}
1271 				element.setOffset(nonWhitespaceOffset);
1272 				correction = nonWhitespaceOffset - originalOffset;
1273 			}
1274 			if (endColumn - startColumn == 0) {
1275 				int offset = getOffset(line, startColumn);
1276 				element.setLength(offset - element.getOffset() - correction);
1277 			} else {
1278 				element.setLength(endColumn - startColumn - correction);
1279 			}
1280 		}
1281 		catch (BadLocationException e) {
1282 			// ignore as the parser may be out of sync with the document during reconciliation
1283 		}
1284 	}
1285 
handleError(Exception exception, int severity)1286 	private void handleError(Exception exception, int severity) {
1287 		IAntElement node = null;
1288 		if (fStillOpenElements.isEmpty()) {
1289 			if (exception instanceof SAXParseException) {
1290 				node = createProblemElement((SAXParseException) exception);
1291 			}
1292 		} else {
1293 			node = fStillOpenElements.peek();
1294 		}
1295 		if (node == null) {
1296 			return;
1297 		}
1298 		markHierarchy(node, severity, exception.getMessage());
1299 
1300 		if (exception instanceof SAXParseException) {
1301 			SAXParseException parseException = (SAXParseException) exception;
1302 			if (node.getOffset() == -1) {
1303 				computeEndLocationForErrorNode(node, parseException.getLineNumber() - 1, parseException.getColumnNumber());
1304 			} else {
1305 				int lineNumber = parseException.getLineNumber();
1306 				int columnNumber = parseException.getColumnNumber();
1307 				if (columnNumber == -1) {
1308 					columnNumber = 1;
1309 				}
1310 				try {
1311 					AntElementNode childNode = node.getNode(getNonWhitespaceOffset(lineNumber, columnNumber) + 1);
1312 					if (childNode != null && childNode != node) {
1313 						node = childNode;
1314 						node.setProblemSeverity(severity);
1315 						node.setProblemMessage(exception.getMessage());
1316 					} else {
1317 						node = createProblemElement(parseException);
1318 					}
1319 				}
1320 				catch (BadLocationException be) {
1321 					node = createProblemElement(parseException);
1322 				}
1323 			}
1324 		}
1325 
1326 		notifyProblemRequestor(exception, node, severity);
1327 
1328 		if (node != null) {
1329 			while (node.getParentNode() != null) {
1330 				IAntElement parentNode = node.getParentNode();
1331 				if (parentNode.getLength() == -1) {
1332 					parentNode.setLength(node.getOffset() - parentNode.getOffset() + node.getLength());
1333 				}
1334 				node = parentNode;
1335 			}
1336 		}
1337 	}
1338 
1339 	@Override
fatalError(Exception exception)1340 	public void fatalError(Exception exception) {
1341 		handleError(exception, AntModelProblem.SEVERITY_FATAL_ERROR);
1342 	}
1343 
getOpenElement()1344 	public AntElementNode getOpenElement() {
1345 		if (fStillOpenElements.isEmpty()) {
1346 			return null;
1347 		}
1348 		return fStillOpenElements.peek();
1349 	}
1350 
1351 	@Override
getEntityName(String path)1352 	public String getEntityName(String path) {
1353 		if (fEntityNameToPath != null) {
1354 			Iterator<String> itr = fEntityNameToPath.keySet().iterator();
1355 			String entityPath;
1356 			String name;
1357 			while (itr.hasNext()) {
1358 				name = itr.next();
1359 				entityPath = fEntityNameToPath.get(name);
1360 				if (entityPath.equals(path)) {
1361 					return name;
1362 				}
1363 			}
1364 		}
1365 		return null;
1366 	}
1367 
getClassLoader(ClassLoader contextClassLoader)1368 	private ClassLoader getClassLoader(ClassLoader contextClassLoader) {
1369 		synchronized (loaderLock) {
1370 			if (fLocalClassLoader != null) {
1371 				((AntClassLoader) fLocalClassLoader).setPluginContextClassloader(contextClassLoader);
1372 				return fLocalClassLoader;
1373 			}
1374 			if (fgClassLoader == null) {
1375 				fgClassLoader = AntCorePlugin.getPlugin().getNewClassLoader(true);
1376 			}
1377 			if (fgClassLoader instanceof AntClassLoader) {
1378 				((AntClassLoader) fgClassLoader).setPluginContextClassloader(contextClassLoader);
1379 			}
1380 			return fgClassLoader;
1381 		}
1382 	}
1383 
getTargetDescription(String targetName)1384 	public String getTargetDescription(String targetName) {
1385 		AntTargetNode target = getTargetNode(targetName);
1386 		if (target != null) {
1387 			return target.getTarget().getDescription();
1388 		}
1389 		return null;
1390 	}
1391 
getTargetNode(String targetName)1392 	public AntTargetNode getTargetNode(String targetName) {
1393 		AntProjectNode projectNode = getProjectNode();
1394 		if (projectNode == null) {
1395 			return null;
1396 		}
1397 		if (projectNode.hasChildren()) {
1398 			List<IAntElement> possibleTargets = projectNode.getChildNodes();
1399 			for (IAntElement node : possibleTargets) {
1400 				if (node instanceof AntTargetNode) {
1401 					AntTargetNode targetNode = (AntTargetNode) node;
1402 					if (targetName.equalsIgnoreCase(targetNode.getTarget().getName())) {
1403 						return targetNode;
1404 					}
1405 				}
1406 			}
1407 		}
1408 		return null;
1409 	}
1410 
1411 	@Override
getProjectNode(boolean doReconcile)1412 	public AntProjectNode getProjectNode(boolean doReconcile) {
1413 		if (doReconcile) {
1414 			synchronized (getLockObject()) { // ensure to wait for any current synchronization
1415 				reconcile();
1416 			}
1417 		}
1418 		return fProjectNode;
1419 	}
1420 
1421 	@Override
getProjectNode()1422 	public AntProjectNode getProjectNode() {
1423 		return getProjectNode(true);
1424 	}
1425 
getNode(int offset, boolean waitForReconcile)1426 	public AntElementNode getNode(int offset, boolean waitForReconcile) {
1427 		if (getProjectNode(waitForReconcile) != null) {
1428 			return getProjectNode(waitForReconcile).getNode(offset);
1429 		}
1430 		return null;
1431 	}
1432 
1433 	/**
1434 	 * Removes any type definitions that no longer exist in the buildfile
1435 	 */
reconcileTaskAndTypes()1436 	private void reconcileTaskAndTypes() {
1437 		if (fCurrentNodeIdentifiers == null || fDefinerNodeIdentifierToDefinedTasks == null) {
1438 			return;
1439 		}
1440 		Iterator<String> iter = fDefinerNodeIdentifierToDefinedTasks.keySet().iterator();
1441 		ComponentHelper helper = ComponentHelper.getComponentHelper(fProjectNode.getProject());
1442 		while (iter.hasNext()) {
1443 			String key = iter.next();
1444 			if (fCurrentNodeIdentifiers.get(key) == null) {
1445 				removeDefinerTasks(key, helper.getAntTypeTable());
1446 			}
1447 		}
1448 	}
1449 
removeDefinerTasks(String definerIdentifier, Hashtable<String, AntTypeDefinition> typeTable)1450 	protected void removeDefinerTasks(String definerIdentifier, Hashtable<String, AntTypeDefinition> typeTable) {
1451 		if (fDefinerNodeIdentifierToDefinedTasks == null) {
1452 			return;
1453 		}
1454 		List<String> tasks = fDefinerNodeIdentifierToDefinedTasks.get(definerIdentifier);
1455 		if (tasks == null) {
1456 			return;
1457 		}
1458 		Iterator<String> iterator = tasks.iterator();
1459 		while (iterator.hasNext()) {
1460 			typeTable.remove(iterator.next());
1461 		}
1462 	}
1463 
1464 	@Override
addComment(int lineNumber, int columnNumber, int length)1465 	public void addComment(int lineNumber, int columnNumber, int length) {
1466 		AntCommentNode commentNode = new AntCommentNode();
1467 		int offset = -1;
1468 		try {
1469 			offset = computeOffset(lineNumber, columnNumber, "-->"); //$NON-NLS-1$
1470 		}
1471 		catch (BadLocationException e) {
1472 			commentNode.setExternal(true);
1473 			commentNode.setExternalInfo(lineNumber, columnNumber);
1474 			offset = length - 1;
1475 		}
1476 		commentNode.setOffset(offset - length);
1477 		commentNode.setLength(length);
1478 		fNonStructuralNodes.add(commentNode);
1479 	}
1480 
1481 	@Override
canGetTaskInfo()1482 	public boolean canGetTaskInfo() {
1483 		return fHasTaskInfo;
1484 	}
1485 
1486 	@Override
canGetLexicalInfo()1487 	public boolean canGetLexicalInfo() {
1488 		return fHasLexicalInfo;
1489 	}
1490 
1491 	@Override
setClassLoader(URLClassLoader loader)1492 	public void setClassLoader(URLClassLoader loader) {
1493 		AntDefiningTaskNode.setJavaClassPath(loader.getURLs());
1494 		fLocalClassLoader = loader;
1495 	}
1496 
1497 	@Override
canGetPositionInfo()1498 	public boolean canGetPositionInfo() {
1499 		return fHasPositionInfo;
1500 	}
1501 
getPath(String text, int offset)1502 	public String getPath(String text, int offset) {
1503 		if (fEntityNameToPath != null) {
1504 			String path = fEntityNameToPath.get(text);
1505 			if (path != null) {
1506 				return path;
1507 			}
1508 		}
1509 		AntElementNode node = getNode(offset, true);
1510 		if (node != null) {
1511 			return node.getReferencedElement(offset);
1512 		}
1513 		return null;
1514 	}
1515 
1516 	@Override
getText(int offset, int length)1517 	public String getText(int offset, int length) {
1518 		try {
1519 			return fDocument.get(offset, length);
1520 		}
1521 		catch (BadLocationException e) {
1522 			// do nothing
1523 		}
1524 		return null;
1525 	}
1526 
findPropertyNode(String text, List<IAntElement> children)1527 	private IAntElement findPropertyNode(String text, List<IAntElement> children) {
1528 		for (IAntElement element : children) {
1529 			if (element instanceof AntPropertyNode) {
1530 				if (((AntPropertyNode) element).getProperty(text) != null) {
1531 					return element;
1532 				}
1533 			} else if (element.hasChildren()) {
1534 				IAntElement found = findPropertyNode(text, element.getChildNodes());
1535 				if (found != null) {
1536 					return found;
1537 				}
1538 			}
1539 		}
1540 		return null;
1541 	}
1542 
getPropertyNode(String text)1543 	public IAntElement getPropertyNode(String text) {
1544 		AntProjectNode node = getProjectNode();
1545 		if (node == null || !node.hasChildren()) {
1546 			return null;
1547 		}
1548 
1549 		return findPropertyNode(text, node.getChildNodes());
1550 	}
1551 
getNonStructuralNodes()1552 	public List<AntElementNode> getNonStructuralNodes() {
1553 		return fNonStructuralNodes;
1554 	}
1555 
updateForInitialReconcile()1556 	public void updateForInitialReconcile() {
1557 		fMarkerUpdater.updateMarkers();
1558 		fShouldReconcile = AntUIPlugin.getDefault().getPreferenceStore().getBoolean(AntEditorPreferenceConstants.EDITOR_RECONCILE);
1559 	}
1560 
updateMarkers()1561 	public void updateMarkers() {
1562 		boolean temp = fShouldReconcile;
1563 		try {
1564 			fShouldReconcile = true;
1565 			reconcile();
1566 			fMarkerUpdater.updateMarkers();
1567 		}
1568 		finally {
1569 			fShouldReconcile = temp;
1570 		}
1571 	}
1572 
getReferenceNode(String text)1573 	public AntElementNode getReferenceNode(String text) {
1574 		Object reference = getReferenceObject(text);
1575 		if (reference == null) {
1576 			return null;
1577 		}
1578 
1579 		Set<Task> nodes = fTaskToNode.keySet();
1580 		Iterator<Task> iter = nodes.iterator();
1581 		while (iter.hasNext()) {
1582 			Task task = iter.next();
1583 			Task tmptask = task;
1584 			if (tmptask instanceof UnknownElement) {
1585 				UnknownElement element = (UnknownElement) tmptask;
1586 				RuntimeConfigurable wrapper = element.getWrapper();
1587 				Map<String, Object> attributes = wrapper.getAttributeMap();
1588 				String id = (String) attributes.get("id"); //$NON-NLS-1$
1589 				if (text.equals(id)) {
1590 					return fTaskToNode.get(task);
1591 				}
1592 			}
1593 		}
1594 		return null;
1595 	}
1596 
getReferenceObject(String refId)1597 	public Object getReferenceObject(String refId) {
1598 		AntProjectNode projectNode = getProjectNode();
1599 		if (projectNode == null) {
1600 			return null;
1601 		}
1602 		try {
1603 			Project project = projectNode.getProject();
1604 			Object ref = project.getReference(refId);
1605 			return ref;
1606 
1607 		}
1608 		catch (BuildException be) {
1609 			handleBuildException(be, null);
1610 		}
1611 		return null;
1612 	}
1613 
getPropertyValue(String propertyName)1614 	public String getPropertyValue(String propertyName) {
1615 		AntProjectNode projectNode = getProjectNode();
1616 		if (projectNode == null) {
1617 			return null;
1618 		}
1619 		return projectNode.getProject().getProperty(propertyName);
1620 	}
1621 
1622 	/**
1623 	 * Only called if the AntModel is associated with an AntEditor
1624 	 */
install()1625 	public void install() {
1626 		fListener = new IDocumentListener() {
1627 			@Override
1628 			public void documentAboutToBeChanged(DocumentEvent event) {
1629 				synchronized (fDirtyLock) {
1630 					fIsDirty = true;
1631 				}
1632 			}
1633 
1634 			@Override
1635 			public void documentChanged(DocumentEvent event) {
1636 				// do nothing
1637 			}
1638 		};
1639 		fDocument.addDocumentListener(fListener);
1640 	}
1641 
reconcileForPropertyChange(boolean classpathChanged)1642 	private void reconcileForPropertyChange(boolean classpathChanged) {
1643 		if (classpathChanged) {
1644 			fProjectNode = null; // need to reset tasks, types and properties
1645 			fgClassLoader = null;
1646 			AntDefiningTaskNode.setJavaClassPath();
1647 			ProjectHelper.reset();
1648 		}
1649 		fIsDirty = true;
1650 		reconcile();
1651 		AntModelCore.getDefault().notifyAntModelListeners(new AntModelChangeEvent(this, true));
1652 		fMarkerUpdater.updateMarkers();
1653 	}
1654 
1655 	@Override
setProperties(Map<String, String> properties)1656 	public void setProperties(Map<String, String> properties) {
1657 		fProperties = properties;
1658 	}
1659 
1660 	@Override
setPropertyFiles(String[] propertyFiles)1661 	public void setPropertyFiles(String[] propertyFiles) {
1662 		if (propertyFiles != null) {
1663 			fPropertyFiles = Arrays.asList(propertyFiles);
1664 		}
1665 	}
1666 
1667 	@Override
setDefiningTaskNodeText(AntDefiningTaskNode node)1668 	public void setDefiningTaskNodeText(AntDefiningTaskNode node) {
1669 		if (fDefinersToText == null) {
1670 			fDefinersToText = new HashMap<>();
1671 			fCurrentNodeIdentifiers = new HashMap<>();
1672 		}
1673 
1674 		String nodeIdentifier = node.getIdentifier();
1675 		String nodeText = null;
1676 		if (fPreviousDefinersToText != null) {
1677 			nodeText = fPreviousDefinersToText.get(nodeIdentifier);
1678 		}
1679 		String newNodeText = getText(node.getOffset(), node.getLength());
1680 		if (nodeText != null) {
1681 			if (nodeText.equals(newNodeText)) {
1682 				node.setNeedsToBeConfigured(false);
1683 				// update the data structures for the new node as the offset may have changed.
1684 				List<String> tasks = fDefinerNodeIdentifierToDefinedTasks.get(nodeIdentifier);
1685 				if (tasks != null) {
1686 					for (String taskName : tasks) {
1687 						fTaskNameToDefiningNode.put(taskName, node);
1688 					}
1689 				}
1690 			}
1691 		}
1692 
1693 		if (newNodeText != null) {
1694 			fDefinersToText.put(nodeIdentifier, newNodeText);
1695 		}
1696 		fCurrentNodeIdentifiers.put(nodeIdentifier, nodeIdentifier);
1697 	}
1698 
removeDefiningTaskNodeInfo(AntDefiningTaskNode node)1699 	protected void removeDefiningTaskNodeInfo(AntDefiningTaskNode node) {
1700 		Object identifier = node.getIdentifier();
1701 		if (identifier != null && fCurrentNodeIdentifiers != null) {
1702 			fCurrentNodeIdentifiers.remove(identifier);
1703 			fDefinersToText.remove(identifier);
1704 		}
1705 	}
1706 
addDefinedTasks(List<String> newTasks, AntDefiningTaskNode node)1707 	protected void addDefinedTasks(List<String> newTasks, AntDefiningTaskNode node) {
1708 		if (fTaskNameToDefiningNode == null) {
1709 			fTaskNameToDefiningNode = new HashMap<>();
1710 			fDefinerNodeIdentifierToDefinedTasks = new HashMap<>();
1711 		}
1712 
1713 		String identifier = node.getIdentifier();
1714 		if (identifier == null) {
1715 			return;
1716 		}
1717 		if (newTasks.isEmpty() && fCurrentNodeIdentifiers != null) {
1718 			fCurrentNodeIdentifiers.remove(identifier);
1719 		}
1720 		fDefinerNodeIdentifierToDefinedTasks.put(identifier, newTasks);
1721 		Iterator<String> iter = newTasks.iterator();
1722 		while (iter.hasNext()) {
1723 			String name = iter.next();
1724 			fTaskNameToDefiningNode.put(name, node);
1725 		}
1726 	}
1727 
getDefininingTaskNode(String nodeName)1728 	public AntDefiningTaskNode getDefininingTaskNode(String nodeName) {
1729 		if (fTaskNameToDefiningNode != null) {
1730 			AntDefiningTaskNode node = fTaskNameToDefiningNode.get(nodeName);
1731 			if (node == null) {
1732 				nodeName = getNamespaceCorrectName(nodeName);
1733 				node = fTaskNameToDefiningNode.get(nodeName);
1734 			}
1735 			return node;
1736 		}
1737 		return null;
1738 	}
1739 
getNamespaceCorrectName(String nodeName)1740 	public String getNamespaceCorrectName(String nodeName) {
1741 		String prefix = org.apache.tools.ant.ProjectHelper.extractUriFromComponentName(nodeName);
1742 		String uri = getPrefixMapping(prefix);
1743 		nodeName = org.apache.tools.ant.ProjectHelper.genComponentName(uri, org.apache.tools.ant.ProjectHelper.extractNameFromComponentName(nodeName));
1744 		return nodeName;
1745 	}
1746 
getUserNamespaceCorrectName(String nodeName)1747 	public String getUserNamespaceCorrectName(String nodeName) {
1748 		String prefix = org.apache.tools.ant.ProjectHelper.extractUriFromComponentName(nodeName);
1749 		if (prefix.length() > 0) {
1750 			String uri = getUserPrefixMapping(prefix);
1751 			nodeName = org.apache.tools.ant.ProjectHelper.genComponentName(uri, org.apache.tools.ant.ProjectHelper.extractNameFromComponentName(nodeName));
1752 		}
1753 		return nodeName;
1754 	}
1755 
getMacroDefAttributeNode(String macroDefAttributeName)1756 	public AntTaskNode getMacroDefAttributeNode(String macroDefAttributeName) {
1757 		if (fTaskNameToDefiningNode == null) {
1758 			return null;
1759 		}
1760 		for (AntDefiningTaskNode definingNode : fTaskNameToDefiningNode.values()) {
1761 			List<IAntElement> attributes = definingNode.getChildNodes();
1762 			if (attributes != null) {
1763 				for (IAntElement element : attributes) {
1764 					if (macroDefAttributeName.equals(element.getLabel())) {
1765 						return (AntTaskNode) element;
1766 					}
1767 				}
1768 			}
1769 		}
1770 		return null;
1771 	}
1772 
1773 	/**
1774 	 * Sets whether the AntModel should reconcile if it become dirty. If set to reconcile, a reconcile is triggered if the model is dirty.
1775 	 *
1776 	 * @param shouldReconcile
1777 	 */
setShouldReconcile(boolean shouldReconcile)1778 	public void setShouldReconcile(boolean shouldReconcile) {
1779 		fShouldReconcile = shouldReconcile;
1780 		if (fShouldReconcile) {
1781 			reconcile();
1782 		}
1783 	}
1784 
1785 	@Override
addPrefixMapping(String prefix, String uri)1786 	public void addPrefixMapping(String prefix, String uri) {
1787 		if (fNamespacePrefixMappings == null) {
1788 			fNamespacePrefixMappings = new HashMap<>();
1789 		}
1790 		fNamespacePrefixMappings.put(prefix, uri);
1791 	}
1792 
getPrefixMapping(String prefix)1793 	private String getPrefixMapping(String prefix) {
1794 		if (fNamespacePrefixMappings != null) {
1795 			return fNamespacePrefixMappings.get(prefix);
1796 		}
1797 		return null;
1798 	}
1799 
getUserPrefixMapping(String prefix)1800 	private String getUserPrefixMapping(String prefix) {
1801 		if (fNamespacePrefixMappings != null) {
1802 			Set<Entry<String, String>> entrySet = fNamespacePrefixMappings.entrySet();
1803 			Iterator<Entry<String, String>> entries = entrySet.iterator();
1804 			while (entries.hasNext()) {
1805 				Map.Entry<String, String> entry = entries.next();
1806 				if (entry.getValue().equals(prefix)) {
1807 					return entry.getKey();
1808 				}
1809 			}
1810 		}
1811 		return null;
1812 	}
1813 
1814 	/**
1815 	 * Compute the encoding for the backing build file
1816 	 *
1817 	 * @since 3.7
1818 	 */
computeEncoding()1819 	void computeEncoding() {
1820 		try {
1821 			IFile file = getFile();
1822 			if (file != null) {
1823 				fEncoding = getFile().getCharset(true);
1824 				return;
1825 			}
1826 		}
1827 		catch (CoreException e) {
1828 			// do nothing. default to UTF-8
1829 		}
1830 		// try the file buffer manager - likely an external file
1831 		IPath path = getLocationProvider().getLocation();
1832 		if (path != null) {
1833 			File buildfile = path.toFile();
1834 			try (FileReader reader = new FileReader(buildfile)) {
1835 				QualifiedName[] options = new QualifiedName[] { IContentDescription.CHARSET };
1836 				IContentDescription desc = Platform.getContentTypeManager().getDescriptionFor(reader, buildfile.getName(), options);
1837 				if (desc != null) {
1838 					fEncoding = desc.getCharset();
1839 					return;
1840 				}
1841 			}
1842 			catch (IOException ioe) {
1843 				// do nothing
1844 			}
1845 		}
1846 		fEncoding = IAntCoreConstants.UTF_8;
1847 	}
1848 
1849 	@Override
getEncoding()1850 	public String getEncoding() {
1851 		return fEncoding;
1852 	}
1853 }