package ru.csu.stan.java.cfg.util;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import ru.csu.stan.java.classgen.jaxb.Class;
import ru.csu.stan.java.classgen.jaxb.Classes;
import ru.csu.stan.java.classgen.jaxb.ParentClass;
import ru.csu.stan.java.classgen.util.CompilationUnit;
import ru.csu.stan.java.classgen.util.ImportRegistry;
import ru.csu.stan.java.classgen.util.PackageRegistry;
/**
*
* @author mz
*
*/
public class UCFRClassNameResolver {
private ImportRegistry imports;
private PackageRegistry packages;
private UCFRClassNameResolver(ImportRegistry imports, PackageRegistry packages){
this.imports = imports;
this.packages = packages;
}
public static UCFRClassNameResolver getInstance(ImportRegistry imports, PackageRegistry packages){
return new UCFRClassNameResolver(imports, packages);
}
/**
* Получение полного имени типа.
* @param name исходное имя класса (может уже быть полным).
* @param currentClass текущий обрабатываемый класс, в котором выполняется поиск полного имени типа.
* @param allClasses все классы проекта.
* @return полное имя типа, null - если в проекте не описан соответствующий тип.
*/
public String getFullTypeName(String name, String fullClassname, Classes allClasses)
{
// все по простому: класс нормально импортирован из проекта
if (packages.isClassInRegistry(name))
return name;
// всякие сложности
else{
return findFullClassNameInProject(name, fullClassname, allClasses);
}
}
/**
* Поиск полного типа класса внутри исследуемого проекта.
* @param name
* @param currentClass
* @param allClasses
* @return
*/
private String findFullClassNameInProject(String name, String fullClassname, Classes allClasses) {
// Получаем метаданные о файле сборки
CompilationUnit unit = imports.findUnitByClass(fullClassname);
if (packages.isClassInRegistry(fullClassname + '.' + name))
return fullClassname + '.' + name;
String currentPackageName = findNameInCurrentPackage(name, unit);
if (currentPackageName != null)
return currentPackageName;
String starImportedName = findNameInStarImports(name, unit);
if (starImportedName != null)
return starImportedName;
return findNameInContainerClass(name, fullClassname, allClasses, unit);
}
/**
* Получение полного имени из класса, для которого текущий является вложенным.
* Если текущий вложенным не является, тогда результат будет null.
* @param name
* @param currentClass
* @param allClasses
* @param unit
* @return
*/
private String findNameInContainerClass(String name, String fullClassname, Classes allClasses, CompilationUnit unit) {
String localCurrentClassName = fullClassname.substring(unit.getPackageName().length()+1);
if (localCurrentClassName.indexOf('.') > 0){
Set<String> sameThings = getAllFullNamesWithSameBeginningAndEnding(name, unit);
if (sameThings.size() > 1){
String fullTypeName = resolveNameInContainerClassParent(allClasses.getClazz(), unit.getPackageName(), localCurrentClassName, name, sameThings);
if (fullTypeName != null && !fullTypeName.isEmpty()){
return fullTypeName;
}
}
if (sameThings.size() == 1){
return sameThings.iterator().next();
}
}
return null;
}
/**
* @param name
* @param unit
* @return
*/
private Set<String> getAllFullNamesWithSameBeginningAndEnding(String name, CompilationUnit unit) {
Set<String> sameThings = new HashSet<String>();
for (String imp : unit.getImports())
sameThings.addAll(packages.getClassesByPrefixAndPostfix(imp, name));
sameThings.addAll(packages.getClassesByPrefixAndPostfix(unit.getPackageName(), name));
return sameThings;
}
/**
* @param name
* @param unit
*/
private String findNameInStarImports(String name, CompilationUnit unit) {
for (String starImport : unit.getStarImports()){
// отбрасываем ".*"
String fullName = packages.findFullNameByShortInPackage(starImport.substring(0, starImport.length()-2), name);
if (fullName != null){
return fullName;
}
}
return null;
}
/**
* Поиск имени в текущем пакете.
* @param name
* @param unit
*/
private String findNameInCurrentPackage(String name, CompilationUnit unit) {
String fullName = packages.findFullNameByShortInPackage(unit.getPackageName(), name);
if (fullName != null){
return fullName;
}
return null;
}
/**
* Поиск в наборе строк тех, что имеют заданное окончание.
* @param strings набор строк для поиска.
* @param ending окончание, по которому идет поиск.
* @return Набор строк, подобранных среди исходного.
*/
private Set<String> searchForEnding(Set<String> strings, String ending){
Set<String> result = new HashSet<String>();
for (String str : strings)
if (str.endsWith(ending))
result.add(str);
return result;
}
/**
* Попытка получение полного имени объемлющего класса.
* @param classes
* @param packageName
* @param localClassName
* @param name
* @param candidates
* @return
*/
private String resolveNameInContainerClassParent(List<Class> classes, String packageName, String localClassName, String name, Set<String> candidates){
if (localClassName.lastIndexOf('.') == -1)
return null;
String containerClassName = localClassName.substring(0, localClassName.lastIndexOf('.'));
String searchClass = packageName + '.' + containerClassName;
for (Class cl : classes)
if (cl.getName().equals(searchClass))
for (ParentClass parentCl : cl.getParent()){
String searchingNameInContainersParent = name;
if (parentCl.getName().lastIndexOf('.') > 0)
searchingNameInContainersParent = parentCl.getName().substring(parentCl.getName().lastIndexOf('.')+1, parentCl.getName().length()-1) + '.' + searchingNameInContainersParent;
else
searchingNameInContainersParent = parentCl.getName() + '.' + searchingNameInContainersParent;
Set<String> sameEndings = searchForEnding(candidates, searchingNameInContainersParent);
if (sameEndings.size() > 1)
return resolveNameInContainerClassParent(classes, packageName, containerClassName, searchingNameInContainersParent, sameEndings);
if (sameEndings.size() == 1)
return sameEndings.iterator().next();
}
return null;
}
}