/*
* Copyright 2010 the original author or authors.
*
* Licensed 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.gradle.build.docs.dsl.source;
import org.apache.commons.lang.StringUtils;
import org.gradle.api.Action;
import org.gradle.build.docs.dsl.source.model.ClassMetaData;
import org.gradle.build.docs.dsl.source.model.TypeMetaData;
import org.gradle.build.docs.model.ClassMetaDataRepository;
import org.gradle.internal.UncheckedException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Resolves partial type names into fully qualified type names.
*/
public class TypeNameResolver {
private final Set<String> primitiveTypes = new HashSet<String>();
private final List<String> groovyImplicitImportPackages = new ArrayList<String>();
private final List<String> groovyImplicitTypes = new ArrayList<String>();
private final ClassMetaDataRepository<ClassMetaData> metaDataRepository;
public TypeNameResolver(ClassMetaDataRepository<ClassMetaData> metaDataRepository) {
this.metaDataRepository = metaDataRepository;
primitiveTypes.add("boolean");
primitiveTypes.add("byte");
primitiveTypes.add("char");
primitiveTypes.add("short");
primitiveTypes.add("int");
primitiveTypes.add("long");
primitiveTypes.add("float");
primitiveTypes.add("double");
primitiveTypes.add("void");
groovyImplicitImportPackages.add("java.util.");
groovyImplicitImportPackages.add("java.io.");
groovyImplicitImportPackages.add("java.net.");
groovyImplicitImportPackages.add("groovy.lang.");
groovyImplicitImportPackages.add("groovy.util.");
groovyImplicitTypes.add("java.math.BigDecimal");
groovyImplicitTypes.add("java.math.BigInteger");
// check that groovy is visible.
try {
getClass().getClassLoader().loadClass("groovy.lang.Closure");
} catch (ClassNotFoundException e) {
throw UncheckedException.throwAsUncheckedException(e);
}
}
/**
* Resolves the names in the given type into fully qualified names.
*/
public void resolve(final TypeMetaData type, final ClassMetaData classMetaData) {
type.visitTypes(new Action<TypeMetaData>() {
public void execute(TypeMetaData t) {
t.setName(resolve(t.getName(), classMetaData));
}
});
}
/**
* Resolves a source type name into a fully qualified type name.
*/
public String resolve(String name, ClassMetaData classMetaData) {
if (primitiveTypes.contains(name)) {
return name;
}
String candidateClassName;
String[] innerNames = name.split("\\.");
ClassMetaData pos = classMetaData;
for (int i = 0; i < innerNames.length; i++) {
String innerName = innerNames[i];
candidateClassName = pos.getClassName() + '.' + innerName;
if (!pos.getInnerClassNames().contains(candidateClassName)) {
break;
}
if (i == innerNames.length - 1) {
return candidateClassName;
}
pos = metaDataRepository.get(candidateClassName);
}
String outerClassName = classMetaData.getOuterClassName();
while (outerClassName != null) {
if (name.equals(StringUtils.substringAfterLast(outerClassName, "."))) {
return outerClassName;
}
ClassMetaData outerClass = metaDataRepository.get(outerClassName);
candidateClassName = outerClassName + '.' + name;
if (outerClass.getInnerClassNames().contains(candidateClassName)) {
return candidateClassName;
}
outerClassName = outerClass.getOuterClassName();
}
if (name.contains(".")) {
return name;
}
for (String importedClass : classMetaData.getImports()) {
String baseName = StringUtils.substringAfterLast(importedClass, ".");
if (baseName.equals("*")) {
candidateClassName = StringUtils.substringBeforeLast(importedClass, ".") + "." + name;
if (metaDataRepository.find(candidateClassName) != null) {
return candidateClassName;
}
if (importedClass.startsWith("java.") && isVisibleSystemClass(candidateClassName)) {
return candidateClassName;
}
} else if (name.equals(baseName)) {
return importedClass;
}
}
candidateClassName = classMetaData.getPackageName() + "." + name;
if (metaDataRepository.find(candidateClassName) != null) {
return candidateClassName;
}
candidateClassName = "java.lang." + name;
if (isVisibleSystemClass(candidateClassName)) {
return candidateClassName;
}
if (classMetaData.isGroovy()) {
candidateClassName = "java.math." + name;
if (groovyImplicitTypes.contains(candidateClassName)) {
return candidateClassName;
}
for (String prefix : groovyImplicitImportPackages) {
candidateClassName = prefix + name;
if (isVisibleSystemClass(candidateClassName)) {
return candidateClassName;
}
}
}
return name;
}
// Only use for system Java/Groovy classes; arbitrary use on the build classpath will result in class/jar leaks.
private boolean isVisibleSystemClass(String candidateClassName) {
try {
getClass().getClassLoader().loadClass(candidateClassName);
return true;
} catch (ClassNotFoundException e) {
// Ignore
}
return false;
}
}