001    /*
002     * Copyright 2005-2008 Sun Microsystems, Inc.  All Rights Reserved.
003     * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004     *
005     * This code is free software; you can redistribute it and/or modify it
006     * under the terms of the GNU General Public License version 2 only, as
007     * published by the Free Software Foundation.  Sun designates this
008     * particular file as subject to the "Classpath" exception as provided
009     * by Sun in the LICENSE file that accompanied this code.
010     *
011     * This code is distributed in the hope that it will be useful, but WITHOUT
012     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013     * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014     * version 2 for more details (a copy is included in the LICENSE file that
015     * accompanied this code).
016     *
017     * You should have received a copy of the GNU General Public License version
018     * 2 along with this work; if not, write to the Free Software Foundation,
019     * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020     *
021     * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022     * CA 95054 USA or visit www.sun.com if you need additional information or
023     * have any questions.
024     */
025    
026    package com.sun.tools.javac.model;
027    
028    import java.lang.annotation.Annotation;
029    import java.lang.annotation.Inherited;
030    import java.util.Map;
031    import javax.lang.model.SourceVersion;
032    import javax.lang.model.element.*;
033    import javax.lang.model.type.DeclaredType;
034    import javax.lang.model.util.Elements;
035    import javax.tools.JavaFileObject;
036    import com.sun.tools.javac.code.*;
037    import com.sun.tools.javac.code.Symbol.*;
038    import com.sun.tools.javac.code.TypeTags;
039    import com.sun.tools.javac.comp.AttrContext;
040    import com.sun.tools.javac.comp.Enter;
041    import com.sun.tools.javac.comp.Env;
042    import com.sun.tools.javac.main.JavaCompiler;
043    import com.sun.tools.javac.processing.PrintingProcessor;
044    import com.sun.tools.javac.tree.JCTree;
045    import com.sun.tools.javac.tree.JCTree.*;
046    import com.sun.tools.javac.tree.TreeInfo;
047    import com.sun.tools.javac.tree.TreeScanner;
048    import com.sun.tools.javac.util.*;
049    import com.sun.tools.javac.util.Name;
050    
051    import static javax.lang.model.util.ElementFilter.methodsIn;
052    
053    /**
054     * Utility methods for operating on program elements.
055     *
056     * <p><b>This is NOT part of any API supported by Sun Microsystems.
057     * If you write code that depends on this, you do so at your own
058     * risk.  This code and its internal interfaces are subject to change
059     * or deletion without notice.</b></p>
060     */
061    public class JavacElements implements Elements {
062    
063        private JavaCompiler javaCompiler;
064        private Symtab syms;
065        private Names names;
066        private Types types;
067        private Enter enter;
068    
069        private static final Context.Key<JavacElements> KEY =
070                new Context.Key<JavacElements>();
071    
072        public static JavacElements instance(Context context) {
073            JavacElements instance = context.get(KEY);
074            if (instance == null) {
075                instance = new JavacElements(context);
076                context.put(KEY, instance);
077            }
078            return instance;
079        }
080    
081        /**
082         * Public for use only by JavacProcessingEnvironment
083         */
084        // TODO JavacElements constructor should be protected
085        public JavacElements(Context context) {
086            setContext(context);
087        }
088    
089        /**
090         * Use a new context.  May be called from outside to update
091         * internal state for a new annotation-processing round.
092         * This instance is *not* then registered with the new context.
093         */
094        public void setContext(Context context) {
095            javaCompiler = JavaCompiler.instance(context);
096            syms = Symtab.instance(context);
097            names = Names.instance(context);
098            types = Types.instance(context);
099            enter = Enter.instance(context);
100        }
101    
102    
103        /**
104         * An internal-use utility that creates a reified annotation.
105         */
106        public static <A extends Annotation> A getAnnotation(Symbol annotated,
107                                                             Class<A> annoType) {
108            if (!annoType.isAnnotation())
109                throw new IllegalArgumentException("Not an annotation type: "
110                                                   + annoType);
111            String name = annoType.getName();
112            for (Attribute.Compound anno : annotated.getAnnotationMirrors())
113                if (name.equals(anno.type.tsym.flatName().toString()))
114                    return AnnotationProxyMaker.generateAnnotation(anno, annoType);
115            return null;
116        }
117    
118        /**
119         * An internal-use utility that creates a reified annotation.
120         * This overloaded version take annotation inheritance into account.
121         */
122        public static <A extends Annotation> A getAnnotation(ClassSymbol annotated,
123                                                             Class<A> annoType) {
124            boolean inherited = annoType.isAnnotationPresent(Inherited.class);
125            A result = null;
126            while (annotated.name != annotated.name.table.names.java_lang_Object) {
127                result = getAnnotation((Symbol)annotated, annoType);
128                if (result != null || !inherited)
129                    break;
130                Type sup = annotated.getSuperclass();
131                if (sup.tag != TypeTags.CLASS || sup.isErroneous())
132                    break;
133                annotated = (ClassSymbol) sup.tsym;
134            }
135            return result;
136        }
137    
138    
139        public PackageSymbol getPackageElement(CharSequence name) {
140            String strName = name.toString();
141            if (strName.equals(""))
142                return syms.unnamedPackage;
143            return SourceVersion.isName(strName)
144                ? nameToSymbol(strName, PackageSymbol.class)
145                : null;
146        }
147    
148        public ClassSymbol getTypeElement(CharSequence name) {
149            String strName = name.toString();
150            return SourceVersion.isName(strName)
151                ? nameToSymbol(strName, ClassSymbol.class)
152                : null;
153        }
154    
155        /**
156         * Returns a symbol given the type's or packages's canonical name,
157         * or null if the name isn't found.
158         */
159        private <S extends Symbol> S nameToSymbol(String nameStr, Class<S> clazz) {
160            Name name = names.fromString(nameStr);
161            // First check cache.
162            Symbol sym = (clazz == ClassSymbol.class)
163                        ? syms.classes.get(name)
164                        : syms.packages.get(name);
165    
166            try {
167                if (sym == null)
168                    sym = javaCompiler.resolveIdent(nameStr);
169    
170                sym.complete();
171    
172                return (sym.kind != Kinds.ERR &&
173                        sym.exists() &&
174                        clazz.isInstance(sym) &&
175                        name.equals(sym.getQualifiedName()))
176                    ? clazz.cast(sym)
177                    : null;
178            } catch (CompletionFailure e) {
179                return null;
180            }
181        }
182    
183        public JavacSourcePosition getSourcePosition(Element e) {
184            Pair<JCTree, JCCompilationUnit> treeTop = getTreeAndTopLevel(e);
185            if (treeTop == null)
186                return null;
187            JCTree tree = treeTop.fst;
188            JCCompilationUnit toplevel = treeTop.snd;
189            JavaFileObject sourcefile = toplevel.sourcefile;
190            if (sourcefile == null)
191                return null;
192            return new JavacSourcePosition(sourcefile, tree.pos, toplevel.lineMap);
193        }
194    
195        public JavacSourcePosition getSourcePosition(Element e, AnnotationMirror a) {
196            Pair<JCTree, JCCompilationUnit> treeTop = getTreeAndTopLevel(e);
197            if (treeTop == null)
198                return null;
199            JCTree tree = treeTop.fst;
200            JCCompilationUnit toplevel = treeTop.snd;
201            JavaFileObject sourcefile = toplevel.sourcefile;
202            if (sourcefile == null)
203                return null;
204    
205            JCTree annoTree = matchAnnoToTree(a, e, tree);
206            if (annoTree == null)
207                return null;
208            return new JavacSourcePosition(sourcefile, annoTree.pos,
209                                           toplevel.lineMap);
210        }
211    
212        public JavacSourcePosition getSourcePosition(Element e, AnnotationMirror a,
213                                                AnnotationValue v) {
214            // TODO: better accuracy in getSourcePosition(... AnnotationValue)
215            return getSourcePosition(e, a);
216        }
217    
218        /**
219         * Returns the tree for an annotation given the annotated element
220         * and the element's own tree.  Returns null if the tree cannot be found.
221         */
222        private JCTree matchAnnoToTree(AnnotationMirror findme,
223                                       Element e, JCTree tree) {
224            Symbol sym = cast(Symbol.class, e);
225            class Vis extends JCTree.Visitor {
226                List<JCAnnotation> result = null;
227                public void visitTopLevel(JCCompilationUnit tree) {
228                    result = tree.packageAnnotations;
229                }
230                public void visitClassDef(JCClassDecl tree) {
231                    result = tree.mods.annotations;
232                }
233                public void visitMethodDef(JCMethodDecl tree) {
234                    result = tree.mods.annotations;
235                }
236                public void visitVarDef(JCVariableDecl tree) {
237                    result = tree.mods.annotations;
238                }
239            }
240            Vis vis = new Vis();
241            tree.accept(vis);
242            if (vis.result == null)
243                return null;
244            return matchAnnoToTree(cast(Attribute.Compound.class, findme),
245                                   sym.getAnnotationMirrors(),
246                                   vis.result);
247        }
248    
249        /**
250         * Returns the tree for an annotation given a list of annotations
251         * in which to search (recursively) and their corresponding trees.
252         * Returns null if the tree cannot be found.
253         */
254        private JCTree matchAnnoToTree(Attribute.Compound findme,
255                                       List<Attribute.Compound> annos,
256                                       List<JCAnnotation> trees) {
257            for (Attribute.Compound anno : annos) {
258                for (JCAnnotation tree : trees) {
259                    JCTree match = matchAnnoToTree(findme, anno, tree);
260                    if (match != null)
261                        return match;
262                }
263            }
264            return null;
265        }
266    
267        /**
268         * Returns the tree for an annotation given an Attribute to
269         * search (recursively) and its corresponding tree.
270         * Returns null if the tree cannot be found.
271         */
272        private JCTree matchAnnoToTree(final Attribute.Compound findme,
273                                       final Attribute attr,
274                                       final JCTree tree) {
275            if (attr == findme)
276                return (tree.type.tsym == findme.type.tsym) ? tree : null;
277    
278            class Vis implements Attribute.Visitor {
279                JCTree result = null;
280                public void visitConstant(Attribute.Constant value) {
281                }
282                public void visitClass(Attribute.Class clazz) {
283                }
284                public void visitCompound(Attribute.Compound anno) {
285                    for (Pair<MethodSymbol, Attribute> pair : anno.values) {
286                        JCExpression expr = scanForAssign(pair.fst, tree);
287                        if (expr != null) {
288                            JCTree match = matchAnnoToTree(findme, pair.snd, expr);
289                            if (match != null) {
290                                result = match;
291                                return;
292                            }
293                        }
294                    }
295                }
296                public void visitArray(Attribute.Array array) {
297                    if (tree.getTag() == JCTree.NEWARRAY &&
298                            types.elemtype(array.type).tsym == findme.type.tsym) {
299                        List<JCExpression> elems = ((JCNewArray) tree).elems;
300                        for (Attribute value : array.values) {
301                            if (value == findme) {
302                                result = elems.head;
303                                return;
304                            }
305                            elems = elems.tail;
306                        }
307                    }
308                }
309                public void visitEnum(Attribute.Enum e) {
310                }
311                public void visitError(Attribute.Error e) {
312                }
313            }
314            Vis vis = new Vis();
315            attr.accept(vis);
316            return vis.result;
317        }
318    
319        /**
320         * Scans for a JCAssign node with a LHS matching a given
321         * symbol, and returns its RHS.  Does not scan nested JCAnnotations.
322         */
323        private JCExpression scanForAssign(final MethodSymbol sym,
324                                           final JCTree tree) {
325            class TS extends TreeScanner {
326                JCExpression result = null;
327                public void scan(JCTree t) {
328                    if (t != null && result == null)
329                        t.accept(this);
330                }
331                public void visitAnnotation(JCAnnotation t) {
332                    if (t == tree)
333                        scan(t.args);
334                }
335                public void visitAssign(JCAssign t) {
336                    if (t.lhs.getTag() == JCTree.IDENT) {
337                        JCIdent ident = (JCIdent) t.lhs;
338                        if (ident.sym == sym)
339                            result = t.rhs;
340                    }
341                }
342            }
343            TS scanner = new TS();
344            tree.accept(scanner);
345            return scanner.result;
346        }
347    
348        /**
349         * Returns the tree node corresponding to this element, or null
350         * if none can be found.
351         */
352        public JCTree getTree(Element e) {
353            Pair<JCTree, ?> treeTop = getTreeAndTopLevel(e);
354            return (treeTop != null) ? treeTop.fst : null;
355        }
356    
357        public String getDocComment(Element e) {
358            // Our doc comment is contained in a map in our toplevel,
359            // indexed by our tree.  Find our enter environment, which gives
360            // us our toplevel.  It also gives us a tree that contains our
361            // tree:  walk it to find our tree.  This is painful.
362            Pair<JCTree, JCCompilationUnit> treeTop = getTreeAndTopLevel(e);
363            if (treeTop == null)
364                return null;
365            JCTree tree = treeTop.fst;
366            JCCompilationUnit toplevel = treeTop.snd;
367            if (toplevel.docComments == null)
368                return null;
369            return toplevel.docComments.get(tree);
370        }
371    
372        public PackageElement getPackageOf(Element e) {
373            return cast(Symbol.class, e).packge();
374        }
375    
376        public boolean isDeprecated(Element e) {
377            Symbol sym = cast(Symbol.class, e);
378            return (sym.flags() & Flags.DEPRECATED) != 0;
379        }
380    
381        public Name getBinaryName(TypeElement type) {
382            return cast(TypeSymbol.class, type).flatName();
383        }
384    
385        public Map<MethodSymbol, Attribute> getElementValuesWithDefaults(
386                                                            AnnotationMirror a) {
387            Attribute.Compound anno = cast(Attribute.Compound.class, a);
388            DeclaredType annotype = a.getAnnotationType();
389            Map<MethodSymbol, Attribute> valmap = anno.getElementValues();
390    
391            for (ExecutableElement ex :
392                     methodsIn(annotype.asElement().getEnclosedElements())) {
393                MethodSymbol meth = (MethodSymbol) ex;
394                Attribute defaultValue = meth.getDefaultValue();
395                if (defaultValue != null && !valmap.containsKey(meth)) {
396                    valmap.put(meth, defaultValue);
397                }
398            }
399            return valmap;
400        }
401    
402        /**
403         * {@inheritDoc}
404         */
405        public FilteredMemberList getAllMembers(TypeElement element) {
406            Symbol sym = cast(Symbol.class, element);
407            Scope scope = sym.members().dupUnshared();
408            List<Type> closure = types.closure(sym.asType());
409            for (Type t : closure)
410                addMembers(scope, t);
411            return new FilteredMemberList(scope);
412        }
413        // where
414            private void addMembers(Scope scope, Type type) {
415                members:
416                for (Scope.Entry e = type.asElement().members().elems; e != null; e = e.sibling) {
417                    Scope.Entry overrider = scope.lookup(e.sym.getSimpleName());
418                    while (overrider.scope != null) {
419                        if (overrider.sym.kind == e.sym.kind
420                            && (overrider.sym.flags() & Flags.SYNTHETIC) == 0)
421                        {
422                            if (overrider.sym.getKind() == ElementKind.METHOD
423                            && overrides((ExecutableElement)overrider.sym, (ExecutableElement)e.sym, (TypeElement)type.asElement())) {
424                                continue members;
425                            }
426                        }
427                        overrider = overrider.next();
428                    }
429                    boolean derived = e.sym.getEnclosingElement() != scope.owner;
430                    ElementKind kind = e.sym.getKind();
431                    boolean initializer = kind == ElementKind.CONSTRUCTOR
432                        || kind == ElementKind.INSTANCE_INIT
433                        || kind == ElementKind.STATIC_INIT;
434                    if (!derived || (!initializer && e.sym.isInheritedIn(scope.owner, types)))
435                        scope.enter(e.sym);
436                }
437            }
438    
439        /**
440         * Returns all annotations of an element, whether
441         * inherited or directly present.
442         *
443         * @param e  the element being examined
444         * @return all annotations of the element
445         */
446        public List<Attribute.Compound> getAllAnnotationMirrors(Element e) {
447            Symbol sym = cast(Symbol.class, e);
448            List<Attribute.Compound> annos = sym.getAnnotationMirrors();
449            while (sym.getKind() == ElementKind.CLASS) {
450                Type sup = ((ClassSymbol) sym).getSuperclass();
451                if (sup.tag != TypeTags.CLASS || sup.isErroneous() ||
452                        sup.tsym == syms.objectType.tsym) {
453                    break;
454                }
455                sym = sup.tsym;
456                List<Attribute.Compound> oldAnnos = annos;
457                for (Attribute.Compound anno : sym.getAnnotationMirrors()) {
458                    if (isInherited(anno.type) &&
459                            !containsAnnoOfType(oldAnnos, anno.type)) {
460                        annos = annos.prepend(anno);
461                    }
462                }
463            }
464            return annos;
465        }
466    
467        /**
468         * Tests whether an annotation type is @Inherited.
469         */
470        private boolean isInherited(Type annotype) {
471            for (Attribute.Compound anno : annotype.tsym.getAnnotationMirrors()) {
472                if (anno.type.tsym == syms.inheritedType.tsym)
473                    return true;
474            }
475            return false;
476        }
477    
478        /**
479         * Tests whether a list of annotations contains an annotation
480         * of a given type.
481         */
482        private static boolean containsAnnoOfType(List<Attribute.Compound> annos,
483                                                  Type type) {
484            for (Attribute.Compound anno : annos) {
485                if (anno.type.tsym == type.tsym)
486                    return true;
487            }
488            return false;
489        }
490    
491        public boolean hides(Element hiderEl, Element hideeEl) {
492            Symbol hider = cast(Symbol.class, hiderEl);
493            Symbol hidee = cast(Symbol.class, hideeEl);
494    
495            // Fields only hide fields; methods only methods; types only types.
496            // Names must match.  Nothing hides itself (just try it).
497            if (hider == hidee ||
498                    hider.kind != hidee.kind ||
499                    hider.name != hidee.name) {
500                return false;
501            }
502    
503            // Only static methods can hide other methods.
504            // Methods only hide methods with matching signatures.
505            if (hider.kind == Kinds.MTH) {
506                if (!hider.isStatic() ||
507                            !types.isSubSignature(hider.type, hidee.type)) {
508                    return false;
509                }
510            }
511    
512            // Hider must be in a subclass of hidee's class.
513            // Note that if M1 hides M2, and M2 hides M3, and M3 is accessible
514            // in M1's class, then M1 and M2 both hide M3.
515            ClassSymbol hiderClass = hider.owner.enclClass();
516            ClassSymbol hideeClass = hidee.owner.enclClass();
517            if (hiderClass == null || hideeClass == null ||
518                    !hiderClass.isSubClass(hideeClass, types)) {
519                return false;
520            }
521    
522            // Hidee must be accessible in hider's class.
523            // The method isInheritedIn is poorly named:  it checks only access.
524            return hidee.isInheritedIn(hiderClass, types);
525        }
526    
527        public boolean overrides(ExecutableElement riderEl,
528                                 ExecutableElement rideeEl, TypeElement typeEl) {
529            MethodSymbol rider = cast(MethodSymbol.class, riderEl);
530            MethodSymbol ridee = cast(MethodSymbol.class, rideeEl);
531            ClassSymbol origin = cast(ClassSymbol.class, typeEl);
532    
533            return rider.name == ridee.name &&
534    
535                   // not reflexive as per JLS
536                   rider != ridee &&
537    
538                   // we don't care if ridee is static, though that wouldn't
539                   // compile
540                   !rider.isStatic() &&
541    
542                   // Symbol.overrides assumes the following
543                   ridee.isMemberOf(origin, types) &&
544    
545                   // check access and signatures; don't check return types
546                   rider.overrides(ridee, origin, types, false);
547        }
548    
549        public String getConstantExpression(Object value) {
550            return Constants.format(value);
551        }
552    
553        /**
554         * Print a representation of the elements to the given writer in
555         * the specified order.  The main purpose of this method is for
556         * diagnostics.  The exact format of the output is <em>not</em>
557         * specified and is subject to change.
558         *
559         * @param w the writer to print the output to
560         * @param elements the elements to print
561         */
562        public void printElements(java.io.Writer w, Element... elements) {
563            for (Element element : elements)
564                (new PrintingProcessor.PrintingElementVisitor(w, this)).visit(element).flush();
565        }
566    
567        public Name getName(CharSequence cs) {
568            return names.fromString(cs.toString());
569        }
570    
571        /**
572         * Returns the tree node and compilation unit corresponding to this
573         * element, or null if they can't be found.
574         */
575        private Pair<JCTree, JCCompilationUnit> getTreeAndTopLevel(Element e) {
576            Symbol sym = cast(Symbol.class, e);
577            Env<AttrContext> enterEnv = getEnterEnv(sym);
578            if (enterEnv == null)
579                return null;
580            JCTree tree = TreeInfo.declarationFor(sym, enterEnv.tree);
581            if (tree == null || enterEnv.toplevel == null)
582                return null;
583            return new Pair<JCTree,JCCompilationUnit>(tree, enterEnv.toplevel);
584        }
585    
586        /**
587         * Returns the best approximation for the tree node and compilation unit
588         * corresponding to the given element, annotation and value.
589         * If the element is null, null is returned.
590         * If the annotation is null or cannot be found, the tree node and
591         * compilation unit for the element is returned.
592         * If the annotation value is null or cannot be found, the tree node and
593         * compilation unit for the annotation is returned.
594         */
595        public Pair<JCTree, JCCompilationUnit> getTreeAndTopLevel(
596                          Element e, AnnotationMirror a, AnnotationValue v) {
597            if (e == null)
598                return null;
599    
600            Pair<JCTree, JCCompilationUnit> elemTreeTop = getTreeAndTopLevel(e);
601            if (elemTreeTop == null)
602                return null;
603    
604            if (a == null)
605                return elemTreeTop;
606    
607            JCTree annoTree = matchAnnoToTree(a, e, elemTreeTop.fst);
608            if (annoTree == null)
609                return elemTreeTop;
610    
611            // 6388543: if v != null, we should search within annoTree to find
612            // the tree matching v. For now, we ignore v and return the tree of
613            // the annotation.
614            return new Pair<JCTree, JCCompilationUnit>(annoTree, elemTreeTop.snd);
615        }
616    
617        /**
618         * Returns a symbol's enter environment, or null if it has none.
619         */
620        private Env<AttrContext> getEnterEnv(Symbol sym) {
621            // Get enclosing class of sym, or sym itself if it is a class
622            // or package.
623            TypeSymbol ts = (sym.kind != Kinds.PCK)
624                            ? sym.enclClass()
625                            : (PackageSymbol) sym;
626            return (ts != null)
627                    ? enter.getEnv(ts)
628                    : null;
629        }
630    
631        /**
632         * Returns an object cast to the specified type.
633         * @throws NullPointerException if the object is {@code null}
634         * @throws IllegalArgumentException if the object is of the wrong type
635         */
636        private static <T> T cast(Class<T> clazz, Object o) {
637            if (! clazz.isInstance(o))
638                throw new IllegalArgumentException(o.toString());
639            return clazz.cast(o);
640        }
641    }