1 /*
   2  * Copyright 2004-2006 Sun Microsystems, Inc.  All Rights Reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Sun designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Sun in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22  * CA 95054 USA or visit www.sun.com if you need additional information or
  23  * have any questions.
  24  */
  25 
  26 package com.sun.tools.apt.mirror.declaration;
  27 
  28 
  29 import java.util.HashMap;
  30 import java.util.Map;
  31 
  32 import com.sun.mirror.declaration.*;
  33 import com.sun.tools.apt.mirror.AptEnv;
  34 import com.sun.tools.javac.code.*;
  35 import com.sun.tools.javac.code.Symbol.*;
  36 import com.sun.tools.javac.util.Context;
  37 import com.sun.tools.javac.util.Name;
  38 import com.sun.tools.javac.main.JavaCompiler;
  39 
  40 /**
  41  * Utilities for constructing and caching declarations.
  42  */
  43 
  44 public class DeclarationMaker {
  45 
  46     private AptEnv env;
  47     private Context context;
  48     private JavaCompiler javacompiler;
  49     private static final Context.Key<DeclarationMaker> declarationMakerKey =
  50             new Context.Key<DeclarationMaker>();
  51 
  52     public static DeclarationMaker instance(Context context) {
  53         DeclarationMaker instance = context.get(declarationMakerKey);
  54         if (instance == null) {
  55             instance = new DeclarationMaker(context);
  56         }
  57         return instance;
  58     }
  59 
  60     private DeclarationMaker(Context context) {
  61         context.put(declarationMakerKey, this);
  62         env = AptEnv.instance(context);
  63         this.context = context;
  64         this.javacompiler = JavaCompiler.instance(context);
  65     }
  66 
  67 
  68 
  69     // Cache of package declarations
  70     private Map<PackageSymbol, PackageDeclaration> packageDecls =
  71             new HashMap<PackageSymbol, PackageDeclaration>();
  72 
  73     /**
  74      * Returns the package declaration for a package symbol.
  75      */
  76     public PackageDeclaration getPackageDeclaration(PackageSymbol p) {
  77         PackageDeclaration res = packageDecls.get(p);
  78         if (res == null) {
  79             res = new PackageDeclarationImpl(env, p);
  80             packageDecls.put(p, res);
  81         }
  82         return res;
  83     }
  84 
  85     /**
  86      * Returns the package declaration for the package with the given name.
  87      * Name is fully-qualified, or "" for the unnamed package.
  88      * Returns null if package declaration not found.
  89      */
  90     public PackageDeclaration getPackageDeclaration(String name) {
  91         PackageSymbol p = null;
  92         if (name.equals("") )
  93             p = env.symtab.unnamedPackage;
  94         else {
  95             if (!isJavaName(name))
  96                 return null;
  97             Symbol s = nameToSymbol(name, false);
  98             if (s instanceof PackageSymbol) {
  99                 p = (PackageSymbol) s;
 100                 if (!p.exists())
 101                     return null;
 102             } else
 103                 return null;
 104         }
 105         return getPackageDeclaration(p);
 106     }
 107 
 108     // Cache of type declarations
 109     private Map<ClassSymbol, TypeDeclaration> typeDecls =
 110             new HashMap<ClassSymbol, TypeDeclaration>();
 111 
 112     /**
 113      * Returns the type declaration for a class symbol.
 114      * Forces completion, and returns null on error.
 115      */
 116     public TypeDeclaration getTypeDeclaration(ClassSymbol c) {
 117         long flags = AptEnv.getFlags(c);        // forces symbol completion
 118         if (c.kind == Kinds.ERR) {
 119             return null;
 120         }
 121         TypeDeclaration res = typeDecls.get(c);
 122         if (res == null) {
 123             if ((flags & Flags.ANNOTATION) != 0) {
 124                 res = new AnnotationTypeDeclarationImpl(env, c);
 125             } else if ((flags & Flags.INTERFACE) != 0) {
 126                 res = new InterfaceDeclarationImpl(env, c);
 127             } else if ((flags & Flags.ENUM) != 0) {
 128                 res = new EnumDeclarationImpl(env, c);
 129             } else {
 130                 res = new ClassDeclarationImpl(env, c);
 131             }
 132             typeDecls.put(c, res);
 133         }
 134         return res;
 135     }
 136 
 137     /**
 138      * Returns the type declaration for the type with the given canonical name.
 139      * Returns null if type declaration not found.
 140      */
 141     public TypeDeclaration getTypeDeclaration(String name) {
 142         if (!isJavaName(name))
 143             return null;
 144         Symbol s = nameToSymbol(name, true);
 145         if (s instanceof ClassSymbol) {
 146             ClassSymbol c = (ClassSymbol) s;
 147             return getTypeDeclaration(c);
 148         } else
 149             return null;
 150     }
 151 
 152     /**
 153      * Returns a symbol given the type's or packages's canonical name,
 154      * or null if the name isn't found.
 155      */
 156     private Symbol nameToSymbol(String name, boolean classCache) {
 157         Symbol s = null;
 158         Name nameName = env.names.fromString(name);
 159         if (classCache)
 160             s = env.symtab.classes.get(nameName);
 161         else
 162             s = env.symtab.packages.get(nameName);
 163 
 164         if (s != null && s.exists())
 165             return s;
 166 
 167         s = javacompiler.resolveIdent(name);
 168         if (s.kind == Kinds.ERR  )
 169             return null;
 170 
 171         if (s.kind == Kinds.PCK)
 172             s.complete();
 173 
 174         return s;
 175     }
 176 
 177     // Cache of method and constructor declarations
 178     private Map<MethodSymbol, ExecutableDeclaration> executableDecls =
 179             new HashMap<MethodSymbol, ExecutableDeclaration>();
 180 
 181     /**
 182      * Returns the method or constructor declaration for a method symbol.
 183      */
 184     ExecutableDeclaration getExecutableDeclaration(MethodSymbol m) {
 185         ExecutableDeclaration res = executableDecls.get(m);
 186         if (res == null) {
 187             if (m.isConstructor()) {
 188                 res = new ConstructorDeclarationImpl(env, m);
 189             } else if (isAnnotationTypeElement(m)) {
 190                 res = new AnnotationTypeElementDeclarationImpl(env, m);
 191             } else {
 192                 res = new MethodDeclarationImpl(env, m);
 193             }
 194             executableDecls.put(m, res);
 195         }
 196         return res;
 197     }
 198 
 199     // Cache of field declarations
 200     private Map<VarSymbol, FieldDeclaration> fieldDecls =
 201             new HashMap<VarSymbol, FieldDeclaration>();
 202 
 203     /**
 204      * Returns the field declaration for a var symbol.
 205      */
 206     FieldDeclaration getFieldDeclaration(VarSymbol v) {
 207         FieldDeclaration res = fieldDecls.get(v);
 208         if (res == null) {
 209             if (hasFlag(v, Flags.ENUM)) {
 210                 res = new EnumConstantDeclarationImpl(env, v);
 211             } else {
 212                 res = new FieldDeclarationImpl(env, v);
 213             }
 214             fieldDecls.put(v, res);
 215         }
 216         return res;
 217     }
 218 
 219     /**
 220      * Returns a parameter declaration.
 221      */
 222     ParameterDeclaration getParameterDeclaration(VarSymbol v) {
 223         return new ParameterDeclarationImpl(env, v);
 224     }
 225 
 226     /**
 227      * Returns a type parameter declaration.
 228      */
 229     public TypeParameterDeclaration getTypeParameterDeclaration(TypeSymbol t) {
 230         return new TypeParameterDeclarationImpl(env, t);
 231     }
 232 
 233     /**
 234      * Returns an annotation.
 235      */
 236     AnnotationMirror getAnnotationMirror(Attribute.Compound a, Declaration decl) {
 237         return new AnnotationMirrorImpl(env, a, decl);
 238     }
 239 
 240 
 241     /**
 242      * Is a string a valid Java identifier?
 243      */
 244     public static boolean isJavaIdentifier(String id) {
 245         return javax.lang.model.SourceVersion.isIdentifier(id);
 246     }
 247 
 248     public static boolean isJavaName(String name) {
 249         for(String id: name.split("\\.")) {
 250             if (! isJavaIdentifier(id))
 251                 return false;
 252         }
 253         return true;
 254     }
 255 
 256     /**
 257      * Is a method an annotation type element?
 258      * It is if it's declared in an annotation type.
 259      */
 260     private static boolean isAnnotationTypeElement(MethodSymbol m) {
 261         return hasFlag(m.enclClass(), Flags.ANNOTATION);
 262     }
 263 
 264     /**
 265      * Does a symbol have a given flag?
 266      */
 267     private static boolean hasFlag(Symbol s, long flag) {
 268         return AptEnv.hasFlag(s, flag);
 269     }
 270 }