blob: b9666e7c49dba51df78c4f8895af5a73636b00fc [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.refactoring.java;
import com.sun.source.tree.*;
import com.sun.source.util.TreePath;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.*;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
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.*;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Types;
import javax.swing.text.Document;
import org.netbeans.api.annotations.common.NullUnknown;
import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.api.java.classpath.ClassPath.Entry;
import org.netbeans.api.java.classpath.GlobalPathRegistry;
import org.netbeans.api.java.classpath.JavaClassPathConstants;
import org.netbeans.api.java.platform.JavaPlatform;
import org.netbeans.api.java.project.JavaProjectConstants;
import org.netbeans.api.java.queries.SourceForBinaryQuery;
import org.netbeans.api.java.queries.SourceForBinaryQuery.Result;
import org.netbeans.api.java.queries.UnitTestForSourceQuery;
import org.netbeans.api.java.source.*;
import org.netbeans.api.project.FileOwnerQuery;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.ProjectUtils;
import org.netbeans.api.project.SourceGroup;
import org.netbeans.api.project.ui.OpenProjects;
import org.netbeans.modules.refactoring.java.plugins.LocalVarScanner;
import org.netbeans.spi.java.classpath.support.ClassPathSupport;
import org.openide.cookies.EditorCookie;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.filesystems.URLMapper;
import org.openide.text.CloneableEditorSupport;
import org.openide.text.NbDocument;
import org.openide.util.NbBundle;
import org.openide.util.Utilities;
import org.openide.windows.TopComponent;
/**
*
* @author Jan Becicka
*/
public class RefactoringUtils {
private static final String JAVA_MIME_TYPE = "text/x-java"; // NOI18N
private static final Logger LOG = Logger.getLogger(RefactoringUtils.class.getName());
/**
* Get all overriding methods for given ExecutableElement
*
* @param e
* @param info
* @return
* @deprecated
*/
@Deprecated
public static Collection<ExecutableElement> getOverridenMethods(ExecutableElement e, CompilationInfo info) {
return getOverridenMethods(e, info.getElementUtilities().enclosingTypeElement(e), info);
}
private static Collection<ExecutableElement> getOverridenMethods(ExecutableElement e, TypeElement parent, CompilationInfo info) {
ArrayList<ExecutableElement> result = new ArrayList<ExecutableElement>();
TypeMirror sup = parent.getSuperclass();
if (sup.getKind() == TypeKind.DECLARED) {
TypeElement next = (TypeElement) ((DeclaredType) sup).asElement();
ExecutableElement overriden = getMethod(e, next, info);
result.addAll(getOverridenMethods(e, next, info));
if (overriden != null) {
result.add(overriden);
}
}
for (TypeMirror tm : parent.getInterfaces()) {
TypeElement next = (TypeElement) ((DeclaredType) tm).asElement();
ExecutableElement overriden2 = getMethod(e, next, info);
result.addAll(getOverridenMethods(e, next, info));
if (overriden2 != null) {
result.add(overriden2);
}
}
return result;
}
private static ExecutableElement getMethod(ExecutableElement method, TypeElement type, CompilationInfo info) {
for (ExecutableElement met : ElementFilter.methodsIn(type.getEnclosedElements())) {
if (info.getElements().overrides(method, met, type)) {
return met;
}
}
return null;
}
public static Set<ElementHandle<TypeElement>> getImplementorsAsHandles(ClassIndex idx, ClasspathInfo cpInfo, TypeElement el, AtomicBoolean cancel) {
LinkedList<ElementHandle<TypeElement>> elements = new LinkedList<ElementHandle<TypeElement>>(
implementorsQuery(idx, ElementHandle.create(el)));
Set<ElementHandle<TypeElement>> result = new HashSet<ElementHandle<TypeElement>>();
while (!elements.isEmpty()) {
if (cancel.get()) {
return Collections.emptySet();
}
ElementHandle<TypeElement> next = elements.removeFirst();
if (!result.add(next)) {
// it is a duplicate; do not query again
continue;
}
Set<ElementHandle<TypeElement>> foundElements = implementorsQuery(idx, next);
elements.addAll(foundElements);
}
return result;
}
private static Set<ElementHandle<TypeElement>> implementorsQuery(ClassIndex idx, ElementHandle<TypeElement> next) {
return idx.getElements(next,
EnumSet.of(ClassIndex.SearchKind.IMPLEMENTORS),
EnumSet.of(ClassIndex.SearchScope.SOURCE, ClassIndex.SearchScope.DEPENDENCIES));
}
/**
*
* @param e
* @param info
* @param cancel
* @return
* @deprecated
*/
@Deprecated
public static Collection<ExecutableElement> getOverridingMethods(ExecutableElement e, CompilationInfo info, AtomicBoolean cancel) {
Collection<ExecutableElement> result = new ArrayList();
TypeElement parentType = (TypeElement) e.getEnclosingElement();
Set<ElementHandle<TypeElement>> subTypes = getImplementorsAsHandles(info.getClasspathInfo().getClassIndex(), info.getClasspathInfo(), parentType, cancel);
for (ElementHandle<TypeElement> subTypeHandle : subTypes) {
TypeElement type = subTypeHandle.resolve(info);
if (type == null) {
// #214462: removed logging, logs show coupling errors
continue;
// #120577: log info to find out what is going wrong
// FileObject file = SourceUtils.getFile(subTypeHandle, info.getClasspathInfo());
// if (file == null) {
// //Deleted file
// continue;
// } else {
// throw new NullPointerException("#120577: Cannot resolve " + subTypeHandle + "; file: " + file + " Classpath: " + info.getClasspathInfo());
// }
}
List<ExecutableElement> methods = new LinkedList<>(ElementFilter.methodsIn(type.getEnclosedElements()));
// #253063 - Anonymous classes of enum constants are not returned by index, need to get them manually
if(type.getKind() == ElementKind.ENUM) {
for (VariableElement variableElement : ElementFilter.fieldsIn(type.getEnclosedElements())) {
TreePath varPath = info.getTrees().getPath(variableElement);
if(varPath != null && varPath.getLeaf().getKind() == Tree.Kind.VARIABLE) {
ExpressionTree initializer = ((VariableTree)varPath.getLeaf()).getInitializer();
if(initializer != null && initializer.getKind() == Tree.Kind.NEW_CLASS) {
NewClassTree ncTree = (NewClassTree) initializer;
ClassTree classBody = ncTree.getClassBody();
if(classBody != null) {
Element anonEl = info.getTrees().getElement(new TreePath(varPath, classBody));
if(anonEl != null) {
methods.addAll(ElementFilter.methodsIn(anonEl.getEnclosedElements()));
}
}
}
}
}
}
for (ExecutableElement method : methods) {
if (info.getElements().overrides(method, e, type)) {
result.add(method);
}
}
}
return result;
}
public static CodeStyle getCodeStyle(CompilationInfo info) {
if (info != null) {
try {
Document doc = info.getDocument();
if (doc != null) {
CodeStyle cs = (CodeStyle)doc.getProperty(CodeStyle.class);
return cs != null ? cs : CodeStyle.getDefault(doc);
}
} catch (IOException ioe) {
// ignore
}
FileObject file = info.getFileObject();
if (file != null) {
return CodeStyle.getDefault(file);
}
}
return CodeStyle.getDefault((Document)null);
}
/**
*
* @param f
* @return true if f is java
*/
public static boolean isJavaFile(FileObject f) {
return JAVA_MIME_TYPE.equals(FileUtil.getMIMEType(f, JAVA_MIME_TYPE));
}
/**
* @param element
* @param info
* @return true if given element comes from library
*/
public static boolean isFromLibrary(ElementHandle<? extends Element> element, ClasspathInfo info) {
FileObject file = SourceUtils.getFile(element, info);
if (file == null) {
//no source for given element. Element is from library
return true;
}
return FileUtil.getArchiveFile(file) != null;
}
/**
* is given name valid package name
*
* @param name
* @return
*/
public static boolean isValidPackageName(String name) {
if (name.endsWith(".")) //NOI18N
{
return false;
}
if (name.startsWith(".")) //NOI18N
{
return false;
}
if (name.contains("..")) //NOI18N
{
return false;
}
StringTokenizer tokenizer = new StringTokenizer(name, "."); // NOI18N
while (tokenizer.hasMoreTokens()) {
if (!Utilities.isJavaIdentifier(tokenizer.nextToken())) {
return false;
}
}
return true;
}
/**
*
* @param f
* @return true if given file is in open project
*/
public static boolean isFileInOpenProject(FileObject file) {
assert file != null;
// Future<Project[]> o.n.api.project.ui.OpenProjects.openProjects()
Future<Project[]> openProjects = OpenProjects.getDefault().openProjects();
if(!openProjects.isDone()) {
return false;
}
Project p = FileOwnerQuery.getOwner(file);
if (p == null) {
return false;
}
return isOpenProject(p);
}
/**
* Is given file on any source classpath?
*
* @param fo
* @return
* @deprecated
*/
@Deprecated
public static boolean isOnSourceClasspath(FileObject fo) {
Project pr = FileOwnerQuery.getOwner(fo);
if (pr == null) {
return false;
}
//workaround for 143542
for (String type : new String[]{JavaProjectConstants.SOURCES_TYPE_JAVA, JavaProjectConstants.SOURCES_TYPE_RESOURCES}) {
for (SourceGroup sg : ProjectUtils.getSources(pr).getSourceGroups(type)) {
if (fo == sg.getRootFolder() || (FileUtil.isParentOf(sg.getRootFolder(), fo) && sg.contains(fo))) {
return ClassPath.getClassPath(fo, ClassPath.SOURCE) != null;
}
}
}
return false;
//end of workaround
//return ClassPath.getClassPath(fo, ClassPath.SOURCE)!=null;
}
/**
* Is given file a root of source classpath?
*
* @param fo
* @return
*/
public static boolean isClasspathRoot(FileObject fo) {
ClassPath cp = ClassPath.getClassPath(fo, ClassPath.SOURCE);
return cp != null ? fo.equals(cp.findOwnerRoot(fo)) : false;
}
/**
* Is the given file "java" && in open projects && on source classpath?
*
* @param file
* @return
* @deprecated
*/
@Deprecated
public static boolean isRefactorable(FileObject file) {
return file != null && isJavaFile(file) && isFileInOpenProject(file) && isOnSourceClasspath(file);
}
/**
* returns package name for given folder. Folder must be on source classpath
*
* @param folder
* @return
*/
public static String getPackageName(FileObject folder) {
assert folder.isFolder() : "argument must be folder";
ClassPath cp = ClassPath.getClassPath(folder, ClassPath.SOURCE);
if (cp == null) {
// see http://www.netbeans.org/issues/show_bug.cgi?id=159228
throw new IllegalStateException(String.format("No classpath for %s.", folder.getPath())); // NOI18N
}
return cp.getResourceName(folder, '.', false);
}
/**
* get package name for given CompilationUnitTree
*
* @param unit
* @return
*/
public static String getPackageName(CompilationUnitTree unit) {
assert unit != null;
ExpressionTree name = unit.getPackageName();
if (name == null) {
//default package
return "";
}
return name.toString();
}
/**
* get package name for given url.
*
* @param url
* @return
*/
public static String getPackageName(URL url) {
File f = null;
try {
f = FileUtil.normalizeFile(Utilities.toFile(url.toURI()));
} catch (URISyntaxException ex) {
throw new IllegalArgumentException(ex);
}
String suffix = "";
do {
FileObject fo = FileUtil.toFileObject(f);
if (fo != null) {
if ("".equals(suffix)) {
return getPackageName(fo);
}
String prefix = getPackageName(fo);
return prefix + ("".equals(prefix) ? "" : ".") + suffix; // NOI18N
}
if (!"".equals(suffix)) {
suffix = "." + suffix; // NOI18N
}
suffix = f.getPath().substring(f.getPath().lastIndexOf(File.separatorChar) + 1) + suffix; // NOI18N
f = f.getParentFile();
} while (f != null);
throw new IllegalArgumentException("Cannot create package name for url " + url); // NOI18N
}
/**
* creates or finds FileObject according to
*
* @param url
* @return FileObject
*/
public static FileObject getOrCreateFolder(URL url) throws IOException {
try {
FileObject result = URLMapper.findFileObject(url);
if (result != null) {
return result;
}
File f = new File(url.toURI());
result = FileUtil.createFolder(f);
return result;
} catch (URISyntaxException ex) {
throw new IOException(ex);
}
}
/**
*
* @param url
* @return
* @throws IOException
*/
public static FileObject getClassPathRoot(URL url) throws IOException {
FileObject result = getRootFileObject(url);
if(result == null) {
return null;
}
ClassPath classPath = ClassPath.getClassPath(result, ClassPath.SOURCE);
if(classPath == null) {
return null;
}
return classPath.findOwnerRoot(result);
}
/**
* Get all supertypes for given type
*
* @param type
* @param info
* @return
* @deprecated
*/
@Deprecated
public static Collection<TypeElement> getSuperTypes(TypeElement type, CompilationInfo info) {
Collection<TypeElement> result = new HashSet<TypeElement>();
LinkedList<TypeElement> l = new LinkedList<TypeElement>();
l.add(type);
while (!l.isEmpty()) {
TypeElement t = l.removeFirst();
TypeElement superClass = typeToElement(t.getSuperclass(), info);
if (superClass != null) {
result.add(superClass);
l.addLast((TypeElement) superClass);
}
Collection<TypeElement> interfaces = typesToElements(t.getInterfaces(), info);
result.addAll(interfaces);
l.addAll(interfaces);
}
return result;
}
/**
* get supertypes of given types
*
* @param type
* @param info
* @param sourceOnly true if only types defined in open project should be
* searched
* @return
* @deprecated
*/
@Deprecated
public static Collection<TypeElement> getSuperTypes(TypeElement type, CompilationInfo info, boolean sourceOnly) {
if (!sourceOnly) {
return getSuperTypes(type, info);
}
Collection<TypeElement> result = new HashSet<TypeElement>();
for (TypeElement el : getSuperTypes(type, info)) {
ElementHandle<TypeElement> handle = ElementHandle.create(el);
FileObject file = SourceUtils.getFile(handle, info.getClasspathInfo());
if (file != null && isFileInOpenProject(file) && !isFromLibrary(handle, info.getClasspathInfo())) {
result.add(el);
}
}
return result;
}
public static TypeElement typeToElement(TypeMirror type, CompilationInfo info) {
return (TypeElement) info.getTypes().asElement(type);
}
private static boolean isOpenProject(Project p) {
return OpenProjects.getDefault().isProjectOpen(p);
}
private static Collection<TypeElement> typesToElements(Collection<? extends TypeMirror> types, CompilationInfo info) {
Collection<TypeElement> result = new HashSet();
for (TypeMirror tm : types) {
result.add(typeToElement(tm, info));
}
return result;
}
public static Collection<FileObject> elementsToFile(Collection<? extends Element> elements, ClasspathInfo cpInfo) {
Collection<FileObject> result = new HashSet();
for (Element handle : elements) {
result.add(SourceUtils.getFile(handle, cpInfo));
}
return result;
}
public static boolean elementExistsIn(TypeElement target, Element member, CompilationInfo info) {
for (Element currentMember : target.getEnclosedElements()) {
if (currentMember.getKind().equals(member.getKind())
&& currentMember.getSimpleName().equals(member.getSimpleName())) {
if (currentMember.getKind() == ElementKind.METHOD) {
ExecutableElement exMethod = (ExecutableElement) currentMember;
ExecutableElement method = (ExecutableElement) member;
if (exMethod.getParameters().size() == method.getParameters().size()) {
boolean sameParameters = true;
for (int j = 0; j < exMethod.getParameters().size(); j++) {
TypeMirror exType = ((VariableElement) exMethod.getParameters().get(j)).asType();
TypeMirror paramType = method.getParameters().get(j).asType();
if (!info.getTypes().isSameType(exType, paramType)) {
sameParameters = false;
}
}
if (sameParameters) {
return true;
}
}
} else {
return true;
}
}
}
return false;
}
/**
* @param fqn
* @param info
* @return
*/
public static boolean typeExists(String fqn, CompilationInfo info) {
return info.getElements().getTypeElement(fqn) != null;
}
/**
* create ClasspathInfo for specified files includes dependencies
*
* @param files
* @return
* @deprecated
*/
@Deprecated
public static ClasspathInfo getClasspathInfoFor(FileObject... files) {
return getClasspathInfoFor(true, files);
}
/**
* create ClasspathInfo for specified files
*
* @param dependencies
* @param files
* @return
*/
public static ClasspathInfo getClasspathInfoFor(boolean dependencies, FileObject... files) {
return getClasspathInfoFor(dependencies, false, files);
}
/**
* create ClasspathInfo for specified files
*
* @param dependencies include dependencies
* @param backSource libraries replaces by sources using
* SourceForBinaryQuery
* @param files
* @return
*/
@SuppressWarnings("CollectionContainsUrl")
public static ClasspathInfo getClasspathInfoFor(boolean dependencies, boolean backSource, FileObject... files) {
assert files.length > 0;
Set<URL> dependentSourceRoots = new HashSet();
Set<URL> dependentCompileRoots = new HashSet();
ClassPath nullPath = ClassPathSupport.createClassPath(new FileObject[0]);
ClassPath boot = null;
ClassPath moduleBoot = null;
ClassPath compile = null;
ClassPath moduleCompile = null;
ClassPath moduleClass = null;
for (FileObject fo : files) {
ClassPath cp = null;
FileObject ownerRoot = null;
if (fo != null) {
cp = ClassPath.getClassPath(fo, ClassPath.SOURCE);
if (cp != null) {
ownerRoot = cp.findOwnerRoot(fo);
}
}
if (cp != null && ownerRoot != null && FileUtil.getArchiveFile(ownerRoot) == null) {
for (FileObject src : cp.getRoots()) { // Keep all source roots from cp. Needed if project has multiple source roots.
URL sourceRoot = URLMapper.findURL(src, URLMapper.INTERNAL);
if (dependencies) {
Set<URL> urls = SourceUtils.getDependentRoots(sourceRoot, false);
Set<ClassPath> cps = GlobalPathRegistry.getDefault().getPaths(ClassPath.SOURCE);
Set<URL> toRetain = new HashSet<URL>();
for (ClassPath path : cps) {
for (ClassPath.Entry e : path.entries()) {
toRetain.add(e.getURL());
}
}
Set<URL> compileUrls = new HashSet<URL>(urls);
urls.retainAll(toRetain);
compileUrls.removeAll(toRetain);
dependentSourceRoots.addAll(urls);
dependentCompileRoots.addAll(compileUrls);
} else {
dependentSourceRoots.add(sourceRoot);
}
if (FileOwnerQuery.getOwner(fo) != null) {
for (FileObject f : cp.getRoots()) {
dependentCompileRoots.add(URLMapper.findURL(f, URLMapper.INTERNAL));
}
}
}
} else {
for (ClassPath scp : GlobalPathRegistry.getDefault().getPaths(ClassPath.SOURCE)) {
for (FileObject root : scp.getRoots()) {
dependentSourceRoots.add(URLMapper.findURL(root, URLMapper.INTERNAL));
}
}
}
if(fo != null) {
ClassPath fboot = ClassPath.getClassPath(fo, ClassPath.BOOT);
ClassPath fmoduleboot = ClassPath.getClassPath(fo, JavaClassPathConstants.MODULE_BOOT_PATH);
ClassPath fcompile = ClassPath.getClassPath(fo, ClassPath.COMPILE);
ClassPath fmodulecompile = ClassPath.getClassPath(fo, JavaClassPathConstants.MODULE_COMPILE_PATH);
ClassPath fmoduleclass = ClassPath.getClassPath(fo, JavaClassPathConstants.MODULE_CLASS_PATH);
//When file[0] is a class file, there is no compile cp but execute cp
//try to get it
if (fcompile == null) {
fcompile = ClassPath.getClassPath(fo, ClassPath.EXECUTE);
}
//If no cp found at all log the file and use nullPath since the ClasspathInfo.create
//doesn't accept null compile or boot cp.
if (fcompile == null) {
LOG.log(Level.WARNING, "No classpath for: {0} {1}", new Object[]{FileUtil.getFileDisplayName(fo), FileOwnerQuery.getOwner(fo)}); //NOI18N
} else {
compile = compile != null ? merge(compile, fcompile) : fcompile;
}
if (fboot != null) {
boot = boot != null ? merge(boot, fboot) : fboot;
}
if (fmoduleboot != null) {
moduleBoot = moduleBoot != null ? merge(moduleBoot, fmoduleboot) : fmoduleboot;
}
if (fmodulecompile != null) {
moduleCompile = moduleCompile != null ? merge(moduleCompile, fmodulecompile) : fmodulecompile;
}
if (fmoduleclass != null) {
moduleClass = moduleClass != null ? merge(moduleClass, fmoduleclass) : fmoduleclass;
}
}
}
if (backSource) {
for (FileObject file : files) {
if (file != null) {
ClassPath source = ClassPath.getClassPath(file, ClassPath.COMPILE);
for (Entry root : source.entries()) {
Result r = SourceForBinaryQuery.findSourceRoots(root.getURL());
for (FileObject root2 : r.getRoots()) {
dependentSourceRoots.add(URLMapper.findURL(root2, URLMapper.INTERNAL));
}
}
}
}
}
ClassPath rcp = ClassPathSupport.createClassPath(dependentSourceRoots.toArray(new URL[dependentSourceRoots.size()]));
if (compile == null) {
compile = nullPath;
}
compile = merge(compile, ClassPathSupport.createClassPath(dependentCompileRoots.toArray(new URL[dependentCompileRoots.size()])));
if (boot == null) {
boot = JavaPlatform.getDefault().getBootstrapLibraries();
}
return new ClasspathInfo.Builder(boot == null ? nullPath : boot)
.setModuleBootPath(moduleBoot == null ? boot == null? nullPath : boot : moduleBoot)
.setClassPath(compile)
.setModuleCompilePath(moduleCompile)
.setModuleClassPath(moduleClass)
.setSourcePath(rcp).
build();
}
/**
* @param handle
* @return
*/
public static FileObject getFileObject(TreePathHandle handle) {
ElementHandle elementHandle = handle.getElementHandle();
if (elementHandle == null ) {
return handle.getFileObject();
}
ClasspathInfo info = getClasspathInfoFor(false, handle.getFileObject());
return SourceUtils.getFile(elementHandle, info);
}
/**
* create ClasspathInfo for specified handles
*
* @param handles
* @return
*/
public static ClasspathInfo getClasspathInfoFor(TreePathHandle... handles) {
FileObject[] result = new FileObject[handles.length];
int i = 0;
for (TreePathHandle handle : handles) {
FileObject fo = getFileObject(handle);
if (i == 0 && fo == null) {
result = new FileObject[handles.length + 1];
result[i++] = handle.getFileObject();
}
result[i++] = fo;
}
return getClasspathInfoFor(result);
}
/**
* Finds type parameters from
* <code>typeArgs</code> list that are referenced by
* <code>tm</code> type.
*
* @param utils compilation type utils
* @param typeArgs modifiable list of type parameters to search; found types
* will be removed (performance reasons).
* @param result modifiable list that will contain referenced type
* parameters
* @param tm parametrized type to analyze
*/
public static void findUsedGenericTypes(Types utils, List<TypeMirror> typeArgs, Set<TypeMirror> result, TypeMirror tm) {
if (typeArgs.isEmpty()) {
return;
} else if (tm.getKind() == TypeKind.TYPEVAR) {
TypeVariable type = (TypeVariable) tm;
int index = findTypeIndex(utils, typeArgs, type);
if (index >= 0) {
result.add(typeArgs.get(index));
} else {
TypeMirror low = type.getLowerBound();
if (low != null && low.getKind() != TypeKind.NULL) {
findUsedGenericTypes(utils, typeArgs, result, low);
}
TypeMirror up = type.getUpperBound();
if (up != null) {
findUsedGenericTypes(utils, typeArgs, result, up);
}
int idx = findTypeIndex(utils, typeArgs, type);
if (idx >= 0) {
result.add(typeArgs.get(idx));
}
}
} else if (tm.getKind() == TypeKind.DECLARED) {
DeclaredType type = (DeclaredType) tm;
for (TypeMirror tp : type.getTypeArguments()) {
findUsedGenericTypes(utils, typeArgs, result, tp);
}
} else if (tm.getKind() == TypeKind.WILDCARD) {
WildcardType type = (WildcardType) tm;
TypeMirror ex = type.getExtendsBound();
if (ex != null) {
findUsedGenericTypes(utils, typeArgs, result, ex);
}
TypeMirror su = type.getSuperBound();
if (su != null) {
findUsedGenericTypes(utils, typeArgs, result, su);
}
}
}
private static int findTypeIndex(Types utils, List<TypeMirror> typeArgs, TypeMirror type) {
int i = -1;
for (TypeMirror typeArg : typeArgs) {
i++;
if (utils.isSameType(type, typeArg)) {
return i;
}
}
return -1;
}
public static List<TypeMirror> filterTypes(List<TypeMirror> source, Set<TypeMirror> used) {
List<TypeMirror> result = new ArrayList<TypeMirror>(source.size());
for (TypeMirror tm : source) {
if (used.contains(tm)) {
result.add(tm);
}
}
return result;
}
/**
* translates list of elements to list of types
*
* @param typeParams elements
* @return types
* @deprecated
*/
@Deprecated
public static List<TypeMirror> resolveTypeParamsAsTypes(List<? extends Element> typeParams) {
if (typeParams.isEmpty()) {
return Collections.<TypeMirror>emptyList();
}
List<TypeMirror> typeArgs = new ArrayList<TypeMirror>(typeParams.size());
for (Element elm : typeParams) {
typeArgs.add(elm.asType());
}
return typeArgs;
}
/**
* finds the nearest enclosing ClassTree on
* <code>path</code> that is class or interface or enum or annotation type
* and is or is not annonymous. In case no ClassTree is found the first top
* level ClassTree is returned.
*
* Especially useful for selecting proper tree to refactor.
*
* @param javac javac
* @param path path to search
* @param isClass stop on class
* @param isInterface stop on interface
* @param isEnum stop on enum
* @param isAnnotation stop on annotation type
* @param isAnonymous check if class or interface is annonymous
* @return path to the enclosing ClassTree
* @deprecated
*/
@Deprecated
public static @NullUnknown
TreePath findEnclosingClass(CompilationInfo javac, TreePath path, boolean isClass, boolean isInterface, boolean isEnum, boolean isAnnotation, boolean isAnonymous) {
if (path == null) {
return null;
}
Tree selectedTree = path.getLeaf();
TreeUtilities utils = javac.getTreeUtilities();
while (true) {
if (TreeUtilities.CLASS_TREE_KINDS.contains(selectedTree.getKind())) {
ClassTree classTree = (ClassTree) selectedTree;
if (isEnum && utils.isEnum(classTree)
|| isInterface && utils.isInterface(classTree)
|| isAnnotation && utils.isAnnotation(classTree)
|| isClass && !(utils.isInterface(classTree) || utils.isEnum(classTree) || utils.isAnnotation(classTree))) {
Tree.Kind parentKind = path.getParentPath().getLeaf().getKind();
if (isAnonymous || Tree.Kind.NEW_CLASS != parentKind) {
break;
}
}
}
path = path.getParentPath();
if (path == null) {
List<? extends Tree> typeDecls = javac.getCompilationUnit().getTypeDecls();
if (typeDecls.isEmpty()) {
return null;
}
selectedTree = typeDecls.get(0);
if (selectedTree.getKind().asInterface() == ClassTree.class) {
return javac.getTrees().getPath(javac.getCompilationUnit(), selectedTree);
} else {
return null;
}
}
selectedTree = path.getLeaf();
}
return path;
}
//XXX: copied from SourceUtils.addImports. Ideally, should be on one place only:
public static CompilationUnitTree addImports(CompilationUnitTree cut, List<String> toImport, TreeMaker make)
throws IOException {
// do not modify the list given by the caller (may be reused or immutable).
toImport = new ArrayList<String>(toImport);
Collections.sort(toImport);
List<ImportTree> imports = new ArrayList<ImportTree>(cut.getImports());
int currentToImport = toImport.size() - 1;
int currentExisting = imports.size() - 1;
while (currentToImport >= 0 && currentExisting >= 0) {
String currentToImportText = toImport.get(currentToImport);
while (currentExisting >= 0 && (imports.get(currentExisting).isStatic() || imports.get(currentExisting).getQualifiedIdentifier().toString().compareTo(currentToImportText) > 0)) {
currentExisting--;
}
if (currentExisting >= 0) {
imports.add(currentExisting + 1, make.Import(make.Identifier(currentToImportText), false));
currentToImport--;
}
}
// we are at the head of import section and we still have some imports
// to add, put them to the very beginning
while (currentToImport >= 0) {
String importText = toImport.get(currentToImport);
imports.add(0, make.Import(make.Identifier(importText), false));
currentToImport--;
}
// return a copy of the unit with changed imports section
return make.CompilationUnit(cut.getPackageAnnotations(), cut.getPackageName(), imports, cut.getTypeDecls(), cut.getSourceFile());
}
/**
* transforms passed modifiers to abstract form
*
* @param make a tree maker
* @param oldMods modifiers of method or class
* @return the abstract form of ModifiersTree
*/
public static ModifiersTree makeAbstract(TreeMaker make, ModifiersTree oldMods) {
if (oldMods.getFlags().contains(Modifier.ABSTRACT)) {
return oldMods;
}
Set<Modifier> flags = new HashSet<Modifier>(oldMods.getFlags());
flags.add(Modifier.ABSTRACT);
flags.remove(Modifier.FINAL);
return make.Modifiers(flags, oldMods.getAnnotations());
}
public static String variableClashes(String newName, TreePath tp, CompilationInfo info) {
LocalVarScanner lookup = new LocalVarScanner(info, newName);
TreePath scopeBlok = tp;
EnumSet set = EnumSet.of(Tree.Kind.BLOCK, Tree.Kind.FOR_LOOP, Tree.Kind.METHOD);
while (scopeBlok != null && !set.contains(scopeBlok.getLeaf().getKind())) {
scopeBlok = scopeBlok.getParentPath();
}
if(scopeBlok == null) {
return null;
}
Element var = info.getTrees().getElement(tp);
if (var != null) {
lookup.scan(scopeBlok, var);
}
if (lookup.hasRefernces()) {
return NbBundle.getMessage(RefactoringUtils.class, "MSG_LocVariableClash",newName);
}
TreePath temp = tp;
while (temp != null && temp.getLeaf().getKind() != Tree.Kind.METHOD) {
Scope scope = info.getTrees().getScope(temp);
for (Element el : scope.getLocalElements()) {
if (el.getSimpleName().toString().equals(newName)) {
return NbBundle.getMessage(RefactoringUtils.class, "MSG_LocVariableClash",newName);
}
}
temp = temp.getParentPath();
}
return null;
}
public static boolean isSetter(CompilationInfo info, ExecutableElement el, Element propertyElement) {
CodeStyle codeStyle = getCodeStyle(info);
String setterName = CodeStyleUtils.computeSetterName(
propertyElement.getSimpleName(),
propertyElement.getModifiers().contains(Modifier.STATIC),
codeStyle);
return el.getSimpleName().contentEquals(setterName)
&& el.getReturnType().getKind() == TypeKind.VOID
&& el.getParameters().size() == 1
&& info.getTypes().isSameType(el.getParameters().iterator().next().asType(), propertyElement.asType());
}
public static boolean isGetter(CompilationInfo info, ExecutableElement el, Element propertyElement) {
CodeStyle codeStyle = getCodeStyle(info);
String getterName = CodeStyleUtils.computeGetterName(
propertyElement.getSimpleName(),
propertyElement.asType().getKind() == TypeKind.BOOLEAN,
propertyElement.getModifiers().contains(Modifier.STATIC),
codeStyle);
return el.getSimpleName().contentEquals(getterName)
&& info.getTypes().isSameType(el.getReturnType(),propertyElement.asType())
&& el.getParameters().isEmpty();
}
public static String removeFieldPrefixSuffix(Element var, CodeStyle cs) {
boolean isStatic = var.getModifiers().contains(Modifier.STATIC);
return CodeStyleUtils.removePrefixSuffix(var.getSimpleName(),
isStatic ? cs.getStaticFieldNamePrefix() : cs.getFieldNamePrefix(),
isStatic ? cs.getStaticFieldNameSuffix() : cs.getFieldNameSuffix());
}
public static String addParamPrefixSuffix(CharSequence name, CodeStyle cs) {
return CodeStyleUtils.addPrefixSuffix(name,
cs.getParameterNamePrefix(),
cs.getParameterNameSuffix());
}
public static String getTestMethodName(String propertyName) {
return "test" + CodeStyleUtils.getCapitalizedName(propertyName); //NOI18N
}
public static boolean isWeakerAccess(Set<Modifier> modifiers, Set<Modifier> modifiers0) {
return accessLevel(modifiers) < accessLevel(modifiers0);
}
private static int accessLevel(Set<Modifier> modifiers) {
if (modifiers.contains(Modifier.PRIVATE)) {
return 0;
}
if (modifiers.contains(Modifier.PROTECTED)) {
return 2;
}
if (modifiers.contains(Modifier.PUBLIC)) {
return 3;
}
return 1;
}
public static String getAccess(Set<Modifier> modifiers) {
if (modifiers.contains(Modifier.PRIVATE)) {
return "private"; //NOI18N
}
if (modifiers.contains(Modifier.PROTECTED)) {
return "protected"; //NOI18N
}
if (modifiers.contains(Modifier.PUBLIC)) {
return "public"; //NOI18N
}
return "<default>"; //NOI18N
}
public static boolean isFromTestRoot(FileObject file, ClassPath cp) {
boolean inTest = false;
if (cp != null) {
FileObject root = cp.findOwnerRoot(file);
if (root != null && UnitTestForSourceQuery.findSources(root).length > 0) {
inTest = true;
}
}
return inTest;
}
public static FileObject getRootFileObject(URL url) throws IOException {
FileObject result = URLMapper.findFileObject(url);
File f;
try {
f = result != null ? null : FileUtil.normalizeFile(Utilities.toFile(url.toURI())); //NOI18N
} catch (URISyntaxException ex) {
throw new IOException(ex);
}
while (result == null && f != null) {
result = FileUtil.toFileObject(f);
f = f.getParentFile();
}
return result;
}
@SuppressWarnings("CollectionContainsUrl")
public static ClassPath merge(final ClassPath... cps) {
final Set<URL> roots = new LinkedHashSet<URL>(cps.length);
for (final ClassPath cp : cps) {
if (cp != null) {
for (final ClassPath.Entry entry : cp.entries()) {
final URL root = entry.getURL();
if (!roots.contains(root)) {
roots.add(root);
}
}
}
}
return ClassPathSupport.createClassPath(roots.toArray(new URL[roots.size()]));
}
public static boolean isFromEditor(EditorCookie ec) {
if (ec != null && NbDocument.findRecentEditorPane(ec) != null) {
TopComponent activetc = TopComponent.getRegistry().getActivated();
if (activetc instanceof CloneableEditorSupport.Pane) {
return true;
}
}
return false;
}
/**
* Checks if the element is a method or constructor. Returns {@code false} for {@code null} input.
* @param e element to check
* @return true iff the element is a constructor or method.
*/
public static boolean isExecutableElement(Element e) {
if (e == null) {
return false;
}
ElementKind ek = e.getKind();
return ek == ElementKind.CONSTRUCTOR || ek == ElementKind.METHOD;
}
private RefactoringUtils() {
}
}