file/src/org/netbeans/modules/jackpot30/file/conditionapi/Context.java
author Jan Lahoda <jlahoda@netbeans.org>
Thu, 21 Jul 2011 22:51:52 +0200
branchrelease701
changeset 648 cb630b7113bf
parent 529 9ce061230c50
permissions -rw-r--r--
bitbucket-20: adding conditions for enclosing class/package (transplanting from trunk).
     1 /*
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
     5  *
     6  * The contents of this file are subject to the terms of either the GNU
     7  * General Public License Version 2 only ("GPL") or the Common
     8  * Development and Distribution License("CDDL") (collectively, the
     9  * "License"). You may not use this file except in compliance with the
    10  * License. You can obtain a copy of the License at
    11  * http://www.netbeans.org/cddl-gplv2.html
    12  * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    13  * specific language governing permissions and limitations under the
    14  * License.  When distributing the software, include this License Header
    15  * Notice in each file and include the License file at
    16  * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
    17  * particular file as subject to the "Classpath" exception as provided
    18  * by Sun in the GPL Version 2 section of the License file that
    19  * accompanied this code. If applicable, add the following below the
    20  * License Header, with the fields enclosed by brackets [] replaced by
    21  * your own identifying information:
    22  * "Portions Copyrighted [year] [name of copyright owner]"
    23  *
    24  * If you wish your version of this file to be governed by only the CDDL
    25  * or only the GPL Version 2, indicate your decision by adding
    26  * "[Contributor] elects to include this software in this distribution
    27  * under the [CDDL or GPL Version 2] license." If you do not indicate a
    28  * single choice of license, a recipient has the option to distribute
    29  * your version of this file under either the CDDL, the GPL Version 2 or
    30  * to extend the choice of license to its licensees as provided above.
    31  * However, if you add GPL Version 2 code and therefore, elected the GPL
    32  * Version 2 license, then the option applies only if the new code is
    33  * made subject to such option by the copyright holder.
    34  *
    35  * Contributor(s):
    36  *
    37  * Portions Copyrighted 2009 Sun Microsystems, Inc.
    38  */
    39 
    40 package org.netbeans.modules.jackpot30.file.conditionapi;
    41 
    42 import com.sun.source.tree.CompilationUnitTree;
    43 import com.sun.source.tree.Tree.Kind;
    44 import com.sun.source.util.TreePath;
    45 import java.util.ArrayList;
    46 import java.util.Collection;
    47 import java.util.Collections;
    48 import java.util.EnumSet;
    49 import java.util.HashMap;
    50 import java.util.LinkedList;
    51 import java.util.List;
    52 import java.util.Map;
    53 import java.util.Set;
    54 import java.util.concurrent.atomic.AtomicInteger;
    55 import javax.lang.model.SourceVersion;
    56 import javax.lang.model.element.Element;
    57 import javax.lang.model.element.ElementKind;
    58 import javax.lang.model.element.Modifier;
    59 import javax.lang.model.element.TypeElement;
    60 import javax.lang.model.type.TypeKind;
    61 import javax.lang.model.type.TypeMirror;
    62 import org.netbeans.api.annotations.common.CheckForNull;
    63 import org.netbeans.api.annotations.common.NonNull;
    64 import org.netbeans.api.java.queries.SourceLevelQuery;
    65 import org.netbeans.api.java.source.TreeUtilities;
    66 import org.netbeans.modules.jackpot30.file.APIAccessor;
    67 import org.netbeans.modules.jackpot30.spi.Hacks;
    68 import org.netbeans.modules.jackpot30.spi.HintContext;
    69 
    70 /**
    71  *
    72  * @author lahvac
    73  */
    74 public class Context {
    75 
    76             final HintContext ctx;
    77             final List<Map<String, TreePath>> variables = new LinkedList<Map<String, TreePath>>();
    78             final List<Map<String, Collection<? extends TreePath>>> multiVariables = new LinkedList<Map<String, Collection<? extends TreePath>>>();
    79             final List<Map<String, String>> variableNames = new LinkedList<Map<String, String>>();
    80     private final AtomicInteger auxiliaryVariableCounter = new AtomicInteger();
    81 
    82     //XXX: should not be public:
    83     public Context(HintContext ctx) {
    84         this.ctx = ctx;
    85         this.variables.add(Collections.unmodifiableMap(ctx.getVariables()));
    86         this.multiVariables.add(Collections.unmodifiableMap(ctx.getMultiVariables()));
    87         this.variableNames.add(Collections.unmodifiableMap(ctx.getVariableNames()));
    88     }
    89 
    90     public @NonNull SourceVersion sourceVersion() {
    91         String sourceLevel = SourceLevelQuery.getSourceLevel(ctx.getInfo().getFileObject());
    92 
    93         if (sourceLevel == null) {
    94             return SourceVersion.latest(); //TODO
    95         }
    96 
    97         String[] splited = sourceLevel.split("\\.");
    98         String   spec    = splited[1];
    99 
   100         return SourceVersion.valueOf("RELEASE_"+  spec);//!!!
   101     }
   102 
   103     public @NonNull Set<Modifier> modifiers(@NonNull Variable variable) {
   104         final Element e = ctx.getInfo().getTrees().getElement(getSingleVariable(variable));
   105 
   106         if (e == null) {
   107             return Collections.unmodifiableSet(EnumSet.noneOf(Modifier.class));
   108         }
   109 
   110         return Collections.unmodifiableSet(e.getModifiers());
   111     }
   112 
   113     public @CheckForNull ElementKind elementKind(@NonNull Variable variable) {
   114         final Element e = ctx.getInfo().getTrees().getElement(getSingleVariable(variable));
   115 
   116         if (e == null) {
   117             return null;
   118         }
   119 
   120         return e.getKind();
   121     }
   122 
   123     public @CheckForNull TypeKind typeKind(@NonNull Variable variable) {
   124         final TypeMirror tm = ctx.getInfo().getTrees().getTypeMirror(getSingleVariable(variable));
   125 
   126         if (tm == null) {
   127             return null;
   128         }
   129 
   130         return tm.getKind();
   131     }
   132 
   133     public @CheckForNull String name(@NonNull Variable variable) {
   134         final Element e = ctx.getInfo().getTrees().getElement(getSingleVariable(variable));
   135 
   136         if (e == null) {
   137             return null;
   138         }
   139 
   140         return e.getSimpleName().toString();
   141     }
   142 
   143     public @CheckForNull Variable parent(@NonNull Variable variable) {
   144         TreePath tp = getSingleVariable(variable);
   145 
   146         if (tp.getParentPath() == null) {
   147             return null;
   148         }
   149 
   150         return enterAuxiliaryVariable(tp.getParentPath());
   151     }
   152 
   153     private Variable enterAuxiliaryVariable(TreePath path) {
   154         String output = "*" + auxiliaryVariableCounter.getAndIncrement();
   155 
   156         variables.get(0).put(output, path);
   157 
   158         return new Variable(output);
   159     }
   160     
   161     public @NonNull Variable variableForName(@NonNull String variableName) {
   162         Variable result = new Variable(variableName);
   163 
   164         if (getSingleVariable(result) == null) {
   165             throw new IllegalStateException("Unknown variable");
   166         }
   167         
   168         return result;
   169     }
   170 
   171     public void createRenamed(@NonNull Variable from, @NonNull Variable to, @NonNull String newName) {
   172         //TODO: check (the variable should not exist)
   173         variableNames.get(0).put(to.variableName, newName);
   174         TreePath origVariablePath = getSingleVariable(from);
   175         TreePath newVariablePath = new TreePath(origVariablePath.getParentPath(), Hacks.createRenameTree(origVariablePath.getLeaf(), newName));
   176         variables.get(0).put(to.variableName, newVariablePath);
   177     }
   178 
   179     public boolean isNullLiteral(@NonNull Variable var) {
   180         TreePath varPath = getSingleVariable(var);
   181 
   182         return varPath.getLeaf().getKind() == Kind.NULL_LITERAL;
   183     }
   184 
   185     public @NonNull Iterable<? extends Variable> getIndexedVariables(@NonNull Variable multiVariable) {
   186         Iterable<? extends TreePath> paths = getMultiVariable(multiVariable);
   187 
   188         if (paths == null) {
   189             throw new IllegalArgumentException("TODO: explanation");
   190         }
   191 
   192         Collection<Variable> result = new LinkedList<Variable>();
   193         int index = 0;
   194 
   195         for (TreePath tp : paths) {
   196             result.add(new Variable(multiVariable.variableName, index++));
   197         }
   198 
   199         return result;
   200     }
   201 
   202     public void enterScope() {
   203         variables.add(0, new HashMap<String, TreePath>());
   204         multiVariables.add(0, new HashMap<String, Collection<? extends TreePath>>());
   205         variableNames.add(0, new HashMap<String, String>());
   206     }
   207 
   208     public void leaveScope() {
   209         variables.remove(0);
   210         multiVariables.remove(0);
   211         variableNames.remove(0);
   212     }
   213 
   214     Iterable<? extends TreePath> getVariable(Variable v) {
   215         if (isMultistatementWildcard(v.variableName) && v.index == (-1)) {
   216             return getMultiVariable(v);
   217         } else {
   218             return Collections.singletonList(getSingleVariable(v));
   219         }
   220     }
   221 
   222     //XXX: copied from jackpot30.impl.Utilities:
   223     private static boolean isMultistatementWildcard(/*@NonNull */CharSequence name) {
   224         return name.charAt(name.length() - 1) == '$';
   225     }
   226 
   227     //TODO: check if correct variable is provided:
   228     TreePath getSingleVariable(Variable v) {
   229         if (v.index == (-1)) {
   230             for (Map<String, TreePath> map : variables) {
   231                 if (map.containsKey(v.variableName)) {
   232                     return map.get(v.variableName);
   233                 }
   234             }
   235             
   236             return null;
   237         } else {
   238             return new ArrayList<TreePath>(getMultiVariable(v)).get(v.index);
   239         }
   240     }
   241 
   242     private Collection<? extends TreePath> getMultiVariable(Variable v) {
   243         for (Map<String, Collection<? extends TreePath>> multi : multiVariables) {
   244             if (multi.containsKey(v.variableName)) {
   245                 return multi.get(v.variableName);
   246             }
   247         }
   248 
   249         return null;
   250     }
   251 
   252     static {
   253         APIAccessor.IMPL = new APIAccessorImpl();
   254     }
   255 
   256     /**Returns canonical names of classes that enclose the {@link Variable}.
   257      * If the given {@link Variable} represents a class, its canonical name is also listed.
   258      * The names are given from the innermost class to the outermost class.
   259      *
   260      * @return the canonical names of the enclosing classes
   261      */
   262     public @NonNull Iterable<? extends String> enclosingClasses(Variable forVariable) {
   263         List<String> result = new ArrayList<String>();
   264         TreePath path = getSingleVariable(forVariable);
   265 
   266         while (path != null) {
   267             TreePath current = path;
   268 
   269             path = path.getParentPath();
   270             
   271             if (!TreeUtilities.CLASS_TREE_KINDS.contains(current.getLeaf().getKind())) continue;
   272 
   273             Element e = ctx.getInfo().getTrees().getElement(current);
   274 
   275             if (e == null) continue;
   276 
   277             if (e.getKind().isClass() || e.getKind().isInterface()) {
   278                 result.add(((TypeElement) e).getQualifiedName().toString());
   279             }
   280         }
   281 
   282         return result;
   283     }
   284 
   285     /**Returns name of package in which the current file is located. Default package
   286      * is represented by an empty string.
   287      *
   288      * @return the name of the enclosing package
   289      */
   290     public @NonNull String enclosingPackage() {
   291         CompilationUnitTree cut = ctx.getInfo().getCompilationUnit();
   292 
   293         return cut.getPackageName() != null ? cut.getPackageName().toString() : "";
   294     }
   295 
   296     static final class APIAccessorImpl extends APIAccessor {
   297 
   298         @Override
   299         public TreePath getSingleVariable(Context ctx, Variable var) {
   300             return ctx.getSingleVariable(var);
   301         }
   302 
   303         @Override
   304         public HintContext getHintContext(Context ctx) {
   305             return ctx.ctx;
   306         }
   307 
   308         @Override
   309         public Map<String, TreePath> getVariables(Context ctx) {
   310             Map<String, TreePath> result = new HashMap<String, TreePath>();
   311 
   312             for (Map<String, TreePath> m : reverse(ctx.variables)) {
   313                 result.putAll(m);
   314             }
   315 
   316             return result;
   317         }
   318 
   319         @Override
   320         public Map<String, Collection<? extends TreePath>> getMultiVariables(Context ctx) {
   321             Map<String, Collection<? extends TreePath>> result = new HashMap<String, Collection<? extends TreePath>>();
   322 
   323             for (Map<String, Collection<? extends TreePath>> m : reverse(ctx.multiVariables)) {
   324                 result.putAll(m);
   325             }
   326 
   327             return result;
   328         }
   329 
   330         @Override
   331         public Map<String, String> getVariableNames(Context ctx) {
   332             Map<String, String> result = new HashMap<String, String>();
   333 
   334             for (Map<String, String> m : reverse(ctx.variableNames)) {
   335                 result.putAll(m);
   336             }
   337 
   338             return result;
   339         }
   340 
   341         private <T> List<T> reverse(List<T> original) {
   342             List<T> result = new LinkedList<T>();
   343 
   344             for (T t : original) {
   345                 result.add(0, t);
   346             }
   347 
   348             return result;
   349         }
   350 
   351         @Override
   352         public Variable enterAuxiliaryVariable(Context ctx, TreePath source) {
   353             return ctx.enterAuxiliaryVariable(source);
   354         }
   355 
   356     }
   357 }