package hu.elte.txtuml.utils.eclipse; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.NoSuchElementException; import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jdt.core.IAnnotation; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IField; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMemberValuePair; import org.eclipse.jdt.core.IPackageDeclaration; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.jdt.ui.JavaElementLabelProvider; import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider; import org.eclipse.jface.viewers.IBaseLabelProvider; import org.eclipse.jface.viewers.StyledString; import hu.elte.txtuml.api.model.Model; import hu.elte.txtuml.utils.Pair; import hu.elte.txtuml.utils.jdt.ModelUtils; import hu.elte.txtuml.utils.jdt.SharedUtils; public class WizardUtils { /** * @return the list of classes in the given package which extends at least * one of the given superclasses directly. */ public static List<IType> getTypesByDirectSuperclass(IPackageFragment packageFragment, Class<?>... superClasses) { List<IType> typesWithGivenSuperclass = new ArrayList<>(); ICompilationUnit[] compilationUnits; try { compilationUnits = packageFragment.getCompilationUnits(); } catch (JavaModelException ex) { return Collections.emptyList(); } for (ICompilationUnit cUnit : compilationUnits) { IType[] types = null; try { types = cUnit.getAllTypes(); } catch (JavaModelException e) { continue; } Stream.of(types).filter(type -> { try { String typeSuperclassName = type.getSuperclassName(); int indexOfTypeParam = typeSuperclassName.indexOf("<"); if (indexOfTypeParam != -1) { typeSuperclassName = typeSuperclassName.substring(0, indexOfTypeParam); } final String superClassName = typeSuperclassName; return Stream.of(superClasses).anyMatch(superClass -> isImportedNameResolvedTo(cUnit, superClassName, superClass.getCanonicalName())); } catch (JavaModelException | NullPointerException ex) { return false; } }).forEach(type -> typesWithGivenSuperclass.add(type)); } return typesWithGivenSuperclass; } /** * * @return the list of classes in the given package which extends at least * one of the given superclasses directly. */ public static List<IType> getTypesBySuperclass(IPackageFragment packageFragment, Class<?>... superClasses) { List<IType> typesWithGivenSuperclass = new ArrayList<>(); ICompilationUnit[] compilationUnits; try { compilationUnits = packageFragment.getCompilationUnits(); } catch (JavaModelException ex) { return Collections.emptyList(); } List<SuperTypeListJob> jobs = new ArrayList<>(); for (ICompilationUnit cUnit : compilationUnits) { SuperTypeListJob job = new SuperTypeListJob("Get supertype list", cUnit, superClasses); jobs.add(job); job.setPriority(Job.INTERACTIVE); job.schedule(); } jobs.stream().forEach(job -> { try { job.join(); typesWithGivenSuperclass.addAll(job.gettypesWithGivenSuperclass()); } catch (InterruptedException e) { } }); return typesWithGivenSuperclass; } public static List<IPackageFragment> getModelPackages(List<IPackageFragment> packageFragments) { List<IPackageFragment> modelPackages = new ArrayList<>(); for (IPackageFragment pFragment : packageFragments) { Optional<ICompilationUnit> foundPackageInfoCompilationUnit = Optional .of(pFragment.getCompilationUnit(PackageUtils.PACKAGE_INFO)); if (!foundPackageInfoCompilationUnit.isPresent() || !foundPackageInfoCompilationUnit.get().exists()) { continue; } ICompilationUnit packageInfoCompilationUnit = foundPackageInfoCompilationUnit.get(); try { for (IPackageDeclaration packDecl : packageInfoCompilationUnit.getPackageDeclarations()) { for (IAnnotation annot : packDecl.getAnnotations()) { boolean isModelPackage = isImportedNameResolvedTo(packageInfoCompilationUnit, annot.getElementName(), Model.class.getCanonicalName()); if (isModelPackage) { modelPackages.add(pFragment); break; } } } } catch (JavaModelException ex) { return Collections.emptyList(); } } return modelPackages; } /** * @return true if the given project contains at least one Class which * extends one of the given superclasses directly */ public static boolean containsClassesWithDirectSuperTypes(IJavaProject javaProject, Class<?>... superClasses) { try { return PackageUtils.findAllPackageFragmentsAsStream(javaProject) .anyMatch(pf -> !getTypesByDirectSuperclass(pf, superClasses).isEmpty()); } catch (JavaModelException ex) { return false; } } /** * @return true if the given project contains at least one Class with at * least one of the given superclasses */ public static boolean containsClassesWithSuperTypes(IJavaProject javaProject, Class<?>... superClasses) { try { return PackageUtils.findAllPackageFragmentsAsStream(javaProject) .anyMatch(pf -> containsAnyClassWithSuperTypes(pf, superClasses)); } catch (JavaModelException ex) { return false; } } public static IBaseLabelProvider getPostQualifiedLabelProvider() { return new DelegatingStyledCellLabelProvider(new JavaElementLabelProvider( JavaElementLabelProvider.SHOW_POST_QUALIFIED | JavaElementLabelProvider.SHOW_SMALL_ICONS)) { @Override protected StyledString getStyledText(Object element) { String nameWithQualifier = getStyledStringProvider().getStyledText(element).getString() + " "; int separatorIndex = nameWithQualifier.indexOf('-'); if (separatorIndex == -1) return new StyledString(nameWithQualifier); StyledString name = new StyledString(nameWithQualifier.substring(0, separatorIndex)); String qualifier = nameWithQualifier.substring(separatorIndex); return name.append(new StyledString(qualifier, StyledString.QUALIFIER_STYLER)); }; }; } /** * @return an empty optional if the given type is not annotated, otherwise * the name of the model package and its java project respectively, * which contains the types of the given annotation */ public static Optional<Pair<String, String>> getModelByAnnotations(IType annotatedType) { try { List<String> referencedProjects = new ArrayList<>( Arrays.asList(annotatedType.getJavaProject().getRequiredProjectNames())); referencedProjects.add(annotatedType.getJavaProject().getElementName()); for (IAnnotation annot : annotatedType.getAnnotations()) { List<Object> annotValues = Stream.of(annot.getMemberValuePairs()) .filter(mvp -> mvp.getValueKind() == IMemberValuePair.K_CLASS) .flatMap(mvp -> Stream.of(mvp.getValue())).collect(Collectors.toList()); if (annotValues.isEmpty()) { throw new NoSuchElementException("Group is empty."); } for (Object val : annotValues) { List<Object> annotations = new ArrayList<>(); if (val instanceof String) { annotations.add(val); } else { annotations.addAll(Arrays.asList((Object[]) val)); } for (Object v : annotations) { String[][] resolvedTypes = resolveType(annotatedType, (String) v); List<String[]> resolvedTypeList = new ArrayList<>(Arrays.asList(resolvedTypes)); for (String[] type : resolvedTypeList) { Optional<Pair<String, String>> model = ModelUtils.getModelOf(type[0], referencedProjects); if (model.isPresent()) { return model; } } } } } } catch (JavaModelException e) { } return Optional.empty(); } /** * @return an empty optional if the given type does not have any field, * otherwise the name of the model package and its java project * respectively, which contains the types of the given diagramType */ public static Optional<Pair<String, String>> getModelByFields(IType diagramType) { try { List<String> referencedProjects = new ArrayList<>( Arrays.asList(diagramType.getJavaProject().getRequiredProjectNames())); referencedProjects.add(diagramType.getJavaProject().getElementName()); for (IField field : diagramType.getFields()) { String typeSignature = field.getTypeSignature(); // generic type parameters int typeParamStartIdx = typeSignature.indexOf("<Q"); if (typeParamStartIdx != -1) { typeSignature = typeSignature.substring(typeParamStartIdx + 1, typeSignature.indexOf(';') + 1); } String[][] resolvedTypes = resolveType(diagramType, typeSignature.substring(1, typeSignature.length() - 1)); List<String[]> resolvedTypeList = new ArrayList<>(Arrays.asList(resolvedTypes)); for (String[] type : resolvedTypeList) { Optional<Pair<String, String>> model = ModelUtils.getModelOf(type[0], referencedProjects); if (model.isPresent()) { return model; } } } IType[] superTypes = diagramType.newSupertypeHierarchy(null).getAllSupertypes(diagramType); if (superTypes.length != 0) { IType superType = superTypes[0]; return getModelByFields(superType); } } catch (JavaModelException | NoSuchElementException e) { } return Optional.empty(); } /** * @return true if there is at least one class in the given package with at * least one of the given superclasses. */ private static boolean containsAnyClassWithSuperTypes(IPackageFragment packageFragment, Class<?>... superClasses) { boolean result = false; ICompilationUnit[] compilationUnits; try { compilationUnits = packageFragment.getCompilationUnits(); } catch (JavaModelException ex) { return false; } List<ContainsClassWithSuperTypesJob> jobs = new ArrayList<>(); for (ICompilationUnit cUnit : compilationUnits) { ContainsClassWithSuperTypesJob job = new ContainsClassWithSuperTypesJob("Search for type with superclasses", cUnit, superClasses); jobs.add(job); job.setPriority(Job.INTERACTIVE); job.schedule(); } for (ContainsClassWithSuperTypesJob job : jobs) { if (!result) { try { job.join(); result = job.getHaveClassWithSuperTypes(); } catch (InterruptedException e) { continue; } } else { job.cancel(); } } return result; } /** * @return the list of types in the given compilation unit. */ private synchronized static List<?> getTypes(ICompilationUnit compilationUnit) { ASTParser parser = ASTParser.newParser(AST.JLS8); parser.setResolveBindings(true); parser.setSource(compilationUnit); CompilationUnit cu = (CompilationUnit) parser.createAST(null); return cu.types(); } private static String[][] resolveType(IType context, String typeName) { try { return context.resolveType(typeName); } catch (JavaModelException e) { return new String[][] {}; } } private static boolean isImportedNameResolvedTo(ICompilationUnit compilationUnit, String elementName, String qualifiedName) { if (!qualifiedName.endsWith(elementName)) { return false; } int lastSection = qualifiedName.lastIndexOf("."); String pack = qualifiedName.substring(0, lastSection); return (compilationUnit.getImport(qualifiedName).exists() || compilationUnit.getImport(pack + ".*").exists()); } /** * Job for finding the types with the given superclasses in a compilation * unit. */ private static class SuperTypeListJob extends Job { private List<IType> typesWithGivenSuperclass; private ICompilationUnit cUnit; private Class<?>[] superClasses; public SuperTypeListJob(String name, ICompilationUnit cUnit, Class<?>... superClasses) { super(name); typesWithGivenSuperclass = new ArrayList<IType>(); this.cUnit = cUnit; this.superClasses = superClasses; } @Override protected IStatus run(IProgressMonitor monitor) { List<?> types = getTypes(cUnit); types.stream().filter(type -> { try { return Stream.of(superClasses).anyMatch( superClass -> SharedUtils.typeIsAssignableFrom((TypeDeclaration) type, superClass)); } catch (NullPointerException ex) { return false; } }).forEach(type -> typesWithGivenSuperclass .add((IType) ((TypeDeclaration) type).resolveBinding().getJavaElement())); return Status.OK_STATUS; } public List<IType> gettypesWithGivenSuperclass() { return typesWithGivenSuperclass; } } /** * * Job for calculating if there is any type in a compilation unit with at * least one of the given superclasses. */ private static class ContainsClassWithSuperTypesJob extends Job { private boolean haveClassWithSuperTypes; private ICompilationUnit cUnit; private Class<?>[] superClasses; public ContainsClassWithSuperTypesJob(String name, ICompilationUnit cUnit, Class<?>... superClasses) { super(name); this.cUnit = cUnit; this.superClasses = superClasses; } @Override protected IStatus run(IProgressMonitor monitor) { List<?> types = getTypes(cUnit); haveClassWithSuperTypes = types.stream().anyMatch(type -> { try { return Stream.of(superClasses).anyMatch( superClass -> SharedUtils.typeIsAssignableFrom((TypeDeclaration) type, superClass)); } catch (NullPointerException ex) { return false; } }); return Status.OK_STATUS; } public boolean getHaveClassWithSuperTypes() { return haveClassWithSuperTypes; } } }