# HG changeset patch # Parent e1dbe19baa32de8787416f6956295cfaecb8b724 diff --git a/java.hints.declarative/src/org/netbeans/modules/java/hints/declarative/conditionapi/Context.java b/java.hints.declarative/src/org/netbeans/modules/java/hints/declarative/conditionapi/Context.java --- a/java.hints.declarative/src/org/netbeans/modules/java/hints/declarative/conditionapi/Context.java +++ b/java.hints.declarative/src/org/netbeans/modules/java/hints/declarative/conditionapi/Context.java @@ -40,6 +40,7 @@ package org.netbeans.modules.java.hints.declarative.conditionapi; import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.tree.Scope; import com.sun.source.tree.Tree.Kind; import com.sun.source.util.TreePath; import java.util.ArrayList; @@ -57,6 +58,7 @@ import javax.lang.model.element.ElementKind; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; +import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import org.netbeans.api.annotations.common.CheckForNull; @@ -292,6 +294,31 @@ return cut.getPackageName() != null ? cut.getPackageName().toString() : ""; } + + /**Checks whether the given Java element is available in the particular source + * code or not. + * + * The elementDescription format is as follows: + *
+ *
for type (class, enum, interface or annotation type)
+ *
the FQN of the type
+ *
for field or enum constant
+ *
the FQN of the enclosing type.field name
+ *
for method
+ *
the FQN of the enclosing type.method name(comma separated parameter types)
+ * The parameter types may include type parameters, but these are ignored. The last parameter type can use ellipsis (...) to denote vararg method.
+ *
for constructor
+ *
the FQN of the enclosing type.simple name of enclosing type(comma separated parameter types)
+ * See method format for more details on parameter types.
+ *
+ * + * @param elementDescription the description of the element that should be checked for existence + * @return true if and only the specified element exists while processing the current source + * @since nb74 + */ + public boolean isAvailable(@NonNull String description) { + return ctx.getInfo().getElementUtilities().findElement(description) != null; + } static final class APIAccessorImpl extends APIAccessor { diff --git a/java.hints.declarative/src/org/netbeans/modules/java/hints/declarative/conditionapi/DefaultRuleUtilities.java b/java.hints.declarative/src/org/netbeans/modules/java/hints/declarative/conditionapi/DefaultRuleUtilities.java --- a/java.hints.declarative/src/org/netbeans/modules/java/hints/declarative/conditionapi/DefaultRuleUtilities.java +++ b/java.hints.declarative/src/org/netbeans/modules/java/hints/declarative/conditionapi/DefaultRuleUtilities.java @@ -162,4 +162,29 @@ Pattern p = Pattern.compile(regexp.toString()); return p; } + + /**Checks whether the given Java element is available in the particular source + * code or not. + * + * The elementDescription format is as follows: + *
+ *
for type (class, enum, interface or annotation type)
+ *
the FQN of the type
+ *
for field or enum constant
+ *
the FQN of the enclosing type.field name
+ *
for method
+ *
the FQN of the enclosing type.method name(comma separated parameter types)
+ * The parameter types may include type parameters, but these are ignored. The last parameter type can use ellipsis (...) to denote vararg method.
+ *
for constructor
+ *
the FQN of the enclosing type.simple name of enclosing type(comma separated parameter types)
+ * See method format for more details on parameter types.
+ *
+ * + * @param elementDescription the description of the element that should be checked for existence + * @return true if and only the specified element exists while processing the current source + * @since nb74 + */ + public boolean isAvailable(@NonNull String elementDescription) { + return context.isAvailable(elementDescription); + } } diff --git a/java.hints.declarative/test/unit/src/org/netbeans/modules/java/hints/declarative/conditionapi/matchWithBind.hint b/java.hints.declarative/test/unit/src/org/netbeans/modules/java/hints/declarative/conditionapi/isAvailable.hint copy from java.hints.declarative/test/unit/src/org/netbeans/modules/java/hints/declarative/conditionapi/matchWithBind.hint copy to java.hints.declarative/test/unit/src/org/netbeans/modules/java/hints/declarative/conditionapi/isAvailable.hint --- a/java.hints.declarative/test/unit/src/org/netbeans/modules/java/hints/declarative/conditionapi/matchWithBind.hint +++ b/java.hints.declarative/test/unit/src/org/netbeans/modules/java/hints/declarative/conditionapi/isAvailable.hint @@ -1,4 +1,7 @@ - return $expr; -=> return $o != $t; :: matchesWithBind($expr, "$o == $t") -=> return $t == $o; :: matchesWithBind($expr, "$t != $o") +1 + 1 :: isAvailable("java.lang.ThreadLocal.initialValue()") +=> 2 ;; + +2 + 2 :: isAvailable("unknown.Class") +=> 2 +;; diff --git a/java.hints.declarative/test/unit/src/org/netbeans/modules/java/hints/declarative/conditionapi/matchWithBind.test b/java.hints.declarative/test/unit/src/org/netbeans/modules/java/hints/declarative/conditionapi/isAvailable.test copy from java.hints.declarative/test/unit/src/org/netbeans/modules/java/hints/declarative/conditionapi/matchWithBind.test copy to java.hints.declarative/test/unit/src/org/netbeans/modules/java/hints/declarative/conditionapi/isAvailable.test --- a/java.hints.declarative/test/unit/src/org/netbeans/modules/java/hints/declarative/conditionapi/matchWithBind.test +++ b/java.hints.declarative/test/unit/src/org/netbeans/modules/java/hints/declarative/conditionapi/isAvailable.test @@ -1,28 +1,21 @@ -%%TestCase bind-works-1 +%%TestCase is-available-1 package test; -public class Test { - private boolean t(int v1, int v2) { - return v1 == v2; +public class Test extends ThreadLocal { + private int t() { + return 1 + 1; } } %%=> package test; -public class Test { - private boolean t(int v1, int v2) { - return v1 != v2; +public class Test extends ThreadLocal { + private int t() { + return 2; } } -%%TestCase scope-works-1 +%%TestCase is-not-available-1 package test; public class Test { - private boolean t(int v1, int v2) { - return v1 != v2; + private int t() { + return 2 + 2; } } -%%=> -package test; -public class Test { - private boolean t(int v1, int v2) { - return v1 == v2; - } -} diff --git a/java.hints/src/org/netbeans/modules/java/hints/bugs/CollectionRemove.java b/java.hints/src/org/netbeans/modules/java/hints/bugs/CollectionRemove.java --- a/java.hints/src/org/netbeans/modules/java/hints/bugs/CollectionRemove.java +++ b/java.hints/src/org/netbeans/modules/java/hints/bugs/CollectionRemove.java @@ -264,55 +264,8 @@ return info.getTypeUtilities().isCastable(type1, type2); } - private static final Pattern SPLIT = Pattern.compile("(.+)\\.([^.]+)\\((.*)\\)"); - private static ExecutableElement resolveMethod(CompilationInfo info, String name) { - Matcher m = SPLIT.matcher(name); - - if (!m.matches()) { - throw new IllegalArgumentException(); - } - - String className = m.group(1); - String methodName = m.group(2); - String paramsSpec = m.group(3); - - TypeElement te = info.getElements().getTypeElement(className); - - if (te == null) { - return null; - } - - String[] paramList = paramsSpec.split(","); - List params = new LinkedList(); - - TypeElement topLevel = info.getTopLevelElements().get(0); - - for (String t : paramList) { - params.add(info.getTreeUtilities().parseType(t, topLevel)); - } - - for (ExecutableElement ee : ElementFilter.methodsIn(te.getEnclosedElements())) { - if (!methodName.equals(ee.getSimpleName().toString()) || ee.getParameters().size() != params.size()) { - continue; - } - - Iterator designed = params.iterator(); - boolean found = true; - - for (VariableElement param : ee.getParameters()) { - if (!info.getTypes().isSameType(info.getTypes().erasure(param.asType()), designed.next())) { - found = false; - break; - } - } - - if (found) { - return ee; - } - } - - return null; + return (ExecutableElement) info.getElementUtilities().findElement(name); } private static ExecutableElement findEnclosingMethod(CompilationInfo info, TreePath path) { diff --git a/java.source/apichanges.xml b/java.source/apichanges.xml --- a/java.source/apichanges.xml +++ b/java.source/apichanges.xml @@ -108,6 +108,19 @@ + + + Added ElementUtilities.findElement + + + + + + Added a new method to find an element based on its description: + ElementUtilities.findElement. + + + Added several methods to support Java 8 features. diff --git a/java.source/nbproject/project.properties b/java.source/nbproject/project.properties --- a/java.source/nbproject/project.properties +++ b/java.source/nbproject/project.properties @@ -46,7 +46,7 @@ javadoc.title=Java Source javadoc.arch=${basedir}/arch.xml javadoc.apichanges=${basedir}/apichanges.xml -spec.version.base=0.113 +spec.version.base=0.114 test.qa-functional.cp.extra=${refactoring.java.dir}/modules/ext/nb-javac-api.jar test.unit.run.cp.extra=${o.n.core.dir}/core/core.jar:\ ${o.n.core.dir}/lib/boot.jar:\ diff --git a/java.source/src/org/netbeans/api/java/source/ElementUtilities.java b/java.source/src/org/netbeans/api/java/source/ElementUtilities.java --- a/java.source/src/org/netbeans/api/java/source/ElementUtilities.java +++ b/java.source/src/org/netbeans/api/java/source/ElementUtilities.java @@ -46,6 +46,7 @@ import com.sun.javadoc.Doc; import com.sun.source.tree.CompilationUnitTree; import com.sun.source.tree.Scope; +import com.sun.source.tree.Tree; import com.sun.source.util.TreePath; import com.sun.source.util.Trees; import com.sun.tools.javac.api.JavacScope; @@ -94,11 +95,14 @@ import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeVariable; +import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; +import org.netbeans.api.annotations.common.CheckForNull; import org.netbeans.api.annotations.common.NonNull; import org.netbeans.api.annotations.common.NullAllowed; +import org.netbeans.modules.java.source.ElementHandleAccessor; import org.netbeans.modules.java.source.builder.ElementsService; import org.netbeans.modules.java.source.JavadocEnv; @@ -547,7 +551,107 @@ */ public boolean isEffectivelyFinal(VariableElement e) { return (((Symbol) e).flags() & (Flags.EFFECTIVELY_FINAL | Flags.FINAL)) != 0; - }; + } + + /**Looks up the given Java element. + * + * The elementDescription format is as follows: + *
+ *
for type (class, enum, interface or annotation type)
+ *
the FQN of the type
+ *
for field or enum constant
+ *
the FQN of the enclosing type.field name
+ *
for method
+ *
the FQN of the enclosing type.method name(comma separated parameter types)
+ * The parameter types may include type parameters, but these are ignored. The last parameter type can use ellipsis (...) to denote vararg method.
+ *
for constructor
+ *
the FQN of the enclosing type.simple name of enclosing type(comma separated parameter types)
+ * See method format for more details on parameter types.
+ *
+ * + * @param elementDescription the description of the element that should be checked for existence + * @return the found element, or null if not available + * @since 0.114 + */ + public @CheckForNull Element findElement(@NonNull String description) { + if (description.contains("(")) { + //method: + String methodFullName = description.substring(0, description.indexOf('(')); + String className = methodFullName.substring(0, methodFullName.lastIndexOf('.')); + TypeElement clazz = info.getElements().getTypeElement(className); + + if (clazz == null) return null; + + String methodSimpleName = methodFullName.substring(methodFullName.lastIndexOf('.') + 1); + boolean constructor = clazz.getSimpleName().contentEquals(methodSimpleName); + String parameters = description.substring(description.indexOf('(') + 1, description.lastIndexOf(')') + 1); + + int paramIndex = 0; + int lastParamStart = 0; + int angleDepth = 0; + //XXX: + List types = new ArrayList(); + + while (paramIndex < parameters.length()) { + switch (parameters.charAt(paramIndex)) { + case '<': angleDepth++; break; + case '>': angleDepth--; break; //TODO: check underflow + case ',': + if (angleDepth > 0) break; + case ')': + if (paramIndex > lastParamStart) { + String type = parameters.substring(lastParamStart, paramIndex).replace("...", "[]"); + //TODO: handle varargs + types.add(info.getTypes().erasure(info.getTreeUtilities().parseType(type, info.getTopLevelElements().get(0)/*XXX*/))); + lastParamStart = paramIndex + 1; + } + break; + } + + paramIndex++; + } + + OUTER: for (ExecutableElement ee : constructor ? ElementFilter.constructorsIn(clazz.getEnclosedElements()) : ElementFilter.methodsIn(clazz.getEnclosedElements())) { + if ((constructor || ee.getSimpleName().contentEquals(methodSimpleName)) && ee.getParameters().size() == types.size()) { + Iterator real = ((ExecutableType) info.getTypes().erasure(ee.asType())).getParameterTypes().iterator(); + Iterator expected = types.iterator(); + + while (real.hasNext() && expected.hasNext()) { + if (!info.getTypes().isSameType(real.next(), expected.next())) { + continue OUTER; + } + } + + assert real.hasNext() == expected.hasNext(); + + return ee; + } + } + } + + //field or class: + TypeElement el = info.getElements().getTypeElement(description); + + if (el != null) return el; + + int dot = description.lastIndexOf('.'); + + if (dot != (-1)) { + String simpleName = description.substring(dot + 1); + + el = info.getElements().getTypeElement(description.substring(0, dot)); + + if (el != null) { + for (VariableElement var : ElementFilter.fieldsIn(el.getEnclosedElements())) { + if (var.getSimpleName().contentEquals(simpleName)) { + return var; + } + } + } + } + + return null; + } // private implementation -------------------------------------------------- diff --git a/java.source/test/unit/src/org/netbeans/api/java/source/ElementsTest.java b/java.source/test/unit/src/org/netbeans/api/java/source/ElementUtilitiesTest.java copy from java.source/test/unit/src/org/netbeans/api/java/source/ElementsTest.java copy to java.source/test/unit/src/org/netbeans/api/java/source/ElementUtilitiesTest.java --- a/java.source/test/unit/src/org/netbeans/api/java/source/ElementsTest.java +++ b/java.source/test/unit/src/org/netbeans/api/java/source/ElementUtilitiesTest.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved. + * Copyright 1997-2013 Oracle and/or its affiliates. All rights reserved. * * Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners. @@ -26,23 +26,17 @@ * * Contributor(s): * - * Portions Copyrighted 2007 Sun Microsystems, Inc. + * Portions Copyrighted 2007-2013 Sun Microsystems, Inc. */ package org.netbeans.api.java.source; -import com.sun.source.tree.AnnotationTree; -import com.sun.source.tree.MethodTree; -import com.sun.source.util.TreePath; import java.io.File; import java.io.IOException; -import java.nio.charset.Charset; -import java.util.List; import javax.lang.model.element.Element; -import javax.lang.model.element.TypeElement; -import javax.lang.model.type.DeclaredType; -import javax.lang.model.type.TypeMirror; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.type.TypeKind; import org.netbeans.junit.NbTestCase; -import org.netbeans.spi.queries.FileEncodingQueryImplementation; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; @@ -50,23 +44,15 @@ * * @author Jan Lahoda */ -public class ElementsTest extends NbTestCase { +public class ElementUtilitiesTest extends NbTestCase { - public ElementsTest(String name) { + public ElementUtilitiesTest(String name) { super(name); } protected void setUp() throws Exception { clearWorkDir(); - SourceUtilsTestUtil.prepareTest(new String[0], new Object[] {new FileEncodingQueryImplementation() { - @Override - public Charset getEncoding(FileObject file) { - if (file.equals(testFO)) - return Charset.forName("UTF-8"); - else - return null; - } - }}); + SourceUtilsTestUtil.prepareTest(new String[0], new Object[0]); } private FileObject sourceRoot; @@ -91,63 +77,82 @@ prepareTest(); TestUtilities.copyStringToFile(FileUtil.toFile(testFO), - "public class Vecernicek {" + + "public class Test {" + "}"); JavaSource javaSource = JavaSource.forFileObject(testFO); javaSource.runUserActionTask(new Task() { public void run(CompilationController controller) throws IOException { controller.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED); - TypeElement typeElement = controller.getElements().getTypeElement("Vecernicek"); - assertNotNull(typeElement); - } - }, true); - - TestUtilities.copyStringToFile(FileUtil.toFile(testFO), - "public class Večerníček {" + - "}"); - testFO.refresh(); - javaSource = JavaSource.forFileObject(testFO); - javaSource.runUserActionTask(new Task() { - public void run(CompilationController controller) throws IOException { - controller.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED); - TypeElement typeElement = controller.getElements().getTypeElement("Večerníček"); - assertNotNull(typeElement); + + { + Element wait = controller.getElementUtilities().findElement("java.lang.Object.wait(long)"); + assertNotNull(wait); + assertEquals(ElementKind.METHOD, wait.getKind()); + ExecutableElement waitMethod = (ExecutableElement) wait; + assertEquals("wait", waitMethod.getSimpleName().toString()); + assertEquals(1, waitMethod.getParameters().size()); + assertEquals(TypeKind.LONG, waitMethod.getParameters().get(0).asType().getKind()); + assertEquals(controller.getElements().getTypeElement("java.lang.Object"), waitMethod.getEnclosingElement()); + } + + { + Element arrayListInit = controller.getElementUtilities().findElement("java.util.ArrayList.ArrayList(java.util.Collection)"); + assertNotNull(arrayListInit); + assertEquals(ElementKind.CONSTRUCTOR, arrayListInit.getKind()); + ExecutableElement arrayListInitMethod = (ExecutableElement) arrayListInit; + assertEquals("", arrayListInitMethod.getSimpleName().toString()); + assertEquals(1, arrayListInitMethod.getParameters().size()); + assertEquals("java.util.Collection", controller.getTypes().erasure(arrayListInitMethod.getParameters().get(0).asType()).toString()); + assertEquals(controller.getElements().getTypeElement("java.util.ArrayList"), arrayListInitMethod.getEnclosingElement()); + } + + { + Element arrayListAdd = controller.getElementUtilities().findElement("java.util.ArrayList.add(int, Object)"); + assertNotNull(arrayListAdd); + assertEquals(ElementKind.METHOD, arrayListAdd.getKind()); + ExecutableElement arrayListAddMethod = (ExecutableElement) arrayListAdd; + assertEquals("add", arrayListAddMethod.getSimpleName().toString()); + assertEquals(2, arrayListAddMethod.getParameters().size()); + assertEquals(TypeKind.INT, arrayListAddMethod.getParameters().get(0).asType().getKind()); + assertEquals("java.lang.Object", controller.getTypes().erasure(arrayListAddMethod.getParameters().get(1).asType()).toString()); + assertEquals(controller.getElements().getTypeElement("java.util.ArrayList"), arrayListAddMethod.getEnclosingElement()); + } + + { + Element arraysAsList = controller.getElementUtilities().findElement("java.util.Arrays.asList(Object...)"); + assertNotNull(arraysAsList); + assertEquals(ElementKind.METHOD, arraysAsList.getKind()); + ExecutableElement arraysAsListMethod = (ExecutableElement) arraysAsList; + assertEquals("asList", arraysAsListMethod.getSimpleName().toString()); + assertEquals(1, arraysAsListMethod.getParameters().size()); + assertEquals(TypeKind.ARRAY, arraysAsListMethod.getParameters().get(0).asType().getKind()); + assertEquals(controller.getElements().getTypeElement("java.util.Arrays"), arraysAsListMethod.getEnclosingElement()); + } + + { + Element hashCode = controller.getElementUtilities().findElement("java.lang.Object.hashCode()"); + assertNotNull(hashCode); + assertEquals(ElementKind.METHOD, hashCode.getKind()); + ExecutableElement hashCodeMethod = (ExecutableElement) hashCode; + assertEquals("hashCode", hashCodeMethod.getSimpleName().toString()); + assertEquals(0, hashCodeMethod.getParameters().size()); + assertEquals(controller.getElements().getTypeElement("java.lang.Object"), hashCodeMethod.getEnclosingElement()); + } + + { + Element bigIntegerOne = controller.getElementUtilities().findElement("java.math.BigInteger.ONE"); + assertNotNull(bigIntegerOne); + assertEquals(ElementKind.FIELD, bigIntegerOne.getKind()); + assertEquals("ONE", bigIntegerOne.getSimpleName().toString()); + assertEquals(controller.getElements().getTypeElement("java.math.BigInteger"), bigIntegerOne.getEnclosingElement()); + } + + { + Element bigInteger = controller.getElementUtilities().findElement("java.math.BigInteger"); + assertEquals(controller.getElements().getTypeElement("java.math.BigInteger"), bigInteger); + } } }, true); } - public void test175535() throws Exception { - prepareTest(); - FileObject otherFO = FileUtil.createData(sourceRoot, "test/A.java"); - TestUtilities.copyStringToFile(FileUtil.toFile(otherFO), - "package test;" + - "public class A implements Runnable {" + - " @Override" + - " public void run() {}" + - "}"); - TestUtilities.copyStringToFile(FileUtil.toFile(testFO), - "public class Test {" + - "}"); - SourceUtilsTestUtil.compileRecursively(sourceRoot); - JavaSource javaSource = JavaSource.forFileObject(testFO); - javaSource.runUserActionTask(new Task() { - public void run(CompilationController controller) throws IOException { - controller.toPhase(JavaSource.Phase.RESOLVED); - TypeElement typeElement = controller.getElements().getTypeElement("test.A"); - assertNotNull(typeElement); - Element el = typeElement.getEnclosedElements().get(1); - assertNotNull(el); - assertEquals("run", el.getSimpleName().toString()); - TreePath mpath = controller.getTrees().getPath(el); - MethodTree mtree = (MethodTree) mpath.getLeaf(); - assertNotNull(mtree); - List annotations = mtree.getModifiers().getAnnotations(); - TypeMirror annotation = controller.getTrees().getTypeMirror(new TreePath(mpath, annotations.get(0))); - assertNotNull(annotation); - Element e = controller.getTrees().getElement(new TreePath(mpath, annotations.get(0))); - assertNotNull(e); - assertEquals(((DeclaredType)annotation).asElement(), e); - } - }, true); - } }