KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > web > core > jsploader > TagLibParseSupport


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.web.core.jsploader;
21
22 import java.lang.ref.WeakReference JavaDoc;
23 import java.lang.ref.SoftReference JavaDoc;
24 import java.util.*;
25 import javax.swing.SwingUtilities JavaDoc;
26 import org.netbeans.modules.web.core.jsploader.api.TagLibParseCookie;
27 import org.netbeans.modules.web.core.syntax.spi.ErrorAnnotation;
28 import org.openide.ErrorManager;
29 import org.openide.util.RequestProcessor;
30 import org.openide.util.RequestProcessor.Task;
31 import org.openide.filesystems.FileObject;
32 import org.netbeans.modules.web.jsps.parserapi.JspParserAPI;
33 import org.netbeans.modules.web.jsps.parserapi.JspParserFactory;
34 import org.netbeans.modules.web.jsps.parserapi.PageInfo;
35 import org.netbeans.modules.web.core.syntax.spi.JSPColoringData;
36 import org.netbeans.modules.web.api.webmodule.WebModule;
37 import org.openide.filesystems.FileUtil;
38
39 /** Support for parsing JSP pages and tag files and cooperation between the parser
40  * and the editor.
41  * Parsing is context-aware, which means that a web module and the associated
42  * environment (libraries) is needed to provide proper tag library coloring and completion and other webmodule-dependent
43  * features. The support tries to do its best to get good parse results even for
44  * pages and tag files for which the web module context is not known, but
45  * in this case nothing can be guaranteed.
46  *
47  * @author Petr Jiricka
48  * @version
49  */

50 public class TagLibParseSupport implements org.openide.nodes.Node.Cookie, TagLibParseCookie {
51
52     private FileObject jspFile;
53     
54     // request processing stuff
55
private boolean documentDirty;
56     private RequestProcessor.Task parsingTask = null;
57     private static RequestProcessor requestProcessor;
58
59     private Object JavaDoc openedLock = new Object JavaDoc(); //lock for parsing thread
60
private boolean opened; //is an editor pane opened?
61
private static final int WAIT_FOR_EDITOR_TIMEOUT = 15 * 1000; //15 seconds
62

63     /** Holds a reference to the JSP coloring data. */
64     private WeakReference JavaDoc jspColoringDataRef;
65     
66     /** Holds a time-based cache of the JspOpenInfo structure. */
67     private TimeReference jspOpenInfoRef;
68     
69     /** Holds the last parse result: JspParserAPI.ParseResult (whether successful or not).
70      * The editor should hold a strong reference to this object. That way, if the editor window
71      * is closed, memory is reclaimed, but important data is kept when it is needed.
72      */

73     private SoftReference JavaDoc parseResultRef;
74
75     /** Holds the last successful parse result: JspParserAPI.ParseResult.
76      * The editor should hold a strong reference to this object. That way, if the editor window
77      * is closed, memory is reclaimed, but important data is kept when it is needed.
78      */

79     private SoftReference JavaDoc parseResultSuccessfulRef;
80     
81     private Object JavaDoc parseResultLock = new Object JavaDoc();
82     private Object JavaDoc openInfoLock = new Object JavaDoc();
83     
84     /** Holds a strong reference to the parsing 'successful' data during an editor
85      * pane is opened for a JSP corresponding to this support.
86      */

87     private Object JavaDoc parseResultSuccessfulRefStrongReference = null;
88
89     //this field is used to try to catch the situation when someone calls the parser
90
//before editor support is initialized - causing #49300
91
private boolean wasAnEditorPaneChangeEvent = false;
92     
93     private boolean parsingTaskCancelled = false;
94     
95     /** Holds reference for annotation errors
96      */

97     private ErrorAnnotation annotations;
98     /** Creates new TagLibParseSupport
99      * @param jspFile the resource to parse
100      */

101     public TagLibParseSupport(FileObject jspFile) {
102         this.jspFile = jspFile;
103         //allow max 10 requests to run in parallel & have one RP for all taglib parsings
104
if(requestProcessor == null) requestProcessor = new RequestProcessor("background jsp parsing", 10); // NOI18N
105
//requestProcessor = RequestProcessor.getDefault();
106
annotations = new ErrorAnnotation (jspFile);
107     }
108
109     /** Gets the tag library data relevant for the editor. */
110     public JSPColoringData getJSPColoringData() {
111         return getJSPColoringData(true);
112     }
113     
114     private WebModule getWebModule(FileObject fo){
115         WebModule wm = WebModule.getWebModule(fo);
116         if (wm != null){
117             FileObject wmRoot = wm.getDocumentBase();
118             if (wmRoot != null && (fo == wmRoot || FileUtil.isParentOf(wmRoot, fo))) {
119                 return wm;
120             }
121         }
122         return null;
123     }
124     
125     JSPColoringData getJSPColoringData(boolean prepare) {
126         if (jspColoringDataRef != null) {
127             Object JavaDoc o = jspColoringDataRef.get();
128             if (o != null)
129                 return (JSPColoringData)o;
130         }
131         JSPColoringData jcd = new JSPColoringData(this);
132         jspColoringDataRef = new WeakReference JavaDoc(jcd);
133         if (prepare) {
134             prepare();
135         }
136         return jcd;
137     }
138
139     /** Sets the dirty flag - if the document was modified after last parsing. */
140     void setDocumentDirty(boolean b) {
141         //synchronized (parseResultLock) {
142
documentDirty = b;
143         //}
144
}
145
146     /** Tests the documentDirty flag. */
147     boolean isDocumentDirty() {
148         return documentDirty;
149     }
150
151     /** Starts the parsing if the this class is 'dirty' and status != STATUS_NOT
152     * and parsing is not running yet.
153       @return parsing task so caller may listen on its completion.
154     */

155     Task autoParse() {
156         //do not parse if it is not necessary
157
//this is the BaseJspEditorSupport optimalization since the autoParse causes the webmodule
158
//to be reparsed even if it has already been reparsed.
159
if(!isDocumentDirty()) {
160             return requestProcessor.post(new Runnable JavaDoc() {
161                 public void run() {
162                     //do nothing, just a dummy task
163
}
164             });
165         } else return parseObject(Thread.MIN_PRIORITY);
166     }
167
168     /** Method that instructs the implementation of the source element
169     * to prepare the element. It is non blocking method that returns
170     * task that can be used to control if the operation finished or not.
171     *
172     * @return task to control the preparation of the elemement
173     */

174     public Task prepare() {
175         return parseObject(Thread.MAX_PRIORITY - 1);
176     }
177
178     private Task parseObject(int priority) {
179         //reset the state so the next parsing will run normally
180
parsingTaskCancelled = false;
181         
182         //debug #49300: print out current stacktrace when the editor support is not initialized yet
183
if(!wasAnEditorPaneChangeEvent)
184             ErrorManager.getDefault().annotate(new IllegalStateException JavaDoc(),
185             "The TagLibParseSupport.parseObject() is called before editor support is created!"); //NOI18N
186

187         synchronized (parseResultLock) {
188             RequestProcessor.Task t = parsingTask;
189
190             if (t != null) {
191                 t.setPriority(Math.max(t.getPriority(), priority));
192                 return t;
193             }
194
195             setDocumentDirty(false);
196             t = requestProcessor.post(new ParsingRunnable(), 0, priority);
197             parsingTask = t;
198             return parsingTask;
199         }
200     }
201     
202     
203     //used for notifying the parsing thread (to start the parsing)
204
void setEditorOpened(boolean state) {
205         //mark that the an editor pane open event was fired
206
wasAnEditorPaneChangeEvent = true;
207         
208         synchronized (openedLock) {
209             opened = state;
210             if(opened) {
211                 openedLock.notifyAll();
212             } else {
213                 //clean the stronref to the parsing data when the editor is closed
214
parseResultSuccessfulRefStrongReference = null;
215             }
216         }
217         
218     }
219   
220     void cancelParsingTask() {
221         if(parsingTask != null) {
222             //there is schedulled or running parsing task -> cancel it!
223
boolean removed = parsingTask.cancel();
224             parsingTask = null;
225             jspColoringDataRef = null;
226         }
227         
228         //resume tha parsing thread if waiting on openedLock
229
parsingTaskCancelled = true;
230         synchronized (openedLock) {
231             openedLock.notifyAll();
232         }
233     }
234     
235     public JspParserAPI.JspOpenInfo getCachedOpenInfo(boolean preferCurrent, boolean useEditor) {
236         synchronized (openInfoLock) {
237             if (preferCurrent)
238                 jspOpenInfoRef = null;
239             long timestamp = jspFile.lastModified().getTime();
240             if (jspOpenInfoRef == null) {
241                 jspOpenInfoRef = new TimeReference();
242             }
243             JspParserAPI.JspOpenInfo info = (JspParserAPI.JspOpenInfo)jspOpenInfoRef.get(timestamp);
244             if (info == null) {
245                 info = JspParserFactory.getJspParser().getJspOpenInfo(jspFile, JspParserAccess.getJspParserWM (getWebModule (jspFile)), useEditor);
246                 jspOpenInfoRef.put(info, timestamp);
247             }
248             return info;
249         }
250     }
251     
252     public JspParserAPI.ParseResult getCachedParseResult(boolean successfulOnly, boolean preferCurrent) {
253         return getCachedParseResult(successfulOnly, preferCurrent, false);
254     }
255     
256     /** Returns a cached parse information about the page.
257      * @param successfulOnly if true, and the page has been parsed successfully in the past, returns
258      * the result of this successful parse. Otherwise returns null.
259      * If set to false, never returns null.
260      * @param needCurrent if true, attempts to return the result corresponding to the page exactly at this moment<br>
261      * If both parameters are true, and the page is currently successfully parsable, then returns this result, If it is
262      * unparsable, returns null.
263      * @return the result of parsing this page
264      */

265     public JspParserAPI.ParseResult getCachedParseResult(boolean successfulOnly, boolean preferCurrent, boolean forceParse) {
266         boolean needToParse = forceParse;
267         
268          if (preferCurrent && isDocumentDirty()) {
269             // need to get an up to date copy
270
needToParse = true;
271         }
272         if (parseResultRef == null) {
273             // no information available
274
needToParse = true;
275         }
276         
277         JspParserAPI.ParseResult ret = null;
278         SoftReference JavaDoc myRef = successfulOnly ? parseResultSuccessfulRef : parseResultRef;
279         if (myRef != null) {
280             ret = (JspParserAPI.ParseResult)myRef.get();
281         }
282         
283         if ((ret == null) && (!successfulOnly)) {
284             // to comply with the Javadoc regarding not returning null
285
needToParse = true;
286         }
287         
288         if (needToParse) {
289             RequestProcessor.Task t = prepare(); // having the reference is important
290
// so the SoftReference does not get garbage collected
291
t.waitFinished();
292             myRef = successfulOnly ? parseResultSuccessfulRef : parseResultRef;
293             if (myRef != null) {
294                 ret = (JspParserAPI.ParseResult)myRef.get();
295             }
296         }
297         return ret;
298     }
299     
300     // Flag, whether there is already an error in the jsp page.
301
private boolean hasError = false;
302     
303     private class ParsingRunnable implements Runnable JavaDoc {
304         
305         /** Holds the result of parsing. Need to hold it here
306          * to make sure that we have a strong reference and the SoftReference
307          * does not get garbage collected.
308          */

309         JspParserAPI.ParseResult locResult = null;
310         
311         public ParsingRunnable () {
312         }
313         
314         public void run() {
315             //wait with the parsing until an editor pane is opened
316
synchronized(TagLibParseSupport.this.openedLock) {
317                 if(!opened) {
318                     try {
319                         //wait max 15 seconds - then start parsing
320
TagLibParseSupport.this.openedLock.wait(WAIT_FOR_EDITOR_TIMEOUT);
321                     }catch(InterruptedException JavaDoc e) { }
322
323                     //since the EditorCookie.Observable fires the event for changed(opened) view panes
324
//before the document is really rendered, we need to slow down the current parsing task,
325
//so the thread doesn't affect the document showing speed significantly.
326
Thread.currentThread().setPriority(Thread.NORM_PRIORITY - 1);
327                 }
328             }
329             long a = System.currentTimeMillis();
330             //test whether the parsing task has been cancelled -
331
//someone called EditorCookie.close() during the parsing was waiting
332
//on openedLock
333
if(!parsingTaskCancelled && getWebModule(jspFile) != null) {
334                 JspParserAPI parser = JspParserFactory.getJspParser();
335                 // assert parser != null;
336
if (parser == null) {
337                     throw new InternalError JavaDoc();
338                 }
339                 
340                 getJSPColoringData(false).parsingStarted();
341                 
342                 locResult = parser.analyzePage(jspFile, JspParserAccess.getJspParserWM (getWebModule (jspFile)), JspParserAPI.ERROR_IGNORE);
343                 assert locResult != null;
344                 
345                 synchronized (TagLibParseSupport.this.parseResultLock) {
346                     parseResultRef = new SoftReference JavaDoc(locResult);
347                     if (locResult.isParsingSuccess()) {
348                         parseResultSuccessfulRef = new SoftReference JavaDoc(locResult);
349                         //hold a reference to the parsing data until last editor pane is closed
350
//motivation: the editor doesn't always hold a strogref to this object
351
//so the SoftRef is sometime cleaned even if there is an editor pane opened.
352
parseResultSuccessfulRefStrongReference = locResult;
353                         //set icon withouth errors
354
if (hasError){
355                             //remove all errors
356
SwingUtilities.invokeLater(new Runnable JavaDoc() {
357                                 public void run() {
358                                     annotations.annotate(new ErrorAnnotation.ErrorInfo[] {});
359                                 }
360                             });
361                             hasError = false;
362                         }
363                     } else {
364                         SwingUtilities.invokeLater(new Runnable JavaDoc() {
365                             public void run() {
366                                 for (int i = 0; i < locResult.getErrors().length; i ++){
367                                     JspParserAPI.ErrorDescriptor err = locResult.getErrors()[i];
368                                     annotations.annotate(new ErrorAnnotation.ErrorInfo[] {
369                                         new ErrorAnnotation.ErrorInfo(translate(err.getErrorMessage()),
370                                                 err.getLine(),
371                                                 err.getColumn(),
372                                                 ErrorAnnotation.JSP_ERROR)
373                                     } );
374                                 }
375                                 // set icon with error.
376
if (!hasError){
377                                     hasError = true;
378                                 }
379                                 
380                             }
381                         });
382                     }
383                     PageInfo pageInfo = locResult.getPageInfo();
384                     
385                     // if failure do nothing
386
parsingTask = null;
387                     
388                     if (pageInfo == null) return;
389                     //Map prefixMapper = (pageInfo.getXMLPrefixMapper().size() > 0) ?
390
// pageInfo.getApproxXmlPrefixMapper() : pageInfo.getJspPrefixMapper();
391
//Map prefixMapper = pageInfo.getJspPrefixMapper();
392
Map prefixMapper = null;
393                     if (pageInfo.getXMLPrefixMapper().size() > 0) {
394                         prefixMapper = pageInfo.getApproxXmlPrefixMapper();
395                         if (prefixMapper.size() == 0){
396                             prefixMapper = pageInfo.getXMLPrefixMapper();
397                         }
398                         prefixMapper.putAll(pageInfo.getJspPrefixMapper());
399                     }
400                     else {
401                         prefixMapper = pageInfo.getJspPrefixMapper();
402                     }
403                     getJSPColoringData(false).applyParsedData(pageInfo.getTagLibraries(), prefixMapper,
404                                                               pageInfo.isELIgnored(), getCachedOpenInfo(false, false).isXmlSyntax(),
405                                                               locResult.isParsingSuccess());
406                 }
407             }
408             
409             
410         }
411         
412         private String JavaDoc translate (String JavaDoc text){
413             String JavaDoc value = text.replaceAll("&lt;", "<");
414             value = value.replaceAll("&gt;", ">");
415             return value;
416         }
417
418     }
419
420 }
421
Popular Tags