KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > javacore > parser > ASTProvider


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.javacore.parser;
21
22 import java.io.*;
23 import java.util.ArrayList JavaDoc;
24 import java.util.Arrays JavaDoc;
25 import java.util.List JavaDoc;
26 import java.util.regex.Pattern JavaDoc;
27 import javax.swing.text.BadLocationException JavaDoc;
28 import javax.swing.text.Document JavaDoc;
29 import javax.swing.text.Position.Bias;
30 import org.netbeans.api.java.queries.SourceLevelQuery;
31 import org.netbeans.jmi.javamodel.Resource;
32 import org.netbeans.lib.java.parser.*;
33 import org.netbeans.modules.javacore.JMManager;
34 import org.openide.ErrorManager;
35 import org.openide.cookies.EditorCookie;
36 import org.openide.filesystems.FileObject;
37 import org.openide.loaders.DataObject;
38 import org.openide.loaders.DataObjectNotFoundException;
39 import org.openide.text.CloneableEditorSupport;
40 import org.openide.text.PositionBounds;
41 import org.openide.text.PositionRef;
42
43
44 /**
45  *
46  * @author Tomas Hurka
47  */

48 public class ASTProvider implements ParserTokens, ASTreeTypes, ASTContext {
49     FileObject fobj;
50     private CloneableEditorSupport editor;
51     private ASTree topNode;
52     private Token[] tokens;
53     private String JavaDoc sourceText;
54     private Resource rsc;
55     static ASTree[] NULL_TREE=new ASTree[0];
56     boolean documentPositions;
57     boolean hasSyntaxErrors;
58     
59     /** Creates a new instance of ASTProvider */
60     public ASTProvider(Resource r,FileObject obj) {
61         rsc=r;
62         fobj = obj;
63         if (obj == null) throw new javax.jmi.reflect.InvalidObjectException(r);
64     }
65     
66     protected ASTProvider(Resource r, FileObject obj, String JavaDoc sourceText, boolean isFromDoc) {
67         this(r, obj);
68         this.sourceText = sourceText;
69         this.documentPositions = isFromDoc;
70     }
71
72     private void createASTree() {
73         Reader r;
74         int status=-1;
75         hasSyntaxErrors = false;
76         
77         try {
78             r=getReader();
79         } catch (Exception JavaDoc ex) {
80             JMManager.getLog().log(ErrorManager.WARNING, "Error opening stream for: " + getResource().getName() + "; " + ex.toString());
81             ErrorManager.getDefault().notify(ex);
82             return;
83         }
84         JParser parser=Factory.getDefault().getParser(this, r, fobj.getName());
85         try {
86             if (JMManager.PERF_DEBUG) {
87                 System.err.println("Parsing file: " + fobj.getName());
88                 Thread.dumpStack();
89             }
90             status=parser.parse(false); // don't generate symbols
91
} catch (Exception JavaDoc ex) {
92             JMManager.getLog().notify(ErrorManager.INFORMATIONAL, ex);
93             dumpSource(ex);
94         }
95         
96     tokens=parser.getTokens();
97     if (tokens == null)
98         tokens = new Token[0];
99         
100         if (status!=0) {
101             hasSyntaxErrors = true;
102             topNode = new ASTRepairer(this, parser).fixTree();
103         } else {
104             topNode = parser.getASTree();
105         }
106     }
107     
108     Token[] getTokens() {
109         return tokens;
110     }
111     
112     public Token getTokenByOffset(int offset) {
113         if (offset >= sourceText.length()) return null;
114         int lo = 0;
115         int hi = tokens.length;
116         int check;
117         int index;
118         while (true) {
119             if (lo >= hi) return null;
120             index = (hi - lo) / 2 + lo;
121             check = checkToken(tokens[index], offset);
122             if (check < 0) {
123                 hi = index;
124             } else if (check > 0) {
125                 lo = index + 1;
126             } else {
127                 break;
128             }
129         }
130         Token result = tokens[index];
131         Token[] paddings = result.getPadding();
132         if (paddings != null) {
133             for (int i = paddings.length - 1; i >= 0; i--) {
134                 if (paddings[i].getEndOffset() > offset) {
135                     result = paddings[i];
136                 }
137             }
138         }
139         return result;
140     }
141     
142     private int checkToken(Token token, int offset) {
143         Token[] padding = token.getPadding();
144         int startOffset = padding != null && padding.length > 0 ? padding[0].getStartOffset() : token.getStartOffset();
145         int endOffset = token.getEndOffset();
146         if (offset < startOffset) return -1;
147         else if (offset >= endOffset) return 1;
148         else return 0;
149     }
150
151     public ASTree getASTree() {
152         //if (!dobj.isValid())
153
//workaround for issue #54232
154
if (!fobj.isValid())
155             return null;
156         if (topNode==null)
157             createASTree();
158         return topNode;
159     }
160
161     public boolean hasSyntaxError() {
162         return hasSyntaxErrors;
163     }
164     
165     FileObject getFileObject() {
166         return fobj;
167     }
168
169     String JavaDoc getRealSource(boolean overridePositions) throws FileNotFoundException, UnsupportedEncodingException, IOException {
170         EditorCookie editor;
171         Document JavaDoc doc=null;
172         DataObject dobj=Util.getModifiedDataObject(fobj);
173         
174         if (dobj!=null) {
175             editor = (EditorCookie) dobj.getCookie(EditorCookie.class);
176             if (editor!=null) {
177                 doc = editor.getDocument();
178             }
179             assert doc!=null:"Modified DO "+fobj.getPath()+" without document";
180             if (overridePositions)
181                 documentPositions = true;
182             // loading from the memory (Document)
183
final String JavaDoc[] str = new String JavaDoc[1];
184             // safely take the text from the document
185
final Document JavaDoc doc2 = doc;
186             Runnable JavaDoc run = new Runnable JavaDoc() {
187                 public void run() {
188                     try {
189                         str[0] = doc2.getText(0, doc2.getLength());
190                     }
191                     catch (BadLocationException JavaDoc e) {
192                         // impossible
193
}
194                 }
195             };
196             doc.render(run);
197             return str[0];
198         } else {
199             // loading from the file
200
InputStream is = fobj.getInputStream();
201             Reader fileReader;
202             String JavaDoc encoding=Util.getFileEncoding(fobj);
203
204             if (encoding == null) {
205                 fileReader = new InputStreamReader(is);
206             } else {
207                 fileReader = new InputStreamReader(is, encoding);
208             }
209             try {
210                 return Util.readContents(fileReader,fobj.getSize());
211             } finally {
212                 fileReader.close();
213             }
214         }
215     }
216
217     Reader getFileReader(boolean overridePositions) throws FileNotFoundException, UnsupportedEncodingException, IOException {
218         return new StringReader(getRealSource(overridePositions));
219     }
220
221     /** Returns contents of DataObject obj. If the object is opened in the editor,
222      * the function returns current contents of the edited document.
223      * If the file is not opened in a JavaEditor, it is read from the disk and
224      * guarded sections are filtered out.
225      * @return contents of the file/editor document; guarded section markers are filtered out.
226      *
227      */

228     public Reader getReader() throws FileNotFoundException, UnsupportedEncodingException, IOException {
229         if (sourceText==null) {
230             sourceText = getRealSource(true);
231         }
232         return new StringReader(sourceText);
233     }
234     
235     public Resource getResource() {
236         return rsc;
237     }
238     
239     public String JavaDoc getJavaDoc(ASTree tree) {
240         String JavaDoc javadoText=null;
241         Token javaDocToken=getComment(tree);
242
243         if (javaDocToken.getType()==DOC_COMMENT) {
244             javadoText=getText(javaDocToken);
245         }
246         return removeJavadocStars(javadoText);
247     }
248
249     public ASTree findTree(ASTree parentTree, int firstToken, int lastToken, int type) {
250         ASTree[] children = parentTree.getSubTrees();
251
252         for (int i = 0; i < children.length; i++) {
253             ASTree ch = children[i];
254             if (ch != null) {
255                 int first = ch.getFirstToken();
256                 int last = ch.getLastToken();
257                 if (first == firstToken && last == lastToken && type == ch.getType()) {
258                     return ch;
259                 }
260                 if (first <= firstToken && last >= lastToken) {
261                     return findTree(ch, firstToken, lastToken, type);
262                 }
263             }
264         }
265         throw new IllegalArgumentException JavaDoc("Child tree not found (type: " + type + " firstToken: " + firstToken + " lastToken: " + lastToken + " resource: " + getResource().getName() + ")"); // NOI18N
266
}
267
268     public Token getComment(ASTree t) {
269         Token first=getToken(t.getFirstToken());
270         Token pad[]=first.getPadding();
271         int i;
272         
273         for(i=pad.length-1;i>=0;i--) {
274             Token c=pad[i];
275             
276             if (c.getType()==DOC_COMMENT)
277                 return c;
278         }
279         return first;
280     }
281     
282     ASTree[] check(ASTree tree) {
283         if (tree==null || tree.getType()==SEMICOLON)
284             return null;
285         return NULL_TREE;
286     }
287     
288     public ASTree[] filterParts(ASTree parts[]) {
289         if (parts!=null) {
290             List JavaDoc typeDecl=null;
291             int i;
292             
293             for (i=parts.length-1;i>=0;i--) {
294                 ASTree tree=parts[i];
295                 ASTree checked[]=check(tree);
296                 
297                 if (checked!=NULL_TREE) {
298                     if (typeDecl==null) {
299                         typeDecl=new ArrayList JavaDoc(Arrays.asList((Object JavaDoc[]) parts));
300                     }
301                     if (checked==null)
302                         typeDecl.remove(i);
303                     else
304                         typeDecl.addAll(i, Arrays.asList((Object JavaDoc[]) checked));
305                 }
306             }
307             if (typeDecl!=null) {
308                 return (ASTree[])typeDecl.toArray(new ASTree[typeDecl.size()]);
309             }
310         }
311         return parts;
312     }
313
314     public String JavaDoc getText(ASTree tree) {
315         if (tree == null) {
316             return null;
317         } else {
318             return getText(tree,tree);
319         }
320     }
321     
322     public String JavaDoc getText(ASTree first,ASTree last) {
323         int start=getToken(first.getFirstToken()).getStartOffset();
324         int end=getToken(last.getLastToken()).getEndOffset();
325
326         return sourceText.substring(start,end);
327     }
328     
329     public String JavaDoc getText(Token token) {
330         int start=token.getStartOffset();
331         int end=token.getEndOffset();
332
333         return sourceText.substring(start,end);
334     }
335
336     public Token getToken(int index) {
337     return (index >= 0 && index < tokens.length) ? tokens[index] : null;
338     }
339     
340     public ASTree getParent(ASTree tree) {
341         ASTree parent=getASTree();
342         int index=tree.getFirstToken();
343         
344         while(true) {
345             ASTree parts[]=parent.getSubTrees();
346             ASTree element=null;
347             int i;
348         
349             for (i=0;i<parts.length;i++) {
350                 element=parts[i];
351                 if (element!=null && element.getFirstToken()<=index && element.getLastToken()>=index)
352                     break;
353             }
354             if (i==parts.length)
355                 return parent;
356             if (element==tree)
357                 return parent;
358             parent=element;
359         }
360     }
361     
362     public boolean isFromDocument() {
363         return documentPositions;
364     }
365     
366     /**
367      * Creates bounds for two ASTree elements. First tree represents begining,
368      * second one represents end of the created bounds. If inclPads is true,
369      * then begining is counted from the paddings of the first tree.
370      *
371      * @param startTree start offset of first token of the tree is begin
372      * position reference offset. It is valid only in case
373      * that inclDoc is false.
374      * Otherwise it takes the first token of javadoc comment.
375      * @param endTree end offset of last token of the tree is end position
376      * reference offset.
377      * @param inclDoc if false, then start offset of first token of startTree
378      * is beginning, otherwise it starts from its javadoc
379      * comment.
380      *
381      * @return element's bounds
382      */

383     public PositionBounds createBounds(ASTree startTree, ASTree endTree, boolean inclDoc) {
384         Token startToken;
385         if (inclDoc) {
386             startToken = getComment(startTree);
387         } else {
388             startToken = getToken(startTree.getFirstToken());
389         }
390         Token endToken=getToken(endTree.getLastToken());
391         
392         if (startToken == null || endToken == null) {
393             RuntimeException JavaDoc ex;
394             String JavaDoc startTreeText = null;
395             String JavaDoc endTreeText = null;
396             try {
397                 startTreeText = startTree.toString();
398                 endTreeText = endTree.toString();
399             } catch (RuntimeException JavaDoc e) {
400                 // ignore
401
}
402             if (startToken == null) {
403                 ex = new RuntimeException JavaDoc("First token is null for ASTree: " + startTreeText + " of type: " + startTree.getType() + // NOI18N
404
";\ncalling startTree.getFirstToken() returns " + startTree.getFirstToken()); // NOI18N
405
} else {
406                 ex = new RuntimeException JavaDoc("Last token is null for ASTree: " + endTreeText + " of type: " + endTree.getType() + // NOI18N
407
";\ncalling endTree.getLastToken() returns " + endTree.getLastToken()); // NOI18N
408
}
409             ex.printStackTrace();
410             dumpSource(ex);
411         }
412         
413         return createBounds(new int[] {startToken.getStartOffset(), endToken.getEndOffset()});
414     }
415     
416     public PositionBounds createBounds(int startOffset,int endOffset) {
417         return createBounds(new int[] {startOffset, endOffset});
418     }
419     
420     public PositionBounds createBounds(int[] oldOffsets) {
421         if (editor == null) {
422             DataObject dobj;
423             try {
424                 dobj=DataObject.find(fobj);
425             } catch (DataObjectNotFoundException ex) {
426                 ErrorManager.getDefault().notify(ex);
427                 return null;
428             }
429             editor = Util.findCloneableEditorSupport(dobj);
430         }
431         int[] offsets = getDocumentOffsets(oldOffsets);
432         
433         PositionRef start=editor.createPositionRef(offsets[0], Bias.Forward);
434         PositionRef end=editor.createPositionRef(offsets[1], Bias.Backward);
435         
436         return new PositionBounds(start,end);
437     }
438     
439     // this method changes the array passed as argument
440
// offsets must be ordered (from lowest to highest)
441
public int[] getDocumentOffsets(int[] offsets) {
442         if (!isFromDocument()) {
443             return convertToDocumentOffsets(offsets);
444         } else {
445             return offsets;
446         }
447     }
448
449     public int[] convertToDocumentOffsets(final int[] offsets) {
450         int pos = -1;
451         int firstOffset = 0;
452
453         int[] newOffsets = (int[]) offsets.clone();
454         int srcLastIdx = sourceText.length()-1;
455         while (pos < offsets[offsets.length - 1]) {
456             pos = sourceText.indexOf('\r',pos+1);
457             if (pos == -1) break;
458             if (pos < srcLastIdx && sourceText.charAt(pos+1) != '\n')
459                 continue;
460             while ((firstOffset < offsets.length) && (offsets[firstOffset] <= pos)) {
461                 firstOffset++;
462             }
463             for (int i = firstOffset; i < offsets.length; i++) {
464                 newOffsets[i]--;
465             }
466         }
467         return newOffsets;
468     }
469
470     public String JavaDoc getSourceText() {
471         if (sourceText == null) {
472             try {
473                 getReader();
474             } catch (Exception JavaDoc ex) {
475                 JMManager.getLog().log(ErrorManager.WARNING, "Error opening stream for: " + getResource().getName() + "; " + ex.toString());
476                 ErrorManager.getDefault().notify(ex);
477             }
478         }
479         return sourceText;
480     }
481
482     /** pattern for removing leading stars from javadoc comment */
483     private static final Pattern JavaDoc docRegExp = Pattern.compile("\\r?\\n(\\s*\\*+)?"); // NOI18N
484

485     private static String JavaDoc removeJavadocStars(String JavaDoc rawText) {
486         if (rawText==null)
487             return null;
488         rawText="\n".concat(rawText.substring(1,rawText.length()-2)); // NOI18N
489
String JavaDoc[] lines=docRegExp.split(rawText,Integer.MAX_VALUE);
490         StringBuffer JavaDoc purgedComment = new StringBuffer JavaDoc(rawText.length());
491         for (int i=1; i<lines.length; i++) {
492             if ((i+1)==lines.length) {
493                 if (lines[i].trim().length() != 0) {
494                     purgedComment.append(lines[i]);
495                 }
496             }
497             else {
498                 purgedComment.append(lines[i]).append('\n'); // NOI18N
499
}
500         }
501         return purgedComment.toString();
502     }
503
504     // ASTContext methods.
505
public ASTree getRootTree() {
506     return getASTree();
507     }
508
509     public String JavaDoc getSourceLevel() {
510         return SourceLevelQuery.getSourceLevel(fobj);
511     }
512     
513     public String JavaDoc getClassPath() {
514     return null;
515     }
516     
517     public ErrConsumer getErrorConsumer() {
518         return null;
519     }
520
521     /**
522      * Dumps the source code to the file. Used for parser debugging. It uses
523      * only five dump files: from source1.dump to source5.dump. If last
524      * source5.dump file exists, methods doesn't dump anything.
525      *
526      * @param src source text which causes parser failure
527      * @param exc exception to write to the end of dump file
528      * @param dumpDir directory where dump file will be stored
529      * @return name of created dump file or null if dump file was not created.
530      */

531     private void dumpSource(Exception JavaDoc exc) {
532         String JavaDoc dumpDir = System.getProperty("netbeans.user") + "/var/log/"; //NOI18N
533
String JavaDoc src = getSourceText();
534         String JavaDoc rscName = getResource() == null ? "null" : getResource().getName();
535         int pos = rscName.lastIndexOf('/');
536         String JavaDoc origName;
537         if (pos >= 0) {
538             origName = rscName.substring(pos + 1);
539         } else {
540             origName = rscName;
541         }
542         String JavaDoc name = null;
543         File f = new File(dumpDir + origName + ".dump"); // NOI18N
544
int i = 1;
545         while (i < 255) {
546             if (!f.exists())
547                 break;
548             f = new File(dumpDir + origName + '_' + i + ".dump"); // NOI18N
549
i++;
550         }
551         if (!f.exists()) {
552             try {
553                 OutputStream os = new FileOutputStream(f);
554                 PrintWriter writer = new PrintWriter(new OutputStreamWriter(os, "UTF-8")); // NOI18N
555
try {
556                     writer.println(src);
557                     writer.println("----- Original exception ---------------------------------------------"); // NOI18N
558
exc.printStackTrace(writer);
559                     name = f.getName();
560                 } finally {
561                     writer.close();
562                 }
563             } catch (IOException ioe) {
564                 ErrorManager.getDefault().annotate(ioe, ErrorManager.UNKNOWN, "Error when writing parser dump file!", null, null, null); // NOI18N
565
ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ioe);
566                 name = null;
567             }
568         }
569         if (name != null) {
570             ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, new RuntimeException JavaDoc("Invalid AST returned from parser or error parsing \'" + rscName + "\'. Please report a bug against java module and attach dump file '" // NOI18N
571
+ dumpDir + name + "'.")); // NOI18N
572
} else {
573             ErrorManager.getDefault().log(ErrorManager.WARNING,
574                 "Dump could not be written. Either dump file could not " + // NOI18N
575
"be created or all dump files were already used. Please " + // NOI18N
576
"check that you have write permission to '" + dumpDir + "' and " + // NOI18N
577
"clean all *.dump files in that directory."); // NOI18N
578
}
579     }
580     
581     public String JavaDoc toString() {
582         return "ASTProvider@" + System.identityHashCode(this) + " for \"" + getResource().getName() + "\"";
583     }
584 }
585
Popular Tags