001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.modules.java.editor.semantic;
042:
043: import com.sun.source.tree.ArrayTypeTree;
044: import com.sun.source.tree.IdentifierTree;
045: import com.sun.source.tree.ClassTree;
046: import com.sun.source.tree.CompilationUnitTree;
047: import com.sun.source.tree.MemberSelectTree;
048: import com.sun.source.tree.MethodTree;
049: import com.sun.source.tree.Tree;
050: import com.sun.source.tree.Tree.Kind;
051: import com.sun.source.tree.VariableTree;
052: import com.sun.source.util.SourcePositions;
053: import com.sun.source.util.TreePath;
054: import java.util.ArrayList;
055: import java.util.Collections;
056: import java.util.HashMap;
057: import java.util.logging.Level;
058: import javax.swing.text.BadLocationException;
059: import javax.swing.text.Document;
060: import java.util.HashSet;
061: import java.util.List;
062: import java.util.Map;
063: import java.util.Set;
064: import java.util.logging.Logger;
065: import javax.lang.model.element.Element;
066: import javax.lang.model.element.ElementKind;
067: import javax.lang.model.element.Modifier;
068: import javax.lang.model.element.Name;
069: import org.netbeans.api.java.lexer.JavaTokenId;
070: import org.netbeans.api.java.source.CompilationInfo;
071: import org.netbeans.api.lexer.Token;
072: import org.netbeans.api.lexer.TokenHierarchy;
073: import org.netbeans.api.lexer.TokenSequence;
074: import org.netbeans.api.java.source.CompilationInfo;
075:
076: /**
077: *
078: * @author Jan Lahoda
079: */
080: public class Utilities {
081:
082: private static final Logger LOG = Logger.getLogger(Utilities.class
083: .getName());
084:
085: @Deprecated
086: private static final boolean DEBUG = false;
087:
088: /** Creates a new instance of Utilities */
089: public Utilities() {
090: }
091:
092: private static Token<JavaTokenId> findTokenWithText(
093: CompilationInfo info, String text, int start, int end) {
094: TokenHierarchy<?> th = info.getTokenHierarchy();
095: TokenSequence<JavaTokenId> ts = th.tokenSequence(
096: JavaTokenId.language()).subSequence(start, end);
097:
098: while (ts.moveNext()) {
099: Token<JavaTokenId> t = ts.token();
100:
101: if (text.equals(t.text().toString())) {
102: return t;
103: }
104: }
105:
106: return null;
107: }
108:
109: private static Tree normalizeLastLeftTree(Tree lastLeft) {
110: while (lastLeft != null
111: && lastLeft.getKind() == Kind.ARRAY_TYPE) {
112: lastLeft = ((ArrayTypeTree) lastLeft).getType();
113: }
114:
115: return lastLeft;
116: }
117:
118: private static Token<JavaTokenId> findIdentifierSpanImpl(
119: CompilationInfo info, Tree decl, Tree lastLeft,
120: List<? extends Tree> firstRight, String name,
121: CompilationUnitTree cu, SourcePositions positions) {
122: int declStart = (int) positions.getStartPosition(cu, decl);
123:
124: lastLeft = normalizeLastLeftTree(lastLeft);
125:
126: int start = lastLeft != null ? (int) positions.getEndPosition(
127: cu, lastLeft) : declStart;
128:
129: if (start == (-1)) {
130: start = declStart;
131: if (start == (-1)) {
132: return null;
133: }
134: }
135:
136: int end = (int) positions.getEndPosition(cu, decl);
137:
138: for (Tree t : firstRight) {
139: if (t == null)
140: continue;
141:
142: int proposedEnd = (int) positions.getStartPosition(cu, t);
143:
144: if (proposedEnd != (-1) && proposedEnd < end)
145: end = proposedEnd;
146: }
147:
148: if (end == (-1)) {
149: return null;
150: }
151:
152: if (start > end) {
153: //may happend in case:
154: //public static String s() [] {}
155: //(meaning: method returning array of Strings)
156: //use a conservative start value:
157: start = (int) positions.getStartPosition(cu, decl);
158: }
159:
160: return findTokenWithText(info, name, start, end);
161: }
162:
163: private static Token<JavaTokenId> findIdentifierSpanImpl(
164: CompilationInfo info, MemberSelectTree tree,
165: CompilationUnitTree cu, SourcePositions positions) {
166: int start = (int) positions.getStartPosition(cu, tree);
167: int endPosition = (int) positions.getEndPosition(cu, tree);
168:
169: if (start == (-1) || endPosition == (-1))
170: return null;
171:
172: String member = tree.getIdentifier().toString();
173:
174: TokenHierarchy<?> th = info.getTokenHierarchy();
175: TokenSequence<JavaTokenId> ts = th.tokenSequence(JavaTokenId
176: .language());
177:
178: if (ts.move(endPosition) == Integer.MAX_VALUE) {
179: return null;
180: }
181:
182: ts.moveNext();
183:
184: while (ts.offset() >= start) {
185: Token<JavaTokenId> t = ts.token();
186:
187: if (member.equals(t.text().toString())) {
188: return t;
189: }
190:
191: if (!ts.movePrevious())
192: break;
193: }
194: return null;
195: }
196:
197: private static final Map<Class, List<Kind>> class2Kind;
198:
199: static {
200: class2Kind = new HashMap<Class, List<Kind>>();
201:
202: for (Kind k : Kind.values()) {
203: Class c = k.asInterface();
204: List<Kind> kinds = class2Kind.get(c);
205:
206: if (kinds == null) {
207: class2Kind.put(c, kinds = new ArrayList<Kind>());
208: }
209:
210: kinds.add(k);
211: }
212: }
213:
214: private static Token<JavaTokenId> findIdentifierSpanImpl(
215: CompilationInfo info, TreePath decl) {
216: if (info.getTreeUtilities().isSynthetic(decl))
217: return null;
218:
219: Tree leaf = decl.getLeaf();
220:
221: if (class2Kind.get(MethodTree.class).contains(leaf.getKind())) {
222: MethodTree method = (MethodTree) leaf;
223: List<Tree> rightTrees = new ArrayList<Tree>();
224:
225: rightTrees.addAll(method.getParameters());
226: rightTrees.addAll(method.getThrows());
227: rightTrees.add(method.getBody());
228:
229: Name name = method.getName();
230:
231: if (method.getReturnType() == null)
232: name = ((ClassTree) decl.getParentPath().getLeaf())
233: .getSimpleName();
234:
235: return findIdentifierSpanImpl(info, leaf, method
236: .getReturnType(), rightTrees, name.toString(), info
237: .getCompilationUnit(), info.getTrees()
238: .getSourcePositions());
239: }
240: if (class2Kind.get(VariableTree.class).contains(leaf.getKind())) {
241: VariableTree var = (VariableTree) leaf;
242:
243: return findIdentifierSpanImpl(info, leaf, var.getType(),
244: Collections.singletonList(var.getInitializer()),
245: var.getName().toString(),
246: info.getCompilationUnit(), info.getTrees()
247: .getSourcePositions());
248: }
249: if (class2Kind.get(MemberSelectTree.class).contains(
250: leaf.getKind())) {
251: return findIdentifierSpanImpl(info,
252: (MemberSelectTree) leaf, info.getCompilationUnit(),
253: info.getTrees().getSourcePositions());
254: }
255: if (class2Kind.get(ClassTree.class).contains(leaf.getKind())) {
256: String name = ((ClassTree) leaf).getSimpleName().toString();
257:
258: if (name.length() == 0)
259: return null;
260:
261: SourcePositions positions = info.getTrees()
262: .getSourcePositions();
263: CompilationUnitTree cu = info.getCompilationUnit();
264: int start = (int) positions.getStartPosition(cu, leaf);
265: int end = (int) positions.getEndPosition(cu, leaf);
266:
267: if (start == (-1) || end == (-1)) {
268: return null;
269: }
270:
271: return findTokenWithText(info, name, start, end);
272: }
273: throw new IllegalArgumentException(
274: "Only MethodDecl, VariableDecl and ClassDecl are accepted by this method.");
275: }
276:
277: public static int[] findIdentifierSpan(final TreePath decl,
278: final CompilationInfo info, final Document doc) {
279: final int[] result = new int[] { -1, -1 };
280: doc.render(new Runnable() {
281: public void run() {
282: Token<JavaTokenId> t = findIdentifierSpan(info, doc,
283: decl);
284: if (t != null) {
285: result[0] = t.offset(null);
286: result[1] = t.offset(null) + t.length();
287: }
288: }
289: });
290:
291: return result;
292: }
293:
294: public static Token<JavaTokenId> findIdentifierSpan(
295: final CompilationInfo info, final Document doc,
296: final TreePath decl) {
297: @SuppressWarnings("unchecked")
298: final Token<JavaTokenId>[] result = new Token[1];
299: doc.render(new Runnable() {
300: public void run() {
301: result[0] = findIdentifierSpanImpl(info, decl);
302: }
303: });
304:
305: return result[0];
306: }
307:
308: private static int findBodyStartImpl(Tree cltree,
309: CompilationUnitTree cu, SourcePositions positions,
310: Document doc) {
311: int start = (int) positions.getStartPosition(cu, cltree);
312: int end = (int) positions.getEndPosition(cu, cltree);
313:
314: if (start == (-1) || end == (-1)) {
315: return -1;
316: }
317:
318: if (start > doc.getLength() || end > doc.getLength()) {
319: if (DEBUG) {
320: System.err.println("Log: position outside document: ");
321: System.err.println("decl = " + cltree);
322: System.err.println("startOffset = " + start);
323: System.err.println("endOffset = " + end);
324: Thread.dumpStack();
325: }
326:
327: return (-1);
328: }
329:
330: try {
331: String text = doc.getText(start, end - start);
332:
333: int index = text.indexOf('{');
334:
335: if (index == (-1)) {
336: return -1;
337: // throw new IllegalStateException("Should NEVER happen.");
338: }
339:
340: return start + index;
341: } catch (BadLocationException e) {
342: LOG.log(Level.INFO, null, e);
343: }
344:
345: return (-1);
346: }
347:
348: public static int findBodyStart(final Tree cltree,
349: final CompilationUnitTree cu,
350: final SourcePositions positions, final Document doc) {
351: Kind kind = cltree.getKind();
352: if (kind != Kind.CLASS && kind != Kind.METHOD)
353: throw new IllegalArgumentException("Unsupported kind: "
354: + kind);
355: final int[] result = new int[1];
356:
357: doc.render(new Runnable() {
358: public void run() {
359: result[0] = findBodyStartImpl(cltree, cu, positions,
360: doc);
361: }
362: });
363:
364: return result[0];
365: }
366:
367: private static int findLastBracketImpl(MethodTree tree,
368: CompilationUnitTree cu, SourcePositions positions,
369: Document doc) {
370: int start = (int) positions.getStartPosition(cu, tree);
371: int end = (int) positions.getEndPosition(cu, tree);
372:
373: if (start == (-1) || end == (-1)) {
374: return -1;
375: }
376:
377: if (start > doc.getLength() || end > doc.getLength()) {
378: if (DEBUG) {
379: System.err.println("Log: position outside document: ");
380: System.err.println("decl = " + tree);
381: System.err.println("startOffset = " + start);
382: System.err.println("endOffset = " + end);
383: Thread.dumpStack();
384: }
385:
386: return (-1);
387: }
388:
389: try {
390: String text = doc.getText(end - 1, 1);
391:
392: if (text.charAt(0) == '}')
393: return end - 1;
394: } catch (BadLocationException e) {
395: LOG.log(Level.INFO, null, e);
396: }
397:
398: return (-1);
399: }
400:
401: public static int findLastBracket(final MethodTree tree,
402: final CompilationUnitTree cu,
403: final SourcePositions positions, final Document doc) {
404: final int[] result = new int[1];
405:
406: doc.render(new Runnable() {
407: public void run() {
408: result[0] = findLastBracketImpl(tree, cu, positions,
409: doc);
410: }
411: });
412:
413: return result[0];
414: }
415:
416: private static Token<JavaTokenId> createHighlightImpl(
417: CompilationInfo info, Document doc, TreePath tree) {
418: Tree leaf = tree.getLeaf();
419: SourcePositions positions = info.getTrees()
420: .getSourcePositions();
421: CompilationUnitTree cu = info.getCompilationUnit();
422:
423: //XXX: do not use instanceof:
424: if (leaf instanceof MethodTree || leaf instanceof VariableTree
425: || leaf instanceof ClassTree
426: || leaf instanceof MemberSelectTree) {
427: return findIdentifierSpan(info, doc, tree);
428: }
429:
430: int start = (int) info.getTrees().getSourcePositions()
431: .getStartPosition(info.getCompilationUnit(), leaf);
432:
433: TokenHierarchy<?> th = info.getTokenHierarchy();
434: TokenSequence<JavaTokenId> ts = th.tokenSequence(JavaTokenId
435: .language());
436:
437: if (ts.move(start) == Integer.MAX_VALUE) {
438: return null;
439: }
440:
441: ts.moveNext();
442:
443: if (ts.offset() == start
444: && ts.token().id() == JavaTokenId.IDENTIFIER) {
445: return ts.token();
446: }
447:
448: return null;
449: }
450:
451: public static Token<JavaTokenId> getToken(
452: final CompilationInfo info, final Document doc,
453: final TreePath tree) {
454: @SuppressWarnings("unchecked")
455: final Token<JavaTokenId>[] result = new Token[1];
456:
457: doc.render(new Runnable() {
458: public void run() {
459: result[0] = createHighlightImpl(info, doc, tree);
460: }
461: });
462:
463: return result[0];
464: }
465:
466: private static final Set<String> keywords;
467:
468: static {
469: keywords = new HashSet<String>();
470:
471: keywords.add("true");
472: keywords.add("false");
473: keywords.add("null");
474: keywords.add("this");
475: keywords.add("super");
476: keywords.add("class");
477: }
478:
479: public static boolean isKeyword(Tree tree) {
480: if (tree.getKind() == Kind.IDENTIFIER) {
481: return keywords.contains(((IdentifierTree) tree).getName()
482: .toString());
483: }
484: if (tree.getKind() == Kind.MEMBER_SELECT) {
485: return keywords.contains(((MemberSelectTree) tree)
486: .getIdentifier().toString());
487: }
488:
489: return false;
490: }
491:
492: public static boolean isPrivateElement(Element el) {
493: if (el.getKind() == ElementKind.PARAMETER)
494: return true;
495:
496: if (el.getKind() == ElementKind.LOCAL_VARIABLE)
497: return true;
498:
499: if (el.getKind() == ElementKind.EXCEPTION_PARAMETER)
500: return true;
501:
502: return el.getModifiers().contains(Modifier.PRIVATE);
503: }
504:
505: }
|