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