blob: d2bd00113c467f6cc2e3e1ed59365ddd98fe4431 [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.java.source.ui;
import org.netbeans.api.java.source.*;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.URL;
import java.util.*;
import javax.swing.event.ChangeListener;
import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.api.java.platform.JavaPlatformManager;
import org.netbeans.api.java.queries.SourceForBinaryQuery;
import org.netbeans.junit.MockServices;
import org.netbeans.junit.NbTestCase;
import org.netbeans.modules.java.source.parsing.FileObjects;
import org.netbeans.modules.java.source.ui.JavaTypeProvider;
import org.netbeans.modules.java.source.usages.IndexUtil;
import org.netbeans.modules.parsing.api.indexing.IndexingManager;
import org.netbeans.spi.java.classpath.ClassPathFactory;
import org.netbeans.spi.java.classpath.ClassPathImplementation;
import org.netbeans.spi.java.classpath.ClassPathProvider;
import org.netbeans.spi.java.classpath.PathResourceImplementation;
import org.netbeans.spi.java.classpath.support.ClassPathSupport;
import org.netbeans.spi.java.queries.SourceForBinaryQueryImplementation;
import org.netbeans.spi.jumpto.type.JumptoAccessor;
import org.netbeans.spi.jumpto.type.SearchType;
import org.netbeans.spi.jumpto.type.TypeDescriptor;
import org.netbeans.spi.jumpto.type.TypeProvider;
import org.openide.filesystems.FileLock;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileStateInvalidException;
import org.openide.filesystems.FileUtil;
/**
* Test setup taken from org.netbeans.api.java.source.ClassIndexTest.
*
* @author Tomas Zezula
* @author markiewb
*/
public class JavaTypeProviderTest extends NbTestCase {
private static FileObject srcRoot;
private static FileObject binRoot2;
private static FileObject libSrc2;
private static ClassPath sourcePath;
private static ClassPath compilePath;
private static ClassPath bootPath;
private static JavaTypeProviderTest.MutableCp spiCp;
private static JavaTypeProviderTest.MutableCp spiSrc;
private JavaTypeProvider provider;
public JavaTypeProviderTest(String name) {
super(name);
}
String[] cA = {"ClassA", "org.me.pkg1"};
String[] cB = {"ClassA", "org.me.pkg1sibling"};
String[] cC = {"ClassA", "org.me.pkg1.subpackage"};
String[] cD = {"ClassB", "org.me.pkg2"};
String[] cE = {"ClassA", "com.other"};
String[] cF = {"ClassB", "org.me.pkg1.subpackage"};
@Override
protected void setUp() throws Exception {
clearWorkDir();
File cache = new File(getWorkDir(), "cache"); //NOI18N
cache.mkdirs();
IndexUtil.setCacheFolder(cache);
File src = new File(getWorkDir(), "src"); //NOI18N
src.mkdirs();
srcRoot = FileUtil.toFileObject(src);
srcRoot.createFolder("foo"); //NOI18N
src = new File(getWorkDir(), "src2"); //NOI18N
src.mkdirs();
src = new File(getWorkDir(), "lib"); //NOI18N
src.mkdirs();
src = new File(getWorkDir(), "lib2"); //NOI18N
src.mkdirs();
binRoot2 = FileUtil.toFileObject(src);
src = new File(getWorkDir(), "lib2Src"); //NOI18N
src.mkdirs();
libSrc2 = FileUtil.toFileObject(src);
spiSrc = new JavaTypeProviderTest.MutableCp(Collections.singletonList(ClassPathSupport.createResource(srcRoot.getURL())));
sourcePath = ClassPathFactory.createClassPath(spiSrc);
spiCp = new JavaTypeProviderTest.MutableCp();
compilePath = ClassPathFactory.createClassPath(spiCp);
bootPath = JavaPlatformManager.getDefault().getDefaultPlatform().getBootstrapLibraries();
MockServices.setServices(JavaTypeProviderTest.ClassPathProviderImpl.class, JavaTypeProviderTest.SFBQ.class);
//create some types to be searched for later
String[][] testTypes = {
cA, cB, cC, cD, cE, cF
};
for (String[] entry : testTypes) {
createJavaFile(srcRoot, entry[1], entry[0]);
}
IndexingManager.getDefault().refreshIndexAndWait(srcRoot.getURL(), null, true);
final ClassPath scp = ClassPathSupport.createClassPath(srcRoot);
final ClasspathInfo cpInfo = ClasspathInfo.create(
ClassPathSupport.createClassPath(new URL[0]),
ClassPathSupport.createClassPath(new URL[0]),
scp);
provider = new JavaTypeProvider(cpInfo, null);
}
@Override
protected void tearDown() throws Exception {
MockServices.setServices();
}
/**
* Tests the behaviour, when no package name is included in searchText.
* Packagenames should be ignored.
*
* @throws Exception
*/
public void testGetDeclaredTypesScopes_DefaultBehaviour() throws Exception {
//original behaviour - without packagename
{
String[][] expectedResults = {cE, cA, cC, cB};
assertComputeTypeNames(expectedResults, "ClassA", provider);
}
//original behaviour - without packagename
{
String[][] expectedResults = {cF, cD};
assertComputeTypeNames(expectedResults, "ClassB", provider);
}
}
/**
* A partially given FQN may result in more than one class type. The
* packagename is used for retrival of package and subpackages, which
* results in more results.
*
* @throws Exception
*/
public void testGetDeclaredTypesScopes_FullyQualifiedName() throws Exception {
//search for classes with prefix "ClassA" and packages with (sub-)package "org.me.pkg1"
{
String[][] expectedResults = {cA, cC, cB};
assertComputeTypeNames(expectedResults, "org.me.pkg1.ClassA", provider);
}
//search for classes with prefix "ClassA" and packages with (sub-)package "org"
{
String[][] expectedResults = {cA, cC, cB};
assertComputeTypeNames(expectedResults, "org.ClassA", provider);
}
//search for classes with prefix "ClassA" and packages with (sub-)package "org.me"
{
String[][] expectedResults = {cA, cC, cB};
assertComputeTypeNames(expectedResults, "org.me.ClassA", provider);
}
//search for classes with prefix "Cl" and packages with (sub-)package "org.me"
{
String[][] expectedResults = {cA, cC, cB, cF, cD};
assertComputeTypeNames(expectedResults, "org.me.Cl", provider);
}
//search for classes with prefix "Cl" and packages with (sub-)package "o*.m*"
{
String[][] expectedResults = {cA, cC, cB, cF, cD};
assertComputeTypeNames(expectedResults, "o.m.Cl", provider);
}
//search for classes with camel case "CA" and packages with (sub-)package "o*.m*"
{
String[][] expectedResults = {cA, cC, cB};
assertComputeTypeNames(expectedResults, "o.m.CA", provider);
}
//search for classes with camel case "CA" and packages with (sub-)package "o*.m*"
{
String[][] expectedResults = {cB};
assertComputeTypeNames(expectedResults, "o.m.pkg1sib.CA", provider);
}
}
private FileObject createJavaFile(
final FileObject root,
final String pkg,
final String name,
final String content) throws IOException {
final FileObject file = FileUtil.createData(
root,
String.format("%s/%s.java",
FileObjects.convertPackage2Folder(pkg),
name));
final FileLock lck = file.lock();
try {
final PrintWriter out = new PrintWriter(new OutputStreamWriter(file.getOutputStream(lck)));
try {
out.print(content);
} finally {
out.close();
}
} finally {
lck.releaseLock();
}
return file;
}
private void assertComputeTypeNames(String[][] expectedResults, String searchText, TypeProvider provider) {
List<TypeDescriptor> results = new ArrayList<TypeDescriptor>();
TypeProvider.Context c = JumptoAccessor.createContext(null, searchText, SearchType.PREFIX);
TypeProvider.Result res = JumptoAccessor.createResult(results, c);
provider.computeTypeNames(c, res);
assertEquals(expectedResults.length, results.size());
//sort to make the result test run reproducable
Collections.sort(results, new Comparator<TypeDescriptor>() {
@Override
public int compare(TypeDescriptor o1, TypeDescriptor o2) {
int compareValue = o1.getSimpleName().compareToIgnoreCase(o2.getSimpleName());
if (compareValue != 0) {
return compareValue;
}
return o1.getContextName().compareToIgnoreCase(o2.getContextName());
}
});
for (int i = 0; i < results.size(); i++) {
assertEquals("not equals at index " + i, expectedResults[i][0], results.get(i).getSimpleName());
assertEquals("not equals at index " + i, " (" + expectedResults[i][1] + ")", results.get(i).getContextName());
}
}
private FileObject createJavaFile(FileObject srcRoot1, String packageName, String className) throws IOException {
return createJavaFile(srcRoot1, packageName, className, "package " + packageName + ";\npublic class " + className + " {}\n");
}
public static class ClassPathProviderImpl implements ClassPathProvider {
public ClassPath findClassPath(final FileObject file, final String type) {
final FileObject[] roots = sourcePath.getRoots();
for (FileObject root : roots) {
if (root.equals(file) || FileUtil.isParentOf(root, file)) {
if (type == ClassPath.SOURCE) {
return sourcePath;
}
if (type == ClassPath.COMPILE) {
return compilePath;
}
if (type == ClassPath.BOOT) {
return bootPath;
}
}
}
if (libSrc2.equals(file) || FileUtil.isParentOf(libSrc2, file)) {
if (type == ClassPath.SOURCE) {
return ClassPathSupport.createClassPath(new FileObject[]{libSrc2});
}
if (type == ClassPath.COMPILE) {
return ClassPathSupport.createClassPath(new URL[0]);
}
if (type == ClassPath.BOOT) {
return bootPath;
}
}
return null;
}
}
public static class SFBQ implements SourceForBinaryQueryImplementation {
public SourceForBinaryQuery.Result findSourceRoots(URL binaryRoot) {
try {
if (binaryRoot.equals(binRoot2.getURL())) {
return new SourceForBinaryQuery.Result() {
public FileObject[] getRoots() {
return new FileObject[]{libSrc2};
}
public void addChangeListener(ChangeListener l) {
}
public void removeChangeListener(ChangeListener l) {
}
};
}
} catch (FileStateInvalidException e) {
}
return null;
}
}
private static final class MutableCp implements ClassPathImplementation {
private final PropertyChangeSupport support;
private List<? extends PathResourceImplementation> impls;
public MutableCp() {
this(Collections.<PathResourceImplementation>emptyList());
}
public MutableCp(final List<? extends PathResourceImplementation> impls) {
assert impls != null;
support = new PropertyChangeSupport(this);
this.impls = impls;
}
public List<? extends PathResourceImplementation> getResources() {
return impls;
}
public void addPropertyChangeListener(final PropertyChangeListener listener) {
assert listener != null;
this.support.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(final PropertyChangeListener listener) {
assert listener != null;
this.support.removePropertyChangeListener(listener);
}
void setImpls(final List<? extends PathResourceImplementation> impls) {
assert impls != null;
this.impls = impls;
this.support.firePropertyChange(PROP_RESOURCES, null, null);
}
}
}