1 19 20 package org.apache.tools.ant.module.xml; 21 22 import java.beans.PropertyChangeEvent ; 23 import java.beans.PropertyChangeListener ; 24 import java.io.File ; 25 import java.io.FileNotFoundException ; 26 import java.io.IOException ; 27 import java.io.InputStream ; 28 import java.io.StringReader ; 29 import java.lang.ref.Reference ; 30 import java.lang.ref.WeakReference ; 31 import java.util.HashSet ; 32 import java.util.Iterator ; 33 import java.util.Set ; 34 import javax.swing.event.ChangeEvent ; 35 import javax.swing.event.ChangeListener ; 36 import javax.swing.event.DocumentEvent ; 37 import javax.swing.event.DocumentListener ; 38 import javax.swing.text.BadLocationException ; 39 import javax.swing.text.StyledDocument ; 40 import javax.xml.parsers.DocumentBuilder ; 41 import javax.xml.parsers.DocumentBuilderFactory ; 42 import org.apache.tools.ant.module.AntModule; 43 import org.apache.tools.ant.module.api.AntProjectCookie; 44 import org.openide.ErrorManager; 45 import org.openide.cookies.EditorCookie; 46 import org.openide.filesystems.FileAttributeEvent; 47 import org.openide.filesystems.FileEvent; 48 import org.openide.filesystems.FileObject; 49 import org.openide.filesystems.FileRenameEvent; 50 import org.openide.filesystems.FileStateInvalidException; 51 import org.openide.filesystems.FileUtil; 52 import org.openide.loaders.DataObject; 53 import org.openide.loaders.DataObjectNotFoundException; 54 import org.openide.util.RequestProcessor; 55 import org.openide.util.WeakListeners; 56 import org.w3c.dom.Document ; 57 import org.w3c.dom.Element ; 58 import org.xml.sax.ErrorHandler ; 59 import org.xml.sax.InputSource ; 60 import org.xml.sax.SAXException ; 61 import org.xml.sax.SAXParseException ; 62 63 public class AntProjectSupport implements AntProjectCookie.ParseStatus, DocumentListener , 64 PropertyChangeListener { 65 66 private FileObject fo; 67 68 private Document projDoc = null; private Throwable exception = null; 70 private boolean parsed = false; 71 private Reference <StyledDocument > styledDocRef = null; 72 private Object parseLock; 74 private Set <ChangeListener > listeners; private EditorCookie.Observable editor = null; 76 77 private DocumentBuilder documentBuilder; 78 79 private static final int REPARSE_DELAY = 3000; 82 83 public AntProjectSupport (FileObject fo) { 84 this.fo = fo; 85 parseLock = new Object (); 86 listeners = new HashSet <ChangeListener >(); 87 rp = new RequestProcessor("AntProjectSupport[" + fo + "]"); } 89 90 private synchronized EditorCookie.Observable getEditor() { 91 FileObject fo = getFileObject(); 92 if (fo == null) return null; 93 if (editor == null) { 94 try { 95 editor = DataObject.find(fo).getCookie(EditorCookie.Observable.class); 96 if (editor != null) { 97 editor.addPropertyChangeListener(WeakListeners.propertyChange(this, editor)); 98 } 99 } catch (DataObjectNotFoundException donfe) { 100 AntModule.err.notify(ErrorManager.INFORMATIONAL, donfe); 101 } 102 } 103 return editor; 104 } 105 106 public File getFile () { 107 FileObject fo = getFileObject(); 108 if (fo != null) { 109 return FileUtil.toFile(fo); 110 } else { 111 return null; 112 } 113 } 114 115 public FileObject getFileObject () { 116 if (fo != null && !fo.isValid()) { return null; 118 } 119 return fo; 120 } 121 122 public void setFile (File f) { fo = FileUtil.toFileObject(f); 124 invalidate (); 125 } 126 127 public void setFileObject (FileObject fo) { this.fo = fo; 129 invalidate (); 130 } 131 132 public boolean isParsed() { 133 return parsed; 134 } 135 136 public Document getDocument () { 137 if (parsed) { 138 return projDoc; 139 } 140 synchronized (parseLock) { 141 if (parsed) { 142 return projDoc; 143 } 144 parseDocument (); 145 return projDoc; 146 } 147 } 148 149 public Throwable getParseException () { 150 if (parsed) { 151 return exception; 152 } 153 synchronized (parseLock) { 154 if (parsed) { 155 return exception; 156 } 157 parseDocument (); 158 return exception; 159 } 160 } 161 162 167 private static synchronized DocumentBuilder createDocumentBuilder() throws Exception { 168 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 170 factory.setNamespaceAware(true); 171 DocumentBuilder documentBuilder = factory.newDocumentBuilder(); 172 documentBuilder.setErrorHandler(ErrHandler.DEFAULT); 173 return documentBuilder; 174 } 175 176 179 private static final class ErrHandler implements ErrorHandler { 180 static final ErrorHandler DEFAULT = new ErrHandler(); 181 private ErrHandler() {} 182 public void error(SAXParseException exception) throws SAXException { 183 throw exception; 184 } 185 public void fatalError(SAXParseException exception) throws SAXException { 186 throw exception; 187 } 188 public void warning(SAXParseException exception) throws SAXException { 189 throw exception; 190 } 191 } 192 193 196 public static InputSource createInputSource(final FileObject fo, final StyledDocument document) throws IOException { 197 if (fo != null) { 198 DataObject d = DataObject.find(fo); 199 if (!d.isModified()) { 200 try { 202 return new InputSource (fo.getURL().toExternalForm()); 203 } catch (FileStateInvalidException e) { 204 assert false : e; 205 } 206 } 207 } 208 final String [] contents = new String [1]; 209 document.render(new Runnable () { 210 public void run() { 211 try { 212 contents[0] = document.getText(0, document.getLength()); 213 } catch (BadLocationException e) { 214 throw new AssertionError (e); 215 } 216 } 217 }); 218 InputSource in = new InputSource (new StringReader (contents[0])); 219 if (fo != null) { try { 221 in.setSystemId(fo.getURL().toExternalForm()); 222 } catch (FileStateInvalidException e) { 223 assert false : e; 224 } 225 } 231 return in; 232 } 233 234 private void parseDocument () { 235 assert Thread.holdsLock(parseLock); FileObject fo = getFileObject (); 237 AntModule.err.log ("AntProjectSupport.parseDocument: fo=" + fo); 238 try { 239 if (documentBuilder == null) { 240 documentBuilder = createDocumentBuilder(); 241 } 242 EditorCookie editor = getEditor (); 243 Document doc; 244 if (editor != null) { 245 final StyledDocument document = editor.openDocument(); 246 if ((styledDocRef != null && styledDocRef.get () != document) || styledDocRef == null) { 248 document.addDocumentListener(this); 249 styledDocRef = new WeakReference <StyledDocument >(document); 250 } 251 InputSource in = createInputSource(fo, document); 252 doc = documentBuilder.parse(in); 253 } else if (fo != null) { 254 InputStream is = fo.getInputStream(); 255 try { 256 InputSource in = new InputSource (is); 257 try { 258 in.setSystemId(fo.getURL().toExternalForm()); 259 } catch (FileStateInvalidException e) { 260 assert false : e; 261 } 262 doc = documentBuilder.parse(is); 263 } finally { 264 is.close(); 265 } 266 } else { 267 exception = new FileNotFoundException ("Ant script probably deleted"); return; 269 } 270 projDoc = doc; 271 exception = null; 272 } catch (Exception e) { 273 exception = e; 275 if (!(exception instanceof SAXParseException )) { 276 AntModule.err.annotate(exception, ErrorManager.UNKNOWN, "Strange parse error in " + this, null, null, null); AntModule.err.notify(ErrorManager.INFORMATIONAL, exception); 278 } 279 } 280 fireChangeEvent(false); 281 parsed = true; 282 } 283 284 public Element getProjectElement () { 285 Document doc = getDocument (); 286 if (doc != null) { 287 return doc.getDocumentElement (); 288 } else { 289 return null; 290 } 291 } 292 293 @Override 294 public boolean equals (Object o) { 295 if (! (o instanceof AntProjectSupport)) return false; 296 AntProjectSupport other = (AntProjectSupport) o; 297 if (fo != null) { 298 return fo.equals (other.fo); 299 } else { 300 return false; 301 } 302 } 303 304 @Override 305 public int hashCode () { 306 return 27825 ^ (fo != null ? fo.hashCode() : 0); 307 } 308 309 @Override 310 public String toString () { 311 FileObject fo = getFileObject (); 312 if (fo != null) { 313 return fo.toString(); 314 } else { 315 return "<missing Ant script>"; } 317 } 318 319 public void addChangeListener (ChangeListener l) { 320 synchronized (listeners) { 321 listeners.add (l); 322 } 323 } 324 325 public void removeChangeListener (ChangeListener l) { 326 synchronized (listeners) { 327 listeners.remove (l); 328 } 329 } 330 331 private final RequestProcessor rp; 332 private RequestProcessor.Task task = null; 333 334 protected void fireChangeEvent(boolean delay) { 335 AntModule.err.log ("AntProjectSupport.fireChangeEvent: fo=" + fo); 336 Iterator <ChangeListener > it; 337 synchronized (listeners) { 338 it = new HashSet <ChangeListener >(listeners).iterator(); 339 } 340 ChangeEvent ev = new ChangeEvent (this); 341 ChangeFirer f = new ChangeFirer(it, ev); 342 synchronized (this) { 343 if (task == null) { 344 task = rp.post(f, delay ? REPARSE_DELAY : 0); 345 } else if (!delay) { 346 task.schedule(0); 347 } 348 } 349 } 350 private final class ChangeFirer implements Runnable { 351 private final Iterator <ChangeListener > it; 352 private final ChangeEvent ev; 353 public ChangeFirer(Iterator <ChangeListener > it, ChangeEvent ev) { 354 this.it = it; 355 this.ev = ev; 356 } 357 public void run () { 358 AntModule.err.log ("AntProjectSupport.ChangeFirer.run"); 359 synchronized (AntProjectSupport.this) { 360 if (task == null) { 361 return; 362 } 363 task = null; 364 } 365 while (it.hasNext ()) { 366 try { 367 it.next().stateChanged(ev); 368 } catch (RuntimeException re) { 369 AntModule.err.notify (re); 370 } 371 } 372 } 373 } 374 375 public void removeUpdate (DocumentEvent ev) { 376 invalidate(); 377 } 378 379 public void changedUpdate (DocumentEvent ev) { 380 } 382 383 public void insertUpdate (DocumentEvent ev) { 384 invalidate(); 385 } 386 387 public void propertyChange(PropertyChangeEvent e) { 389 if (EditorCookie.Observable.PROP_DOCUMENT.equals(e.getPropertyName())) { 390 invalidate(); 391 } 392 } 393 394 public void fileDeleted(FileEvent p1) { 395 } 397 398 public void fileDataCreated(FileEvent p1) { 399 } 401 402 public void fileFolderCreated(FileEvent p1) { 403 } 405 406 public void fileRenamed(FileRenameEvent p1) { 407 } 409 410 public void fileAttributeChanged(FileAttributeEvent p1) { 411 } 413 414 public void fileChanged(FileEvent p1) { 415 invalidate (); 416 } 417 418 protected final void invalidate () { 419 AntModule.err.log ("AntProjectSupport.invalidate: fo=" + fo); 420 parsed = false; 421 fireChangeEvent(true); 422 } 423 424 } 425 | Popular Tags |