KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > java > parser > ParsingSupport


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.java.parser;
21
22 import java.beans.PropertyChangeEvent JavaDoc;
23 import java.beans.PropertyChangeListener JavaDoc;
24 import java.beans.PropertyChangeSupport JavaDoc;
25 import java.lang.ref.Reference JavaDoc;
26 import java.lang.ref.ReferenceQueue JavaDoc;
27 import java.lang.ref.WeakReference JavaDoc;
28 import java.util.*;
29 import java.io.InputStream JavaDoc;
30 import java.io.IOException JavaDoc;
31
32 import javax.swing.event.ChangeListener JavaDoc;
33 import javax.swing.event.ChangeEvent JavaDoc;
34
35 import org.netbeans.api.java.classpath.ClassPath;
36
37 import org.openide.nodes.Node;
38 import org.openide.src.*;
39 import org.openide.text.CloneableEditorSupport;
40 import org.openide.util.RequestProcessor;
41 import org.openide.util.Task;
42 import org.openide.util.Utilities;
43
44 import org.netbeans.modules.java.bridge.LangModel;
45 import org.netbeans.modules.java.bridge.CommitListener;
46 import org.netbeans.modules.java.bridge.SrcElementImpl;
47
48 import org.netbeans.modules.java.codegen.DocumentBinding;
49 import org.netbeans.modules.java.ParserEngine;
50 import org.netbeans.modules.java.ElementFactory;
51 import org.netbeans.modules.java.JavaDataObject;
52
53 /**
54  * This class serves as a default implementation of JavaParser interface and coordinates
55  * one-way interaction between a LangModel and the underlying StyledDocument. The other
56  * way is implemented through various bindings attached to individual model elements.
57  * The parser is responsible for maintaining `dirty' status for the model data, with respect
58  * to changes made to the document, parsing when the client asks to parse and updating
59  * the model contents when appropriate.
60  *
61  * @author sdedic
62  * @version
63  */

64 public class ParsingSupport implements JavaParser {
65     
66     private PropertyChangeSupport JavaDoc propSupport;
67     
68     public static final String JavaDoc PROP_STATUS = "status"; // NOI18N
69

70     private static final boolean DEBUG = false;
71     
72     /** Initially true. Set to false when the Support is no longer valid.
73      */

74     private boolean valid;
75     
76     /**
77      * True during the model update operation. When true, the refImplementation
78      * cannot be cleared, otherwise the updater thread may lock up.
79      */

80     private boolean updating;
81
82     private boolean clean;
83     
84     private Env parsingEnv;
85     
86     /**
87      * Parser engine this support is working with.
88      */

89     private ParserEngine engine;
90
91     /** IOException saved (?) from the last parsing process. If no change is
92      * reported to the underlying document, the exception is immediately
93      * thrown without attempting to parse.
94      */

95     private SourceException savedException;
96     
97     /**
98      * SourceElement that is bound to the proxy. The proxy, if needed constructs the
99      * real Impl. The element, once constructed, is not freed unless the ParsingSupport
100      * dies as well.
101      */

102     private SourceElement src;
103     
104     /**
105      * The request object that is filed to the parsing queue. Since the request has not
106      * yet touched by the RequestProcessor, it is safe to change paramterers for it.
107      */

108     private Processor currentRequest;
109     
110     /** The request that is currently running, null if none. This request is currently
111      * being serviced by the RequestProcessor.
112      */

113     private Processor runningRequest;
114
115     /**
116      * Language model interface.
117      */

118     LangModel model;
119
120     /**
121      * Extended interface for updating the language model.
122      */

123     LangModel.Updater updater;
124
125     /**
126      * A Weak/Soft reference to the real implementation.
127      */

128     private Reference JavaDoc refImplementation;
129     
130     /**
131      * Parsing status of the source - it is one of SourceElement.STATUS_* symbolic
132      * constatnts.
133      */

134     private int status;
135     
136     /**
137      * Collection of ChangeListeners registered with this Support.
138      */

139     Collection changeList;
140     
141     DocumentBinding docBinding;
142     
143     JavaDataObject jdo;
144     
145     /**
146      * Creates a ParsingSupport and initializes it with an environment, binding,
147      * updater interface and the model.
148      */

149     public ParsingSupport(Env parsingEnv, JavaDataObject jdo, DocumentBinding docBinding,
150         LangModel.Updater updater, LangModel model) {
151         this.parsingEnv = parsingEnv;
152         this.jdo = jdo;
153         this.docBinding = docBinding;
154         this.updater = updater;
155         this.model = model;
156         this.valid = true;
157     }
158     
159     public void addChangeListener(ChangeListener JavaDoc l) {
160         if (changeList == null) {
161             synchronized (this) {
162                 if (changeList == null)
163                     changeList = new LinkedList();
164             }
165         }
166         synchronized (changeList) {
167             changeList.add(l);
168         }
169     }
170     
171     public void removeChangeListener(ChangeListener JavaDoc l) {
172         if (changeList == null)
173             return;
174         synchronized (changeList) {
175             changeList.remove(l);
176         }
177     }
178
179     /**
180      * Adds a property change listener to this object.
181      */

182     public void addPropertyChangeListener(PropertyChangeListener JavaDoc l) {
183         if (propSupport == null) {
184             synchronized (this) {
185                 if (propSupport == null)
186                     propSupport = new PropertyChangeSupport JavaDoc(this);
187             }
188         }
189         propSupport.addPropertyChangeListener(l);
190     }
191     
192     /**
193      * Removes the change listener.
194      */

195     public void removePropertyChangeListener(PropertyChangeListener JavaDoc l) {
196         if (propSupport == null)
197             return;
198         propSupport.removePropertyChangeListener(l);
199     }
200     
201     /** Schedules a refresh/reparse of the associated document. The request is given
202      * the passed priority. If there are some errors during parsing and acceptErrors
203      * is false, the hierarchy is <B>not</B> updated. After the parsing task finishes,
204      * the hiearchy will reflect the state of the document at the time parse() was called,
205      * or a more recent state, if there were multiple parse() requests before the task
206      * was processed.
207      * @return Task that can be tested for completion or can be listened on
208      */

209     public Task parse(int priority, boolean ignoreClean, boolean acceptErrors) {
210         Thread.dumpStack();
211         // create a shallow request (no line annotation)
212
// ParsableObjectRequest req = new ParseSourceRequest();
213
// return parse(priority, ignoreClean, acceptErrors, req);
214
return Task.EMPTY;
215     }
216
217     public Task parse(int priority, boolean ignoreClean, boolean acceptErrors, ParsableObjectRequest req) {
218         
219         if (req.getParserType () == JavaParser.MDR_PARSER) {
220             // System.out.println("MDR_PARSER used !!!");
221
// Thread.dumpStack ();
222
return new FinishedTask(null);
223         }
224         
225         Processor immediate;
226         SourceElement.Impl i = null;
227         // #20100 - order locks properly: getEditorSupport() is likely to
228
// access/lock some CookieSet, and cookies tend to call/synchronize
229
// on the parser support.
230
CloneableEditorSupport editSupport = docBinding.getEditorSupport();
231         synchronized (this) {
232             if (DEBUG) {
233                 System.err.println("Got parse request, prio = " + priority + " ignoreClean = " + ignoreClean + " acceptErrs = " + acceptErrors); // NOI18N
234
i = getSourceImpl();
235                 System.err.println("Data = " + i + " clean = " + this.clean); // NOI18N
236
System.err.println("Parsing task = " + currentRequest + "/" + runningRequest); // NOI18N
237
}
238             if (currentRequest != null) {
239                 // if THIS is the parsing thread, then -- well, it's bad.
240
// temporal resolution: take out the request IMMEDIATELY.
241
if (PARSING_RP.isRequestProcessorThread()) {
242                     if (DEBUG)
243                         System.err.println("Running in parsing thread!"); // NOI18N
244
immediate = currentRequest;
245                 } else {
246                     currentRequest.enableErrors(acceptErrors);
247                     currentRequest.setPriority(priority);
248                     if (DEBUG)
249                         System.err.println("Returning task from current request " + currentRequest); // NOI18N
250
return currentRequest.getClientTask();
251                 }
252             } else {
253                 i = getSourceImpl();
254                 if (i != null && this.clean && !ignoreClean) {
255                     if (DEBUG)
256                         System.err.println("Returning finished task"); // NOI18N
257
return new FinishedTask(i);
258                 }
259                 Processor proc = new Processor(priority, parsingEnv, editSupport,req);
260                 proc.enableErrors(acceptErrors);
261                 if (PARSING_RP != null && PARSING_RP.isRequestProcessorThread()) {
262                     immediate = proc;
263                 } else {
264                     addRequest(proc, priority);
265                     return proc.getClientTask();
266                 }
267             }
268         }
269         immediate.run();
270         i = getSourceImpl();
271         return new FinishedTask(i);
272     }
273     public synchronized Task getCurrentTask() {
274         if (currentRequest!=null)
275             return currentRequest.getClientTask();
276         return new FinishedTask(null);
277     }
278
279     private static class FinishedTask extends Task {
280         private Object JavaDoc hook;
281         
282         public FinishedTask(Object JavaDoc o) {
283             super(null);
284             hook = o;
285         }
286     }
287     
288     public void fireElementPropertyChange(Element source, PropertyChangeEvent JavaDoc evt) {
289         if (source == getSource()) {
290             ((SrcElementImpl) getSourceImpl ()).propertyChange(evt);
291         } else {
292             updater.firePropertyChange(source, evt);
293         }
294     }
295     
296     /**
297      * Returns the parser engine used by this Support.
298      */

299     public ParserEngine getParserEngine() {
300         return this.engine;
301     }
302     
303     /** Replaces the parser engine with the given implementation. All
304      * parse requests issued from now on will use the new engine impl.
305      */

306     public void setParserEngine(ParserEngine eng) {
307         this.engine = eng;
308     }
309
310     /**
311      * Prepares data for the Java hierarchy from the source. If some data already
312      * exists, they are not refreshed, and the Task is reaturned in already finished
313      * state.
314      * @return task object that is set to completed after the data is prepared.
315      */

316     public Task prepare() {
317         return Task.EMPTY; //parse(PRIORITY_BACKGROUND, false, false);
318
}
319     
320     public SourceElement getSource() {
321         if (src == null)
322             src = jdo.getSource ();
323         return src;
324         /*
325         synchronized (this) {
326             if (src != null)
327                 return src;
328             createSourceProxy ();
329             return src = new SourceElement (new org.netbeans.modules.java.bridge.SrcElementImpl (jdo));
330         }
331          */

332     }
333     
334     /**
335      * Invalidates contents of the parser support. This method should be called
336      * when the original data source is discarded. The parser support is not
337      * expecting to be valid ever again after this call.
338      */

339     public void invalidate() {
340         SourceElement.Impl impl;
341         synchronized (this) {
342             if (!valid)
343                 return;
344             impl = getSourceImpl();
345             if (impl == null)
346                 return;
347             valid = false;
348         }
349         updater.invalidateModel(getSource());
350         synchronized (this) {
351             if (!updating)
352                 refImplementation = null;
353         }
354         changeStatus(SourceElement.STATUS_NOT);
355     }
356         
357     
358     protected void changeStatus(int newStatus) {
359         int oldStatus = status;
360         status = newStatus;
361         if (propSupport != null && propSupport.hasListeners(null))
362             propSupport.firePropertyChange(PROP_STATUS, oldStatus, newStatus);
363         fireStateChange();
364     }
365     
366     protected void fireStateChange() {
367         if (changeList == null)
368             return;
369         Collection copy;
370         
371         synchronized (changeList) {
372             if (changeList.isEmpty())
373                 return;
374             copy = new ArrayList(changeList);
375         }
376         ChangeEvent JavaDoc e = new ChangeEvent JavaDoc(this);
377         
378         for (Iterator it = copy.iterator(); it.hasNext(); ) {
379             try {
380                 ((ChangeListener JavaDoc)it.next()).stateChanged(e);
381             } catch (RuntimeException JavaDoc x) {
382                 org.openide.ErrorManager.getDefault().notify(org.openide.ErrorManager.WARNING, x);
383             }
384         }
385     }
386     
387     public SourceElement.Impl getSourceImpl() {
388         SourceElement sourceElem = getSource ();
389         if (sourceElem == null)
390             return null;
391         return (SourceElement.Impl) sourceElem.getCookie (SourceElement.Impl.class);
392     }
393     
394     public LangModel getModel() {
395         return this.model;
396     }
397     
398     public SourceElement.Impl findSourceImpl() throws SourceException {
399         synchronized (this) {
400             SourceElement.Impl impl = getSourceImpl();
401             if (impl != null)
402                 return impl;
403             Util.log("impl = null"); // NOI18N
404
if (savedException != null)
405                 throw savedException;
406         }
407         throw new SourceException("Cannot acquire source"); // NOI18N
408
}
409     
410     /**
411      * Invalidates the data in the specified region (bounds inclusive).
412      * @return true, if validity pattern of the parsed data changes.
413      */

414     public void sourceChanged(int from, int to) {
415         Processor req = currentRequest;
416         
417         clean = false;
418         if (req != null)
419             req.sourceChanged();
420     }
421     
422     /**
423      * Retrieves the Throwable that caused the parsing to abort. This will be
424      * mainly an IOException.
425      * @return Throwable that aborted the last most recent parse request.
426      */

427     public SourceException getErrorCause() {
428         return savedException;
429     }
430
431     /*--------------------------------------------------------------------------------*/
432     
433     Node.Cookie findCookieForSource(Class JavaDoc type) {
434         if (src == null)
435             return null;
436         return parsingEnv.findCookie(getSource(), type);
437     }
438     
439     /**
440      * Retrieves the parsing status of the source. The value is one of the symbolic
441      * SourceElement.STATUS_* constants.
442      */

443     public int getStatus() {
444         int s = status;
445         if (s != SourceElement.STATUS_OK)
446             return s;
447         SourceElement.Impl impl = getSourceImpl();
448         if (impl == null)
449             changeStatus(SourceElement.STATUS_NOT);
450         return status;
451     }
452     
453     public Task addParsingRunnable(Runnable JavaDoc r, int priority) {
454         return PARSING_RP.post(r, 0, priority);
455     }
456     
457
458     static final Runnable JavaDoc EMPTY_RUNNABLE = new Runnable JavaDoc() {
459         public void run() {}
460     };
461     
462     /**
463      * Helper class that is put to the parsing RequestProcessor to carry out
464      * a specific ParseRequest.
465      */

466     class Processor extends Object JavaDoc implements
467         Runnable JavaDoc, CommitListener, ParseObjectRequest {
468         Processor chained;
469         int priority;
470         RequestProcessor.Task ownTask;
471         boolean errorsOK;
472         //SourceElement.Impl implHook;
473
int stage;
474         int resultStatus;
475         ParsableObjectRequest request;
476         T task;
477         
478         Processor(int priority, Env env, CloneableEditorSupport supp,ParsableObjectRequest req) {
479             request=req;
480             task = new T();
481             request.setEnvironment(env);
482             request.setEditorSupport(supp);
483         }
484         
485         /**
486          * If run() is directly invoked on the request processor task, it means
487          * that the request processor thread itself needs to complete the task before
488          * executing. We have to evaluate the task at once.
489          */

490         protected void directRun() {
491             do {
492                 run();
493             } while (stage >= 0);
494         }
495         
496         public void setPriority(int prior) {
497             if (this.priority > prior)
498                 return;
499             priority = prior;
500             // if the task cannot be cancelled, it is being processed right now,
501
// or it was already completed.
502
if (ownTask.cancel()) {
503                 addRequest(this, prior);
504             }
505         }
506         
507         public void enableErrors(boolean enable) {
508             errorsOK |= enable;
509         }
510         
511         public void chainRequest(Processor other) {
512             chained = other;
513         }
514         
515         public void setProcessorTask(RequestProcessor.Task t) {
516             ownTask = t;
517         }
518                 
519         public void run() {
520             Util.log("processing request " + this + " stage " + stage); // NOI18N
521
try {
522                 switch (stage++) {
523                     case 0:
524                         if (DEBUG) {
525                             System.err.println("Starting request " + this); // NOI18N
526
}
527                         parseLockModel();
528                         break;
529                     case 1:
530                         break;
531                 }
532             } catch (SourceException.IO e) {
533             // the exception is expected - I/O in reading sources. Mark the source as errorenous.
534
resultStatus = SourceElement.STATUS_ERROR;
535             } catch (Throwable JavaDoc e) {
536                 savedException = new SourceException(e.getLocalizedMessage());
537                 parsingEnv.annotateThrowable(savedException, e);
538                 parsingEnv.annotateThrowable(savedException, "Parser error", false); // NOI18N
539
org.openide.ErrorManager.getDefault().notify(e);
540                 resultStatus = SourceElement.STATUS_ERROR;
541             } finally {
542                 stage--;
543             }
544             Util.log("request " + this + " stage " + (stage + 1) + " end"); // NOI18N
545
if (stage > 0)
546                 return;
547             
548             if (resultStatus != -1) {
549                 complete();
550             } else {
551                 // it's a pity -- reschedule the request.
552
request.notifyReschedule();
553                 Util.log("Rescheduling request"); // NOI18N
554
stage = 0;
555                 addRequest(this, priority);
556             }
557         }
558         
559         private void parseLockModel() throws SourceException {
560             model.addPreCommitListener(this);
561             resultStatus = -1;
562             synchronized (ParsingSupport.this) {
563                 runningRequest = this;
564                 Util.log("Running request = " + this); // NOI18N
565
}
566             savedException = null;
567             try {
568                 runningRequest = this;
569                 process(getParserEngine());
570                 if (isValid()) {
571                     Util.log("Request " + this + " processed. Still valid"); // NOI18N
572
stage = 1;
573                     Util.log("trying to run update"); // NOI18N
574
// ParsingSupport.this.updater.runUpdate(this, true);
575
resultStatus = SourceElement.STATUS_OK;
576                 }
577             } catch (IOException JavaDoc ex) {
578                 savedException = new SourceException.IO(ex);
579                 parsingEnv.annotateThrowable(savedException, ex);
580                 // notify listeners that we are finished.
581
resultStatus = SourceElement.STATUS_ERROR;
582             } catch (InternalError JavaDoc er) {
583                 // the parser sometimes issues this one -- just issue an error
584
savedException = new SourceException(er.getMessage());
585                 parsingEnv.annotateThrowable(savedException, er);
586                 resultStatus = SourceElement.STATUS_ERROR;
587             } finally {
588                 model.removePreCommitListener(this);
589             }
590             runningRequest = null;
591         }
592         
593         public void complete() {
594             synchronized (ParsingSupport.this) {
595                 if (currentRequest == this)
596                     currentRequest = null;
597             }
598             changeStatus(resultStatus);
599             task.complete();
600             if (chained != null)
601                 chained.complete();
602             // signal -- processing is finally over.
603
stage = -1;
604         }
605         
606         public void changesCommited(Set created, Set removed, Map changed) {
607             request.modelChanged();
608         }
609
610         /**
611          * Causes the request to be passed immediately to the parsing engine.
612          * If the request is already processed, it returns immediately.
613          */

614         public void process(ParserEngine eng) throws IOException JavaDoc {
615             eng.process(this);
616         }
617
618         /**
619          * Returns a task clients can wait for. This is *NOT* a task which processes the
620          * request, since the request can be rescheduled several times before it is
621          * completed (if it is completed).
622          */

623         public Task getClientTask() {
624             return task;
625         }
626
627         /**
628          * Notifies the request that the source text has been changed. This causes
629          * cancellation of the request in some cases.
630          */

631         public void sourceChanged() {
632             request.sourceChanged();
633         }
634
635         /* ParseObjectRequest method is delegated to request object */
636         public void setSyntaxErrors(int errors) {
637             request.setSyntaxErrors(errors);
638         }
639
640         public void setSemanticErrors(int errors) {
641             request.setSemanticErrors(errors);
642         }
643
644         public void notifyStart() {
645             request.notifyStart();
646         }
647
648         public void notifyComplete() {
649             request.notifyComplete();
650         }
651
652         public boolean isValid() {
653             return request.isValid();
654         }
655
656         public boolean needsProcessing() {
657             return request.needsProcessing();
658         }
659
660         public int getSyntaxErrors() {
661             return request.getSyntaxErrors();
662         }
663
664         public Collection getMessages() {
665             return request.getMessages();
666         }
667
668         public ElementFactory getFactory() {
669             return request.getFactory();
670         }
671
672         public char[] getSource() throws java.io.IOException JavaDoc {
673             clean = true;
674             return request.getSource();
675         }
676
677         public InputStream JavaDoc findCompiledClass(String JavaDoc className) {
678             return request.findCompiledClass(className);
679         }
680
681         public Object JavaDoc getParserType() {
682             return request.getParserType();
683         }
684
685         public String JavaDoc getSourceName() {
686             return request.getSourceName();
687         }
688         
689         public ClassPath getSourcePath() {
690             return request.getSourcePath();
691         }
692         
693         public ClassPath getLibraryPath() {
694             return request.getLibraryPath();
695         }
696         
697         public ClassPath getBootClassPath() {
698             return request.getBootClassPath();
699         }
700         
701         /* --------------------------------------------------------------------*/
702
703         /** The class is NOT static intentionally - it holds a hard reference
704          * to the request, keeping its data alive.
705          */

706         private class T extends org.openide.util.Task {
707             T() {
708                 super(EMPTY_RUNNABLE);
709             }
710
711             public void run() {
712                 Processor.this.directRun();
713             }
714
715             protected void complete() {
716                 super.notifyFinished();
717             }
718         }
719     }
720     
721     /* --------------------------------------------------------------------*/
722     static RequestProcessor PARSING_RP;
723
724     private void addRequest(Processor proc, int priority) {
725         synchronized (this) {
726             if (PARSING_RP == null) {
727                 PARSING_RP = new RequestProcessor("Java source parsing"); // NOI18N
728
}
729             if (currentRequest != proc) {
730                 if (currentRequest != null)
731                     proc.chainRequest(currentRequest);
732                 currentRequest = proc;
733             }
734             proc.setProcessorTask(PARSING_RP.post(proc, 0, priority));
735         }
736     }
737     
738     protected void registerData(SourceElement.Impl data) {
739         Reference JavaDoc newRef;
740
741         synchronized (ParsingSupport.class) {
742             newRef = new DataFinalizer(this, data, Utilities.activeReferenceQueue());
743         }
744         synchronized (this) {
745             refImplementation = newRef;
746         }
747     }
748     
749     protected void notifyFinalized(Reference JavaDoc refImpl) {
750         
751         synchronized (this) {
752             if (refImplementation != refImpl)
753                 return;
754             refImplementation = null;
755         }
756         changeStatus(SourceElement.STATUS_NOT);
757     }
758     
759     private static class DataFinalizer extends WeakReference JavaDoc implements Runnable JavaDoc {
760         Reference JavaDoc refSupp;
761
762         DataFinalizer(ParsingSupport supp, Object JavaDoc data, ReferenceQueue JavaDoc rqueue) {
763             super(data, rqueue);
764             refSupp = new WeakReference JavaDoc(supp);
765         }
766         
767         public void run() {
768             ParsingSupport supp = (ParsingSupport)refSupp.get();
769             if (supp != null)
770                 supp.notifyFinalized(this);
771         }
772     }
773 }
774
Popular Tags