/* * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate * copyright notice. All rights reserved. */ package jqian.util.eclipse; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jdt.core.*; import org.eclipse.jdt.ui.JavaUI; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextSelection; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.part.FileEditorInput; import org.eclipse.ui.texteditor.IDocumentProvider; import org.eclipse.ui.texteditor.ITextEditor; public class JDTUtils { private static final IJavaElement[] EMPTY_RESULT = new IJavaElement[0]; /** * Finds a type by its qualified type name (dot separated). * @param jproject The java project to search in * @param fullyQualifiedName The fully qualified name (type name with enclosing type names and package (all separated by dots)) * @return The type found, or null if not existing */ public static IType findType(IJavaProject jproject, String fullyQualifiedName) throws JavaModelException { fullyQualifiedName = fullyQualifiedName.replace('$', '.'); IType type = jproject.findType(fullyQualifiedName, (IProgressMonitor)null); if (type != null) return type; IPackageFragmentRoot[] roots = jproject.getPackageFragmentRoots(); for (int i = 0; i < roots.length; i++) { IPackageFragmentRoot root = roots[i]; type = findType(root, fullyQualifiedName); if (type != null && type.exists()) return type; } return null; } private static IType findType(IPackageFragmentRoot root, String fullyQualifiedName) throws JavaModelException { IJavaElement[] children = root.getChildren(); for (int i = 0; i < children.length; i++) { IJavaElement element = children[i]; if (element.getElementType() == IJavaElement.PACKAGE_FRAGMENT) { IPackageFragment pack = (IPackageFragment)element; if (!fullyQualifiedName.startsWith(pack.getElementName())) continue; IType type = findType(pack, fullyQualifiedName); if (type != null && type.exists()) return type; } } return null; } private static IType findType(IPackageFragment pack, String fullyQualifiedName) throws JavaModelException { ICompilationUnit[] cus = pack.getCompilationUnits(); for (int i = 0; i < cus.length; i++) { ICompilationUnit unit = cus[i]; IType type = findType(unit, fullyQualifiedName); if (type != null && type.exists()) return type; } return null; } private static IType findType(ICompilationUnit cu, String fullyQualifiedName) throws JavaModelException { IType[] types = cu.getAllTypes(); for (int i = 0; i < types.length; i++) { IType type = types[i]; if (getFullyQualifiedName(type).equals(fullyQualifiedName)) return type; } return null; } /** * Returns the fully qualified name of the given type using '.' as separators. * This is a replace for IType.getFullyQualifiedTypeName * which uses '$' as separators. As '$' is also a valid character in an id * this is ambiguous. JavaCore PR: 1GCFUNT */ public static String getFullyQualifiedName(IType type) { try { if (type.isBinary() && !type.isAnonymous()) { IType declaringType = type.getDeclaringType(); if (declaringType != null) { return getFullyQualifiedName(declaringType) + '.' + type.getElementName(); } } } catch (JavaModelException e) { } return type.getFullyQualifiedName('.'); } /** * Force a reconcile of a compilation unit. * @param unit */ public static void reconcile(ICompilationUnit unit) throws JavaModelException { unit.reconcile(ICompilationUnit.NO_AST, false, null, null); } /** * Returns the package fragment root of <code>IJavaElement</code>. If the given * element is already a package fragment root, the element itself is returned. */ public static IPackageFragmentRoot getPackageFragmentRoot(IJavaElement element) { return (IPackageFragmentRoot)element.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT); } /** * Resolves a type name in the context of the declaring type. * @param refTypeSig the type name in signature notation (for example 'QVector') this can also be an array type, * but dimensions will be ignored. * @param declaringType the context for resolving (type where the reference was made in) * @return returns the fully qualified type name or build-in-type name. if a unresolved type couldn't be * resolved null is returned */ public static String getResolvedTypeName(String refTypeSig, IType declaringType) throws JavaModelException { int arrayCount = Signature.getArrayCount(refTypeSig); char type = refTypeSig.charAt(arrayCount); String fullName = null; if (type == Signature.C_UNRESOLVED) { String name = ""; int bracket = refTypeSig.indexOf(Signature.C_GENERIC_START, arrayCount + 1); if (bracket > 0){ name = refTypeSig.substring(arrayCount + 1, bracket); } else{ int semi = refTypeSig.indexOf(Signature.C_SEMICOLON, arrayCount + 1); if (semi == -1) { throw new IllegalArgumentException(); } name = refTypeSig.substring(arrayCount + 1, semi); } String[][] resolvedNames = declaringType.resolveType(name); if (resolvedNames != null && resolvedNames.length > 0) { String pkgname = resolvedNames[0][0]; String clsname = resolvedNames[0][1]; StringBuffer buf = new StringBuffer(); for(int i=0;i<clsname.length();i++){ char ch = clsname.charAt(i); if(ch=='.') buf.append('$'); else buf.append(ch); } clsname = buf.toString(); fullName = concatenateName(pkgname, clsname); } } else { fullName = Signature.toString(refTypeSig.substring(arrayCount)); } //append array tag for(int i=0;fullName!=null && i<arrayCount;i++){ fullName += "[]"; } return fullName; } /*public static String getResolvedTypeFileName(String refTypeSig, IType declaringType) throws JavaModelException { int arrayCount = Signature.getArrayCount(refTypeSig); char type = refTypeSig.charAt(arrayCount); if (type == Signature.C_UNRESOLVED) { String name = ""; int bracket = refTypeSig.indexOf(Signature.C_GENERIC_START, arrayCount + 1); if (bracket > 0) name = refTypeSig.substring(arrayCount + 1, bracket); else { int semi = refTypeSig.indexOf(Signature.C_SEMICOLON, arrayCount + 1); if (semi == -1) { throw new IllegalArgumentException(); } name = refTypeSig.substring(arrayCount + 1, semi); } String[][] resolvedNames = declaringType.resolveType(name); if (resolvedNames != null && resolvedNames.length > 0) { return concatenateName(resolvedNames[0][0], resolvedNames[0][1].replace('.', '$')); } return "*"; } else { return Signature.toString(refTypeSig.substring(arrayCount)); } }*/ /** * Determine if refTypeSig is unresolved. */ /*public static boolean isTypeNameUnresolved(String refTypeSig) { return refTypeSig.charAt(Signature.getArrayCount(refTypeSig)) == Signature.C_UNRESOLVED; } public static String resolveTypeName(String refTypeSig, IType declaringType) throws JavaModelException { int arrayCount = Signature.getArrayCount(refTypeSig); char type = refTypeSig.charAt(arrayCount); if (type == Signature.C_UNRESOLVED) { String name = ""; int bracket = refTypeSig.indexOf(Signature.C_GENERIC_START, arrayCount + 1); if (bracket > 0) name = refTypeSig.substring(arrayCount + 1, bracket); else { int semi = refTypeSig.indexOf(Signature.C_SEMICOLON, arrayCount + 1); if (semi == -1) { throw new IllegalArgumentException(); } name = refTypeSig.substring(arrayCount + 1, semi); } String[][] resolvedNames = declaringType.resolveType(name); if (resolvedNames != null && resolvedNames.length > 0) { return JDTUtils.concatenateName(resolvedNames[0][0], resolvedNames[0][1]); } else { throw new JavaModelException(new Exception(name + " not resolvable"), IJavaModelStatusConstants.EVALUATION_ERROR); } } return refTypeSig; } public static void resolveTypeName(String refTypeSig, IType declaringType, StringBuffer sb) throws JavaModelException { if (isTypeNameUnresolved(refTypeSig)) { sb.append(refTypeSig.substring(0, refTypeSig.indexOf(Signature.C_UNRESOLVED))); sb.append('L'); sb.append(resolveTypeName(refTypeSig, declaringType)); sb.append(';'); } else { sb.append(refTypeSig); } }*/ /** * Concatenates two names. Uses a dot for separation. * Both strings can be empty or <code>null</code>. */ public static String concatenateName(String name1, String name2) { StringBuffer buf = new StringBuffer(); if (name1 != null && name1.length() > 0) { buf.append(name1); } if (name2 != null && name2.length() > 0) { if (buf.length() > 0) { buf.append('.'); } buf.append(name2); } return buf.toString(); } public static IJavaElement[] codeResolve(ITextEditor editor) throws JavaModelException { return codeResolve(editor, true); } /** * @param primaryOnly if <code>true</code> only primary working copies will be returned * @since 3.2 */ public static IJavaElement[] codeResolve(ITextEditor editor, boolean primaryOnly) throws JavaModelException { return codeResolve(getInput(editor, primaryOnly), (ITextSelection)editor.getSelectionProvider().getSelection()); } public static IJavaElement getInput(IEditorPart editor) { return getInput(editor, true); } /** * @param primaryOnly if <code>true</code> only primary working copies will be returned * @since 3.2 */ private static IJavaElement getInput(IEditorPart editor, boolean primaryOnly) { if (editor == null) return null; return getEditorInputJavaElement(editor, primaryOnly); } public static ICompilationUnit getInputAsCompilationUnit(ITextEditor editor) { Object editorInput = getInput(editor); if (editorInput instanceof ICompilationUnit) return (ICompilationUnit)editorInput; return null; } /** * Returns the given editor's input as Java element. * @param editor the editor * @param primaryOnly if <code>true</code> only primary working copies will be returned * @return the given editor's input as Java element or <code>null</code> if none * @since 3.2 */ public static IJavaElement getEditorInputJavaElement(IEditorPart editor, boolean primaryOnly) { assert (editor!=null); IEditorInput editorInput = editor.getEditorInput(); if (editorInput == null) return null; IJavaElement je = getEditorInputJavaElement(editorInput); if (je != null || primaryOnly) return je; return JavaUI.getWorkingCopyManager().getWorkingCopy(editorInput); } /** * Returns the Java element wrapped by the given editor input. * @param editorInput the editor input * @return the Java element wrapped by <code>editorInput</code> or <code>null</code> if none * @since 3.2 */ public static IJavaElement getEditorInputJavaElement(IEditorInput editorInput) { IJavaElement je = JavaUI.getWorkingCopyManager().getWorkingCopy(editorInput); if (je != null) return je; return (IJavaElement)editorInput.getAdapter(IJavaElement.class); } public static IJavaElement[] codeResolve(IJavaElement input, ITextSelection selection) throws JavaModelException { return codeResolve(input,selection.getOffset(), selection.getLength()); } public static IJavaElement[] codeResolve(IJavaElement input, int offset,int length) throws JavaModelException { if (input instanceof ICodeAssist) { if (input instanceof ICompilationUnit) { reconcile((ICompilationUnit)input); } IJavaElement[] elements = ((ICodeAssist)input).codeSelect(offset, length); if (elements != null && elements.length > 0) return elements; } return EMPTY_RESULT; } public static IJavaElement[] codeResolve(IEditorPart editor, int offset,int length) throws JavaModelException { IJavaElement input = getInput(editor); return codeResolve( input, offset, length); } /** * @param primaryOnly if <code>true</code> only primary working copies will be returned * @since 3.2 */ public static IJavaElement getElementAtOffset(ITextEditor editor, boolean primaryOnly) throws JavaModelException { ITextSelection selection = (ITextSelection)editor.getSelectionProvider().getSelection(); return getElementAtOffset(getInput(editor, primaryOnly), selection.getOffset()); } public static IJavaElement getElementAtOffset(IJavaElement input, int offset) throws JavaModelException { if (input instanceof ICompilationUnit) { ICompilationUnit cunit = (ICompilationUnit) input; reconcile(cunit); IJavaElement ref = cunit.getElementAt(offset); if (ref == null) return input; else return ref; } else if (input instanceof IClassFile) { IJavaElement ref = ((IClassFile) input).getElementAt(offset); if (ref == null) return input; else return ref; } return null; } public static IMethod getMethod(ILocalVariable local){ IJavaElement parent = local.getParent(); IMethod method = null; while(parent!=null){ if(parent instanceof IMethod){ method = (IMethod)parent; break; } parent = parent.getParent(); } return method; } /** Get the soot signature (recognizable by soot) of a given method. */ public static String getMethodSootSignature(IMethod method){ IType declaringType = method.getDeclaringType(); try { String m = "<" + method.getDeclaringType().getFullyQualifiedName(); String returnType = method.getReturnType(); returnType = JDTUtils.getResolvedTypeName(returnType, declaringType); // returnType = Signature.toString(returnType); m += ": " + returnType; m += " " + method.getElementName() + "("; String[] params = method.getParameterTypes(); for (int i = 0; i < params.length; i++) { String p = params[i]; p = JDTUtils.getResolvedTypeName(p, declaringType); m += p; if (i < params.length - 1) { m += ","; } } m += ")>"; //m+=""; return m; } catch (Exception e) { } return null; } public static String getTypeSootSignature(IType type){ String name = type.getTypeQualifiedName(); String pkg = type.getPackageFragment().getElementName(); String signature = pkg+"."+name; return signature; } public static String getFieldSignature(IField field){ try{ IType declaringType = field.getDeclaringType(); String classname = declaringType.getFullyQualifiedName(); //classname = JDTUtils.getResolvedTypeName(classname, declaringType); String type = field.getTypeSignature(); type = JDTUtils.getResolvedTypeName(type, declaringType); //if the declaration is a generic type, then unfortunately, the type will //be null, but actually it is of java.lang.Object type if(type==null){ type = "java.lang.Object"; } String sig = "<"+classname + ": " + type + " " +field.getElementName()+">"; return sig; }catch(Exception e){ } return ""; } public static String getLineText(ITextEditor editor,int line,boolean includeLineLabel,boolean keepHeadBlanks){ try{ IDocumentProvider provider = editor.getDocumentProvider(); IDocument doc = provider.getDocument(editor.getEditorInput()); IRegion region = doc.getLineInformation(line); int offset = region.getOffset(); int length = region.getLength(); String text = doc.get(offset,length); if(keepHeadBlanks) text = text.replaceAll("\t", " "); else text = text.trim(); if(includeLineLabel){ String linestr = "" + (line+1); while(linestr.length()<5){ linestr += " "; } return linestr+": "+text; } else{ return text; } } catch(Exception e){ } return ""; } public static String getSelectedText(ITextEditor editor, ITextSelection selection) throws Exception { IDocumentProvider provider = editor.getDocumentProvider(); IDocument doc = provider.getDocument(editor.getEditorInput()); return doc.get(selection.getOffset(),selection.getLength()); } public static IMethod getEnclosingMethod(ITextEditor editor,int line) throws JavaModelException{ ICompilationUnit cunit = getInputAsCompilationUnit(editor); reconcile(cunit); int startOffset = 0; int endOffset = 0; try{ IDocumentProvider provider = editor.getDocumentProvider(); IDocument doc = provider.getDocument(editor.getEditorInput()); IRegion region = doc.getLineInformation(line); startOffset = region.getOffset(); endOffset = startOffset + region.getLength(); } catch(Exception e){} for(int i=startOffset;i<endOffset;i++){ IJavaElement ref = cunit.getElementAt(i); if(ref!=null){ IMethod m = findMethod(ref); if(m!=null) return m; } } return null; } public static int getMethodStartLine(ITextEditor editor,IMethod method){ try{ IDocumentProvider provider = editor.getDocumentProvider(); IDocument doc = provider.getDocument(editor.getEditorInput()); //ISourceRange range = method.getSourceRange(); ISourceRange range = method.getNameRange(); if(range!=null){ int offset = range.getOffset(); return doc.getLineOfOffset(offset); } } catch(Exception e){ e.printStackTrace(); } return -1; } private static IMethod findMethod(IJavaElement elmt){ while(elmt!=null){ if(elmt instanceof IMethod){ return (IMethod)elmt; } elmt = elmt.getParent(); } return null; } public static ICompilationUnit getCompliationUnit(ITextEditor editor){ IEditorInput input = editor.getEditorInput(); ICompilationUnit icompilationUnit = null; if(input instanceof ICompilationUnit){ icompilationUnit = (ICompilationUnit)input; } else{ FileEditorInput fileEditorInput = (FileEditorInput)input; IFile ifile = fileEditorInput.getFile(); icompilationUnit = JavaCore.createCompilationUnitFrom(ifile); } return icompilationUnit; } } /* private IJavaElement[] codeResolve(ICompilationUnit input, ITextSelection selection) throws JavaModelException { if (input instanceof ICodeAssist) { IJavaElement[] elements = ((ICodeAssist) input).codeSelect( selection.getOffset(), selection.getLength()); if (elements != null && elements.length > 0) return elements; } return EMPTY_RESULT; } */ /* package de.loskutov.bco.ui; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IPathVariableManager; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.jdt.core.Flags; import org.eclipse.jdt.core.IClassFile; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IInitializer; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMember; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.IParent; import org.eclipse.jdt.core.ISourceRange; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.ITypeParameter; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.Signature; import org.eclipse.jface.text.ITextSelection; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.InnerClassNode; import de.loskutov.bco.BytecodeOutlinePlugin; import de.loskutov.bco.asm.DecompiledClass; public class JdtUtils { // package separator in bytecode notation private static final char PACKAGE_SEPARATOR = '/'; // type name separator (for inner types) in bytecode notation private static final char TYPE_SEPARATOR = '$'; public static IJavaElement getMethod(IParent parent, String signature){ try { IJavaElement[] children = parent.getChildren(); for (int i = 0; i < children.length; i++) { IJavaElement javaElement = children[i]; switch (javaElement.getElementType()) { case IJavaElement.INITIALIZER : // fall through case IJavaElement.METHOD : if(signature.equals(getMethodSignature(javaElement))){ return javaElement; } break; default : break; } if(javaElement instanceof IParent){ javaElement = getMethod((IParent) javaElement, signature); if(javaElement != null){ return javaElement; } } } } catch (JavaModelException e) { // just ignore it. Mostly caused by class files not on the class path // which is not a problem for us, but a big problem for JDT } return null; } * @param childEl * @return method signature, if given java element is either initializer or method, * otherwise returns null. public static String getMethodSignature(IJavaElement childEl) { String methodName = null; if (childEl.getElementType() == IJavaElement.INITIALIZER) { IInitializer ini = (IInitializer) childEl; try { if (Flags.isStatic(ini.getFlags())) { methodName = "<clinit>()V"; } else { methodName = "<init>()"; } } catch (JavaModelException e) { // this is compilation problem - don't show the message BytecodeOutlinePlugin.log(e, IStatus.WARNING); } } else if (childEl.getElementType() == IJavaElement.METHOD) { IMethod iMethod = (IMethod) childEl; try { methodName = createMethodSignature(iMethod); } catch (JavaModelException e) { // this is compilation problem - don't show the message BytecodeOutlinePlugin.log(e, IStatus.WARNING); } } return methodName; } public static String createMethodSignature(IMethod iMethod) throws JavaModelException { StringBuffer sb = new StringBuffer(); // Eclipse put class name as constructor name - we change it! if (iMethod.isConstructor()) { sb.append("<init>"); //$NON-NLS-1$ } else { sb.append(iMethod.getElementName()); } if (iMethod.isBinary()) { // iMethod instanceof BinaryMember // binary info should be full qualified return sb.append(iMethod.getSignature()).toString(); } // start method parameter descriptions list sb.append('('); IType declaringType = iMethod.getDeclaringType(); String[] parameterTypes = iMethod.getParameterTypes(); * For non - static inner classes bytecode constructor should contain as first * parameter the enclosing type instance, but in Eclipse AST there are no * appropriated parameter. So we need to create enclosing type signature and * add it as first parameter. if (iMethod.isConstructor() && isNonStaticInner(declaringType)) { // this is a very special case String typeSignature = getTypeSignature(getFirstAncestor(declaringType)); if(typeSignature != null) { String [] newParams = new String [parameterTypes.length + 1]; newParams[0] = typeSignature; System.arraycopy(parameterTypes, 0, newParams, 1, parameterTypes.length); parameterTypes = newParams; } } // doSomething(Lgenerics/DummyForAsmGenerics;)Lgenerics/DummyForAsmGenerics; for (int i = 0; i < parameterTypes.length; i++) { String resolvedType = getResolvedType(parameterTypes[i], declaringType); if(resolvedType != null && resolvedType.length() > 0){ sb.append(resolvedType); } else { // this is a generic type appendGenericType(sb, iMethod, parameterTypes[i]); } } sb.append(')'); // continue here with adding resolved return type String returnType = iMethod.getReturnType(); String resolvedType = getResolvedType(returnType, declaringType); if(resolvedType != null && resolvedType.length() > 0){ sb.append(resolvedType); } else { // this is a generic type appendGenericType(sb, iMethod, returnType); } return sb.toString(); } * @param type * @return full qualified, resolved type name in bytecode notation private static String getTypeSignature(IType type) { if(type == null){ return null; } * getFullyQualifiedName() returns name, where package separator is '.', * but we need '/' for bytecode. The hack with ',' is to use a character * which is not allowed as Java char to be sure not to replace too much String name = type.getFullyQualifiedName(','); // replace package separators name = name.replace(Signature.C_DOT, PACKAGE_SEPARATOR); // replace class separators name = name.replace(',', TYPE_SEPARATOR); return Signature.C_RESOLVED + name + Signature.C_SEMICOLON; } private static void appendGenericType(StringBuffer sb, IMethod iMethod, String unresolvedType) throws JavaModelException{ IType declaringType = iMethod.getDeclaringType(); // unresolvedType is here like "QA;" => we remove "Q" and ";" if(unresolvedType.length() < 3){ // ???? something wrong here .... sb.append(unresolvedType); return; } unresolvedType = unresolvedType.substring(1, unresolvedType.length() - 1); ITypeParameter typeParameter = iMethod.getTypeParameter(unresolvedType); if(typeParameter == null || !typeParameter.exists()){ typeParameter = declaringType.getTypeParameter(unresolvedType); } String[] bounds = typeParameter.getBounds(); if(bounds.length == 0){ sb.append("Ljava/lang/Object;"); } else { for (int i = 0; i < bounds.length; i++) { String simplyName = bounds[i]; simplyName = Signature.C_UNRESOLVED + simplyName + Signature.C_NAME_END; String resolvedType = getResolvedType(simplyName, declaringType); sb.append(resolvedType); } } } * @param typeToResolve * @param declaringType * @return full qualified "bytecode formatted" type * @throws JavaModelException private static String getResolvedType(String typeToResolve, IType declaringType) throws JavaModelException { StringBuffer sb = new StringBuffer(); int arrayCount = Signature.getArrayCount(typeToResolve); // test which letter is following - Q or L are for reference types boolean isPrimitive = isPrimitiveType(typeToResolve.charAt(arrayCount)); if (isPrimitive) { // simply add whole string (probably with array chars like [[I etc.) sb.append(typeToResolve); } else { boolean isUnresolvedType = isUnresolvedType(typeToResolve, arrayCount); if(!isUnresolvedType) { sb.append(typeToResolve); } else { // we need resolved types String resolved = getResolvedTypeName(typeToResolve, declaringType); if(resolved != null) { while (arrayCount > 0) { sb.append(Signature.C_ARRAY); arrayCount--; } sb.append(Signature.C_RESOLVED); sb.append(resolved); sb.append(Signature.C_SEMICOLON); } } } return sb.toString(); } * Copied and modified from JavaModelUtil. Resolves a type name in the context of the * declaring type. * @param refTypeSig the type name in signature notation (for example 'QVector') this * can also be an array type, but dimensions will be ignored. * @param declaringType the context for resolving (type where the reference was made * in) * @return returns the fully qualified <b>bytecode </b> type name or build-in-type * name. if a unresoved type couldn't be resolved null is returned private static String getResolvedTypeName(String refTypeSig, IType declaringType) throws JavaModelException { the whole method is copied from JavaModelUtil.getResolvedTypeName(...). * The problem is, that JavaModelUtil uses '.' to separate package * names, but we need '/' -> see JavaModelUtil.concatenateName() vs * JdtUtils.concatenateName() int arrayCount = Signature.getArrayCount(refTypeSig); if (isUnresolvedType(refTypeSig, arrayCount)) { String name= ""; //$NON-NLS-1$ int bracket= refTypeSig.indexOf(Signature.C_GENERIC_START, arrayCount + 1); if (bracket > 0) { name= refTypeSig.substring(arrayCount + 1, bracket); } else { int semi= refTypeSig.indexOf(Signature.C_SEMICOLON, arrayCount + 1); if (semi == -1) { throw new IllegalArgumentException(); } name= refTypeSig.substring(arrayCount + 1, semi); } String[][] resolvedNames= declaringType.resolveType(name); if (resolvedNames != null && resolvedNames.length > 0) { return concatenateName(resolvedNames[0][0], resolvedNames[0][1]); } return null; } return refTypeSig.substring(arrayCount);// Signature.toString(substring); } * Test which letter is following - Q or L are for reference types * @param first * @return true, if character is not a simbol for reference types private static boolean isPrimitiveType(char first) { return (first != Signature.C_RESOLVED && first != Signature.C_UNRESOLVED); } * @param childEl may be null * @return first ancestor with IJavaElement.TYPE element type, or null public static IType getEnclosingType(IJavaElement childEl) { if (childEl == null) { return null; } return (IType) childEl.getAncestor(IJavaElement.TYPE); } * @param cf * @param dc * @return inner type which has the same name as the given string, or null public static IClassFile getInnerType(IClassFile cf, DecompiledClass dc, String typeSignature) { if(typeSignature.endsWith(";")){ typeSignature = typeSignature.substring(0, typeSignature.length()-1); if(typeSignature.startsWith("L")){ typeSignature = typeSignature.substring(1, typeSignature.length()); } } * For inner and anonymous classes from the blocks or methods * getFullyQualifiedName() does not work if class was compiled with 1.5 * and will never match the fullTypeName... * I'm not sure if it is intended or if it is a bug * in Eclipse: instead of A$1B we get A$B for B class from a method in A * * NB: for binary types without source attachment the method elements doesn't * contain source and therefore could not resolve child elements. * So the search for local types will never work... * * Therefore we do not use Eclipse API and use ClassNode->InnerClassNode elements ClassNode cn = dc.getClassNode(); List*<InnerClassNode>* innerClasses = cn.innerClasses; for (int i = 0; i < innerClasses.size(); i++) { InnerClassNode in = (InnerClassNode) innerClasses.get(i); if(typeSignature.equals(in.name)){ int idx = typeSignature.lastIndexOf(PACKAGE_SEPARATOR); String className = typeSignature; if (idx > 0) { className = typeSignature.substring(idx + 1, typeSignature.length()); } className += ".class"; return cf.getType().getPackageFragment().getClassFile(className); } } return null; } * Modified copy from org.eclipse.jdt.internal.ui.actions.SelectionConverter * @param input * @param selection * @return null, if selection is null or could not be resolved to java element * @throws JavaModelException public static IJavaElement getElementAtOffset(IJavaElement input, ITextSelection selection) throws JavaModelException { if(selection == null){ return null; } ICompilationUnit workingCopy = null; if (input instanceof ICompilationUnit) { workingCopy = (ICompilationUnit) input; // be in-sync with model // instead of using internal JavaModelUtil.reconcile(workingCopy); synchronized(workingCopy) { workingCopy.reconcile( ICompilationUnit.NO_AST, false don't force problem detection , null use primary owner null no progress monitor ); } IJavaElement ref = workingCopy.getElementAt(selection.getOffset()); if (ref != null) { return ref; } } else if (input instanceof IClassFile) { IClassFile iClass = (IClassFile) input; IJavaElement ref = iClass.getElementAt(selection.getOffset()); if (ref != null) { // If we are in the inner class, try to refine search result now if(ref instanceof IType){ IType type = (IType) ref; IClassFile classFile = type.getClassFile(); if(classFile != iClass){ * WORKAROUND it seems that source range for constructors from * bytecode with source attached from zip files is not computed * in Eclipse (SourceMapper returns nothing useful). * Example: HashMap$Entry class with constructor * <init>(ILjava/lang/Object;Ljava/lang/Object;Ljava/util/HashMap$Entry;)V * We will get here at least the inner class... * see https://bugs.eclipse.org/bugs/show_bug.cgi?id=137847 ref = classFile.getElementAt(selection.getOffset()); } } return ref; } } return null; } * Modified copy from JavaModelUtil. * @param javaElt * @return true, if corresponding java project has compiler setting to generate * bytecode for jdk 1.5 and above public static boolean is50OrHigher(IJavaElement javaElt) { IJavaProject project = javaElt.getJavaProject(); String option = project.getOption(JavaCore.COMPILER_COMPLIANCE, true); boolean result = JavaCore.VERSION_1_5.equals(option); if(result){ return result; } // probably > 1.5? result = JavaCore.VERSION_1_4.equals(option); if(result){ return false; } result = JavaCore.VERSION_1_3.equals(option); if(result){ return false; } result = JavaCore.VERSION_1_2.equals(option); if(result){ return false; } result = JavaCore.VERSION_1_1.equals(option); if(result){ return false; } // unknown = > 1.5 return true; } * Cite: jdk1.1.8/docs/guide/innerclasses/spec/innerclasses.doc10.html: For the sake * of tools, there are some additional requirements on the naming of an inaccessible * class N. Its bytecode name must consist of the bytecode name of an enclosing class * (the immediately enclosing class, if it is a member), followed either by `$' and a * positive decimal numeral chosen by the compiler, or by `$' and the simple name of * N, or else by both (in that order). Moreover, the bytecode name of a block-local N * must consist of its enclosing package member T, the characters `$1$', and N, if the * resulting name would be unique. * <br> * Note, that this rule was changed for static blocks after 1.5 jdk. * @param javaElement * @return simply element name public static String getElementName(IJavaElement javaElement) { if (isAnonymousType(javaElement)) { IType anonType = (IType) javaElement; List allAnonymous = new ArrayList(); * in order to resolve anon. class name we need to know about all other * anonymous classes in declaring class, therefore we need to collect all here collectAllAnonymous(allAnonymous, anonType); int idx = getAnonimousIndex(anonType, (IType[]) allAnonymous .toArray(new IType[allAnonymous.size()])); return Integer.toString(idx); } String name = javaElement.getElementName(); if (isLocal(javaElement)) { * Compiler have different naming conventions for inner non-anon. classes in * static blocks or any methods, this difference was introduced with 1.5 JDK. * The problem is, that we could have projects with classes, generated * with both 1.5 and earlier settings. One could not see on particular * java element, for which jdk version the existing bytecode was generated. * If we could have a *.class file, but we are just searching for one... * So there could be still a chance, that this code fails, if java element * is not compiled with comiler settings from project, but with different if(is50OrHigher(javaElement)){ name = "1" + name; // compiler output changed for > 1.5 code } else { name = "1$" + name; // see method comment, this was the case for older code } } if (name.endsWith(".java")) { //$NON-NLS-1$ name = name.substring(0, name.lastIndexOf(".java")); //$NON-NLS-1$ } else if (name.endsWith(".class")) { //$NON-NLS-1$ name = name.substring(0, name.lastIndexOf(".class")); //$NON-NLS-1$ } return name; } * @param javaElement * @return null, if javaElement is top level class static IType getFirstAncestor(IJavaElement javaElement) { IJavaElement parent = javaElement; if (javaElement.getElementType() == IJavaElement.TYPE) { parent = javaElement.getParent(); } if (parent != null) { return (IType) parent.getAncestor(IJavaElement.TYPE); } return null; } static IJavaElement getLastAncestor(IJavaElement javaElement, int elementType) { IJavaElement lastFound = null; if (elementType == javaElement.getElementType()) { lastFound = javaElement; } IJavaElement parent = javaElement.getParent(); if (parent == null) { return lastFound; } IJavaElement ancestor = parent.getAncestor(elementType); if (ancestor != null) { return getLastAncestor(ancestor, elementType); } return lastFound; } * @param javaElement * @return distance to given ancestor, 0 if it is the same, -1 if ancestor with type * IJavaElement.TYPE does not exist static int getTopAncestorDistance(IJavaElement javaElement, IJavaElement topAncestor) { if (topAncestor == javaElement) { return 0; } IJavaElement ancestor = getFirstAncestor(javaElement); if (ancestor != null) { return 1 + getTopAncestorDistance(ancestor, topAncestor); } // this is not possible, if ancestor exist - which return value we should use? return -1; } * @param javaElement * @return first non-anonymous ancestor static IJavaElement getFirstNonAnonymous(IJavaElement javaElement, IJavaElement topAncestor) { if (javaElement.getElementType() == IJavaElement.TYPE && !isAnonymousType(javaElement)) { return javaElement; } IJavaElement parent = javaElement.getParent(); if (parent == null) { return topAncestor; } IJavaElement ancestor = parent.getAncestor(IJavaElement.TYPE); if (ancestor != null) { return getFirstNonAnonymous(ancestor, topAncestor); } return topAncestor; } * @param javaElement * @return true, if given element is anonymous inner class private static boolean isAnonymousType(IJavaElement javaElement) { try { return javaElement instanceof IType && ((IType)javaElement).isAnonymous(); } catch (JavaModelException e) { BytecodeOutlinePlugin.log(e, IStatus.ERROR); } return false; } * @param innerType should be inner type. * @return true, if given element is inner class from initializer block or method body private static boolean isLocal(IJavaElement innerType) { try { return innerType instanceof IType && ((IType)innerType).isLocal(); } catch (JavaModelException e) { BytecodeOutlinePlugin.log(e, IStatus.ERROR); } return false; } * @param type * @return true, if given element is non static inner class * @throws JavaModelException private static boolean isNonStaticInner(IType type) throws JavaModelException { if(type.isMember()){ return !Flags.isStatic(type.getFlags()); } return false; } * @param innerType should be inner type. * @return true, if given element is inner class from initializer block private static boolean isInnerFromBlock(IType type) { try { IJavaElement ancestor = type.getAncestor(IJavaElement.INITIALIZER); return type.isLocal() && ancestor != null; } catch (JavaModelException e) { BytecodeOutlinePlugin.log(e, IStatus.ERROR); } return false; } * @param javaElement * @return absolute path of generated bytecode package for given element * @throws JavaModelException private static String getPackageOutputPath(IJavaElement javaElement) throws JavaModelException { String dir = ""; //$NON-NLS-1$ if (javaElement == null) { return dir; } IJavaProject project = javaElement.getJavaProject(); if (project == null) { return dir; } // default bytecode location IPath path = project.getOutputLocation(); IResource resource = javaElement.getUnderlyingResource(); if (resource == null) { return dir; } // resolve multiple output locations here if (project.exists() && project.getProject().isOpen()) { IClasspathEntry entries[] = project.getRawClasspath(); for (int i = 0; i < entries.length; i++) { IClasspathEntry classpathEntry = entries[i]; if (classpathEntry.getEntryKind() == IClasspathEntry.CPE_SOURCE) { IPath outputPath = classpathEntry.getOutputLocation(); if (outputPath != null && classpathEntry.getPath().isPrefixOf( resource.getFullPath())) { path = outputPath; break; } } } } if (path == null) { // check the default location if not already included IPath def = project.getOutputLocation(); if (def != null && def.isPrefixOf(resource.getFullPath())){ path = def; } } if(path == null){ return dir; } IWorkspace workspace = ResourcesPlugin.getWorkspace(); if (!project.getPath().equals(path)) { IFolder outputFolder = workspace.getRoot().getFolder(path); if (outputFolder != null) { // linked resources will be resolved here! IPath rawPath = outputFolder.getRawLocation(); if (rawPath != null) { path = rawPath; } } } else { path = project.getProject().getLocation(); } // here we should resolve path variables, // probably existing at first place of path IPathVariableManager pathManager = workspace.getPathVariableManager(); path = pathManager.resolvePath(path); if (path == null) { return dir; } if (isPackageRoot(project, resource)) { dir = path.toOSString(); } else { String packPath = EclipseUtils.getJavaPackageName(javaElement) .replace(Signature.C_DOT, PACKAGE_SEPARATOR); dir = path.append(packPath).toOSString(); } return dir; } * @param project * @param pack * @return true if 'pack' argument is package root * @throws JavaModelException private static boolean isPackageRoot(IJavaProject project, IResource pack) throws JavaModelException { boolean isRoot = false; if (project == null || pack == null) { return isRoot; } IPackageFragmentRoot root = project.getPackageFragmentRoot(pack); IClasspathEntry clPathEntry = null; if (root != null) { clPathEntry = root.getRawClasspathEntry(); } isRoot = clPathEntry != null; return isRoot; } * Works only for eclipse - managed/generated bytecode, ergo not with imported * classes/jars * @param javaElement * @return full os-specific file path to .class resource, containing given element public static String getByteCodePath(IJavaElement javaElement) { if (javaElement == null) { return "";//$NON-NLS-1$ } String packagePath = ""; //$NON-NLS-1$ try { packagePath = getPackageOutputPath(javaElement); } catch (JavaModelException e) { BytecodeOutlinePlugin.log(e, IStatus.ERROR); return ""; } IJavaElement ancestor = getLastAncestor(javaElement, IJavaElement.TYPE); StringBuffer sb = new StringBuffer(packagePath); sb.append(File.separator); sb.append(getClassName(javaElement, ancestor)); sb.append(".class"); //$NON-NLS-1$ return sb.toString(); } * @param javaElement * @return new generated input stream for given element bytecode class file, or null * if class file cannot be found or this element is not from java source path public static InputStream createInputStream(IJavaElement javaElement) { IClassFile classFile = (IClassFile) javaElement .getAncestor(IJavaElement.CLASS_FILE); InputStream is = null; // existing read-only class files if (classFile != null) { IJavaElement jarParent = classFile.getParent(); // TODO dirty hack to be sure, that package is from jar - // because JarPackageFragment is not public class, we cannot // use instanceof here boolean isJar = jarParent != null && jarParent.getClass().getName() .endsWith("JarPackageFragment"); //$NON-NLS-1$ if (isJar) { is = createStreamFromJar(classFile); } else { is = createStreamFromClass(classFile); } } else { // usual eclipse - generated bytecode boolean inJavaPath = isOnClasspath(javaElement); if (!inJavaPath) { return null; } String classPath = getByteCodePath(javaElement); try { is = new FileInputStream(classPath); } catch (FileNotFoundException e) { // if autobuild is disabled, we get tons of this errors. // but I think we cannot ignore them, therefore WARNING and not // ERROR status BytecodeOutlinePlugin.log(e, IStatus.WARNING); } } return is; } * Creates stream from external class file from Eclipse classpath (means, that this * class file is read-only) * @param classFile * @return new generated input stream from external class file, or null, if class file * for this element cannot be found private static InputStream createStreamFromClass(IClassFile classFile) { IResource underlyingResource = null; try { // to tell the truth, I don't know why that different methods // are not working in a particular case. But it seems to be better // to use getResource() with non-java elements (not in model) // and getUnderlyingResource() with java elements. if (classFile.exists()) { underlyingResource = classFile.getUnderlyingResource(); } else { // this is a class file that is not in java model underlyingResource = classFile.getResource(); } } catch (JavaModelException e) { BytecodeOutlinePlugin.log(e, IStatus.ERROR); return null; } IPath rawLocation = underlyingResource.getRawLocation(); // here we should resolve path variables, // probably existing at first place of "rawLocation" path IWorkspace workspace = ResourcesPlugin.getWorkspace(); IPathVariableManager pathManager = workspace.getPathVariableManager(); rawLocation = pathManager.resolvePath(rawLocation); try { return new FileInputStream(rawLocation.toOSString()); } catch (FileNotFoundException e) { BytecodeOutlinePlugin.log(e, IStatus.ERROR); } return null; } * Creates stream from external class file that is stored in jar file * @param classFile * @param javaElement * @return new generated input stream from external class file that is stored in jar * file, or null, if class file for this element cannot be found private static InputStream createStreamFromJar(IClassFile classFile) { IPath path = null; IResource resource = classFile.getResource(); // resource == null => this is a external archive if (resource != null) { path = resource.getRawLocation(); } else { path = classFile.getPath(); } if (path == null) { return null; } // here we should resolve path variables, // probably existing at first place of path IWorkspace workspace = ResourcesPlugin.getWorkspace(); IPathVariableManager pathManager = workspace.getPathVariableManager(); path = pathManager.resolvePath(path); JarFile jar = null; try { jar = new JarFile(path.toOSString()); } catch (IOException e) { BytecodeOutlinePlugin.log(e, IStatus.ERROR); return null; } String fullClassName = getFullBytecodeName(classFile); if (fullClassName == null) { return null; } JarEntry jarEntry = jar.getJarEntry(fullClassName); if (jarEntry != null) { try { return jar.getInputStream(jarEntry); } catch (IOException e) { BytecodeOutlinePlugin.log(e, IStatus.ERROR); } } return null; } private static boolean isOnClasspath(IJavaElement javaElement) { IJavaProject project = javaElement.getJavaProject(); if (project != null) { boolean result = project.isOnClasspath(javaElement); return result; } return false; } * @param classFile * @return full qualified bytecode name of given class public static String getFullBytecodeName(IClassFile classFile) { IPackageFragment packageFr = (IPackageFragment) classFile .getAncestor(IJavaElement.PACKAGE_FRAGMENT); if (packageFr == null) { return null; } String packageName = packageFr.getElementName(); // switch to java bytecode naming conventions packageName = packageName.replace(Signature.C_DOT, PACKAGE_SEPARATOR); String className = classFile.getElementName(); if (packageName != null && packageName.length() > 0) { return packageName + PACKAGE_SEPARATOR + className; } return className; } * @param javaElement * @param topAncestor * @param sb private static String getClassName(IJavaElement javaElement, IJavaElement topAncestor) { StringBuffer sb = new StringBuffer(); if (!javaElement.equals(topAncestor)) { int elementType = javaElement.getElementType(); if(elementType == IJavaElement.FIELD || elementType == IJavaElement.METHOD || elementType == IJavaElement.INITIALIZER) { // it's field or method javaElement = getFirstAncestor(javaElement); } else { boolean is50OrHigher = is50OrHigher(javaElement); if (!is50OrHigher && (isAnonymousType(javaElement) || isLocal(javaElement))) { // it's inner type sb.append(getElementName(topAncestor)); sb.append(TYPE_SEPARATOR); } else { * TODO there is an issue with < 1.5 compiler setting and with inner * classes with the same name but defined in different methods in the same * source file. Then compiler needs to generate *different* content for * A$1$B and A$1$B, which is not possible so therefore compiler generates * A$1$B and A$2$B. The naming order is the source range order of inner * classes, so the first inner B class will get A$1$B and the second * inner B class A$2$B etc. // override top ancestor with immediate ancestor topAncestor = getFirstAncestor(javaElement); while (topAncestor != null) { sb.insert(0, getElementName(topAncestor) + TYPE_SEPARATOR); topAncestor = getFirstAncestor(topAncestor); } } } } sb.append(getElementName(javaElement)); return sb.toString(); } * Collect all anonymous classes which are on the same "name shema level" * as the given element for the compiler. The list could contain different set of * elements for the same source code, depends on the compiler and jdk version * @param list for the found anon. classes, elements instanceof IType. * @param anonType the anon. type private static void collectAllAnonymous(List list, IType anonType) { * For JDK >= 1.5 in Eclipse 3.1+ the naming shema for nested anonymous * classes was changed from A$1, A$2, A$3, A$4, ..., A$n * to A$1, A$1$1, A$1$2, A$1$2$1, ..., A$2, A$2$1, A$2$2, ..., A$x$y boolean allowNested = ! is50OrHigher(anonType); IParent declaringType; if(allowNested) { declaringType = (IType) getLastAncestor(anonType, IJavaElement.TYPE); } else { declaringType = anonType.getDeclaringType(); } try { collectAllAnonymous(list, declaringType, allowNested); } catch (JavaModelException e) { BytecodeOutlinePlugin.log(e, IStatus.ERROR); } } * Traverses down the children tree of this parent and collect all child anon. classes * @param list * @param parent * @param allowNested true to search in IType child elements too * @throws JavaModelException private static void collectAllAnonymous(List list, IParent parent, boolean allowNested) throws JavaModelException { IJavaElement[] children = parent.getChildren(); for (int i = 0; i < children.length; i++) { IJavaElement childElem = children[i]; if (isAnonymousType(childElem)) { list.add(childElem); } if (childElem instanceof IParent) { if(allowNested || !(childElem instanceof IType)) { collectAllAnonymous(list, (IParent) childElem, allowNested); } } } } * @param anonType * @param anonymous * @return the index of given java element in the anon. classes list, which was used * by compiler to generate bytecode name for given element. If the given type is not * in the list, then return value is '-1' private static int getAnonimousIndex(IType anonType, IType[] anonymous) { sortAnonymous(anonymous, anonType); for (int i = 0; i < anonymous.length; i++) { if (anonymous[i] == anonType) { // +1 because compiler starts generated classes always with 1 return i + 1; } } return -1; } * Sort given anonymous classes in order like java compiler would generate output * classes, in context of given anonymous type * @param anonymous private static void sortAnonymous(IType[] anonymous, IType anonType) { SourceOffsetComparator sourceComparator = new SourceOffsetComparator(); Arrays.sort(anonymous, new AnonymClassComparator( anonType, sourceComparator)); } * 1) from instance init 2) from deepest inner from instance init (deepest first) 3) from * static init 4) from deepest inner from static init (deepest first) 5) from deepest inner * (deepest first) 6) regular anon classes from main class * * <br> * Note, that nested inner anon. classes which do not have different non-anon. inner class * ancestors, are compiled in they nesting order, opposite to rule 2) * * @param javaElement * @return priority - lesser mean wil be compiled later, a value > 0 * @throws JavaModelException static int getAnonCompilePriority(IJavaElement javaElement, IJavaElement firstAncestor, IJavaElement topAncestor, boolean is50OrHigher) { // search for initializer block IJavaElement lastAncestor = getLastAncestor( javaElement, IJavaElement.INITIALIZER); // test is for anon. classes from initializer blocks if (lastAncestor != null) { IInitializer init = (IInitializer) lastAncestor; int initFlags = 0; try { initFlags = init.getFlags(); } catch (JavaModelException e) { BytecodeOutlinePlugin.log(e, IStatus.ERROR); } if(is50OrHigher){ if (!Flags.isStatic(initFlags)) { if (firstAncestor == topAncestor) { return 10; // instance init } return 9; // from inner from instance init } if (firstAncestor == topAncestor) { return 8; // class init } return 7; // from inner from class init } * crazy 1.4 logic if (Flags.isStatic(initFlags)) { return 10; } IJavaElement firstNonAnon = getFirstNonAnonymous(javaElement, topAncestor); if (isStatic((IMember)firstNonAnon)) { return 9; } return 6; // class init } // from inner from main type if(!is50OrHigher){ * crazy 1.4 logic int topFlags = 0; IJavaElement firstNonAnon = getFirstNonAnonymous(javaElement, topAncestor); try { topFlags = ((IType)firstNonAnon).getFlags(); } catch (JavaModelException e) { BytecodeOutlinePlugin.log(e, IStatus.ERROR); } if (Flags.isStatic(topFlags)) { return 8; } if (firstAncestor == topAncestor) { return 6; // regular anonyme classes } return 6; // the same prio as regular anonyme classes } // test for anon. classes from "regular" code if (firstAncestor == topAncestor) { return 5; // regular anonyme classes } return 6; } private static boolean isStatic(IMember firstNonAnon) { int topFlags = 0; try { topFlags = firstNonAnon.getFlags(); } catch (JavaModelException e) { BytecodeOutlinePlugin.log(e, IStatus.ERROR); } return Flags.isStatic(topFlags); } * @param type * @return public static ClassLoader getClassLoader(IJavaElement type) { ClassLoader cl; IJavaProject javaProject = type.getJavaProject(); List urls = new ArrayList(); getClassURLs(javaProject, urls); if (urls.isEmpty()) { cl = JdtUtils.class.getClassLoader(); } else { cl = new URLClassLoader((URL[]) urls.toArray(new URL[urls.size()])); } return cl; } private static void getClassURLs(IJavaProject javaProject, List urls) { IProject project = javaProject.getProject(); IWorkspaceRoot workspaceRoot = project.getWorkspace().getRoot(); IClasspathEntry[] paths = null; IPath defaultOutputLocation = null; try { paths = javaProject.getResolvedClasspath(true); defaultOutputLocation = javaProject.getOutputLocation(); } catch (JavaModelException e) { // don't show message to user neither log it // BytecodeOutlinePlugin.log(e, IStatus.ERROR); } if (paths != null) { IPath projectPath = javaProject.getProject().getLocation(); for (int i = 0; i < paths.length; ++i) { IClasspathEntry cpEntry = paths[i]; IPath p = null; if (cpEntry.getEntryKind() == IClasspathEntry.CPE_SOURCE) { // filter out source container - there are unused for class // search - add bytecode output location instead p = cpEntry.getOutputLocation(); if (p == null) { // default output used: p = defaultOutputLocation; } } else if (cpEntry.getEntryKind() == IClasspathEntry.CPE_PROJECT) { String projName = cpEntry.getPath().toPortableString() .substring(1); IProject proj = workspaceRoot.getProject(projName); IJavaProject projj = JavaCore.create(proj); getClassURLs(projj, urls); continue; } else { p = cpEntry.getPath(); } if (p == null) { continue; } if (!p.toFile().exists()) { // removeFirstSegments: remove project from relative path p = projectPath.append(p.removeFirstSegments(1)); if (!p.toFile().exists()) { continue; } } try { urls.add(p.toFile().toURL()); } catch (MalformedURLException e) { // don't show message to user BytecodeOutlinePlugin.log(e, IStatus.ERROR); } } } } * Check if java element is an interface or abstract method or a method from * interface. public static boolean isAbstractOrInterface(IJavaElement javaEl) { if (javaEl == null) { return true; } boolean abstractOrInterface = false; try { switch (javaEl.getElementType()) { case IJavaElement.CLASS_FILE : IClassFile classFile = (IClassFile) javaEl; if(isOnClasspath(javaEl)) { abstractOrInterface = classFile.isInterface(); } else { this is the case for eclipse-generated class files. if we do not perform the check in if, then we will have java model exception on classFile.isInterface() call. } break; case IJavaElement.COMPILATION_UNIT : ICompilationUnit cUnit = (ICompilationUnit) javaEl; IType type = cUnit.findPrimaryType(); abstractOrInterface = type != null && type.isInterface(); break; case IJavaElement.TYPE : abstractOrInterface = ((IType) javaEl).isInterface(); break; case IJavaElement.METHOD : // test for "abstract" flag on method in a class abstractOrInterface = Flags.isAbstract(((IMethod) javaEl) .getFlags()); // "abstract" flags could be not exist on interface methods if (!abstractOrInterface) { IType ancestor = (IType) javaEl .getAncestor(IJavaElement.TYPE); abstractOrInterface = ancestor != null && ancestor.isInterface(); } break; default : IType ancestor1 = (IType) javaEl .getAncestor(IJavaElement.TYPE); abstractOrInterface = ancestor1 != null && ancestor1.isInterface(); break; } } catch (JavaModelException e) { // No point to log it here // BytecodeOutlinePlugin.log(e, IStatus.ERROR); } return abstractOrInterface; } static class SourceOffsetComparator implements Comparator { * First source occurence win. * @param o1 should be IType * @param o2 should be IType * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) public int compare(Object o1, Object o2) { IType m1 = (IType) o1; IType m2 = (IType) o2; int idx1, idx2; try { ISourceRange sr1 = m1.getSourceRange(); ISourceRange sr2 = m2.getSourceRange(); if (sr1 == null || sr2 == null) { return 0; } idx1 = sr1.getOffset(); idx2 = sr2.getOffset(); } catch (JavaModelException e) { BytecodeOutlinePlugin.log(e, IStatus.ERROR); return 0; } if (idx1 < idx2) { return -1; } else if (idx1 > idx2) { return 1; } return 0; } } static class AnonymClassComparator implements Comparator { private IType topAncestorType; private SourceOffsetComparator sourceComparator; private boolean is50OrHigher; * @param javaElement * @param sourceComparator public AnonymClassComparator(IType javaElement, SourceOffsetComparator sourceComparator) { this.sourceComparator = sourceComparator; is50OrHigher = is50OrHigher(javaElement); topAncestorType = (IType) getLastAncestor( javaElement, IJavaElement.TYPE); } * If "deep" is the same, then source order win. 1) from instance init 2) from * deepest inner from instance init (deepest first) 3) from static init 4) from * deepest inner from static init (deepest first) 5) from deepest inner (deepest * first) 7) regular anon classes from main class * * <br> * Note, that nested inner anon. classes which do not have different * non-anon. inner class ancestors, are compiled in they nesting order, opposite * to rule 2) * * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) public int compare(Object o1, Object o2) { if(o1 == o2){ return 0; } IType m1 = (IType) o1; IType m2 = (IType) o2; // both have the same ancestor as immediate ancestor if (!is50OrHigher && isInnerFromBlock(m1) && isInnerFromBlock(m2)) { return sourceComparator.compare(o1, o2); } IJavaElement firstAncestor1 = getFirstAncestor(m1); IJavaElement firstAncestor2 = getFirstAncestor(m2); int compilePrio1 = getAnonCompilePriority( m1, firstAncestor1, topAncestorType, is50OrHigher); int compilePrio2 = getAnonCompilePriority( m2, firstAncestor2, topAncestorType, is50OrHigher); // System.out.println("" + compilePrio1 + " -> " + o1.toString().substring(0, 50)); // System.out.println("" + compilePrio2 + " -> " + o2.toString().substring(0, 50)); if (compilePrio1 > compilePrio2) { return -1; } else if (compilePrio1 < compilePrio2) { return 1; } else { firstAncestor1 = getFirstNonAnonymous(m1, topAncestorType); firstAncestor2 = getFirstNonAnonymous(m2, topAncestorType); if(firstAncestor1 == firstAncestor2){ * for anonymous classes from same chain and same first common ancestor, * the order is the definition order int topAncestorDistance1 = getTopAncestorDistance( m1, topAncestorType); int topAncestorDistance2 = getTopAncestorDistance( m2, topAncestorType); if (topAncestorDistance1 < topAncestorDistance2) { return -1; } else if (topAncestorDistance1 > topAncestorDistance2) { return 1; } else { return sourceComparator.compare(o1, o2); } } // if(!is50OrHigher && !isStatic(m1) && !isStatic(m2)){ // return sourceComparator.compare(o1, o2); // } * for anonymous classes which have first non-common non-anonymous ancestor, * the order is the reversed definition order int topAncestorDistance1 = getTopAncestorDistance( firstAncestor1, topAncestorType); int topAncestorDistance2 = getTopAncestorDistance( firstAncestor2, topAncestorType); if (topAncestorDistance1 > topAncestorDistance2) { return -1; } else if (topAncestorDistance1 < topAncestorDistance2) { return 1; } else { return sourceComparator.compare(o1, o2); } } } } }*/ /* public static String getJavaPackageName(IJavaElement resource) { String name = resource == null ? null : resource.getElementName(); //$NON-NLS-1$ if (name == null) { return ""; //$NON-NLS-1$ } int type = resource.getElementType(); if (type == IJavaElement.PACKAGE_FRAGMENT || type == IJavaElement.PACKAGE_FRAGMENT_ROOT) { return name; } IJavaElement ancestor = resource .getAncestor(IJavaElement.PACKAGE_FRAGMENT); if (ancestor != null) { return ancestor.getElementName(); } return ""; //$NON-NLS-1$ }*/