diff --git a/java.source.queries/build.xml b/java.source.queries/build.xml new file mode 100644 --- /dev/null +++ b/java.source.queries/build.xml @@ -0,0 +1,5 @@ + + + Builds, tests, and runs the project org.netbeans.modules.java.source.queries + + diff --git a/java.source.queries/manifest.mf b/java.source.queries/manifest.mf new file mode 100644 --- /dev/null +++ b/java.source.queries/manifest.mf @@ -0,0 +1,5 @@ +Manifest-Version: 1.0 +OpenIDE-Module: org.netbeans.modules.java.source.queries +OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/java/source/queries/Bundle.properties +OpenIDE-Module-Specification-Version: 1.0 + diff --git a/java.source.queries/nbproject/project.properties b/java.source.queries/nbproject/project.properties new file mode 100644 --- /dev/null +++ b/java.source.queries/nbproject/project.properties @@ -0,0 +1,3 @@ +is.autoload=true +javac.source=1.6 +javac.compilerargs=-Xlint -Xlint:-serial diff --git a/java.source.queries/nbproject/project.xml b/java.source.queries/nbproject/project.xml new file mode 100644 --- /dev/null +++ b/java.source.queries/nbproject/project.xml @@ -0,0 +1,64 @@ + + + org.netbeans.modules.apisupport.project + + + org.netbeans.modules.java.source.queries + + + org.netbeans.api.annotations.common + + + + 1 + 1.10 + + + + org.openide.filesystems + + + + 7.48 + + + + org.openide.util + + + + 8.16 + + + + org.openide.util.lookup + + + + 8.9 + + + + + + unit + + org.netbeans.libs.junit4 + + + + org.netbeans.modules.nbjunit + + + + + + + org.netbeans.modules.form + org.netbeans.modules.java.source.queriesimpl + org.netbeans.modules.java.source.queries.api + org.netbeans.modules.java.source.queries.spi + + + + diff --git a/java.source.queries/src/org/netbeans/modules/java/source/queries/APIAccessor.java b/java.source.queries/src/org/netbeans/modules/java/source/queries/APIAccessor.java new file mode 100644 --- /dev/null +++ b/java.source.queries/src/org/netbeans/modules/java/source/queries/APIAccessor.java @@ -0,0 +1,77 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 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. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ +package org.netbeans.modules.java.source.queries; + +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.modules.java.source.queries.api.Queries; +import org.netbeans.modules.java.source.queries.spi.ModelOperations; +import org.openide.util.Exceptions; + +/** + * + * @author Tomas Zezula + */ +public abstract class APIAccessor { + private static volatile APIAccessor instance; + + public static void setInstance(@NonNull final APIAccessor _instance) { + assert _instance != null; + instance = _instance; + } + + public static synchronized APIAccessor getInstance() { + if (instance == null) { + try { + Class.forName( + Queries.class.getName(), + true, + APIAccessor.class.getClassLoader()); + } catch (ClassNotFoundException e) { + Exceptions.printStackTrace(e); + } + } + return instance; + } + + public abstract void attach(@NonNull Queries q, @NonNull ModelOperations ops); + public abstract void detach(@NonNull Queries q); +} diff --git a/java.source.queries/src/org/netbeans/modules/java/source/queries/Bundle.properties b/java.source.queries/src/org/netbeans/modules/java/source/queries/Bundle.properties new file mode 100644 --- /dev/null +++ b/java.source.queries/src/org/netbeans/modules/java/source/queries/Bundle.properties @@ -0,0 +1,1 @@ +OpenIDE-Module-Name=Java Source Queries diff --git a/java.source.queries/src/org/netbeans/modules/java/source/queries/SPIAccessor.java b/java.source.queries/src/org/netbeans/modules/java/source/queries/SPIAccessor.java new file mode 100644 --- /dev/null +++ b/java.source.queries/src/org/netbeans/modules/java/source/queries/SPIAccessor.java @@ -0,0 +1,83 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 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. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ +package org.netbeans.modules.java.source.queries; + +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.modules.java.source.queries.api.Function; +import org.netbeans.modules.java.source.queries.api.Queries; +import org.netbeans.modules.java.source.queries.spi.QueriesController; +import org.openide.util.Exceptions; + +/** + * + * @author Tomas Zezula + */ +public abstract class SPIAccessor { + + private static volatile SPIAccessor instance; + + public static synchronized SPIAccessor getInstance() { + if (instance == null) { + try { + Class.forName( + QueriesController.Context.class.getName(), + true, + SPIAccessor.class.getClassLoader()); + assert instance != null; + } catch (ClassNotFoundException e) { + Exceptions.printStackTrace(e); + } + } + return instance; + } + + public static void setInstance(@NonNull final SPIAccessor _instance) { + assert _instance != null; + instance = _instance; + } + + public abstract

QueriesController.Context createContext( + final Function fnc, + final P param + ); + +} diff --git a/java.source.queries/src/org/netbeans/modules/java/source/queries/api/Function.java b/java.source.queries/src/org/netbeans/modules/java/source/queries/api/Function.java new file mode 100644 --- /dev/null +++ b/java.source.queries/src/org/netbeans/modules/java/source/queries/api/Function.java @@ -0,0 +1,56 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 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. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ +package org.netbeans.modules.java.source.queries.api; + +/** + * Query or update function + * todo: Move to some generic module and change to Function@lt;P,R,E extends Exception> + * @author Tomas Zezula + */ +public interface Function { + /** + * Performs the function on given parameter returning a result + * @param param the function parameter + * @return result + */ + R apply (P param) throws QueryException; +} diff --git a/java.source.queries/src/org/netbeans/modules/java/source/queries/api/Queries.java b/java.source.queries/src/org/netbeans/modules/java/source/queries/api/Queries.java new file mode 100644 --- /dev/null +++ b/java.source.queries/src/org/netbeans/modules/java/source/queries/api/Queries.java @@ -0,0 +1,271 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 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. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ +package org.netbeans.modules.java.source.queries.api; + +import java.util.Collection; +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.queries.APIAccessor; +import org.netbeans.modules.java.source.queries.SPIAccessor; +import org.netbeans.modules.java.source.queries.spi.ModelOperations; +import org.netbeans.modules.java.source.queries.spi.QueriesController; +import org.openide.filesystems.FileObject; +import org.openide.util.Lookup; +import org.openide.util.Parameters; + +/** + * Support for queries + * @author Tomas Zezula + */ +public class Queries { + + static { + APIAccessor.setInstance(new Accessor()); + } + + static final QueriesController ctl = Lookup.getDefault().lookup(QueriesController.class); + private final FileObject forFile; + ModelOperations impl; + + Queries(@NonNull final FileObject forFile) { + assert forFile != null; + this.forFile = forFile; + } + + /** + * Returns a file for which the {@link Queries} was created. + * @return a file + */ + @NonNull + public final FileObject getFile() { + return forFile; + } + + /** + * Returns the fully qualified names of top level classes in the + * compilation unit represented by {@link Queries#getFile()} + * @return a collection of fully qualified names + * @throws QueryException in case of failure + */ + @NonNull + public final Collection getTopLevelClasses() throws QueryException { + return impl.getTopLevelClasses(getFile()); + } + + /** + * Checks if a class represented by fully qualified name is a + * Java Bean. (has no param public constructor). + * @param cls the fully qualified name of the class to be checked + * @return true if the class is a Java Bean + * @throws QueryException in case of failure + */ + public final boolean isJavaBean(final @NonNull String cls) throws QueryException { + Parameters.notNull("cls", cls); //NOI18N + return impl.isJavaBean(cls); + } + + /** + * Checks if a class represented given fully qualified name is + * available on class path. + * @param cls the fully qualified name of the class + * @return true if the class is available + * @throws QueryException in case of failure + */ + public final boolean isAvailable(final @NonNull String cls) throws QueryException { + Parameters.notNull("cls", cls); //NOI18N + return impl.isAvailable(cls); + } + + /** + * Returns a binary name as specified by JLS §13.1 for given + * fully qualified name + * @param cls the fully qualified name of the class + * @return the binary name or null if cls cannot be resolved + * @throws QueryException in case of failure + */ + @CheckForNull + public final String getClassBinaryName( + @NonNull final String cls) throws QueryException { + Parameters.notNull("cls", cls); //NOI18N + return impl.getClassBinaryName(cls); + } + + /** + * Returns the fully qualified name of the super class for + * for given class. + * @param cls the class fully qualified name + * @return the fully qualified name of the super class or null + * when the class has no super type, eg j.l.Object, error type + * @throws QueryException in case of failure + */ + @CheckForNull + public final String getSuperClass(final @NonNull String cls) throws QueryException { + Parameters.notNull("cls", cls); //NOI18N + return impl.getSuperClass(cls); + } + + /** + * Returns the fully qualified names of the implemented interfaces for + * for given class. + * @param cls the class fully qualified name + * @return the fully qualified names of the implemented interfaces + * @throws QueryException in case of failure + */ + @NonNull + public final Collection getInterfaces(final @NonNull String cls) throws QueryException { + Parameters.notNull("cls", cls); //NOI18N + return impl.getInterfaces(cls); + } + + /** + * Returns names of the methods complying to given types. + * @param cls the fully qualified name of the class to look up methods in + * @param useRawTypes if true the erasure is done before comparison + * @param returnType the return type of the method specified by the name of the + * primitive type or fully qualified name of the declared type (null represents + * any type) + * @param parameterTypes the types of the method parameters specified by + * name of the primitive type or fully qualified name of the declared type + * (null represent any type) + * @return a collection of method names + * @throws QueryException in case of failure + */ + @NonNull + public final Collection getMethodNames( + @NonNull final String cls, + boolean useRawTypes, + @NullAllowed final String returnType, + @NullAllowed final String... parameterTypes) throws QueryException { + Parameters.notNull("cls", cls); //NOI18N + return impl.getMethodNames(cls, useRawTypes, returnType, parameterTypes); + } + + /** + * Returns names of the fields complying to type. + * @param cls the fully qualified name of the class to look up methods in + * @param useRawTypes if true the erasure is done before comparison + * @param type the type of the field specified by the name of the + * primitive type or fully qualified name of the declared type + * (null represents any type) + * @return a collection of field names + * @throws QueryException in case of failure + */ + @NonNull + public final Collection getFieldNames( + @NonNull final String cls, + final boolean useRawTypes, + @NullAllowed final String type) throws QueryException { + Parameters.notNull("cls", cls); //NOI18N + return impl.getFieldNames(cls, useRawTypes, type); + } + + /** + * Returns annotations annotating given method. + * @param cls the fully qualified name of the class to look up methods in + * @param methodName name of the method + * @param useRawTypes if true the erasure is done before comparison + * @param returnType the return type of the method specified by the name of the + * primitive type or fully qualified name of the declared type + * @param parameterTypes the types of the method parameters specified by + * name of the primitive type or fully qualified name of the declared type + * @return collection of fully qualified names + * @throws QueryException in case of failure + */ + @NonNull + public final Collection getMethodAnnotations( + @NonNull final String cls, + @NonNull final String methodName, + final boolean useRawTypes, + @NonNull final String returnType, + @NonNull final String... parameterTypes) throws QueryException { + Parameters.notNull("cls", cls); //NOI18N + Parameters.notNull("methodName", methodName); //NOI18N + Parameters.notNull("returnType", returnType); //NOI18N + Parameters.notNull("parameterTypes", parameterTypes); //NOI18N + return impl.getMethodAnnotations(cls, methodName, useRawTypes, returnType, parameterTypes); + } + + /** + * Performs the query + * @param forFile file implying the scope to perform query on + * @param queryFnc the query function + * @return the result of query function + * @throws QueryException in case of exception + */ + @CheckForNull + public static T query ( + @NonNull final FileObject forFile, + @NonNull final Function queryFnc) throws QueryException { + Parameters.notNull("forFile", forFile); //NOI18N + Parameters.notNull("queryFnc", queryFnc); //NOI18N + if (ctl == null) { + throw new IllegalStateException("No QueriesController found in the Lookup"); //NOI18N + } + final Queries q = new Queries(forFile); + final QueriesController.Context ctx = SPIAccessor.getInstance().createContext(queryFnc, q); + return ctl.runQuery(ctx); + } + + + private void attach(@NonNull final ModelOperations impl) { + assert impl != null; + this.impl = impl; + } + + private void detach() { + this.impl = null; + } + + private static class Accessor extends APIAccessor { + + @Override + public void attach(Queries q, ModelOperations ops) { + q.attach(ops); + } + + @Override + public void detach(Queries q) { + q.detach(); + } + } +} diff --git a/java.source.queries/src/org/netbeans/modules/java/source/queries/api/QueryException.java b/java.source.queries/src/org/netbeans/modules/java/source/queries/api/QueryException.java new file mode 100644 --- /dev/null +++ b/java.source.queries/src/org/netbeans/modules/java/source/queries/api/QueryException.java @@ -0,0 +1,64 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 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. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ +package org.netbeans.modules.java.source.queries.api; + +/** + * Exception thrown by queries + * @author Tomas Zezula + */ +public class QueryException extends Exception { + + public QueryException() { + } + + public QueryException(final String message) { + super(message); + } + + public QueryException(final Throwable rootCause) { + super(rootCause); + } + + public QueryException(final String message, final Throwable rootCause) { + super (message, rootCause); + } +} diff --git a/java.source.queries/src/org/netbeans/modules/java/source/queries/api/Updates.java b/java.source.queries/src/org/netbeans/modules/java/source/queries/api/Updates.java new file mode 100644 --- /dev/null +++ b/java.source.queries/src/org/netbeans/modules/java/source/queries/api/Updates.java @@ -0,0 +1,182 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 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. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ +package org.netbeans.modules.java.source.queries.api; + +import java.util.Collection; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.modules.java.source.queries.SPIAccessor; +import org.netbeans.modules.java.source.queries.spi.QueriesController; +import org.openide.filesystems.FileObject; +import org.openide.util.Parameters; + +/** + * Support for modifications + * @author Tomas Zezula + */ +public final class Updates extends Queries { + + Updates(@NonNull final FileObject forFile) { + super(forFile); + } + + /** + * Modifies class interfaces. + * @param cls the fully qualified name of class to be changed + * @param toAdd the {@link Collection} of fully qualified names of + * interfaces to be added + * @param toRemove the {@link Collection} of fully qualified names of + * interfaces to be removed + * @throws QueryException in case of failure + */ + public void modifyInterfaces( + @NonNull String cls, + @NonNull Collection toAdd, + @NonNull Collection toRemove) throws QueryException { + Parameters.notNull("cls", cls); //NOI18N + Parameters.notNull("toAdd", toAdd); //NOI18N + Parameters.notNull("toRemove", toRemove); //NOI18N + impl.modifyInterfaces(cls,toAdd,toRemove); + } + + /** + * Modifies method annotations. + * @param cls the fully qualified name of the class to look up methods in + * @param methodName name of the method + * @param useRawTypes if true the erasure is done before comparison + * @param returnType the return type of the method specified by the name of the + * primitive type or fully qualified name of the declared type + * @param parameterTypes the types of the method parameters specified by + * name of the primitive type or fully qualified name of the declared type + * @param toAdd the {@link Collection} of fully qualified names of + * annotations to be added + * @param toRemove the {@link Collection} of fully qualified names of + * annotations to be removed + * @throws QueryException in case of failure + */ + public void modifyMethodAnnotations( + @NonNull final String cls, + @NonNull final String methodName, + final boolean useRawTypes, + @NonNull final String returnType, + @NonNull final String[] parameterTypes, + @NonNull final Collection toAdd, + @NonNull final Collection toRemove) throws QueryException { + Parameters.notNull("cls", cls); //NOI18N + Parameters.notNull("methodName", methodName); //NOI18N + Parameters.notNull("returnType", returnType); //NOI18N + Parameters.notNull("parameterTypes", parameterTypes); //NOI18N + Parameters.notNull("toAdd", toAdd); //NOI18N + Parameters.notNull("toRemove", toRemove); //NOI18N + impl.modifyMethodAnnotations( + cls, + methodName, + useRawTypes, + returnType, + parameterTypes, + toAdd, + toRemove); + } + + /** + * Sets class super class. + * @param cls the fully qualified name of class to be changed + * @param superCls the fully qualified name of super class + * @throws QueryException in case of failure + */ + public void setSuperClass( + @NonNull String cls, + @NonNull String superCls) throws QueryException { + Parameters.notNull("cls", cls); //NOI18N + Parameters.notNull("superCls", superCls); //NOI18N + impl.setSuperClass(cls, superCls); + } + + /** + * Adds required imports into the java source if needed. + * @param requiredFQNs the fully qualified names of imports to be added + * @throws QueryException in case of failure + */ + public void fixImports( + @NonNull final Collection requiredFQNs) throws QueryException { + Parameters.notNull("requiredFQNs", requiredFQNs); //NOI18N + impl.fixImports(getFile(),requiredFQNs); + } + + /** + * Renames a field in a class. + * @param cls the fully qualified name of the class owning the field + * @param oldName the old field name + * @param newName the new field name + * @throws QueryException in case of failure + */ + public void renameField( + @NonNull final String cls, + @NonNull final String oldName, + @NonNull final String newName) throws QueryException { + Parameters.notNull("oldName", oldName); //NOI18N + Parameters.notNull("newName", newName); //NOI18N + impl.renameField(cls,oldName,newName); + } + + + /** + * Performs the update. + * @param forFile file implying the scope to perform query on + * @param updateFnc the query function. If the function returns true + * the changes are applied otherwise the changes are rolled back. + * @throws QueryException in case of exception + */ + public static void update( + @NonNull final FileObject forFile, + @NonNull final Function updateFnc) throws QueryException { + Parameters.notNull("forFile", forFile); //NOI18N + Parameters.notNull("updateFnc", forFile); //NOI18N + if (ctl == null) { + throw new IllegalStateException("No QueriesController found in the Lookup"); //NOI18N + } + final Updates u = new Updates(forFile); + final QueriesController.Context ctx = + SPIAccessor.getInstance().createContext(updateFnc, u); + ctl.runUpdate(ctx); + } + +} diff --git a/java.source.queries/src/org/netbeans/modules/java/source/queries/spi/ModelOperations.java b/java.source.queries/src/org/netbeans/modules/java/source/queries/spi/ModelOperations.java new file mode 100644 --- /dev/null +++ b/java.source.queries/src/org/netbeans/modules/java/source/queries/spi/ModelOperations.java @@ -0,0 +1,250 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 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. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ +package org.netbeans.modules.java.source.queries.spi; + +import java.util.Collection; +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.queries.api.QueryException; +import org.netbeans.modules.java.source.queries.api.Queries; +import org.netbeans.modules.java.source.queries.api.Updates; +import org.openide.filesystems.FileObject; + +/** + * SPI for {@link Queries} and {@link Updates}. + * The instance is created by implementor of {@link QueriesController} + * and passed to {@link QueriesController.Context} + * @author Tomas Zezula + */ +public interface ModelOperations { + + /** + * Returns the fully qualified names of top level classes in the + * compilation unit represented by given file + * @param file to get the top level classes + * @return a collection of fully qualified names + * @throws QueryException in case of failure + */ + @NonNull + Collection getTopLevelClasses(@NonNull FileObject file) throws QueryException; + + /** + * Checks if a class represented by fully qualified name is a + * Java Bean (has no param public constructor). + * @param cls the fully qualified name of the class to be checked + * @return true if the class is a Java Bean + * @throws QueryException in case of failure + */ + boolean isJavaBean(@NonNull String cls) throws QueryException; + + /** + * Checks if a class represented given fully qualified name is + * available on class path. + * @param cls the fully qualified name of the class + * @return true if the class is available + * @throws QueryException in case of failure + */ + boolean isAvailable(@NonNull String cls) throws QueryException; + + /** + * Returns the fully qualified name of the super class for + * for given class. + * @param cls the class fully qualified name + * @return the fully qualified name of the super class or null + * when the class has no super type, eg j.l.Object, error type + * @throws QueryException in case of failure + */ + @CheckForNull + String getSuperClass(@NonNull String cls) throws QueryException; + + /** + * Returns the fully qualified names of the implemented interfaces for + * for given class. + * @param cls the class fully qualified name + * @return the fully qualified names of the implemented interfaces + * @throws QueryException in case of failure + */ + @NonNull + public Collection getInterfaces(@NonNull String cls) throws QueryException; + + /** + * Returns names of the methods complying to given types. + * @param cls the fully qualified name of the class to look up methods in + * @param useRawTypes if true the erasure is done before comparison + * @param returnType the return type of the method specified by the name of the + * primitive type or fully qualified name of the declared type (null represents + * any type) + * @param parameterTypes the types of the method parameters specified by + * name of the primitive type or fully qualified name of the declared type + * (null represent any type) + * @return a collection of method names + * @throws QueryException in case of failure + */ + @NonNull + Collection getMethodNames( + @NonNull String cls, + boolean useRawTypes, + @NullAllowed String returnType, + @NullAllowed String... parameterTypes) throws QueryException; + + + /** + * Returns names of the fields complying to type. + * @param cls the fully qualified name of the class to look up methods in + * @param useRawTypes if true the erasure is done before comparison + * @param type the type of the field specified by the name of the + * primitive type or fully qualified name of the declared type + * (null represents any type) + * @return a collection of field names + * @throws QueryException in case of failure + */ + @NonNull + Collection getFieldNames( + @NonNull String cls, + boolean useRawTypes, + @NullAllowed String type) throws QueryException; + + + /** + * Returns annotations annotating given method. + * @param cls the fully qualified name of the class to look up methods in + * @param methodName name of the method + * @param useRawTypes if true the erasure is done before comparison + * @param returnType the return type of the method specified by the name of the + * primitive type or fully qualified name of the declared type + * @param parameterTypes the types of the method parameters specified by + * name of the primitive type or fully qualified name of the declared type + * @return collection of fully qualified names + * @throws QueryException in case of failure + */ + Collection getMethodAnnotations( + @NonNull String cls, + @NonNull String methodName, + boolean useRawTypes, + @NonNull String returnType, + @NonNull String... parameterTypes) throws QueryException; + + /** + * Returns a binary name as specified by JLS §13.1 for given + * fully qualified name. + * @param cls the fully qualified name of the class + * @return the binary name or null if cls cannot be resolved + * @throws QueryException in case of failure + */ + @CheckForNull + String getClassBinaryName(@NonNull String cls) throws QueryException; + + + /** + * Adds required imports into the java source if needed. + * @param file a file to add imports into + * @param requiredFQNs the fully qualified names of imports to be added + * @throws QueryException in case of failure + */ + void fixImports( + @NonNull FileObject file, + Collection requiredFQNs) throws QueryException; + + + /** + * Modifies class interfaces. + * @param cls the fully qualified name of class to be changed + * @param toAdd the {@link Collection} of fully qualified names of + * interfaces to be added + * @param toRemove the {@link Collection} of fully qualified names of + * interfaces to be removed + * @throws QueryException in case of failure + */ + void modifyInterfaces( + @NonNull String cls, + @NonNull Collection toAdd, + @NonNull Collection toRemove) throws QueryException; + + + /** + * Modifies method annotations. + * @param cls the fully qualified name of the class to look up methods in + * @param methodName name of the method + * @param useRawTypes if true the erasure is done before comparison + * @param returnType the return type of the method specified by the name of the + * primitive type or fully qualified name of the declared type + * @param parameterTypes the types of the method parameters specified by + * name of the primitive type or fully qualified name of the declared type + * @param toAdd the {@link Collection} of fully qualified names of + * annotations to be added + * @param toRemove the {@link Collection} of fully qualified names of + * annotations to be removed + * @throws QueryException in case of failure + */ + void modifyMethodAnnotations( + @NonNull String cls, + @NonNull String methodName, + boolean useRawTypes, + @NonNull String returnType, + @NonNull String[] parameterTypes, + @NonNull Collection toAdd, + @NonNull Collection toRemove) throws QueryException; + + /** + * Sets class super class. + * @param cls the fully qualified name of class to be changed + * @param superCls the fully qualified name of super class + * @throws QueryException in case of failure + */ + void setSuperClass( + @NonNull String cls, + @NonNull String superCls) throws QueryException; + + + /** + * Renames a field in a class. + * @param cls the fully qualified name of the class owning the field + * @param oldName the old field name + * @param newName the new field name + * @throws QueryException in case of failure + */ + void renameField( + @NonNull String cls, + @NonNull String oldName, + @NonNull String newName) throws QueryException; +} diff --git a/java.source.queries/src/org/netbeans/modules/java/source/queries/spi/QueriesController.java b/java.source.queries/src/org/netbeans/modules/java/source/queries/spi/QueriesController.java new file mode 100644 --- /dev/null +++ b/java.source.queries/src/org/netbeans/modules/java/source/queries/spi/QueriesController.java @@ -0,0 +1,135 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 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. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ +package org.netbeans.modules.java.source.queries.spi; + +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.modules.java.source.queries.APIAccessor; +import org.netbeans.modules.java.source.queries.api.QueryException; +import org.netbeans.modules.java.source.queries.SPIAccessor; +import org.netbeans.modules.java.source.queries.api.Function; +import org.netbeans.modules.java.source.queries.api.Queries; +import org.netbeans.modules.java.source.queries.api.Updates; +import org.openide.filesystems.FileObject; + +/** + * SPI for the {@link Queries} and {@link Updates}. + * The implementation is registered in META-INF/services. + * @author Tomas Zezula + */ +public interface QueriesController { + /** + * Performs the query. + * @param ctx the query {@link Context} holding the + * query function and query parameter + * @return the result of the query function + * @throws IOException in case of error + */ + R runQuery(@NonNull Context ctx) throws QueryException; + + /** + * Performs the modification. + * @param ctx the modification {@link Context} holding the + * modification function and modification parameter + * @throws IOException in case of error + */ + void runUpdate(@NonNull Context ctx) throws QueryException; + + /** + * Context of the query or modification + */ + public static final class Context

{ + + static { + SPIAccessor.setInstance(new Accessor()); + } + + private final Function toRun; + private final P param; + private final FileObject forFile; + + Context ( + @NonNull final FileObject forFile, + @NonNull final Function toRun, + @NonNull final P param) { + assert forFile != null; + assert toRun != null; + assert param != null; + this.toRun = toRun; + this.param = param; + this.forFile = forFile; + } + + /** + * Return the file for which the {@link Queries} or + * {@link Updates} instance was created. + * @return the file + */ + public FileObject getFile(){ + return forFile; + } + + /** + * Performs the query function on query parameter. + * using the provided {@link ModelOperations} + * @param op the {@link ModelOperations} SPI to used to + * perform the operation. + * @return the query function result + */ + public R execute(ModelOperations op) throws QueryException { + APIAccessor.getInstance().attach(param, op); + try { + return toRun.apply(param); + } finally { + APIAccessor.getInstance().detach(param); + } + } + + private static class Accessor extends SPIAccessor { + @Override + public

QueriesController.Context createContext( + final Function fnc, + final P param) { + return new Context(param.getFile(), fnc, param); + } + } + } +} diff --git a/java.source.queries/test/unit/src/org/netbeans/modules/java/source/queries/spi/ModelOperationsTest.java b/java.source.queries/test/unit/src/org/netbeans/modules/java/source/queries/spi/ModelOperationsTest.java new file mode 100644 --- /dev/null +++ b/java.source.queries/test/unit/src/org/netbeans/modules/java/source/queries/spi/ModelOperationsTest.java @@ -0,0 +1,760 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 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. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ +package org.netbeans.modules.java.source.queries.spi; +import java.io.File; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.annotations.common.NullAllowed; +import org.netbeans.junit.NbTestCase; +import org.netbeans.modules.java.source.queries.api.Function; +import org.netbeans.modules.java.source.queries.api.Queries; +import org.netbeans.modules.java.source.queries.api.QueryException; +import org.netbeans.modules.java.source.queries.api.Updates; +import org.openide.filesystems.FileLock; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; +import org.openide.util.Lookup; + +/** + * + * @author Tomas Zezula + */ +public class ModelOperationsTest extends NbTestCase { + + protected static final String[] TEST_1 = { + "org.me.test", + "Test1", + "package org.me.test;\n"+ + "import java.util.Map;\n"+ + "public class Test1{\n"+ + " int a, b;\n"+ + " float c\n;"+ + " Test1 d\n;"+ + " java.util.List e;\n"+ + " Map f;\n"+ + "}" + }; + + protected static final String[] TEST_2 = { + "org.me.test", + "Test2", + "package org.me.test;\n"+ + "public class Test2 extends Exception implements Runnable {\n"+ + " public Test2(int a){}\n"+ + " public void run(){}\n"+ + "}\n"+ + "class Test2Other implements Runnable,java.io.Serializable{\n" + + " public void run(){}\n"+ + "}" + }; + + protected static final String[] TEST_3 = { + "org.me.test", + "Test3", + "package org.me.test;\n"+ + "public class Test3{\n"+ + " Test3(){}\n"+ + "}" + }; + + protected static final String[] TEST_4 = { + "org.me.test", + "Test4", + "package org.me.test;\n"+ + "import java.util.Map;\n"+ + "public class Test4{\n"+ + " void m1() {}\n"+ + " void m2(int a){}\n"+ + " int m3(int a, int b){}\n"+ + " java.util.List m4(Mapa, Mapb){}\n"+ + " java.util.List m5(Map>a, String b){}\n"+ + " void m6(int... a){}\n"+ + " int m7(int... a){}\n" + + "}" + }; + + protected static final String[] TEST_5 = { + "org.me.test", + "Test5", + "package org.me.test;\n"+ + "import java.util.Map;\n"+ + "public class Test5{\n"+ + " void m1() {}\n"+ + " @SuppressWarnings void m1(int a){}\n"+ + " @SuppressWarnings int m2(int a, int b){}\n"+ + "}" + }; + + protected static final String[] TEST_6 = { + "org.me.test", + "Test6", + "package org.me.test;\n"+ + "public class Test6{\n"+ + " int a;\n"+ + "}" + }; + + protected FileObject srcRoot; + + public ModelOperationsTest(String testName) { + super(testName); + } + + @Override + protected void setUp() throws Exception { + this.clearWorkDir(); + srcRoot = FileUtil.createFolder(new File (getWorkDir(),"src")); + } + + public void testGetTopLevelClasses() throws Exception { + doTestGetTopLevelClasses(TEST_1, Arrays.asList("org.me.test.Test1")); + doTestGetTopLevelClasses(TEST_2, Arrays.asList("org.me.test.Test2","org.me.test.Test2Other")); + } + + private void doTestGetTopLevelClasses( + @NonNull final String[] testCase, + @NonNull final Collection expected) throws Exception { + final FileObject fo1 = prepareTest( + srcRoot, + testCase[0], + testCase[1], + testCase[2]); + final Collection tlc = Queries.query( + fo1, + new Function>(){ + @Override + public Collection apply(Queries param) throws QueryException { + return param.getTopLevelClasses(); + } + }); + assertContentEquals(expected,tlc); + } + + public void testIsJavaBean() throws Exception { + doTestIsJavaBean(TEST_1, "org.me.test.Test1", true); + doTestIsJavaBean(TEST_2, "org.me.test.Test2", false); + doTestIsJavaBean(TEST_2, "org.me.test.Test2Other", false); + doTestIsJavaBean(TEST_3, "org.me.test.Test3", false); + } + + private void doTestIsJavaBean( + @NonNull final String[] testCase, + @NonNull final String fqn, + final boolean expected) throws Exception { + final FileObject file = prepareTest( + srcRoot, + testCase[0], + testCase[1], + testCase[2]); + final Boolean res = Queries.query( + file, + new Function(){ + @Override + public Boolean apply(Queries param) throws QueryException { + return param.isJavaBean(fqn); + } + }); + assertEquals(expected, res.booleanValue()); + } + + public void testIsAvialable() throws Exception { + doTestIsAvailable(TEST_1, "org.me.test.Test1", true); + doTestIsAvailable(TEST_1, "java.lang.String", true); + } + + private void doTestIsAvailable( + @NonNull final String[] testCase, + @NonNull final String fqn, + final boolean expected) throws Exception { + final FileObject file = prepareTest( + srcRoot, + testCase[0], + testCase[1], + testCase[2]); + final Boolean res = Queries.query( + file, + new Function(){ + @Override + public Boolean apply(Queries param) throws QueryException { + return param.isAvailable(fqn); + } + }); + assertEquals(expected, res.booleanValue()); + } + + public void testGetSuperClass() throws Exception { + doTestGetSuperClass(TEST_1, "org.me.test.Test1", "java.lang.Object"); + doTestGetSuperClass(TEST_2, "org.me.test.Test2", "java.lang.Exception"); + doTestGetSuperClass(TEST_3, "java.lang.RuntimeException", "java.lang.Exception"); + doTestGetSuperClass(TEST_3, "java.lang.Object", null); + } + + private void doTestGetSuperClass( + @NonNull final String[] testCase, + @NonNull final String fqn, + @NonNull final String expected) throws Exception { + final FileObject file = prepareTest( + srcRoot, + testCase[0], + testCase[1], + testCase[2]); + final String res = Queries.query( + file, + new Function(){ + @Override + public String apply(Queries param) throws QueryException { + return param.getSuperClass(fqn); + } + }); + assertEquals(expected, res); + } + + public void testGetInterfaces() throws Exception { + doTestGetInterfaces(TEST_1, "org.me.test.Test1", Collections.emptyList()); + doTestGetInterfaces(TEST_2, "org.me.test.Test2", Collections.singleton("java.lang.Runnable")); + doTestGetInterfaces(TEST_2, "org.me.test.Test2Other", + Arrays.asList("java.lang.Runnable","java.io.Serializable")); + } + + private void doTestGetInterfaces( + @NonNull final String[] testCase, + @NonNull final String fqn, + @NonNull final Collection< ? extends String> expected) throws Exception { + final FileObject file = prepareTest( + srcRoot, + testCase[0], + testCase[1], + testCase[2]); + final Collection< ? extends String> res = Queries.query( + file, + new Function>(){ + @Override + public Collection apply(Queries param) throws QueryException { + return param.getInterfaces(fqn); + } + }); + assertContentEquals(expected, res); + } + + public void testGetBinaryName() throws Exception { + doTestGetBinaryName(TEST_1,"org.me.test.Test1","org.me.test.Test1"); + doTestGetBinaryName(TEST_2,"org.me.test.Test2Other","org.me.test.Test2Other"); + doTestGetBinaryName(TEST_2,"java.util.Map","java.util.Map"); + doTestGetBinaryName(TEST_2,"java.util.Map.Entry","java.util.Map$Entry"); + } + + private void doTestGetBinaryName( + @NonNull final String[] testCase, + @NonNull final String fqn, + @NonNull final String expected) throws Exception { + final FileObject file = prepareTest( + srcRoot, + testCase[0], + testCase[1], + testCase[2]); + final String res = Queries.query( + file, + new Function(){ + @Override + public String apply(Queries param) throws QueryException { + return param.getClassBinaryName(fqn); + } + }); + assertEquals(expected, res); + } + + public void testGetFieldNames() throws Exception { + doTestGetFieldNames(TEST_1,"org.me.test.Test1", false, "org.me.test.Test1", Collections.singletonList("d")); + doTestGetFieldNames(TEST_1,"org.me.test.Test1", false, "float", Collections.singletonList("c")); + doTestGetFieldNames(TEST_1,"org.me.test.Test1", false, "int", Arrays.asList("a","b")); + doTestGetFieldNames(TEST_1,"org.me.test.Test1", false, "java.util.List", Collections.singletonList("e")); + doTestGetFieldNames(TEST_1,"org.me.test.Test1", true, "java.util.List", Collections.singletonList("e")); + doTestGetFieldNames(TEST_1,"org.me.test.Test1", false, "java.util.List", Collections.emptyList()); + doTestGetFieldNames(TEST_1,"org.me.test.Test1", true, "java.util.List", Collections.singletonList("e")); + doTestGetFieldNames(TEST_1,"org.me.test.Test1", false, "java.util.Map", Collections.emptyList()); + doTestGetFieldNames(TEST_1,"org.me.test.Test1", true, "java.util.Map", Collections.singletonList("f")); + doTestGetFieldNames(TEST_1,"org.me.test.Test1", false, "java.util.Map", Collections.singletonList("f")); + doTestGetFieldNames(TEST_1,"org.me.test.Test1", false, null, Arrays.asList("a","b","c","d","e","f")); + } + + private void doTestGetFieldNames( + @NonNull final String[] testCase, + @NonNull final String fqn, + final boolean useRT, + @NonNull final String type, + @NonNull final Collection expected) throws Exception { + final FileObject file = prepareTest( + srcRoot, + testCase[0], + testCase[1], + testCase[2]); + final Collection res = Queries.query( + file, + new Function>(){ + @Override + public Collection< ? extends String> apply(Queries param) throws QueryException { + return param.getFieldNames(fqn, useRT, type); + } + }); + assertContentEquals(expected, res); + } + + public void testGetMethodNames() throws Exception { + doTestGetMethodNames(TEST_4,"org.me.test.Test4", false, + "void", + Collections.emptyList(), + Collections.singletonList("m1")); + doTestGetMethodNames(TEST_4,"org.me.test.Test4", false, + "void", + Collections.singletonList("int"), + Collections.singletonList("m2")); + doTestGetMethodNames(TEST_4,"org.me.test.Test4", false, + "int", + Arrays.asList(new String[]{"int","int"}), + Collections.singletonList("m3")); + doTestGetMethodNames(TEST_4,"org.me.test.Test4", true, + "java.util.List", + Arrays.asList("java.util.Map","java.util.Map"), + Collections.singletonList("m4")); + doTestGetMethodNames(TEST_4,"org.me.test.Test4", false, + "java.util.List", + Arrays.asList("java.util.Map","java.util.Map"), + Collections.emptyList()); + doTestGetMethodNames(TEST_4,"org.me.test.Test4", false, + "java.util.List", + Arrays.asList("java.util.Map","java.util.Map"), + Collections.emptyList()); + doTestGetMethodNames(TEST_4,"org.me.test.Test4", false, + "java.util.List", + Arrays.asList("java.util.Map","java.util.Map"), + Collections.singletonList("m4")); + doTestGetMethodNames(TEST_4,"org.me.test.Test4", true, + "java.util.List", + Arrays.asList("java.util.Map","java.lang.String"), + Collections.singletonList("m5")); + doTestGetMethodNames(TEST_4,"org.me.test.Test4", false, + "java.util.List", + Arrays.asList("java.util.Map","java.lang.String"), + Collections.emptyList()); + doTestGetMethodNames(TEST_4,"org.me.test.Test4", false, + "java.util.List", + Arrays.asList("java.util.Map>","java.lang.String"), + Collections.emptyList()); + doTestGetMethodNames(TEST_4,"org.me.test.Test4", false, + "java.util.List", + Arrays.asList("java.util.Map","java.lang.String"), + Collections.emptyList()); + doTestGetMethodNames(TEST_4,"org.me.test.Test4", false, + "java.util.List", + Arrays.asList("java.util.Map>","java.lang.String"), + Collections.singletonList("m5")); + doTestGetMethodNames(TEST_4,"org.me.test.Test4", false, + "void", + Collections.singletonList("int[]"), + Collections.singletonList("m6")); + doTestGetMethodNames(TEST_4,"org.me.test.Test4", false, + null, + Collections.singletonList("int[]"), + Arrays.asList("m6","m7")); + doTestGetMethodNames(TEST_4,"org.me.test.Test4", false, + "void", + null, + Arrays.asList("m1","m2","m6")); + doTestGetMethodNames(TEST_4,"org.me.test.Test4", false, + null, + null, + Arrays.asList("m1","m2","m3","m4","m5","m6","m7")); + } + + private void doTestGetMethodNames( + @NonNull final String[] testCase, + @NonNull final String fqn, + final boolean useRT, + @NonNull final String retType, + @NonNull final List paramTypes, + @NonNull final Collection expected) throws Exception { + final FileObject file = prepareTest( + srcRoot, + testCase[0], + testCase[1], + testCase[2]); + final Collection res = Queries.query( + file, + new Function>(){ + @Override + public Collection< ? extends String> apply(Queries param) throws QueryException { + return param.getMethodNames( + fqn, + useRT, + retType, + paramTypes == null ? null : paramTypes.toArray(new String[paramTypes.size()])); + } + }); + assertContentEquals(expected, res); + } + + public void testGetMethodAnnotations() throws Exception { + doTestGetMethodAnnotations(TEST_5,"org.me.test.Test5","m1",false, + "void", + Collections.emptyList(), + Collections.emptyList()); + doTestGetMethodAnnotations(TEST_5,"org.me.test.Test5", "m1",false, + "void", + Collections.singletonList("int"), + Collections.singletonList("java.lang.SuppressWarnings")); + doTestGetMethodAnnotations(TEST_5,"org.me.test.Test5", "m2",false, + "int", + Arrays.asList("int","int"), + Collections.singletonList("java.lang.SuppressWarnings")); + } + + private void doTestGetMethodAnnotations( + @NonNull final String[] testCase, + @NonNull final String fqn, + @NonNull final String methodName, + final boolean useRT, + @NonNull final String retType, + @NonNull final List paramTypes, + @NonNull final Collection expected) throws Exception { + final FileObject file = prepareTest( + srcRoot, + testCase[0], + testCase[1], + testCase[2]); + final Collection res = Queries.query( + file, + new Function>(){ + @Override + public Collection< ? extends String> apply(Queries param) throws QueryException { + return param.getMethodAnnotations( + fqn, + methodName, + useRT, + retType, + paramTypes == null ? null : paramTypes.toArray(new String[paramTypes.size()])); + } + }); + assertContentEquals(expected, res); + } + + public void testModifyInterfaces() throws Exception { + doTestModifyInterfaces(TEST_5,"org.me.test.Test5", + Collections.singletonList("java.lang.Runnable"), + Collections.emptySet(), + Collections.singletonList("java.lang.Runnable")); + doTestModifyInterfaces(TEST_5,"org.me.test.Test5", + Collections.singletonList("java.io.Serializable"), + Collections.emptySet(), + Arrays.asList("java.lang.Runnable","java.io.Serializable")); + doTestModifyInterfaces(TEST_5,"org.me.test.Test5", + Collections.singletonList("java.io.Externalizable"), + Collections.singletonList("java.io.Serializable"), + Arrays.asList("java.lang.Runnable","java.io.Externalizable")); + doTestModifyInterfaces(TEST_5,"org.me.test.Test5", + Collections.emptySet(), + Arrays.asList("java.lang.Runnable","java.io.Externalizable"), + Collections.emptySet()); + } + + private void doTestModifyInterfaces( + @NonNull final String[] testCase, + @NonNull final String clz, + @NonNull final Collection toAdd, + @NonNull final Collection toRemove, + @NonNull final Collection expected + ) throws Exception { + final FileObject file = prepareTest( + srcRoot, + testCase[0], + testCase[1], + testCase[2]); + Updates.update( + file, + new Function(){ + @Override + public Boolean apply(final Updates param) throws QueryException { + param.modifyInterfaces( + clz, + toAdd, + toRemove); + return true; + } + }); + final Collection< ? extends String> res = Queries.query( + file, + new Function>(){ + @Override + public Collection apply(Queries param) throws QueryException { + return param.getInterfaces(clz); + } + }); + assertContentEquals(expected, res); + } + + public void testSetSuperClass() throws Exception { + doTestSetSuperClass(TEST_5,"org.me.test.Test5", + "java.util.ArrayList", + "java.util.ArrayList"); + doTestSetSuperClass(TEST_5,"org.me.test.Test5", + "java.lang.Object", + "java.lang.Object"); + } + + private void doTestSetSuperClass( + @NonNull final String[] testCase, + @NonNull final String clz, + @NonNull final String superClz, + @NonNull final String expected) throws Exception { + final FileObject file = prepareTest( + srcRoot, + testCase[0], + testCase[1], + testCase[2]); + Updates.update( + file, + new Function(){ + @Override + public Boolean apply(final Updates param) throws QueryException { + param.setSuperClass( + clz, + superClz); + return true; + } + }); + final String res = Queries.query( + file, + new Function(){ + @Override + public String apply(Queries param) throws QueryException { + return param.getSuperClass(clz); + } + }); + assertEquals(expected, res); + } + + public void testModifyMethodAnnotations() throws Exception { + doTestModifyMethodAnnotations(TEST_5,"org.me.test.Test5","m1",false, + "void", + Collections.emptyList(), + Collections.singleton("java.lang.Override"), + Collections.emptyList(), + Collections.singleton("java.lang.Override")); + doTestModifyMethodAnnotations(TEST_5,"org.me.test.Test5","m1",false, + "void", + Collections.emptyList(), + Collections.singleton("java.lang.SuppressWarnings"), + Collections.emptyList(), + Arrays.asList("java.lang.Override","java.lang.SuppressWarnings")); + doTestModifyMethodAnnotations(TEST_5,"org.me.test.Test5","m1",false, + "void", + Collections.emptyList(), + Collections.emptyList(), + Collections.singleton("java.lang.SuppressWarnings"), + Collections.singleton("java.lang.Override")); + doTestModifyMethodAnnotations(TEST_5,"org.me.test.Test5","m1",false, + "void", + Collections.emptyList(), + Collections.emptyList(), + Collections.singleton("java.lang.Override"), + Collections.emptySet()); + doTestModifyMethodAnnotations(TEST_5,"org.me.test.Test5","m1",false, + "void", + Collections.emptyList(), + Arrays.asList("java.lang.Override","java.lang.SuppressWarnings"), + Collections.emptyList(), + Arrays.asList("java.lang.Override","java.lang.SuppressWarnings")); + doTestModifyMethodAnnotations(TEST_5,"org.me.test.Test5","m1",false, + "void", + Collections.emptyList(), + Collections.emptyList(), + Arrays.asList("java.lang.Override","java.lang.SuppressWarnings"), + Collections.emptySet()); + } + + private void doTestModifyMethodAnnotations( + @NonNull final String[] testCase, + @NonNull final String fqn, + @NonNull final String methodName, + final boolean useRT, + @NonNull final String retType, + @NonNull final List paramTypes, + @NonNull final Collection toAdd, + @NonNull final Collection toRemove, + @NonNull final Collection expected) throws Exception { + final FileObject file = prepareTest( + srcRoot, + testCase[0], + testCase[1], + testCase[2]); + Updates.update( + file, + new Function(){ + @Override + public Boolean apply(final Updates param) throws QueryException { + param.modifyMethodAnnotations( + fqn, + methodName, + useRT, + retType, + paramTypes.toArray(new String[paramTypes.size()]), + toAdd, + toRemove); + return true; + } + }); + final Collection res = Queries.query( + file, + new Function>(){ + @Override + public Collection apply(Queries param) throws QueryException { + return param.getMethodAnnotations( + fqn, + methodName, + useRT, + retType, + paramTypes.toArray(new String[paramTypes.size()])); + } + }); + assertContentEquals(expected, res); + } + + public void testRenameField() throws Exception { + doTestRenameField(TEST_6,"org.me.test.Test6", + "a", + "b"); + } + + private void doTestRenameField( + @NonNull final String[] testCase, + @NonNull final String fqn, + @NonNull final String oldFieldName, + @NonNull final String newFieldName) throws Exception { + final FileObject file = prepareTest( + srcRoot, + testCase[0], + testCase[1], + testCase[2]); + Updates.update( + file, + new Function(){ + @Override + public Boolean apply(final Updates param) throws QueryException { + param.renameField( + fqn, + oldFieldName, + newFieldName); + return true; + } + }); + final Collection res = Queries.query( + file, + new Function>(){ + @Override + public Collection apply(Queries param) throws QueryException { + return param.getFieldNames(fqn, true, null); + } + }); + assertContentEquals(Collections.singleton(newFieldName), res); + } + + protected final void assertContentEquals( + @NullAllowed final Collection expected, + @NullAllowed final Collection result) { + if (expected == null) { + assertNull("Expected null but got: " + result ,result); + } else { + final Set e = new HashSet(expected); + for (T r : result) { + if (!e.remove(r)) { + throw new AssertionError("Expected: " + expected +" got:" + result); + } + } + if (!e.isEmpty()) { + throw new AssertionError("Expected: " + expected +" got:" + result); + } + } + } + + protected final FileObject prepareTest( + @NonNull final FileObject root, + @NonNull final String pkg, + @NonNull final String name, + @NonNull final String content) throws IOException { + if (Lookup.getDefault().lookup(QueriesController.class) == null) { + throw new IllegalStateException("Run the ModelOperationsTest subclass in impl module."); + } + assert root != null; + assert pkg != null; + assert name != null; + assert content != null; + final String fileName = String.format("%s/%s.java", + pkg.replace('.', '/'), + name); + FileObject fo = root.getFileObject(fileName); + if (fo != null) { + return fo; + } + fo = FileUtil.createData( + srcRoot, + fileName); + final FileLock lock = fo.lock(); + try { + final PrintWriter out = new PrintWriter (new OutputStreamWriter(fo.getOutputStream(lock))); + try { + out.print(content); + } finally { + out.close(); + } + } finally { + lock.releaseLock(); + } + return fo; + } +} diff --git a/java.source.queriesimpl/build.xml b/java.source.queriesimpl/build.xml new file mode 100644 --- /dev/null +++ b/java.source.queriesimpl/build.xml @@ -0,0 +1,5 @@ + + + Builds, tests, and runs the project org.netbeans.modules.java.source.queriesimpl + + diff --git a/java.source.queriesimpl/manifest.mf b/java.source.queriesimpl/manifest.mf new file mode 100644 --- /dev/null +++ b/java.source.queriesimpl/manifest.mf @@ -0,0 +1,5 @@ +Manifest-Version: 1.0 +OpenIDE-Module: org.netbeans.modules.java.source.queriesimpl +OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/java/source/queriesimpl/Bundle.properties +OpenIDE-Module-Specification-Version: 1.0 + diff --git a/java.source.queriesimpl/nbproject/project.properties b/java.source.queriesimpl/nbproject/project.properties new file mode 100644 --- /dev/null +++ b/java.source.queriesimpl/nbproject/project.properties @@ -0,0 +1,3 @@ +javac.source=1.6 +javac.compilerargs=-Xlint -Xlint:-serial +requires.nb.javac=true diff --git a/java.source.queriesimpl/nbproject/project.xml b/java.source.queriesimpl/nbproject/project.xml new file mode 100644 --- /dev/null +++ b/java.source.queriesimpl/nbproject/project.xml @@ -0,0 +1,91 @@ + + + org.netbeans.modules.apisupport.project + + + org.netbeans.modules.java.source.queriesimpl + + + org.netbeans.api.annotations.common + + + + 1 + 1.10 + + + + org.netbeans.modules.java.source + + + + 0.81 + + + + org.netbeans.modules.java.source.queries + + + + 1.0 + + + + org.netbeans.modules.parsing.api + + + + 1 + 1.43 + + + + org.openide.filesystems + + + + 7.48 + + + + org.openide.util.lookup + + + + 8.9 + + + + + + unit + + org.netbeans.libs.junit4 + + + + org.netbeans.modules.editor.mimelookup + + + + + org.netbeans.modules.java.source + + + + + org.netbeans.modules.java.source.queries + + + + + org.netbeans.modules.nbjunit + + + + + + + + + diff --git a/java.source.queriesimpl/src/org/netbeans/modules/java/source/queriesimpl/Bundle.properties b/java.source.queriesimpl/src/org/netbeans/modules/java/source/queriesimpl/Bundle.properties new file mode 100644 --- /dev/null +++ b/java.source.queriesimpl/src/org/netbeans/modules/java/source/queriesimpl/Bundle.properties @@ -0,0 +1,1 @@ +OpenIDE-Module-Name=Java Source Queries Implementation diff --git a/java.source.queriesimpl/src/org/netbeans/modules/java/source/queriesimpl/JavaOperationsImpl.java b/java.source.queriesimpl/src/org/netbeans/modules/java/source/queriesimpl/JavaOperationsImpl.java new file mode 100644 --- /dev/null +++ b/java.source.queriesimpl/src/org/netbeans/modules/java/source/queriesimpl/JavaOperationsImpl.java @@ -0,0 +1,589 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 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. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ +package org.netbeans.modules.java.source.queriesimpl; + +import com.sun.source.tree.AnnotationTree; +import com.sun.source.tree.ClassTree; +import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.tree.ExpressionTree; +import com.sun.source.tree.IdentifierTree; +import com.sun.source.tree.MethodTree; +import com.sun.source.tree.ModifiersTree; +import com.sun.source.tree.Tree; +import com.sun.source.tree.Tree.Kind; +import com.sun.source.tree.VariableTree; +import com.sun.source.util.TreePath; +import com.sun.source.util.TreePathScanner; +import com.sun.source.util.Trees; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.ElementFilter; +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.api.java.source.CompilationController; +import org.netbeans.api.java.source.ElementUtilities; +import org.netbeans.api.java.source.GeneratorUtilities; +import org.netbeans.api.java.source.JavaSource.Phase; +import org.netbeans.api.java.source.TreeMaker; +import org.netbeans.api.java.source.TreeUtilities; +import org.netbeans.api.java.source.WorkingCopy; +import org.netbeans.modules.java.source.queries.api.QueryException; +import org.netbeans.modules.java.source.queries.spi.ModelOperations; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; + +/** + * + * @author Tomas Zezula + */ +class JavaOperationsImpl implements ModelOperations { + + final CompilationController control; + + JavaOperationsImpl(@NonNull final CompilationController control) { + assert control != null; + this.control = control; + } + + @Override + @NonNull + public Collection getTopLevelClasses(@NonNull final FileObject file) throws QueryException { + try { + control.toPhase(Phase.ELEMENTS_RESOLVED); + } catch (IOException ioe) { + throw new QueryException(ioe); + } + final Collection topLevels = control.getTopLevelElements(); + final List result = new ArrayList(topLevels.size()); + for (Element topLevel : topLevels) { + result.add(((TypeElement)topLevel).getQualifiedName().toString()); + } + return result; + } + + @Override + public boolean isJavaBean(@NonNull final String cls) throws QueryException { + final TypeElement clz = findClass(cls); + if (clz != null && clz.getModifiers().contains(Modifier.PUBLIC)) { + for (Element member : clz.getEnclosedElements()) { + if (member.getKind() == ElementKind.CONSTRUCTOR && + ((ExecutableElement)member).getParameters().isEmpty() && + ((ExecutableElement)member).getModifiers().contains(Modifier.PUBLIC)) { + return true; + } + } + } + return false; + } + + @Override + public boolean isAvailable(@NonNull final String cls) throws QueryException { + return findClass(cls) != null; + } + + @Override + @CheckForNull + public String getSuperClass(@NonNull final String cls) throws QueryException { + final TypeElement te = findClass(cls); + if (te == null) { + return null; + } + final TypeMirror superType = te.getSuperclass(); + if (superType.getKind() != TypeKind.DECLARED) { + return null; + } + return ((TypeElement)((DeclaredType)superType).asElement()).getQualifiedName().toString(); + } + + @NonNull + @Override + public final Collection getInterfaces(@NonNull final String cls) throws QueryException { + final TypeElement te = findClass(cls); + if (te == null) { + return null; + } + final List interfaceTypes = te.getInterfaces(); + final List result = new ArrayList(interfaceTypes.size()); + for (TypeMirror tm : interfaceTypes) { + if (tm.getKind() == TypeKind.DECLARED) { + result.add( + ((TypeElement)((DeclaredType)tm).asElement()).getQualifiedName().toString()); + } + } + return Collections.unmodifiableCollection(result); + } + + @Override + @CheckForNull + public String getClassBinaryName(@NonNull final String cls) throws QueryException { + final TypeElement te = findClass(cls); + return te == null ? null : ElementUtilities.getBinaryName(te); + } + + @Override + @NonNull + public Collection getFieldNames( + @NonNull final String clz, + final boolean rt, + @NullAllowed final String type) throws QueryException { + final TypeElement te = findClass(clz); + if (te == null) { + return Collections.emptyList(); + } + final Types types = control.getTypes(); + TypeMirror tm = null; + if (type != null) { + final List topLevels = control.getTopLevelElements(); + tm = topLevels.isEmpty() ? + null : + control.getTreeUtilities().parseType(type, topLevels.get(0)); + if (tm == null) { + return Collections.emptyList(); + } else if (rt) { + tm = types.erasure(tm); + } + } + final Collection result = new ArrayList(); + for (VariableElement ve : ElementFilter.fieldsIn(te.getEnclosedElements())) { + if (isSameType(types,tm,ve.asType(),rt)) { + result.add(ve.getSimpleName().toString()); + } + } + return Collections.unmodifiableCollection(result); + } + + @Override + @NonNull + public Collection getMethodNames( + final @NonNull String clz, + final boolean rt, + final @NullAllowed String returnType, + final @NullAllowed String... parameterTypes) throws QueryException { + final List methods = getMethods(clz, null, rt, returnType, parameterTypes); + final List result = new ArrayList(methods.size()); + for (ExecutableElement method : methods) { + result.add(method.getSimpleName().toString()); + } + return Collections.unmodifiableCollection(result); + } + + @Override + @NonNull + public Collection getMethodAnnotations( + @NonNull final String clz, + @NonNull final String methodName, + final boolean rt, + @NonNull final String returnType, + @NonNull final String... parameterTypes) throws QueryException { + final List methods = getMethods(clz, null, rt, returnType, parameterTypes); + if (methods.isEmpty()) { + return Collections.emptyList(); + } else { + //Todo: if size > 1 => 2 methods with same signature (invalid source) use the first one + final ExecutableElement method = methods.get(0); + final List result = new ArrayList(); + for (AnnotationMirror ann : method.getAnnotationMirrors()) { + if (ann.getAnnotationType().getKind() == TypeKind.DECLARED) { + result.add (((TypeElement)ann.getAnnotationType().asElement()).getQualifiedName().toString()); + } + } + return Collections.unmodifiableCollection(result); + } + } + + @Override + public void modifyInterfaces( + @NonNull final String clz, + @NonNull final Collection toAdd, + @NonNull final Collection toRemove) throws QueryException { + if (!(control instanceof WorkingCopy)) { + throw new IllegalStateException(); + } + final WorkingCopy wcopy = (WorkingCopy) control; + final TreePath mainClassTreePath = findClassInCompilationUnit(clz); + if (mainClassTreePath == null) { + throw new IllegalArgumentException("No class: " + clz + " in source: " + //NOI18N + FileUtil.getFileDisplayName(control.getFileObject())); + } + final Element mainClassElm = wcopy.getTrees().getElement(mainClassTreePath); + assert mainClassElm != null; + ClassTree mainClassTree = (ClassTree) mainClassTreePath.getLeaf(); + final ClassTree origMainTree = mainClassTree; + if (mainClassElm != null) { + Set actualInterfaces = new HashSet(); + TreeMaker maker = wcopy.getTreeMaker(); + // first take the current interfaces and exclude the removed ones + List interfaces = ((TypeElement) mainClassElm).getInterfaces(); + for (int infIndex = interfaces.size() - 1; infIndex >= 0; infIndex--) { + TypeMirror infMirror = interfaces.get(infIndex); + TypeElement infElm = (TypeElement) wcopy.getTypes().asElement(infMirror); + actualInterfaces.add(infElm.getQualifiedName().toString()); + if (toRemove.contains(infElm.getQualifiedName().toString())) { + mainClassTree = maker.removeClassImplementsClause(mainClassTree, infIndex); + } + } + for (String name : toAdd) { + if (!actualInterfaces.contains(name)) { + TypeElement inf2add = wcopy.getElements().getTypeElement(name); + ExpressionTree infTree2add = inf2add != null + ? maker.QualIdent(inf2add) + : maker.Identifier(name); + mainClassTree = maker.addClassImplementsClause(mainClassTree, infTree2add); + } + } + if (origMainTree != mainClassTree) { + wcopy.rewrite(origMainTree, mainClassTree); + } + } + } + + @Override + public void setSuperClass( + @NonNull final String clz, + @NonNull final String superClz) throws QueryException { + if (!(control instanceof WorkingCopy)) { + throw new IllegalStateException(); + } + final WorkingCopy wcopy = (WorkingCopy) control; + final TreePath mainClassTreePath = findClassInCompilationUnit(clz); + if (mainClassTreePath == null) { + throw new IllegalArgumentException("No class: " + clz + " in source: " + //NOI18N + FileUtil.getFileDisplayName(control.getFileObject())); + } + final Element mainClassElm = wcopy.getTrees().getElement(mainClassTreePath); + assert mainClassElm != null; + ClassTree mainClassTree = (ClassTree) mainClassTreePath.getLeaf(); + final ClassTree origMainTree = mainClassTree; + if (mainClassElm != null) { + final TreeMaker maker = wcopy.getTreeMaker(); + ExpressionTree superClsTree = null; + if (!Object.class.getName().equals(superClz)){ + final TypeElement inf2add = wcopy.getElements().getTypeElement(superClz); + superClsTree = inf2add != null + ? maker.QualIdent(inf2add) + : maker.Identifier(superClz); + } + mainClassTree = maker.setExtends(mainClassTree,superClsTree); + if (origMainTree != mainClassTree) { + wcopy.rewrite(origMainTree, mainClassTree); + } + } + } + + @Override + public void modifyMethodAnnotations( + @NonNull final String clz, + @NonNull final String methodName, + final boolean rt, + @NonNull final String returnType, + @NonNull final String[] parameterTypes, + @NonNull Collection toAdd, + @NonNull Collection toRemove) throws QueryException { + if (!(control instanceof WorkingCopy)) { + throw new IllegalStateException(); + } + final WorkingCopy wcopy = (WorkingCopy) control; + final List methods = getMethods(clz, null, rt, returnType, parameterTypes); + if (methods.isEmpty()) { + throw new IllegalArgumentException("No method: " + clz + "." + methodName + " in source: " + //NOI18N + FileUtil.getFileDisplayName(control.getFileObject())); + } else { + //Todo: if size > 1 => 2 methods with same signature (invalid source) use the first one + final ExecutableElement method = methods.get(0); + final TreePath methodTreePath = control.getTrees().getPath(method); + if (methodTreePath == null) { + throw new IllegalArgumentException("No method: " + clz + "." + methodName + " in source: " + //NOI18N + FileUtil.getFileDisplayName(control.getFileObject())); + } + final TreeMaker maker = wcopy.getTreeMaker(); + final MethodTree methodTree = (MethodTree) methodTreePath.getLeaf(); + ModifiersTree modTree = methodTree.getModifiers(); + final ModifiersTree origModTree = modTree; + final Set actualAnnotations = new HashSet(); + for (AnnotationTree annotation : modTree.getAnnotations()) { + final Element ae = control.getTrees().getElement( + TreePath.getPath(methodTreePath.getCompilationUnit(), + annotation.getAnnotationType())); + if (ae != null && + (ae.getKind().isClass() || ae.getKind().isInterface())) { + final String afqn = ((TypeElement)ae).getQualifiedName().toString(); + actualAnnotations.add(afqn); + if (toRemove.contains(afqn)) { + modTree = maker.removeModifiersAnnotation(modTree, annotation); + } + } + } + for (String afqn : toAdd) { + if (!actualAnnotations.contains(afqn)) { + TypeElement ann2add = wcopy.getElements().getTypeElement(afqn); + ExpressionTree annTree2add = ann2add != null + ? maker.QualIdent(ann2add) + : maker.Identifier(afqn); + final AnnotationTree annotation = maker.Annotation( + annTree2add, + Collections.emptyList()); + modTree = maker.addModifiersAnnotation(modTree, annotation); + } + } + if (origModTree != modTree) { + wcopy.rewrite(origModTree, modTree); + } + } + } + + @Override + public void renameField( + @NonNull final String clz, + @NonNull final String oldName, + @NonNull final String newName) throws QueryException { + final WorkingCopy wcopy = (WorkingCopy) control; + final TypeElement te = findClass(clz); + if (te == null) { + throw new IllegalArgumentException("No class: " + clz + " in source: " + //NOI18N + FileUtil.getFileDisplayName(control.getFileObject())); + } + VariableElement field = null; + for (VariableElement ve : ElementFilter.fieldsIn(te.getEnclosedElements())) { + if (oldName.contentEquals(ve.getSimpleName())) { + field = ve; + break; + } + } + if (field == null) { + throw new IllegalArgumentException("No field: " + clz +"."+oldName + " in source: " + //NOI18N + FileUtil.getFileDisplayName(control.getFileObject())); + } + final TreePath fieldPath = wcopy.getTrees().getPath(field); + if (fieldPath == null) { + throw new IllegalArgumentException("No field: " + clz +"."+oldName + " in source: " + //NOI18N + FileUtil.getFileDisplayName(control.getFileObject())); + } + final VariableTree oldVarTree = (VariableTree) fieldPath.getLeaf(); + final VariableTree newVarTree = wcopy.getTreeMaker().Variable( + oldVarTree.getModifiers(), + newName, + oldVarTree.getType(), + oldVarTree.getInitializer()); + wcopy.rewrite(oldVarTree, newVarTree); + } + + @Override + public void fixImports( + final FileObject file, + final Collection requiredFQNs) throws QueryException { + if (!(control instanceof WorkingCopy)) { + throw new IllegalStateException(); + } + final WorkingCopy wcopy = (WorkingCopy) control; + try { + control.toPhase(Phase.RESOLVED); + } catch (IOException ioe) { + throw new QueryException(ioe); + } + final CompilationUnitTree cu = wcopy.getCompilationUnit(); + final Trees trees = wcopy.getTrees(); + final GeneratorUtilities utils = GeneratorUtilities.get(wcopy); + final List toImport = new ArrayList(); + final TreePathScanner scanner = new TreePathScanner(){ + @Override + public Void visitIdentifier(IdentifierTree node, Void p) { + final TreePath select = findSelect(getCurrentPath()); + if (select.getLeaf() != node) { + final Element e = trees.getElement(select); + if (e != null && + (e.getKind().isInterface() || e.getKind().isClass()) && + requiredFQNs.contains(((TypeElement)e).getQualifiedName().toString())) { + toImport.add(select.getLeaf()); + } + } + return super.visitIdentifier(node, p); + } + + private TreePath findSelect(final TreePath path) { + if (path.getParentPath().getLeaf().getKind() != Kind.MEMBER_SELECT) { + return path; + } + return findSelect(path.getParentPath()); + } + }; + scanner.scan(cu, null); + for (Tree tree : toImport) { + wcopy.rewrite(tree, utils.importFQNs(tree)); + } + } + + private List getMethods( + final @NonNull String clz, + final @NullAllowed String methodName, + final boolean rt, + final @NullAllowed String returnType, + final @NullAllowed String... parameterTypes) throws QueryException { + final TypeElement te = findClass(clz); + if (te == null) { + return Collections.emptyList(); + } + final TreeUtilities treeUtils = control.getTreeUtilities(); + final Types types = control.getTypes(); + final List topLevels = control.getTopLevelElements(); + if (topLevels.isEmpty()) { + return Collections.emptyList(); + } + TypeMirror rType = null; + List pTypes = null; + if (returnType != null) { + rType = treeUtils.parseType( + returnType, + topLevels.get(0)); + if (rType == null) { + return Collections.emptyList(); + } else if (rt) { + rType = types.erasure(rType); + } + } + if (parameterTypes != null) { + pTypes = new ArrayList(parameterTypes.length + 1); + for (final String parameterType : parameterTypes) { + TypeMirror tm = treeUtils.parseType( + parameterType, + topLevels.get(0)); + if (tm == null) { + return Collections.emptyList(); + } else if (rt) { + tm = types.erasure(tm); + } + pTypes.add(tm); + } + } + final List result = new ArrayList(); +nextM: for (ExecutableElement me : ElementFilter.methodsIn(te.getEnclosedElements())) { + if (methodName != null && methodName.contentEquals(me.getSimpleName())) { + continue nextM; + } + if (pTypes != null) { + final List params = me.getParameters(); + if (params.size() != pTypes.size()) { + continue nextM; + } + final Iterator paramsIt = params.iterator(); + final Iterator pTypesIt = pTypes.iterator(); + for (;paramsIt.hasNext();) { + if (!isSameType(types, pTypesIt.next(), paramsIt.next().asType(), rt)) { + continue nextM; + } + } + } + if (!isSameType(types, rType, me.getReturnType(), rt)) { + continue nextM; + } + result.add(me); + } + return result; + } + + private TypeElement findClass( + @NonNull final String clz) throws QueryException { + try { + control.toPhase(Phase.ELEMENTS_RESOLVED); + } catch (IOException ioe) { + throw new QueryException(ioe); + } + return control.getElements().getTypeElement(clz); + } + + private TreePath findClassInCompilationUnit( + @NonNull final String clz) throws QueryException { + try { + control.toPhase(Phase.ELEMENTS_RESOLVED); + } catch (IOException ioe) { + throw new QueryException(ioe); + } + final Trees trees = control.getTrees(); + final TreePathScanner visitor = new TreePathScanner() { + @Override + public TreePath visitClass(ClassTree node, Void p) { + final Element el = trees.getElement(getCurrentPath()); + if (el != null && + (el.getKind().isClass() || el.getKind().isInterface()) && + clz.contentEquals(((TypeElement)el).getQualifiedName())) { + return getCurrentPath(); + } else { + return super.visitClass(node, p); + } + } + @Override + public TreePath visitMethod(MethodTree node, Void p) { + return null; + } + }; + return visitor.scan(control.getCompilationUnit(), null); + } + + private static boolean isSameType ( + @NonNull Types types, + @NullAllowed final TypeMirror t1, + @NonNull final TypeMirror t2, + final boolean rawType) { + return t1 == null || + types.isSameType( + t1, + rawType ? types.erasure(t2) : t2); + } + +} diff --git a/java.source.queriesimpl/src/org/netbeans/modules/java/source/queriesimpl/QueriesControllerImpl.java b/java.source.queriesimpl/src/org/netbeans/modules/java/source/queriesimpl/QueriesControllerImpl.java new file mode 100644 --- /dev/null +++ b/java.source.queriesimpl/src/org/netbeans/modules/java/source/queriesimpl/QueriesControllerImpl.java @@ -0,0 +1,118 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 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. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ +package org.netbeans.modules.java.source.queriesimpl; + +import java.io.IOException; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.java.source.CompilationController; +import org.netbeans.api.java.source.JavaSource; +import org.netbeans.api.java.source.ModificationResult; +import org.netbeans.api.java.source.Task; +import org.netbeans.api.java.source.WorkingCopy; +import org.netbeans.modules.java.source.queries.api.QueryException; +import org.netbeans.modules.java.source.queries.api.Queries; +import org.netbeans.modules.java.source.queries.api.Updates; +import org.netbeans.modules.java.source.queries.spi.QueriesController; +import org.openide.filesystems.FileObject; +import org.openide.util.lookup.ServiceProvider; + +/** + * + * @author Tomas Zezula + */ +@ServiceProvider(service=QueriesController.class) +public class QueriesControllerImpl implements QueriesController { + + @Override + public R runQuery(@NonNull Context ctx) throws QueryException { + final FileObject file = ctx.getFile(); + assert file != null; + final JavaSource src = JavaSource.forFileObject(file); + assert src != null; + final QueryTask qt = + new QueryTask(ctx); + try { + src.runUserActionTask(qt, true); + } catch (IOException ioe) { + throw new QueryException(ioe); + } + return qt.getResult(); + } + + @Override + public void runUpdate(@NonNull Context ctx) throws QueryException { + final FileObject file = ctx.getFile(); + assert file != null; + final JavaSource src = JavaSource.forFileObject(file); + assert src != null; + final QueryTask qt = + new QueryTask(ctx); + try { + final ModificationResult result = src.runModificationTask(qt); + if (qt.getResult() == Boolean.TRUE) { + result.commit(); + } + } catch (IOException ioe) { + throw new QueryException(ioe); + } + } + + private static class QueryTask implements Task { + + private final Context ctx; + private R result; + + private QueryTask(@NonNull final Context ctx) { + assert ctx != null; + this.ctx = ctx; + } + + @Override + public void run(C parameter) throws Exception { + result = ctx.execute(new JavaOperationsImpl(parameter)); + } + + public R getResult() { + return result; + } + } +} diff --git a/java.source.queriesimpl/test/unit/src/org/netbeans/modules/java/source/queriesimpl/JavaOperationsImplTest.java b/java.source.queriesimpl/test/unit/src/org/netbeans/modules/java/source/queriesimpl/JavaOperationsImplTest.java new file mode 100644 --- /dev/null +++ b/java.source.queriesimpl/test/unit/src/org/netbeans/modules/java/source/queriesimpl/JavaOperationsImplTest.java @@ -0,0 +1,218 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 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. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ +package org.netbeans.modules.java.source.queriesimpl; + +import com.sun.source.tree.IdentifierTree; +import com.sun.source.tree.Tree; +import com.sun.source.tree.Tree.Kind; +import com.sun.source.util.TreePath; +import com.sun.source.util.TreePathScanner; +import java.io.File; +import java.net.URL; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import junit.framework.Test; +import junit.framework.TestSuite; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.api.editor.mimelookup.test.MockMimeLookup; +import org.netbeans.api.java.classpath.ClassPath; +import org.netbeans.api.java.source.CompilationController; +import org.netbeans.api.java.source.JavaSource; +import org.netbeans.api.java.source.Task; +import org.netbeans.junit.MockServices; +import org.netbeans.junit.NbTestSuite; +import org.netbeans.modules.java.source.parsing.JavacParserFactory; +import org.netbeans.modules.java.source.queries.api.Function; +import org.netbeans.modules.java.source.queries.api.QueryException; +import org.netbeans.modules.java.source.queries.api.Updates; +import org.netbeans.modules.java.source.queries.spi.ModelOperationsTest; +import org.netbeans.modules.parsing.impl.indexing.CacheFolder; +import org.netbeans.spi.java.classpath.ClassPathProvider; +import org.netbeans.spi.java.classpath.support.ClassPathSupport; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; +/** + * + * @author Tomas Zezula + */ +public class JavaOperationsImplTest extends ModelOperationsTest { + + private static final String[] TEST_7 = { + "org.me.test", + "Test7.java", + "package org.me.test;\n"+ + "public class Test7{\n"+ + " java.util.Map m = new java.util.HashMap();\n"+ + " java.util.Map call(final java.util.List l){\n"+ + " final java.util.Map res = new java.util.HashMap(m);\n"+ + " final java.util.Iterator> it = res.entrySet().iterator();\n"+ + " final java.util.Map.Entry e = it.next();\n"+ + " return res;\n"+ + " }\n"+ + "}" + }; + + + public JavaOperationsImplTest(final String name) { + super(name); + } + + public static Test suite() { + TestSuite suite = new NbTestSuite(); + suite.addTest(new JavaOperationsImplTest("testGetTopLevelClasses")); + suite.addTest(new JavaOperationsImplTest("testIsJavaBean")); + suite.addTest(new JavaOperationsImplTest("testIsAvialable")); + suite.addTest(new JavaOperationsImplTest("testGetSuperClass")); + suite.addTest(new JavaOperationsImplTest("testGetInterfaces")); + suite.addTest(new JavaOperationsImplTest("testGetBinaryName")); + suite.addTest(new JavaOperationsImplTest("testGetFieldNames")); + suite.addTest(new JavaOperationsImplTest("testGetMethodNames")); + suite.addTest(new JavaOperationsImplTest("testGetMethodAnnotations")); + suite.addTest(new JavaOperationsImplTest("testModifyInterfaces")); + suite.addTest(new JavaOperationsImplTest("testSetSuperClass")); + suite.addTest(new JavaOperationsImplTest("testModifyMethodAnnotations")); + suite.addTest(new JavaOperationsImplTest("testRenameField")); + suite.addTest(new JavaOperationsImplTest("testFixImports")); + return suite; + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + File cacheFolder = new File (getWorkDir(), "cache"); //NOI18N + cacheFolder.mkdirs(); + CacheFolder.setCacheFolder(FileUtil.toFileObject(cacheFolder)); + CPProvider.srcPath = ClassPathSupport.createClassPath(srcRoot); + CPProvider.bootPath = ClassPathSupport.createClassPath(System.getProperty("sun.boot.class.path")); + CPProvider.compilePath = ClassPathSupport.createClassPath(new URL[0]); + MockServices.setServices(CPProvider.class); + MockMimeLookup.setInstances(MimePath.parse("text/x-java"), new JavacParserFactory()); + } + + + public void testFixImports() throws Exception { + doTestFixImports(TEST_7, Arrays.asList("java.util.List","java.util.Map","java.lang.String")); + } + + private void doTestFixImports( + @NonNull final String[] testCase, + @NonNull final Collection< ? extends String> requiredFQNs) throws Exception { + final FileObject file = prepareTest( + srcRoot, + testCase[0], + testCase[1], + testCase[2]); + Updates.update( + file, + new Function(){ + @Override + public Boolean apply(final Updates param) throws QueryException { + param.fixImports(requiredFQNs); + return true; + } + }); + final Set fqns = new HashSet(); + final JavaSource src = JavaSource.forFileObject(file); + src.runUserActionTask(new Task() { + @Override + public void run(final CompilationController parameter) throws Exception { + parameter.toPhase(JavaSource.Phase.RESOLVED); + final TreePathScanner scanner = new TreePathScanner(){ + @Override + public Void visitIdentifier(IdentifierTree node, Void p) { + final TreePath select = findSelect(getCurrentPath()); + if (select.getLeaf() != node) { + final Element e = parameter.getTrees().getElement(select); + if (e != null && + (e.getKind().isInterface() || e.getKind().isClass())) { + fqns.add(((TypeElement)e).getQualifiedName().toString()); + } + } + return super.visitIdentifier(node, p); + } + + private TreePath findSelect(final TreePath path) { + if (path.getParentPath().getLeaf().getKind() != Kind.MEMBER_SELECT) { + return path; + } + return findSelect(path.getParentPath()); + } + }; + for (Tree tree : parameter.getCompilationUnit().getTypeDecls()) { + scanner.scan(TreePath.getPath( + parameter.getCompilationUnit(),tree), null); + } + } + }, true); + fqns.retainAll(requiredFQNs); + assertTrue(fqns.isEmpty()); + } + + public static class CPProvider implements ClassPathProvider { + private static ClassPath srcPath; + private static ClassPath bootPath; + private static ClassPath compilePath; + @Override + public ClassPath findClassPath( + FileObject file, + String type) { + for (FileObject srcRoot : srcPath.getRoots()) { + if (srcRoot.equals(file) || FileUtil.isParentOf(srcRoot, file)) { + if (type == ClassPath.SOURCE) { + return srcPath; + } else if (type == ClassPath.BOOT) { + return bootPath; + } else if (type == ClassPath.COMPILE) { + return compilePath; + } + } + } + return null; + } + } + +} diff --git a/nbbuild/javadoctools/links.xml b/nbbuild/javadoctools/links.xml --- a/nbbuild/javadoctools/links.xml +++ b/nbbuild/javadoctools/links.xml @@ -204,3 +204,4 @@ + diff --git a/nbbuild/javadoctools/properties.xml b/nbbuild/javadoctools/properties.xml --- a/nbbuild/javadoctools/properties.xml +++ b/nbbuild/javadoctools/properties.xml @@ -202,3 +202,4 @@ + diff --git a/nbbuild/javadoctools/replaces.xml b/nbbuild/javadoctools/replaces.xml --- a/nbbuild/javadoctools/replaces.xml +++ b/nbbuild/javadoctools/replaces.xml @@ -202,3 +202,4 @@ +