| 1 12 13 package org.eclipse.ant.internal.ui.editor.outline; 14 15 import java.io.File ; 16 import java.text.MessageFormat ; 17 import java.util.ArrayList ; 18 import java.util.Collection ; 19 import java.util.HashMap ; 20 import java.util.Iterator ; 21 import java.util.List ; 22 import java.util.Map ; 23 import java.util.Set ; 24 import java.util.Stack ; 25 26 import org.apache.tools.ant.AntTypeDefinition; 27 import org.apache.tools.ant.BuildException; 28 import org.apache.tools.ant.ComponentHelper; 29 import org.apache.tools.ant.IntrospectionHelper; 30 import org.apache.tools.ant.Location; 31 import org.apache.tools.ant.Project; 32 import org.apache.tools.ant.Target; 33 import org.apache.tools.ant.Task; 34 import org.apache.tools.ant.TaskAdapter; 35 import org.apache.tools.ant.UnknownElement; 36 import org.eclipse.ant.core.AntCorePlugin; 37 import org.eclipse.ant.core.Type; 38 import org.eclipse.ant.internal.core.IAntCoreConstants; 39 import org.eclipse.ant.internal.ui.editor.model.AntDefiningTaskNode; 40 import org.eclipse.ant.internal.ui.editor.model.AntElementNode; 41 import org.eclipse.ant.internal.ui.editor.model.AntImportNode; 42 import org.eclipse.ant.internal.ui.editor.model.AntProjectNode; 43 import org.eclipse.ant.internal.ui.editor.model.AntPropertyNode; 44 import org.eclipse.ant.internal.ui.editor.model.AntTargetNode; 45 import org.eclipse.ant.internal.ui.editor.model.AntTaskNode; 46 import org.eclipse.ant.internal.ui.editor.model.IAntModelConstants; 47 import org.eclipse.ant.internal.ui.editor.utils.ProjectHelper; 48 import org.eclipse.ant.internal.ui.model.AntUIPlugin; 49 import org.eclipse.ant.internal.ui.preferences.AntEditorPreferenceConstants; 50 import org.eclipse.core.resources.IFile; 51 import org.eclipse.core.resources.ResourcesPlugin; 52 import org.eclipse.core.runtime.IPath; 53 import org.eclipse.core.runtime.Preferences; 54 import org.eclipse.jface.text.BadLocationException; 55 import org.eclipse.jface.text.DocumentEvent; 56 import org.eclipse.jface.text.IDocument; 57 import org.eclipse.jface.text.IDocumentListener; 58 import org.eclipse.jface.text.reconciler.DirtyRegion; 59 import org.xml.sax.Attributes ; 60 import org.xml.sax.SAXParseException ; 61 62 public class AntModel { 63 64 private static ClassLoader fgClassLoader; 65 private static int fgInstanceCount= 0; 66 67 private XMLCore fCore; 68 private IDocument fDocument; 69 private IProblemRequestor fProblemRequestor; 70 private LocationProvider fLocationProvider; 71 72 private AntProjectNode fProjectNode; 73 private AntTargetNode fCurrentTargetNode; 74 private AntElementNode fLastNode; 75 private AntElementNode fNodeBeingResolved; 76 77 private AntTargetNode fIncrementalTarget= null; 78 private boolean fReplaceHasOccurred= false; 79 private int fRemoveLengthOfReplace= 0; 80 private DirtyRegion fDirtyRegion= null; 81 82 87 private Stack fStillOpenElements = new Stack (); 88 89 private Map fTaskToNode= new HashMap (); 90 91 private List fTaskNodes= new ArrayList (); 92 93 private Map fEntityNameToPath; 94 95 private final Object fDirtyLock= new Object (); 96 private boolean fIsDirty= true; 97 private IDocumentListener fListener; 98 private File fEditedFile= null; 99 private AntEditorMarkerUpdater fMarkerUpdater= null; 100 private Set fNamesOfOldDefiningNodes; 101 102 private Preferences.IPropertyChangeListener fCorePropertyChangeListener= new Preferences.IPropertyChangeListener() { 103 public void propertyChange(Preferences.PropertyChangeEvent event) { 104 if (event.getProperty().equals(IAntCoreConstants.PREFERENCE_CLASSPATH_CHANGED)) { 105 if (((Boolean )event.getNewValue()) == Boolean.TRUE) { 106 reconcileForPropertyChange(true); 107 } 108 } 109 } 110 }; 111 112 private Preferences.IPropertyChangeListener fUIPropertyChangeListener= new Preferences.IPropertyChangeListener() { 113 public void propertyChange(Preferences.PropertyChangeEvent event) { 114 String property= event.getProperty(); 115 if (property.equals(AntEditorPreferenceConstants.PROBLEM)) { 116 AntUIPlugin.getDefault().getPluginPreferences().removePropertyChangeListener(fUIPropertyChangeListener); 117 reconcileForPropertyChange(false); 118 AntUIPlugin.getDefault().getPluginPreferences().setToDefault(AntEditorPreferenceConstants.PROBLEM); 119 AntUIPlugin.getDefault().getPluginPreferences().addPropertyChangeListener(fUIPropertyChangeListener); 120 } else if (property.equals(AntEditorPreferenceConstants.CODEASSIST_USER_DEFINED_TASKS)) { 121 if (((Boolean )event.getNewValue()).booleanValue()) { 122 reconcileForPropertyChange(false); 123 } 124 } 125 } 126 }; 127 128 public AntModel(XMLCore core, IDocument document, IProblemRequestor problemRequestor, LocationProvider locationProvider) { 129 fCore= core; 130 fDocument= document; 131 fProblemRequestor= problemRequestor; 132 fMarkerUpdater= new AntEditorMarkerUpdater(); 133 fMarkerUpdater.setModel(this); 134 fLocationProvider= locationProvider; 135 AntCorePlugin.getPlugin().getPluginPreferences().addPropertyChangeListener(fCorePropertyChangeListener); 136 AntUIPlugin.getDefault().getPluginPreferences().addPropertyChangeListener(fUIPropertyChangeListener); 137 AntDefiningTaskNode.setJavaClassPath(); 138 fgInstanceCount++; 139 } 140 141 private void reconcileForPropertyChange(boolean classpathChanged) { 142 if (classpathChanged) { 143 fgClassLoader= null; 144 AntDefiningTaskNode.setJavaClassPath(); 145 } 146 fIsDirty= true; 147 reconcile(null); 148 fCore.notifyDocumentModelListeners(new DocumentModelChangeEvent(this, true)); 149 fMarkerUpdater.updateMarkers(); 150 } 151 152 public void install() { 153 fListener= new IDocumentListener() { 154 public void documentAboutToBeChanged(DocumentEvent event) { 155 synchronized (fDirtyLock) { 156 fIsDirty= true; 157 } 158 } 159 public void documentChanged(DocumentEvent event) {} 160 }; 161 fDocument.addDocumentListener(fListener); 162 } 163 164 public void dispose() { 165 synchronized (this) { 166 if (fDocument != null) { 167 fDocument.removeDocumentListener(fListener); 168 } 169 fDocument= null; 170 fCore= null; 171 ProjectHelper.setAntModel(null); 172 } 173 174 AntCorePlugin.getPlugin().getPluginPreferences().removePropertyChangeListener(fCorePropertyChangeListener); 175 AntUIPlugin.getDefault().getPluginPreferences().removePropertyChangeListener(fUIPropertyChangeListener); 176 fgInstanceCount--; 177 if (fgInstanceCount == 0) { 178 fgClassLoader= null; 179 } 180 if (getProjectNode() != null) { 181 IntrospectionHelper.getHelper(getProjectNode().getProject(), AntModel.class); 183 getProjectNode().getProject().fireBuildFinished(null); 184 } 185 } 186 187 public void reconcile(DirtyRegion region) { 188 region= null; 190 fDirtyRegion= region; 191 synchronized (fDirtyLock) { 192 if (!fIsDirty) { 193 return; 194 } 195 if (fReplaceHasOccurred && region != null) { 196 fRemoveLengthOfReplace= region.getLength(); 199 fReplaceHasOccurred= false; 200 return; 201 } 202 fIsDirty= false; 203 } 204 205 synchronized (this) { 206 if (fCore == null) { 207 return; 209 } 210 211 if (fDocument == null) { 212 fProjectNode= null; 213 } else { 214 reset(region); 215 parseDocument(fDocument, region); 216 fRemoveLengthOfReplace= 0; 217 fDirtyRegion= null; 218 reconcileTaskAndTypes(); 219 } 220 221 fCore.notifyDocumentModelListeners(new DocumentModelChangeEvent(this)); 222 } 223 } 224 225 private void reset(DirtyRegion region) { 226 fCurrentTargetNode= null; 229 230 if (region == null ) { 231 fStillOpenElements= new Stack (); 232 fTaskToNode= new HashMap (); 233 fTaskNodes= new ArrayList (); 234 fNodeBeingResolved= null; 235 fLastNode= null; 236 } 237 } 238 239 public AntElementNode[] getRootElements() { 240 reconcile(null); 241 if (fProjectNode == null) { 242 return new AntElementNode[0]; 243 } 244 return new AntElementNode[] {fProjectNode}; 245 } 246 247 private void parseDocument(IDocument input, DirtyRegion region) { 248 boolean parsed= true; 249 if (input.getLength() == 0) { 250 fProjectNode= null; 251 parsed= false; 252 return; 253 } 254 ClassLoader parsingClassLoader= getClassLoader(); 255 ClassLoader originalClassLoader= Thread.currentThread().getContextClassLoader(); 256 Thread.currentThread().setContextClassLoader(parsingClassLoader); 257 boolean incremental= false; 258 Project project= null; 259 try { 260 String textToParse= null; 261 ProjectHelper projectHelper= null; 262 if (region == null || fProjectNode == null) { if (fProjectNode == null || !fProjectNode.hasChildren()) { 264 fProjectNode= null; 265 project = new AntModelProject(); 266 projectHelper= prepareForFullParse(project, parsingClassLoader); 267 textToParse= input.get(); } else { 269 project= fProjectNode.getProject(); 270 projectHelper= (ProjectHelper)project.getReference("ant.projectHelper"); textToParse= prepareForFullIncremental(input); 272 } 273 } else { project= fProjectNode.getProject(); 275 textToParse= prepareForIncrementalParse(project, region, input); 276 if (textToParse == null) { 277 parsed= false; 278 return; 279 } 280 incremental= true; 281 projectHelper= (ProjectHelper)project.getReference("ant.projectHelper"); } 283 beginReporting(); 284 Map references= project.getReferences(); 285 references.remove("ant.parsing.context"); ProjectHelper.setAntModel(this); 287 projectHelper.parse(project, textToParse); 288 289 } catch(BuildException e) { 290 handleBuildException(e, null); 291 } finally { 292 Thread.currentThread().setContextClassLoader(originalClassLoader); 293 if (parsed) { 294 if (incremental) { 295 updateAfterIncrementalChange(region, true); 296 } 297 resolveBuildfile(); 298 endReporting(); 299 project.fireBuildFinished(null); fIncrementalTarget= null; 301 } 302 } 303 } 304 305 private void updateAfterIncrementalChange(DirtyRegion region, boolean updateProjectLength) { 306 if (fProjectNode == null) { 307 return; 308 } 309 int editAdjustment= determineEditAdjustment(region); 310 if (editAdjustment == 0) { 311 return; 312 } 313 if (updateProjectLength) { fProjectNode.setLength(fProjectNode.getLength() + editAdjustment); 315 } else { 316 fProjectNode.setOffset(fProjectNode.getOffset() + editAdjustment); 317 } 318 if ((fIncrementalTarget != null || !updateProjectLength) && fProjectNode.hasChildren()) { 319 List children= fProjectNode.getChildNodes(); 320 int index= children.indexOf(fIncrementalTarget) + 1; 321 updateNodesForIncrementalParse(editAdjustment, children, index); 322 } 323 } 324 325 private void updateNodesForIncrementalParse(int editAdjustment, List children, int index) { 326 AntElementNode node; 327 for (int i = index; i < children.size(); i++) { 328 node= (AntElementNode)children.get(i); 329 node.setOffset(node.getOffset() + editAdjustment); 330 if (node.hasChildren()) { 331 updateNodesForIncrementalParse(editAdjustment, node.getChildNodes(), 0); 332 } 333 } 334 } 335 336 private ProjectHelper prepareForFullParse(Project project, ClassLoader parsingClassLoader) { 337 initializeProject(project, parsingClassLoader); 338 File file = getEditedFile(); 342 String filePath= ""; if (file != null) { 344 filePath= file.getAbsolutePath(); 345 } 346 project.setUserProperty("ant.file", filePath); 348 ProjectHelper projectHelper= new ProjectHelper(this); 349 projectHelper.setBuildFile(file); 350 project.addReference("ant.projectHelper", projectHelper); return projectHelper; 352 } 353 354 private String prepareForIncrementalParse(Project project, DirtyRegion region, IDocument input) { 355 String textToParse= null; 356 AntElementNode node= fProjectNode.getNode(region.getOffset()); 357 if (node == null) { 358 if (fProjectNode.getLength() > 0) { 359 if (region.getOffset() < fProjectNode.getOffset()) { 361 updateAfterIncrementalChange(region, false); 362 } 363 return null; 364 } 365 textToParse = prepareForFullIncremental(input); 367 return textToParse; 368 } 369 370 while (node != null && !(node instanceof AntTargetNode)) { 371 node= node.getParentNode(); 372 } 373 if (node == null) { if (region.getText() != null && region.getText().trim().length() == 0) { 375 return null; } 377 textToParse= prepareForFullIncremental(input); 378 } else { 379 fIncrementalTarget= (AntTargetNode)node; 380 if (fIncrementalTarget.hasChildren()) { 381 Collection nodes= fTaskToNode.values(); 382 nodes.removeAll(fIncrementalTarget.getDescendents()); 383 } 384 385 markHierarchy(node, XMLProblem.NO_PROBLEM); 386 387 StringBuffer temp = createIncrementalContents(project); 388 fIncrementalTarget.reset(); 389 try { 390 int editAdjustment = determineEditAdjustment(region) + 1; 391 String targetString= input.get(node.getOffset() - 1, node.getLength() + editAdjustment); 392 temp.append(targetString); 393 temp.append("\n</project>"); textToParse= temp.toString(); 395 } catch (BadLocationException e) { 396 textToParse= input.get(); 397 } 398 } 399 return textToParse; 400 } 401 402 private String prepareForFullIncremental(IDocument input) { 403 String textToParse= input.get(); 404 fProjectNode.reset(); 405 fTaskToNode= new HashMap (); 406 fTaskNodes= new ArrayList (); 407 return textToParse; 408 } 409 410 private StringBuffer createIncrementalContents(Project project) { 411 int offset= fIncrementalTarget.getOffset(); 412 int line= getLine(offset) - 1; 413 414 StringBuffer temp= new StringBuffer ("<project"); String defltTarget= project.getDefaultTarget(); 416 if (defltTarget != null) { 417 temp.append(" default=\""); temp.append(defltTarget); 419 temp.append("\""); } 421 temp.append(">"); while (line > 0) { 423 temp.append("\n"); line--; 425 } 426 return temp; 427 } 428 429 private int determineEditAdjustment(DirtyRegion region) { 430 int editAdjustment= 0; 431 if (region.getType().equals(DirtyRegion.INSERT)) { 432 editAdjustment+= region.getLength() - fRemoveLengthOfReplace; 433 } else { 434 editAdjustment-= region.getLength(); 435 } 436 return editAdjustment; 437 } 438 439 private void initializeProject(Project project, ClassLoader loader) { 440 project.init(); 441 setTasks(project, loader); 442 setTypes(project, loader); 443 } 444 445 private void setTasks(Project project, ClassLoader loader) { 446 List tasks = AntCorePlugin.getPlugin().getPreferences().getTasks(); 447 for (Iterator iterator = tasks.iterator(); iterator.hasNext();) { 448 org.eclipse.ant.core.Task task = (org.eclipse.ant.core.Task) iterator.next(); 449 AntTypeDefinition def= new AntTypeDefinition(); 450 def.setName(task.getTaskName()); 451 def.setClassName(task.getClassName()); 452 def.setClassLoader(loader); 453 def.setAdaptToClass(Task.class); 454 def.setAdapterClass(TaskAdapter.class); 455 ComponentHelper.getComponentHelper(project).addDataTypeDefinition(def); 456 } 457 } 458 459 private void setTypes(Project project, ClassLoader loader) { 460 List types = AntCorePlugin.getPlugin().getPreferences().getTypes(); 461 for (Iterator iterator = types.iterator(); iterator.hasNext();) { 462 Type type = (Type) iterator.next(); 463 AntTypeDefinition def = new AntTypeDefinition(); 464 def.setName(type.getTypeName()); 465 def.setClassName(type.getClassName()); 466 def.setClassLoader(loader); 467 ComponentHelper.getComponentHelper(project).addDataTypeDefinition(def); 468 } 469 } 470 471 private void resolveBuildfile() { 472 Collection nodeCopy= new ArrayList (fTaskNodes.size()); 473 nodeCopy.addAll(fTaskNodes); 474 Iterator iter= nodeCopy.iterator(); 475 while (iter.hasNext()) { 476 AntTaskNode node = (AntTaskNode) iter.next(); 477 fNodeBeingResolved= node; 478 if (node.configure(false)) { 479 resolveBuildfile(); 481 } 482 } 483 fNodeBeingResolved= null; 484 485 checkTargets(); 486 } 487 488 492 private void checkTargets() { 493 if (fProjectNode == null) { 494 return; 495 } 496 String defaultTargetName= fProjectNode.getProject().getDefaultTarget(); 497 if (defaultTargetName == null || fProjectNode.getProject().getTargets().get(defaultTargetName) == null) { 498 String message; 500 if (defaultTargetName == null) { 501 message= AntOutlineMessages.getString("AntModel.0"); } else { 503 message= MessageFormat.format(AntOutlineMessages.getString("AntModel.43"), new String []{defaultTargetName}); } 505 IProblem problem= createProblem(message, fProjectNode.getOffset(), fProjectNode.getSelectionLength(), XMLProblem.SEVERITY_ERROR); 506 acceptProblem(problem); 507 markHierarchy(fProjectNode, XMLProblem.SEVERITY_ERROR); 508 } 509 if (!fProjectNode.hasChildren()) { 510 return; 511 } 512 List children= fProjectNode.getChildNodes(); 513 Iterator iter= children.iterator(); 514 while (iter.hasNext()) { 515 AntElementNode node = (AntElementNode) iter.next(); 516 AntElementNode originalNode= node; 517 if (node instanceof AntTargetNode) { 518 String missing= ((AntTargetNode)node).checkDependencies(); 519 if (missing != null) { 520 String message= MessageFormat.format(AntOutlineMessages.getString("AntModel.44"), new String []{missing}); AntElementNode importNode= node.getImportNode(); 522 if (importNode != null) { 523 node= importNode; 524 } 525 IProblem problem= createProblem(message, node.getOffset(), node.getSelectionLength(), XMLProblem.SEVERITY_ERROR); 526 acceptProblem(problem); 527 markHierarchy(originalNode, XMLProblem.SEVERITY_ERROR); 528 } 529 } 530 } 531 532 } 533 534 public void handleBuildException(BuildException e, AntElementNode node, int severity) { 535 try { 536 if (node != null) { 537 markHierarchy(node, severity); 538 } 539 Location location= e.getLocation(); 540 int line= 0; 541 int originalOffset= 0; 542 int nonWhitespaceOffset= 0; 543 int length= 0; 544 if (location == Location.UNKNOWN_LOCATION && node != null) { 545 nonWhitespaceOffset= node.getOffset(); 546 length= node.getLength(); 547 } else { 548 line= location.getLineNumber(); 549 if (line == 0) { 550 if (getProjectNode() != null) { 551 length= getProjectNode().getSelectionLength(); 552 nonWhitespaceOffset= getProjectNode().getOffset(); 553 if (severity == XMLProblem.SEVERITY_ERROR) { 554 getProjectNode().setProblemSeverity(XMLProblem.NO_PROBLEM); 555 } 556 } else { 557 return; 558 } 559 } else { 560 if (node == null) { 561 originalOffset= getOffset(line, 1); 562 nonWhitespaceOffset= originalOffset; 563 try { 564 nonWhitespaceOffset= getNonWhitespaceOffset(line, 1); 565 } catch (BadLocationException be) { 566 } 567 length= getLastCharColumn(line) - (nonWhitespaceOffset - originalOffset); 568 } else { 569 nonWhitespaceOffset= node.getOffset(); 570 length= node.getLength(); 571 } 572 } 573 } 574 notifyProblemRequestor(e, nonWhitespaceOffset, length, severity); 575 } catch (BadLocationException e1) { 576 } 577 } 578 579 public void handleBuildException(BuildException e, AntElementNode node) { 580 handleBuildException(e, node, XMLProblem.SEVERITY_ERROR); 581 } 582 583 public File getEditedFile() { 584 if (fLocationProvider != null && fEditedFile == null) { 585 fEditedFile= fLocationProvider.getLocation().toFile(); 586 } 587 return fEditedFile; 588 } 589 590 private void markHierarchy(AntElementNode openElement, int severity) { 591 while (openElement != null) { 592 openElement.setProblemSeverity(severity); 593 openElement= openElement.getParentNode(); 594 } 595 } 596 597 public LocationProvider getLocationProvider() { 598 return fLocationProvider; 599 } 600 601 public void addTarget(Target newTarget, int line, int column) { 602 if (fIncrementalTarget != null) { 603 fCurrentTargetNode= fIncrementalTarget; 604 fCurrentTargetNode.setTarget(newTarget); 605 fStillOpenElements.push(fCurrentTargetNode); 606 } else { 607 AntTargetNode targetNode= new AntTargetNode(newTarget); 608 fProjectNode.addChildNode(targetNode); 609 fCurrentTargetNode= targetNode; 610 fStillOpenElements.push(targetNode); 611 computeOffset(targetNode, line, column); 612 if (fNodeBeingResolved instanceof AntImportNode) { 613 targetNode.setImportNode(fNodeBeingResolved); 614 } 615 } 616 } 617 618 public void addProject(Project project, int line, int column) { 619 if (fIncrementalTarget != null) { 620 return; 621 } 622 fProjectNode= new AntProjectNode((AntModelProject)project, this); 623 fStillOpenElements.push(fProjectNode); 624 computeOffset(fProjectNode, line, column); 625 } 626 627 public void addTask(Task newTask, Task parentTask, Attributes attributes, int line, int column) { 628 AntTaskNode taskNode= null; 629 if (parentTask == null) { 630 taskNode= newTaskNode(newTask, attributes); 631 if (fCurrentTargetNode == null) { 632 fProjectNode.addChildNode(taskNode); 633 } else { 634 fCurrentTargetNode.addChildNode(taskNode); 635 if (taskNode.isExternal()) { 636 fCurrentTargetNode.setExternal(true); 637 fCurrentTargetNode.setFilePath(taskNode.getFilePath()); 638 } 639 } 640 } else { 641 taskNode= newNotWellKnownTaskNode(newTask, attributes); 642 ((AntTaskNode)fTaskToNode.get(parentTask)).addChildNode(taskNode); 643 } 644 fTaskToNode.put(newTask, taskNode); 645 646 fStillOpenElements.push(taskNode); 647 computeOffset(taskNode, line, column); 648 if (fNodeBeingResolved instanceof AntImportNode) { 649 taskNode.setImportNode(fNodeBeingResolved); 650 int index= fTaskNodes.indexOf(fNodeBeingResolved) + 1; 652 fTaskNodes.add(index, taskNode); 653 } else { 654 fTaskNodes.add(taskNode); 655 } 656 } 657 658 public void addEntity(String entityName, String entityPath) { 659 if (fEntityNameToPath == null) { 660 fEntityNameToPath= new HashMap (); 661 } 662 fEntityNameToPath.put(entityName, entityPath); 663 } 664 665 private AntTaskNode newTaskNode(Task newTask, Attributes attributes) { 666 AntTaskNode newNode= null; 667 String taskName= newTask.getTaskName(); 668 if (isPropertySettingTask(taskName)) { newNode= new AntPropertyNode(newTask, attributes); 670 } else if (taskName.equalsIgnoreCase("import")) { newNode= new AntImportNode(newTask, attributes); 672 } else if (taskName.equalsIgnoreCase("macrodef") || taskName.equalsIgnoreCase("presetdef") || taskName.equalsIgnoreCase("typedef") || taskName.equalsIgnoreCase("taskdef")) { String name = attributes.getValue(IAntModelConstants.ATTR_NAME); 677 newNode= new AntDefiningTaskNode(newTask, name); 678 } else if(taskName.equalsIgnoreCase("antcall")) { |