KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > java > source > builder > CommentHandlerService


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-2007 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.java.source.builder;
21
22 import org.netbeans.modules.java.source.builder.ASTService;
23 import org.netbeans.api.java.source.Comment;
24 import org.netbeans.api.java.source.query.CommentHandler;
25 import org.netbeans.api.java.source.query.CommentSet;
26 import org.netbeans.modules.java.source.engine.ASTModel;
27
28 import com.sun.source.tree.CompilationUnitTree;
29 import com.sun.source.tree.Tree;
30 import com.sun.tools.javac.tree.JCTree;
31
32 import com.sun.tools.javac.tree.JCTree.*;
33 import com.sun.tools.javac.tree.TreeScanner;
34 import com.sun.tools.javac.util.Context;
35
36 import java.util.*;
37
38 import static org.netbeans.modules.java.source.builder.BufferRun.Kind.*;
39 import static org.netbeans.api.java.source.Comment.Style.*;
40 import static com.sun.tools.javac.parser.Token.*;
41
42 /**
43  * Generate Comments during scanning.
44  */

45 public class CommentHandlerService implements CommentHandler {
46     private static final Context.Key<CommentHandlerService> commentHandlerKey =
47         new Context.Key<CommentHandlerService>();
48     
49     /** Get the CommentMaker instance for this context. */
50     public static CommentHandlerService instance(Context context) {
51     CommentHandlerService instance = context.get(commentHandlerKey);
52     if (instance == null) {
53         instance = new CommentHandlerService(context);
54             setCommentHandler(context, instance);
55         }
56     return instance;
57     }
58     
59     /**
60      * Called from reattributor.
61      */

62     public static void setCommentHandler(Context context, CommentHandlerService instance) {
63         assert context.get(commentHandlerKey) == null;
64         context.put(commentHandlerKey, instance);
65     }
66
67     private Map<Tree, CommentSetImpl> map = new WeakHashMap<Tree, CommentSetImpl>();
68     protected ASTModel model;
69     
70     private CommentHandlerService(Context context) {
71         model = ASTService.instance(context);
72     }
73     
74     public boolean hasComments(Tree tree) {
75         synchronized (map) {
76             return map.containsKey(tree);
77         }
78     }
79     
80     public CommentSet getComments(Tree tree) {
81         synchronized (map) {
82             CommentSetImpl cs = map.get(tree);
83             if (cs == null) {
84                 cs = new CommentSetImpl();
85                 map.put(tree, cs);
86             }
87             return cs;
88         }
89     }
90
91     /**
92      * Copies preceding and trailing comments from one tree to another,
93      * appending the new entries to the existing comment lists.
94      */

95     public void copyComments(Tree fromTree, Tree toTree) {
96         synchronized (map) {
97             CommentSetImpl from = map.get(fromTree);
98             if (from != null) {
99                 CommentSetImpl to = map.get(toTree);
100                 if (to == null) {
101                     to = (CommentSetImpl)from.clone();
102                     to.setTree(toTree);
103                     map.put(toTree, to);
104                 }
105                 else {
106                     to.addPrecedingComments(from.getPrecedingComments());
107                     to.addTrailingComments(from.getTrailingComments());
108                 }
109             }
110         }
111     }
112     
113     /**
114      * Add a comment to a tree's comment set. If a comment set
115      * for the tree doesn't exist, one will be created.
116      */

117     public void addComment(Tree tree, Comment c) {
118         synchronized (map) {
119             CommentSetImpl set = map.get(tree);
120             if (set == null) {
121                 set = new CommentSetImpl();
122                 map.put(tree, set);
123             }
124             set.addPrecedingComment(c);
125         }
126     }
127
128     /**
129      * Associates comment texts with their respective trees.
130      */

131     public void mapComments(final CompilationUnitTree compilationUnit, CharSequence JavaDoc sbuf, BufferRunQueue runs) {
132         final JCCompilationUnit toplevel = (JCCompilationUnit)compilationUnit;
133         final SortedMap<Integer JavaDoc, JCTree> positions = getPositions(toplevel);
134         if (positions.size() == 0)
135             return;
136         JCTree lastTree = null;
137         LineColMapper lineCol = new LineColMapper() {
138             public int getColumn(int offset) {
139                 return toplevel.lineMap.getColumnNumber(offset);
140             }
141             public int getLine(int offset) {
142                 return toplevel.lineMap.getLineNumber(offset);
143             }
144         };
145         
146         // handle TopLevel comments separately
147
CommentSetImpl topComments = (CommentSetImpl)getComments(toplevel);
148         for (Iterator<BufferRun> iter = runs.iterator(); iter.hasNext();) {
149             BufferRun run = iter.next();
150             if (run.getKind() == TOKEN)
151                 break;
152             else if (run.getKind() == COMMENT) {
153                 CommentRun cr = (CommentRun)run;
154                 if (cr.getStyle() == JAVADOC)
155                     break; // Javadoc comments belong to classdef
156
Comment c = cr.toComment(cr.getString(sbuf), lineCol.getColumn(cr.getStart()));
157                 topComments.addPrecedingComment(c);
158             }
159         }
160
161         for (int pos : positions.keySet()) {
162             JCTree tree = positions.get(pos);
163             int start = runs.findRunStartingAt(model.getStartPos(tree));
164             if (start == (-1)) //see CommentHandlerServiceTest.testBrokenFile1
165
continue;
166             mapComments(start, tree, lastTree, sbuf, runs, lineCol);
167             int endPos = model.getEndPos(tree, toplevel);
168             int tail = runs.findRunEndingWith(endPos);
169             if (tail == (-1))
170                 continue;
171             BufferRun br = runs.get(tail);
172             if (br.getKind() == TOKEN && ((TokenRun)br).getToken() == RBRACE)
173                 mapTrailingBlockComments(start, tail, tree, getComments(tree), sbuf, runs, lineCol);
174             lastTree = tree;
175         }
176
177         // append any final comments, such as revision history
178
int tail = runs.findRunEndingWith(model.getEndPos(toplevel, toplevel)) + 1;
179         while (tail < runs.size()) {
180             if (runs.get(tail).getKind() == COMMENT) {
181                 CommentRun cr = (CommentRun)runs.get(tail);
182                 Comment c = cr.toComment(cr.getString(sbuf), lineCol.getColumn(cr.getStart()));
183                 topComments.addTrailingComment(c);
184             }
185             tail++;
186         }
187     }
188     
189     private void mapTrailingBlockComments(int begin, int end, JCTree tree, CommentSet comments, CharSequence JavaDoc sbuf,
190                                           BufferRunQueue runs, LineColMapper lineCol) {
191         while (--end > begin && runs.get(end).getKind() != TOKEN) {
192             BufferRun br = runs.get(end);
193             if (br.getKind() == COMMENT) {
194                 CommentRun cr = (CommentRun)br;
195                 Comment c = cr.toComment(cr.getString(sbuf), lineCol.getColumn(cr.getStart()));
196                 comments.addTrailingComment(c);
197             }
198         }
199     }
200     
201     private interface LineColMapper {
202         int getColumn(int offset);
203         int getLine(int offset);
204     }
205     
206     private void mapComments(int startRun, JCTree tree, JCTree lastTree, CharSequence JavaDoc sbuf, BufferRunQueue runs, LineColMapper lineCol) {
207         assert startRun != -1;
208         int i = startRun - 1;
209         int lastEOL = -1;
210         while (i >= 0 && runs.get(i).getKind() != TOKEN) {
211             if (runs.get(i).getKind() == LINE_ENDING)
212                 lastEOL = lineCol.getLine(runs.get(i).getStart());
213             i--;
214         }
215
216         while (++i < startRun) {
217             if (runs.get(i).getKind() == COMMENT) {
218                 CommentRun cr = (CommentRun)runs.get(i);
219                 Comment c = cr.toComment(cr.getString(sbuf), lineCol.getColumn(cr.getStart()));
220                 if (lastTree != null && lineCol.getLine(cr.getEnd()) == lastEOL) {
221                     // Add comments on the same line as the previous tree to that tree.
222
CommentSetImpl cs = (CommentSetImpl)getComments(lastTree);
223                     cs.addTrailingComment(c);
224                 } else
225                     addComment(tree, c);
226             }
227         }
228     }
229     
230     /**
231      * Move the comments stored in a comment set to the top CommentSet as trailing
232      * comments.
233      */

234     private CommentSetImpl flushTrailingComments(CommentSetImpl comments, CommentSetImpl lastComments) {
235         List<Comment> cmts = comments.getPrecedingComments();
236         if (cmts.isEmpty())
237             return comments;
238        for (Comment c : cmts)
239             lastComments.addTrailingComment(c);
240         return new CommentSetImpl(); // equivalent to resetting it
241
}
242
243     /** Create map of trees, indexed by position. Because a depth-first scan
244      * is used, parent trees which have the same start position as child
245      * trees overwrite their children. For example, the start position of
246      * "public void foo {...}" returns a MethodDef, not Modifiers.
247      *
248      * Note: the TopLevel is handled separately, since a TopLevel without
249      * a package or import statements has the same start position as its
250      * ClassDef.
251      */

252     private SortedMap<Integer JavaDoc, JCTree> getPositions(JCCompilationUnit tree) {
253         final SortedMap<Integer JavaDoc, JCTree> positions = new TreeMap<Integer JavaDoc, JCTree>();
254         tree.accept(new TreeScanner() {
255             @Override JavaDoc
256             public void scan(JCTree tree) {
257                 if (tree != null) {
258                     // scan children first
259
tree.accept(this);
260                     int pos = model.getStartPos(tree);
261                     if (pos != com.sun.tools.javac.util.Position.NOPOS)
262                         positions.put(pos, tree);
263                 }
264             }
265             @Override JavaDoc
266             public void visitTypeIdent(JCPrimitiveTypeTree tree) {
267                 super.visitTypeIdent(tree);
268             }
269             @Override JavaDoc
270             public void visitMethodDef(JCMethodDecl tree) {
271                 super.visitMethodDef(tree);
272             }
273         });
274         return positions;
275     }
276 }
277
Popular Tags