/** * Copyright (c) 2014 Codetrails GmbH. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Marcel Bruch - initial API and implementation. */ package org.eclipse.recommenders.jdt; import static com.google.common.base.Optional.*; import static org.apache.commons.lang3.ArrayUtils.isEmpty; import static org.apache.commons.lang3.StringUtils.repeat; import static org.eclipse.jdt.core.IJavaElement.*; import static org.eclipse.jdt.core.IPackageFragmentRoot.*; import static org.eclipse.jdt.core.compiler.CharOperation.charToString; import static org.eclipse.recommenders.internal.jdt.l10n.LogMessages.*; import static org.eclipse.recommenders.utils.Logs.log; import static org.eclipse.recommenders.utils.names.VmTypeName.OBJECT; import java.io.File; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.IPath; import org.eclipse.jdt.core.IClassFile; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.ITypeParameter; import org.eclipse.jdt.core.ITypeRoot; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.Signature; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.core.JavaModelManager; import org.eclipse.recommenders.internal.jdt.l10n.LogMessages; import org.eclipse.recommenders.utils.Logs; import org.eclipse.recommenders.utils.Nullable; import org.eclipse.recommenders.utils.names.ITypeName; import org.eclipse.recommenders.utils.names.VmTypeName; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList.Builder; @SuppressWarnings("restriction") public final class JavaElementsFinder { private JavaElementsFinder() { // Not meant to be instantiated } public static ImmutableList<IJavaProject> findAccessibleJavaProjects() { Builder<IJavaProject> b = ImmutableList.builder(); try { JavaModelManager mgr = JavaModelManager.getJavaModelManager(); b.add(mgr.getJavaModel().getJavaProjects()); } catch (Exception e) { log(ERROR_CANNOT_FETCH_JAVA_PROJECTS, e); } return b.build(); } public static ImmutableList<IPackageFragmentRoot> findSourceRoots(IJavaProject project) { Builder<IPackageFragmentRoot> b = ImmutableList.builder(); try { for (IPackageFragmentRoot root : project.getPackageFragmentRoots()) { if (K_SOURCE == getPackageFragmentRootKind(root)) { b.add(root); } } } catch (Exception e) { log(ERROR_CANNOT_FETCH_PACKAGE_FRAGMENT_ROOTS, e, project); } return b.build(); } public static ImmutableList<IPackageFragmentRoot> findPackageRootsWithSources(IJavaProject project) { Builder<IPackageFragmentRoot> b = ImmutableList.builder(); try { for (IPackageFragmentRoot root : project.getPackageFragmentRoots()) { int kind = getPackageFragmentRootKind(root); if (K_SOURCE == kind) { b.add(root); } else if (K_BINARY == kind) { if (hasSourceAttachment(root)) { b.add(root); } } } } catch (Exception e) { log(ERROR_CANNOT_FETCH_PACKAGE_FRAGMENT_ROOTS, e, project); } return b.build(); } public static ImmutableList<IPackageFragmentRoot> findPackageFragmentRoots(IJavaProject project) { Builder<IPackageFragmentRoot> b = ImmutableList.builder(); try { for (IPackageFragmentRoot root : project.getPackageFragmentRoots()) { b.add(root); } } catch (JavaModelException e) { log(ERROR_CANNOT_FETCH_PACKAGE_FRAGMENT_ROOTS, e, project); } return b.build(); } public static ImmutableList<IPackageFragment> findPackages(IPackageFragmentRoot root) { Builder<IPackageFragment> b = ImmutableList.builder(); try { for (IJavaElement e : root.getChildren()) { b.add((IPackageFragment) e); } } catch (Exception e) { log(ERROR_CANNOT_FETCH_PACKAGE_FRAGMENT, e, root); } return b.build(); } public static ImmutableList<ICompilationUnit> findCompilationUnits(IPackageFragment fragment) { Builder<ICompilationUnit> b = ImmutableList.builder(); try { b.add(fragment.getCompilationUnits()); } catch (Exception e) { log(ERROR_CANNOT_FETCH_COMPILATION_UNITS, e, fragment); } return b.build(); } public static ImmutableList<IClassFile> findClassFiles(IPackageFragment fragment) { Builder<IClassFile> b = ImmutableList.builder(); try { b.add(fragment.getClassFiles()); } catch (Exception e) { log(ERROR_CANNOT_FETCH_CLASS_FILES, e, fragment); } return b.build(); } public static Optional<IType> findType(String typename, IJavaProject project) { try { return fromNullable(project.findType(typename)); } catch (Exception e) { log(ERROR_CANNOT_FIND_TYPE_IN_PROJECT, e, typename, project); return absent(); } } public static ImmutableList<IType> findTypes(IJavaProject project) { Builder<IType> b = ImmutableList.builder(); for (ITypeRoot root : findTypeRoots(project)) { b.addAll(findTypes(root)); } return b.build(); } public static ImmutableList<IType> findTypes(IPackageFragmentRoot root) { Builder<IType> b = ImmutableList.builder(); for (ITypeRoot typeRoot : findTypeRoots(root)) { b.addAll(findTypes(typeRoot)); } return b.build(); } public static ImmutableList<IType> findTypes(ITypeRoot root) { Builder<IType> b = ImmutableList.builder(); try { if (root instanceof ICompilationUnit) { for (IType type : ((ICompilationUnit) root).getTypes()) { b.add(type); } } else if (root instanceof IClassFile) { b.add(((IClassFile) root).getType()); } } catch (JavaModelException e) { log(ERROR_CANNOT_FETCH_TYPES, e, root); } return b.build(); } public static ImmutableList<ITypeRoot> findTypeRoots(IJavaProject project) { Builder<ITypeRoot> b = ImmutableList.builder(); for (IPackageFragmentRoot root : findPackageFragmentRoots(project)) { b.addAll(findTypeRoots(root)); } return b.build(); } public static ImmutableList<ITypeRoot> findTypeRoots(IPackageFragmentRoot root) { Builder<ITypeRoot> b = ImmutableList.builder(); for (IPackageFragment pkg : findPackages(root)) { b.addAll(findTypeRoots(pkg)); } return b.build(); } public static ImmutableList<ITypeRoot> findTypeRoots(IPackageFragment fragment) { Builder<ITypeRoot> b = ImmutableList.builder(); ImmutableList<ICompilationUnit> cus = findCompilationUnits(fragment); ImmutableList<IClassFile> classFiles = findClassFiles(fragment); b.addAll(cus); b.addAll(classFiles); return b.build(); } /** * Returns the compilation unit's absolute location on the local hard drive - if it exists. */ public static Optional<File> findLocation(@Nullable ICompilationUnit cu) { if (cu == null) { return absent(); } IResource resource = cu.getResource(); if (resource == null) { return absent(); } IPath location = resource.getLocation(); if (location == null) { return absent(); } File file = location.toFile(); if (!file.exists()) { return absent(); } return Optional.of(file); } /** * Returns the compilation unit's absolute location on the local hard drive - if it exists. */ public static Optional<File> findLocation(@Nullable IPackageFragmentRoot root) { if (root == null) { return absent(); } File res = null; final IResource resource = root.getResource(); if (resource != null) { if (resource.getLocation() == null) { res = resource.getRawLocation().toFile().getAbsoluteFile(); } else { res = resource.getLocation().toFile().getAbsoluteFile(); } } if (root.isExternal()) { res = root.getPath().toFile().getAbsoluteFile(); } // if the file (for whatever reasons) does not exist return absent(). if (res != null && !res.exists()) { return absent(); } return fromNullable(res); } public static boolean hasSourceAttachment(IPackageFragmentRoot fragmentRoot) { try { return fragmentRoot.getSourceAttachmentPath() != null; } catch (Exception e) { log(ERROR_CANNOT_FETCH_SOURCE_ATTACHMENT_PATH, e, fragmentRoot); return false; } } /** * * @param typeSignature * e.g., QList; * @param enclosing * @return */ public static Optional<ITypeName> resolveType(char[] typeSignature, @Nullable IJavaElement enclosing) { typeSignature = CharOperation.replaceOnCopy(typeSignature, '.', '/'); VmTypeName res = null; try { int dimensions = Signature.getArrayCount(typeSignature); outer: switch (typeSignature[dimensions]) { case Signature.C_BOOLEAN: case Signature.C_BYTE: case Signature.C_CHAR: case Signature.C_DOUBLE: case Signature.C_FLOAT: case Signature.C_INT: case Signature.C_LONG: case Signature.C_SHORT: case Signature.C_VOID: // take the whole string including any arrays res = VmTypeName.get(new String(typeSignature, 0, typeSignature.length)); break; case Signature.C_RESOLVED: // take the whole string including any arrays but remove the trailing ';' res = VmTypeName.get(new String(typeSignature, 0, typeSignature.length - 1 /* ';' */)); break; case Signature.C_UNRESOLVED: if (enclosing == null) { break; } // take the whole string (e.g. QList; or [QList;) String unresolved = new String(typeSignature, dimensions + 1, typeSignature.length - (dimensions + 2 /* 'Q' + ';' */)); IType ancestor = (IType) enclosing.getAncestor(IJavaElement.TYPE); if (ancestor == null) { break; } final String[][] resolvedNames = ancestor.resolveType(unresolved); if (isEmpty(resolvedNames)) { break; } String array = repeat('[', dimensions); final String pkg = resolvedNames[0][0].replace('.', '/'); final String name = resolvedNames[0][1].replace('.', '$'); res = VmTypeName.get(array + 'L' + pkg + '/' + name); break; case Signature.C_TYPE_VARIABLE: String varName = new String(typeSignature, dimensions + 1, typeSignature.length - (dimensions + 2 /* 'Q' + ';' */)); array = repeat('[', dimensions); for (IJavaElement cur = enclosing; cur instanceof IType || cur instanceof IMethod; cur = cur.getParent()) { switch (cur.getElementType()) { case TYPE: { IType type = (IType) cur; ITypeParameter param = type.getTypeParameter(varName); if (param.exists()) { String[] signatures = getBoundSignatures(param); if (isEmpty(signatures)) { res = VmTypeName.OBJECT; break outer; } // XXX we only consider the first type. char[] append = array.concat(signatures[0]).toCharArray(); return resolveType(append, type); } } case METHOD: { IMethod method = (IMethod) cur; ITypeParameter param = method.getTypeParameter(varName); if (param.exists()) { String[] signatures = getBoundSignatures(param); if (isEmpty(signatures)) { res = dimensions == 0 ? OBJECT : VmTypeName.get(repeat('[', dimensions) + OBJECT.getIdentifier()); break outer; } // XXX we only consider the first type. char[] append = array.concat(signatures[0]).toCharArray(); return resolveType(append, method); } } } } break; default: break; } } catch (Exception e) { Logs.log(LogMessages.ERROR_FAILED_TO_CREATE_TYPENAME, e, charToString(typeSignature) + (enclosing != null ? " in " + enclosing.getElementName() : "")); } return Optional.<ITypeName>fromNullable(res); } private static String[] getBoundSignatures(ITypeParameter param) throws JavaModelException { String[] res = CharOperation.NO_STRINGS; try { res = param.getBoundsSignatures(); } catch (NullPointerException e) { // swallow. That happened during testing in JDT Mars M6 } return res; } public static IPackageFragmentRoot[] getAllPackageFragmentRoots(IJavaProject javaProject) { try { return javaProject.getAllPackageFragmentRoots(); } catch (JavaModelException e) { Logs.log(LogMessages.ERROR_CANNOT_FETCH_ALL_PACKAGE_FRAGMENT_ROOTS, e, javaProject); return new IPackageFragmentRoot[0]; } } public static int getPackageFragmentRootKind(IPackageFragmentRoot packageFragmentRoot) { try { return packageFragmentRoot.getKind(); } catch (JavaModelException e) { Logs.log(LogMessages.ERROR_CANNOT_FETCH_PACKAGE_FRAGMENT_ROOT_KIND, e, packageFragmentRoot); return 0; } } }