KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > module > xml > AntProjectSupport


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.apache.tools.ant.module.xml;
21
22 import java.beans.PropertyChangeEvent JavaDoc;
23 import java.beans.PropertyChangeListener JavaDoc;
24 import java.io.File JavaDoc;
25 import java.io.FileNotFoundException JavaDoc;
26 import java.io.IOException JavaDoc;
27 import java.io.InputStream JavaDoc;
28 import java.io.StringReader JavaDoc;
29 import java.lang.ref.Reference JavaDoc;
30 import java.lang.ref.WeakReference JavaDoc;
31 import java.util.HashSet JavaDoc;
32 import java.util.Iterator JavaDoc;
33 import java.util.Set JavaDoc;
34 import javax.swing.event.ChangeEvent JavaDoc;
35 import javax.swing.event.ChangeListener JavaDoc;
36 import javax.swing.event.DocumentEvent JavaDoc;
37 import javax.swing.event.DocumentListener JavaDoc;
38 import javax.swing.text.BadLocationException JavaDoc;
39 import javax.swing.text.StyledDocument JavaDoc;
40 import javax.xml.parsers.DocumentBuilder JavaDoc;
41 import javax.xml.parsers.DocumentBuilderFactory JavaDoc;
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 JavaDoc;
57 import org.w3c.dom.Element JavaDoc;
58 import org.xml.sax.ErrorHandler JavaDoc;
59 import org.xml.sax.InputSource JavaDoc;
60 import org.xml.sax.SAXException JavaDoc;
61 import org.xml.sax.SAXParseException JavaDoc;
62
63 public class AntProjectSupport implements AntProjectCookie.ParseStatus, DocumentListener JavaDoc,
64     /*FileChangeListener,*/ PropertyChangeListener JavaDoc {
65     
66     private FileObject fo;
67
68     private Document JavaDoc projDoc = null; // [PENDING] SoftReference
69
private Throwable JavaDoc exception = null;
70     private boolean parsed = false;
71     private Reference JavaDoc<StyledDocument JavaDoc> styledDocRef = null;
72     private Object JavaDoc parseLock; // see init()
73

74     private Set JavaDoc<ChangeListener JavaDoc> listeners; // see init()
75
private EditorCookie.Observable editor = null;
76     
77     private DocumentBuilder JavaDoc documentBuilder;
78     
79     // milliseconds of quiet time after a textual document change after which
80
// changes will be fired and the XML may be reparsed
81
private static final int REPARSE_DELAY = 3000;
82
83     public AntProjectSupport (FileObject fo) {
84         this.fo = fo;
85         parseLock = new Object JavaDoc ();
86         listeners = new HashSet JavaDoc<ChangeListener JavaDoc>();
87         rp = new RequestProcessor("AntProjectSupport[" + fo + "]"); // NOI18N
88
}
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 JavaDoc 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()) { // #11065
117
return null;
118         }
119         return fo;
120     }
121     
122     public void setFile (File JavaDoc f) { // #11979
123
fo = FileUtil.toFileObject(f);
124         invalidate ();
125     }
126     
127     public void setFileObject (FileObject fo) { // #11979
128
this.fo = fo;
129         invalidate ();
130     }
131     
132     public boolean isParsed() {
133         return parsed;
134     }
135     
136     public Document JavaDoc 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 JavaDoc 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     /**
163      * Make a DocumentBuilder object for use in this support.
164      * Thread-safe, but of course the result is not.
165      * @throws Exception for various reasons of configuration
166      */

167     private static synchronized DocumentBuilder JavaDoc createDocumentBuilder() throws Exception JavaDoc {
168         //DocumentBuilderFactory factory = (DocumentBuilderFactory)Class.forName(XERCES_DOCUMENT_BUILDER_FACTORY).newInstance();
169
DocumentBuilderFactory JavaDoc factory = DocumentBuilderFactory.newInstance();
170         factory.setNamespaceAware(true);
171         DocumentBuilder JavaDoc documentBuilder = factory.newDocumentBuilder();
172         documentBuilder.setErrorHandler(ErrHandler.DEFAULT);
173         return documentBuilder;
174     }
175     
176     /**
177      * XML parser error handler; passes on all errors.
178      */

179     private static final class ErrHandler implements ErrorHandler JavaDoc {
180         static final ErrorHandler JavaDoc DEFAULT = new ErrHandler();
181         private ErrHandler() {}
182         public void error(SAXParseException JavaDoc exception) throws SAXException JavaDoc {
183             throw exception;
184         }
185         public void fatalError(SAXParseException JavaDoc exception) throws SAXException JavaDoc {
186             throw exception;
187         }
188         public void warning(SAXParseException JavaDoc exception) throws SAXException JavaDoc {
189             throw exception;
190         }
191     }
192     
193     /**
194      * Utility method to get a properly configured XML input source for a script.
195      */

196     public static InputSource JavaDoc createInputSource(final FileObject fo, final StyledDocument JavaDoc document) throws IOException JavaDoc {
197         if (fo != null) {
198             DataObject d = DataObject.find(fo);
199             if (!d.isModified()) {
200                 // #58194: no need to parse the live document.
201
try {
202                     return new InputSource JavaDoc(fo.getURL().toExternalForm());
203                 } catch (FileStateInvalidException e) {
204                     assert false : e;
205                 }
206             }
207         }
208         final String JavaDoc[] contents = new String JavaDoc[1];
209         document.render(new Runnable JavaDoc() {
210             public void run() {
211                 try {
212                     contents[0] = document.getText(0, document.getLength());
213                 } catch (BadLocationException JavaDoc e) {
214                     throw new AssertionError JavaDoc(e);
215                 }
216             }
217         });
218         InputSource JavaDoc in = new InputSource JavaDoc(new StringReader JavaDoc(contents[0]));
219         if (fo != null) { // #10348
220
try {
221                 in.setSystemId(fo.getURL().toExternalForm());
222             } catch (FileStateInvalidException e) {
223                 assert false : e;
224             }
225             // [PENDING] Ant's ProjectHelper has an elaborate set of work-
226
// arounds for inconsistent parser behavior, e.g. file:foo.xml
227
// works in Ant but not with Xerces parser. You must use just foo.xml
228
// as the system ID. If necessary, Ant's algorithm could be copied
229
// here to make the behavior match perfectly, but it ought not be necessary.
230
}
231         return in;
232     }
233     
234     private void parseDocument () {
235         assert Thread.holdsLock(parseLock); // so it is OK to use documentBuilder
236
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 JavaDoc doc;
244             if (editor != null) {
245                 final StyledDocument JavaDoc document = editor.openDocument();
246                 // add only one Listener (listeners for doc are hold in a List!)
247
if ((styledDocRef != null && styledDocRef.get () != document) || styledDocRef == null) {
248                     document.addDocumentListener(this);
249                     styledDocRef = new WeakReference JavaDoc<StyledDocument JavaDoc>(document);
250                 }
251                 InputSource JavaDoc in = createInputSource(fo, document);
252                 doc = documentBuilder.parse(in);
253             } else if (fo != null) {
254                 InputStream JavaDoc is = fo.getInputStream();
255                 try {
256                     InputSource JavaDoc in = new InputSource JavaDoc(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 JavaDoc("Ant script probably deleted"); // NOI18N
268
return;
269             }
270             projDoc = doc;
271             exception = null;
272         } catch (Exception JavaDoc e) {
273             // leave projDoc the way it is...
274
exception = e;
275             if (!(exception instanceof SAXParseException JavaDoc)) {
276                 AntModule.err.annotate(exception, ErrorManager.UNKNOWN, "Strange parse error in " + this, null, null, null); // NOI18N
277
AntModule.err.notify(ErrorManager.INFORMATIONAL, exception);
278             }
279         }
280         fireChangeEvent(false);
281         parsed = true;
282     }
283     
284     public Element JavaDoc getProjectElement () {
285         Document JavaDoc doc = getDocument ();
286         if (doc != null) {
287             return doc.getDocumentElement ();
288         } else {
289             return null;
290         }
291     }
292     
293     @Override JavaDoc
294     public boolean equals (Object JavaDoc 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 JavaDoc
305     public int hashCode () {
306         return 27825 ^ (fo != null ? fo.hashCode() : 0);
307     }
308     
309     @Override JavaDoc
310     public String JavaDoc toString () {
311         FileObject fo = getFileObject ();
312         if (fo != null) {
313             return fo.toString();
314         } else {
315             return "<missing Ant script>"; // NOI18N
316
}
317     }
318     
319     public void addChangeListener (ChangeListener JavaDoc l) {
320         synchronized (listeners) {
321             listeners.add (l);
322         }
323     }
324     
325     public void removeChangeListener (ChangeListener JavaDoc 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 JavaDoc<ChangeListener JavaDoc> it;
337         synchronized (listeners) {
338             it = new HashSet JavaDoc<ChangeListener JavaDoc>(listeners).iterator();
339         }
340         ChangeEvent JavaDoc ev = new ChangeEvent JavaDoc (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 JavaDoc {
351         private final Iterator JavaDoc<ChangeListener JavaDoc> it;
352         private final ChangeEvent JavaDoc ev;
353         public ChangeFirer(Iterator JavaDoc<ChangeListener JavaDoc> it, ChangeEvent JavaDoc 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 JavaDoc re) {
369                     AntModule.err.notify (re);
370                 }
371             }
372         }
373     }
374     
375     public void removeUpdate (DocumentEvent JavaDoc ev) {
376         invalidate();
377     }
378     
379     public void changedUpdate (DocumentEvent JavaDoc ev) {
380         // Not to worry, just text attributes or something...
381
}
382     
383     public void insertUpdate (DocumentEvent JavaDoc ev) {
384         invalidate();
385     }
386     
387     // Called when editor support changes state: #11616
388
public void propertyChange(PropertyChangeEvent JavaDoc e) {
389         if (EditorCookie.Observable.PROP_DOCUMENT.equals(e.getPropertyName())) {
390             invalidate();
391         }
392     }
393     
394     public void fileDeleted(FileEvent p1) {
395         // Hmm, not our problem.
396
}
397     
398     public void fileDataCreated(FileEvent p1) {
399         // ignore
400
}
401     
402     public void fileFolderCreated(FileEvent p1) {
403         // ignore
404
}
405     
406     public void fileRenamed(FileRenameEvent p1) {
407         // ignore
408
}
409     
410     public void fileAttributeChanged(FileAttributeEvent p1) {
411         // ignore
412
}
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