KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > xml > text > completion > GrammarManager


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.netbeans.modules.xml.text.completion;
21
22 import java.io.IOException JavaDoc;
23 import java.util.ArrayList JavaDoc;
24 import java.util.Enumeration JavaDoc;
25 import java.util.List JavaDoc;
26 import java.util.LinkedList JavaDoc;
27 import java.util.Collections JavaDoc;
28
29 import javax.swing.event.DocumentEvent JavaDoc;
30 import javax.swing.event.DocumentListener JavaDoc;
31 import javax.swing.text.BadLocationException JavaDoc;
32 import javax.swing.text.Document JavaDoc;
33 import javax.swing.text.Position JavaDoc;
34 import org.netbeans.api.xml.parsers.DocumentInputSource;
35 import org.netbeans.modules.editor.NbEditorUtilities;
36 import org.netbeans.modules.xml.api.model.ExtendedGrammarQuery;
37 import org.netbeans.modules.xml.api.model.GrammarEnvironment;
38 import org.netbeans.modules.xml.api.model.GrammarQuery;
39 import org.netbeans.modules.xml.api.model.GrammarQueryManager;
40 import org.netbeans.modules.xml.text.syntax.SyntaxElement;
41 import org.netbeans.modules.xml.text.syntax.XMLSyntaxSupport;
42 import org.netbeans.modules.xml.text.syntax.dom.SyntaxNode;
43 import org.openide.filesystems.FileChangeAdapter;
44 import org.openide.filesystems.FileChangeListener;
45 import org.openide.filesystems.FileEvent;
46 import org.openide.filesystems.FileObject;
47 import org.openide.filesystems.FileSystem;
48 import org.openide.loaders.DataObject;
49 import org.openide.text.NbDocument;
50 import org.openide.util.WeakListeners;
51 import org.w3c.dom.Node JavaDoc;
52 import org.xml.sax.InputSource JavaDoc;
53 import org.openide.awt.StatusDisplayer;
54
55 /**
56  * Manages grammar to text editor association. It is able to
57  * dynamically switch among providers.
58  *
59  * @author Petr Kuzel, mfukala@netbeans.org
60  */

61 class GrammarManager extends FileChangeAdapter implements DocumentListener JavaDoc {
62
63     private static final String JavaDoc FILE_PROTOCOL_URI_PREFIX = "file:/"; //NOI18N
64

65     private ArrayList JavaDoc<FileObject> externalDTDs = new ArrayList JavaDoc();
66     
67     // current cache state
68
private int state = INVALID;
69     
70     static final int VALID = 1;
71     static final int INVALID = 3;
72     
73     // cache entry
74
private GrammarQuery grammar;
75     
76     // grammar is provided for this document
77
private final XMLSyntaxSupport syntax;
78     private final Document JavaDoc doc;
79     
80     // guarded positions pairs
81
private Position JavaDoc[] guarded;
82     
83     // maximal gurded offset
84
private Position JavaDoc maxGuarded;
85     
86     private int environmentElementsCount = -1;
87     
88     /**
89      * Create new manager.
90      */

91     public GrammarManager(Document JavaDoc doc, XMLSyntaxSupport syntax) {
92         this.doc = doc;
93         this.syntax = syntax;
94     }
95     
96     /**
97      * Return any suitable grammar that you can get
98      * till expires given timeout.
99      */

100     public synchronized GrammarQuery getGrammar() {
101         
102         switch (state) {
103             case VALID:
104                 return grammar;
105                 
106             case INVALID:
107                 loadGrammar();
108                 return grammar;
109                 
110             default:
111                 throw new IllegalStateException JavaDoc();
112         }
113     }
114     
115     
116     /**
117      * Notification from invalidator thread, the grammar need to be reloaded.
118      */

119     public synchronized void invalidateGrammar() {
120         
121         // make current loader a zombie
122
if (state == VALID) {
123             String JavaDoc msg = Util.THIS.getString("MSG_loading_cancel");
124             StatusDisplayer.getDefault().setStatusText(msg);
125         }
126         
127         doc.removeDocumentListener(this);
128         
129         //remove FileChangeListeners from the external DTD files
130
for(FileObject fo : externalDTDs) {
131 // System.out.println("[GrammarManager] removed FileObjectListener from " + fo.getPath());
132
fo.removeFileChangeListener(this);
133         }
134         externalDTDs.clear();
135         
136         guarded = new Position JavaDoc[0];
137         state = INVALID;
138     }
139     
140     public void fileChanged(FileEvent fe) {
141         //one of the external DTD files has changed => invalidate grammar
142
invalidateGrammar();
143     }
144     
145     public void fileDeleted(FileEvent fe) {
146         invalidateGrammar();
147     }
148     
149     public void insertUpdate(DocumentEvent JavaDoc e) {
150         //test whether there is a change in the grammar environment - e.g. is a grammar
151
//declaration was added and so.
152
checkDocumentEnvironment(e);
153         
154         if (isGuarded(e.getOffset(), e.getLength())) {
155             invalidateGrammar();
156         }
157     }
158     
159     public void removeUpdate(DocumentEvent JavaDoc e) {
160         //test whether there is a change in the grammar environment - e.g. is a grammar
161
//declaration was removed and so.
162
checkDocumentEnvironment(e);
163         
164         if (isGuarded(e.getOffset(), e.getLength())) {
165             invalidateGrammar();
166         }
167     }
168     
169     private void checkDocumentEnvironment(DocumentEvent JavaDoc e) {
170         long current = System.currentTimeMillis();
171         
172         try {
173             LinkedList JavaDoc ll = getEnvironmentElements();
174             if(ll.size() != environmentElementsCount) {
175                 invalidateGrammar();
176                 environmentElementsCount = ll.size();
177             }
178         }catch(BadLocationException JavaDoc ble) {}
179         
180     }
181     
182     public void changedUpdate(DocumentEvent JavaDoc e) {
183         // not interested
184
}
185     
186     private boolean isGuarded(int offset, int length) {
187         
188         // optimalization for common case
189
if ((maxGuarded != null) && (offset > maxGuarded.getOffset())) {
190             return false;
191         }
192         
193         // slow loop matchibng range overlaps
194
for (int i = 0; i<guarded.length; i+=2) {
195             int start = guarded[i].getOffset();
196             int end = guarded[i+1].getOffset();
197             if (start < offset && offset < end) {
198                 return true;
199             }
200             int changeEnd = offset + length;
201             if (offset < start && start < changeEnd) {
202                 return true;
203             }
204         }
205         
206         return false;
207     }
208     
209     
210     /**
211      * Nofification from grammar loader thread, new valid grammar.
212      * @param grammar grammar or <code>null</code> if cannot load.
213      */

214     private synchronized void grammarLoaded(GrammarQuery grammar) {
215         
216         String JavaDoc status = (grammar != null) ? Util.THIS.getString("MSG_loading_done")
217         : Util.THIS.getString("MSG_loading_failed");
218         
219         this.grammar = grammar == null ? EmptyQuery.INSTANCE : grammar;
220         state = VALID;
221         
222         StatusDisplayer.getDefault().setStatusText(status);
223     }
224     
225     
226     /**
227      * Async grammar fetching
228      */

229     private void loadGrammar() {
230         
231         
232         GrammarQuery loaded = null;
233         try {
234             
235             String JavaDoc status = Util.THIS.getString("MSG_loading");
236             StatusDisplayer.getDefault().setStatusText(status);
237             
238             // prepare grammar environment
239

240             try {
241                 
242                 LinkedList JavaDoc ctx = getEnvironmentElements();
243                 InputSource inputSource = new DocumentInputSource(doc);
244                 FileObject fileObject = null;
245                 Object JavaDoc obj = doc.getProperty(Document.StreamDescriptionProperty);
246                 if (obj instanceof DataObject) {
247                     DataObject dobj = (DataObject) obj;
248                     fileObject = dobj.getPrimaryFile();
249                 }
250                 GrammarEnvironment env = new GrammarEnvironment(Collections.enumeration(ctx), inputSource, fileObject);
251                 
252                 // lookup for grammar
253

254                 GrammarQueryManager g = GrammarQueryManager.getDefault();
255                 Enumeration JavaDoc en = g.enabled(env);
256                 if (en == null) return;
257                 
258                 // set guarded regions
259

260                 List JavaDoc positions = new ArrayList JavaDoc(10);
261                 int max = 0;
262                 
263                 while (en.hasMoreElements()) {
264                     Node next = (Node) en.nextElement();
265                     if (next instanceof SyntaxNode) {
266                         SyntaxNode node = (SyntaxNode) next;
267                         int start = node.getElementOffset();
268                         int end = start + node.getElementLength();
269                         if (end > max) max = end;
270                         Position JavaDoc startPosition =
271                                 NbDocument.createPosition(doc, start, Position.Bias.Forward);
272                         positions.add(startPosition);
273                         Position JavaDoc endPosition =
274                                 NbDocument.createPosition(doc, end, Position.Bias.Backward);
275                         positions.add(endPosition);
276                     }
277                 }
278                 
279                 guarded = (Position JavaDoc[]) positions.toArray(new Position JavaDoc[positions.size()]);
280                 maxGuarded = NbDocument.createPosition(doc, max, Position.Bias.Backward);
281                 
282                 
283                 // retrieve the grammar and start invalidation listener
284

285                 loaded = g.getGrammar(env);
286                 
287                 if(loaded instanceof ExtendedGrammarQuery) {
288                     //attach listeners to external files and if any of them changes then reload this grammar
289
for(String JavaDoc resolvedEntity : (List JavaDoc<String JavaDoc>)((ExtendedGrammarQuery)loaded).getResolvedEntities()) {
290                         //filter non-files resolved entities
291
if(!resolvedEntity.startsWith(FILE_PROTOCOL_URI_PREFIX)) continue;
292                         
293                         DataObject docDo = NbEditorUtilities.getDataObject(doc);
294                         if(docDo != null) {
295                             FileObject docFo = docDo.getPrimaryFile();
296                             if(docFo != null) {
297                                 try {
298                                     FileSystem fs = docFo.getFileSystem();
299                                     FileObject fo = fs.findResource(resolvedEntity.substring(FILE_PROTOCOL_URI_PREFIX.length())); //NOI18N
300
if(fo != null) {
301                                         externalDTDs.add(fo);
302                                         //add a week listener to the fileobject - detach when document is being disposed
303
fo.addFileChangeListener((FileChangeListener)WeakListeners.create(FileChangeListener.class, this, doc));
304 // System.out.println("[GrammarManager] added FileObjectListener to " + fo.getPath());
305
}
306                                 }catch(IOException JavaDoc e) {
307                                     e.printStackTrace();
308                                 }
309                             }
310                         }
311                     }
312                 }
313                 
314             } catch (BadLocationException JavaDoc ex) {
315                 loaded = null;
316             }
317             
318         } finally {
319             
320             doc.addDocumentListener(GrammarManager.this);
321             
322             grammarLoaded(loaded);
323         }
324     }
325     
326     private LinkedList JavaDoc getEnvironmentElements() throws BadLocationException JavaDoc {
327         LinkedList JavaDoc ctx = new LinkedList JavaDoc();
328         SyntaxElement first = syntax.getElementChain(1);
329         while (true) {
330             if (first == null) break;
331             if (first instanceof SyntaxNode) {
332                 SyntaxNode node = (SyntaxNode) first;
333                 ctx.add(node);
334                 if (node.ELEMENT_NODE == node.getNodeType()) {
335                     break;
336                 }
337             }
338             first = first.getNext();
339         }
340         return ctx;
341     }
342 }
343
344
Popular Tags