Source Code Cross Referenced for JdtUtils.java in  » Byte-Code » asm » de » loskutov » bco » ui » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Byte Code » asm » de.loskutov.bco.ui 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*****************************************************************************************
0002:         * Copyright (c) 2004 Andrei Loskutov. All rights reserved. This program and the
0003:         * accompanying materials are made available under the terms of the BSD License which
0004:         * accompanies this distribution, and is available at
0005:         * http://www.opensource.org/licenses/bsd-license.php Contributor: Andrei Loskutov -
0006:         * initial API and implementation
0007:         ****************************************************************************************/package de.loskutov.bco.ui;
0008:
0009:        import java.io.File;
0010:        import java.io.FileInputStream;
0011:        import java.io.FileNotFoundException;
0012:        import java.io.IOException;
0013:        import java.io.InputStream;
0014:        import java.net.MalformedURLException;
0015:        import java.net.URL;
0016:        import java.net.URLClassLoader;
0017:        import java.util.ArrayList;
0018:        import java.util.Arrays;
0019:        import java.util.Comparator;
0020:        import java.util.List;
0021:        import java.util.jar.JarEntry;
0022:        import java.util.jar.JarFile;
0023:
0024:        import org.eclipse.core.resources.IFolder;
0025:        import org.eclipse.core.resources.IPathVariableManager;
0026:        import org.eclipse.core.resources.IProject;
0027:        import org.eclipse.core.resources.IResource;
0028:        import org.eclipse.core.resources.IWorkspace;
0029:        import org.eclipse.core.resources.IWorkspaceRoot;
0030:        import org.eclipse.core.resources.ResourcesPlugin;
0031:        import org.eclipse.core.runtime.IPath;
0032:        import org.eclipse.core.runtime.IStatus;
0033:        import org.eclipse.jdt.core.Flags;
0034:        import org.eclipse.jdt.core.IClassFile;
0035:        import org.eclipse.jdt.core.IClasspathEntry;
0036:        import org.eclipse.jdt.core.ICompilationUnit;
0037:        import org.eclipse.jdt.core.IInitializer;
0038:        import org.eclipse.jdt.core.IJavaElement;
0039:        import org.eclipse.jdt.core.IJavaProject;
0040:        import org.eclipse.jdt.core.IMember;
0041:        import org.eclipse.jdt.core.IMethod;
0042:        import org.eclipse.jdt.core.IPackageFragment;
0043:        import org.eclipse.jdt.core.IPackageFragmentRoot;
0044:        import org.eclipse.jdt.core.IParent;
0045:        import org.eclipse.jdt.core.ISourceRange;
0046:        import org.eclipse.jdt.core.IType;
0047:        import org.eclipse.jdt.core.ITypeParameter;
0048:        import org.eclipse.jdt.core.JavaCore;
0049:        import org.eclipse.jdt.core.JavaModelException;
0050:        import org.eclipse.jdt.core.Signature;
0051:        import org.eclipse.jface.text.ITextSelection;
0052:        import org.objectweb.asm.tree.ClassNode;
0053:        import org.objectweb.asm.tree.InnerClassNode;
0054:
0055:        import de.loskutov.bco.BytecodeOutlinePlugin;
0056:        import de.loskutov.bco.asm.DecompiledClass;
0057:
0058:        /**
0059:         * @author Andrei
0060:         */
0061:        public class JdtUtils {
0062:            /** package separator in bytecode notation */
0063:            private static final char PACKAGE_SEPARATOR = '/';
0064:            /** type name separator (for inner types) in bytecode notation */
0065:            private static final char TYPE_SEPARATOR = '$';
0066:
0067:            /**
0068:             *
0069:             */
0070:            private JdtUtils() {
0071:                // don't call
0072:            }
0073:
0074:            public static IJavaElement getMethod(IParent parent,
0075:                    String signature) {
0076:                try {
0077:                    IJavaElement[] children = parent.getChildren();
0078:                    for (int i = 0; i < children.length; i++) {
0079:                        IJavaElement javaElement = children[i];
0080:                        switch (javaElement.getElementType()) {
0081:                        case IJavaElement.INITIALIZER:
0082:                            // fall through
0083:                        case IJavaElement.METHOD:
0084:                            if (signature
0085:                                    .equals(getMethodSignature(javaElement))) {
0086:                                return javaElement;
0087:                            }
0088:                            break;
0089:                        default:
0090:                            break;
0091:                        }
0092:                        if (javaElement instanceof  IParent) {
0093:                            javaElement = getMethod((IParent) javaElement,
0094:                                    signature);
0095:                            if (javaElement != null) {
0096:                                return javaElement;
0097:                            }
0098:                        }
0099:                    }
0100:                } catch (JavaModelException e) {
0101:                    // just ignore it. Mostly caused by class files not on the class path
0102:                    // which is not a problem for us, but a big problem for JDT
0103:                }
0104:                return null;
0105:            }
0106:
0107:            /**
0108:             * @param childEl
0109:             * @return method signature, if given java element is either initializer or method,
0110:             * otherwise returns null.
0111:             */
0112:            public static String getMethodSignature(IJavaElement childEl) {
0113:                String methodName = null;
0114:                if (childEl.getElementType() == IJavaElement.INITIALIZER) {
0115:                    IInitializer ini = (IInitializer) childEl;
0116:                    try {
0117:                        if (Flags.isStatic(ini.getFlags())) {
0118:                            methodName = "<clinit>()V";
0119:                        } else {
0120:                            methodName = "<init>()";
0121:                        }
0122:                    } catch (JavaModelException e) {
0123:                        // this is compilation problem - don't show the message
0124:                        BytecodeOutlinePlugin.log(e, IStatus.WARNING);
0125:                    }
0126:                } else if (childEl.getElementType() == IJavaElement.METHOD) {
0127:                    IMethod iMethod = (IMethod) childEl;
0128:                    try {
0129:                        methodName = createMethodSignature(iMethod);
0130:                    } catch (JavaModelException e) {
0131:                        // this is compilation problem - don't show the message
0132:                        BytecodeOutlinePlugin.log(e, IStatus.WARNING);
0133:                    }
0134:                }
0135:                return methodName;
0136:            }
0137:
0138:            public static String createMethodSignature(IMethod iMethod)
0139:                    throws JavaModelException {
0140:                StringBuffer sb = new StringBuffer();
0141:
0142:                // Eclipse put class name as constructor name - we change it!
0143:                if (iMethod.isConstructor()) {
0144:                    sb.append("<init>"); //$NON-NLS-1$
0145:                } else {
0146:                    sb.append(iMethod.getElementName());
0147:                }
0148:
0149:                if (iMethod.isBinary()) { // iMethod instanceof BinaryMember
0150:                    // binary info should be full qualified
0151:                    return sb.append(iMethod.getSignature()).toString();
0152:                }
0153:
0154:                // start method parameter descriptions list
0155:                sb.append('(');
0156:                IType declaringType = iMethod.getDeclaringType();
0157:                String[] parameterTypes = iMethod.getParameterTypes();
0158:
0159:                /*
0160:                 * For non - static inner classes bytecode constructor should contain as first
0161:                 * parameter the enclosing type instance, but in Eclipse AST there are no
0162:                 * appropriated parameter. So we need to create enclosing type signature and
0163:                 * add it as first parameter.
0164:                 */
0165:                if (iMethod.isConstructor() && isNonStaticInner(declaringType)) {
0166:                    // this is a very special case
0167:                    String typeSignature = getTypeSignature(getFirstAncestor(declaringType));
0168:                    if (typeSignature != null) {
0169:                        String[] newParams = new String[parameterTypes.length + 1];
0170:                        newParams[0] = typeSignature;
0171:                        System.arraycopy(parameterTypes, 0, newParams, 1,
0172:                                parameterTypes.length);
0173:                        parameterTypes = newParams;
0174:                    }
0175:                }
0176:
0177:                // doSomething(Lgenerics/DummyForAsmGenerics;)Lgenerics/DummyForAsmGenerics;
0178:                for (int i = 0; i < parameterTypes.length; i++) {
0179:                    String resolvedType = getResolvedType(parameterTypes[i],
0180:                            declaringType);
0181:                    if (resolvedType != null && resolvedType.length() > 0) {
0182:                        sb.append(resolvedType);
0183:                    } else {
0184:                        // this is a generic type
0185:                        appendGenericType(sb, iMethod, parameterTypes[i]);
0186:                    }
0187:                }
0188:                sb.append(')');
0189:
0190:                // continue here with adding resolved return type
0191:                String returnType = iMethod.getReturnType();
0192:                String resolvedType = getResolvedType(returnType, declaringType);
0193:                if (resolvedType != null && resolvedType.length() > 0) {
0194:                    sb.append(resolvedType);
0195:                } else {
0196:                    // this is a generic type
0197:                    appendGenericType(sb, iMethod, returnType);
0198:                }
0199:
0200:                return sb.toString();
0201:            }
0202:
0203:            /**
0204:             * @param type
0205:             * @return full qualified, resolved type name in bytecode notation
0206:             */
0207:            private static String getTypeSignature(IType type) {
0208:                if (type == null) {
0209:                    return null;
0210:                }
0211:                /*
0212:                 * getFullyQualifiedName() returns name, where package separator is '.',
0213:                 * but we need '/' for bytecode. The hack with ',' is to use a character
0214:                 * which is not allowed as Java char to be sure not to replace too much
0215:                 */
0216:                String name = type.getFullyQualifiedName(',');
0217:                // replace package separators
0218:                name = name.replace(Signature.C_DOT, PACKAGE_SEPARATOR);
0219:                // replace class separators
0220:                name = name.replace(',', TYPE_SEPARATOR);
0221:                return Signature.C_RESOLVED + name + Signature.C_SEMICOLON;
0222:            }
0223:
0224:            private static void appendGenericType(StringBuffer sb,
0225:                    IMethod iMethod, String unresolvedType)
0226:                    throws JavaModelException {
0227:                IType declaringType = iMethod.getDeclaringType();
0228:
0229:                // unresolvedType is here like "QA;" => we remove "Q" and ";"
0230:                if (unresolvedType.length() < 3) {
0231:                    // ???? something wrong here ....
0232:                    sb.append(unresolvedType);
0233:                    return;
0234:                }
0235:                unresolvedType = unresolvedType.substring(1, unresolvedType
0236:                        .length() - 1);
0237:
0238:                ITypeParameter typeParameter = iMethod
0239:                        .getTypeParameter(unresolvedType);
0240:                if (typeParameter == null || !typeParameter.exists()) {
0241:                    typeParameter = declaringType
0242:                            .getTypeParameter(unresolvedType);
0243:                }
0244:
0245:                String[] bounds = typeParameter.getBounds();
0246:                if (bounds.length == 0) {
0247:                    sb.append("Ljava/lang/Object;");
0248:                } else {
0249:                    for (int i = 0; i < bounds.length; i++) {
0250:                        String simplyName = bounds[i];
0251:                        simplyName = Signature.C_UNRESOLVED + simplyName
0252:                                + Signature.C_NAME_END;
0253:                        String resolvedType = getResolvedType(simplyName,
0254:                                declaringType);
0255:                        sb.append(resolvedType);
0256:                    }
0257:                }
0258:            }
0259:
0260:            /**
0261:             * @param typeToResolve
0262:             * @param declaringType
0263:             * @return full qualified "bytecode formatted" type
0264:             * @throws JavaModelException
0265:             */
0266:            private static String getResolvedType(String typeToResolve,
0267:                    IType declaringType) throws JavaModelException {
0268:                StringBuffer sb = new StringBuffer();
0269:                int arrayCount = Signature.getArrayCount(typeToResolve);
0270:                // test which letter is following - Q or L are for reference types
0271:                boolean isPrimitive = isPrimitiveType(typeToResolve
0272:                        .charAt(arrayCount));
0273:                if (isPrimitive) {
0274:                    // simply add whole string (probably with array chars like [[I etc.)
0275:                    sb.append(typeToResolve);
0276:                } else {
0277:                    boolean isUnresolvedType = isUnresolvedType(typeToResolve,
0278:                            arrayCount);
0279:                    if (!isUnresolvedType) {
0280:                        sb.append(typeToResolve);
0281:                    } else {
0282:                        // we need resolved types
0283:                        String resolved = getResolvedTypeName(typeToResolve,
0284:                                declaringType);
0285:                        if (resolved != null) {
0286:                            while (arrayCount > 0) {
0287:                                sb.append(Signature.C_ARRAY);
0288:                                arrayCount--;
0289:                            }
0290:                            sb.append(Signature.C_RESOLVED);
0291:                            sb.append(resolved);
0292:                            sb.append(Signature.C_SEMICOLON);
0293:                        }
0294:                    }
0295:                }
0296:                return sb.toString();
0297:            }
0298:
0299:            /**
0300:             * Copied and modified from JavaModelUtil. Resolves a type name in the context of the
0301:             * declaring type.
0302:             * @param refTypeSig the type name in signature notation (for example 'QVector') this
0303:             * can also be an array type, but dimensions will be ignored.
0304:             * @param declaringType the context for resolving (type where the reference was made
0305:             * in)
0306:             * @return returns the fully qualified <b>bytecode </b> type name or build-in-type
0307:             * name. if a unresoved type couldn't be resolved null is returned
0308:             */
0309:            private static String getResolvedTypeName(String refTypeSig,
0310:                    IType declaringType) throws JavaModelException {
0311:
0312:                /* the whole method is copied from JavaModelUtil.getResolvedTypeName(...).
0313:                 * The problem is, that JavaModelUtil uses '.' to separate package
0314:                 * names, but we need '/' -> see JavaModelUtil.concatenateName() vs
0315:                 * JdtUtils.concatenateName()
0316:                 */
0317:                int arrayCount = Signature.getArrayCount(refTypeSig);
0318:                if (isUnresolvedType(refTypeSig, arrayCount)) {
0319:                    String name = ""; //$NON-NLS-1$
0320:                    int bracket = refTypeSig.indexOf(Signature.C_GENERIC_START,
0321:                            arrayCount + 1);
0322:                    if (bracket > 0) {
0323:                        name = refTypeSig.substring(arrayCount + 1, bracket);
0324:                    } else {
0325:                        int semi = refTypeSig.indexOf(Signature.C_SEMICOLON,
0326:                                arrayCount + 1);
0327:                        if (semi == -1) {
0328:                            throw new IllegalArgumentException();
0329:                        }
0330:                        name = refTypeSig.substring(arrayCount + 1, semi);
0331:                    }
0332:                    String[][] resolvedNames = declaringType.resolveType(name);
0333:                    if (resolvedNames != null && resolvedNames.length > 0) {
0334:                        return concatenateName(resolvedNames[0][0],
0335:                                resolvedNames[0][1]);
0336:                    }
0337:                    return null;
0338:                }
0339:                return refTypeSig.substring(arrayCount);// Signature.toString(substring);
0340:            }
0341:
0342:            /**
0343:             * @param refTypeSig
0344:             * @param arrayCount expected array count in the signature
0345:             * @return true if the given string is an unresolved signature (Eclipse - internal
0346:             * representation)
0347:             */
0348:            private static boolean isUnresolvedType(String refTypeSig,
0349:                    int arrayCount) {
0350:                char type = refTypeSig.charAt(arrayCount);
0351:                return type == Signature.C_UNRESOLVED;
0352:            }
0353:
0354:            /**
0355:             * Concatenates package and class name. Both strings can be empty or <code>null</code>.
0356:             */
0357:            private static String concatenateName(String packageName,
0358:                    String className) {
0359:                StringBuffer buf = new StringBuffer();
0360:                if (packageName != null && packageName.length() > 0) {
0361:                    packageName = packageName.replace(Signature.C_DOT,
0362:                            PACKAGE_SEPARATOR);
0363:                    buf.append(packageName);
0364:                }
0365:                if (className != null && className.length() > 0) {
0366:                    if (buf.length() > 0) {
0367:                        buf.append(PACKAGE_SEPARATOR);
0368:                    }
0369:                    className = className.replace(Signature.C_DOT,
0370:                            TYPE_SEPARATOR);
0371:                    buf.append(className);
0372:                }
0373:                return buf.toString();
0374:            }
0375:
0376:            /**
0377:             * Test which letter is following - Q or L are for reference types
0378:             * @param first
0379:             * @return true, if character is not a simbol for reference types
0380:             */
0381:            private static boolean isPrimitiveType(char first) {
0382:                return (first != Signature.C_RESOLVED && first != Signature.C_UNRESOLVED);
0383:            }
0384:
0385:            /**
0386:             * @param childEl may be null
0387:             * @return first ancestor with IJavaElement.TYPE element type, or null
0388:             */
0389:            public static IType getEnclosingType(IJavaElement childEl) {
0390:                if (childEl == null) {
0391:                    return null;
0392:                }
0393:                return (IType) childEl.getAncestor(IJavaElement.TYPE);
0394:            }
0395:
0396:            /**
0397:             * @param cf
0398:             * @param dc
0399:             * @return inner type which has the same name as the given string, or null
0400:             */
0401:            public static IClassFile getInnerType(IClassFile cf,
0402:                    DecompiledClass dc, String typeSignature) {
0403:                if (typeSignature.endsWith(";")) {
0404:                    typeSignature = typeSignature.substring(0, typeSignature
0405:                            .length() - 1);
0406:                    if (typeSignature.startsWith("L")) {
0407:                        typeSignature = typeSignature.substring(1,
0408:                                typeSignature.length());
0409:                    }
0410:                }
0411:                /*
0412:                 * For inner and anonymous classes from the blocks or methods
0413:                 * getFullyQualifiedName() does not work if class was compiled with 1.5
0414:                 * and will never match the fullTypeName...
0415:                 * I'm not sure if it is intended or if it is a bug
0416:                 * in Eclipse: instead of A$1B we get A$B for B class from a method in A
0417:                 *
0418:                 * NB: for binary types without source attachment the method elements doesn't
0419:                 * contain source and therefore could not resolve child elements.
0420:                 * So the search for local types will never work...
0421:                 *
0422:                 * Therefore we do not use Eclipse API and use ClassNode->InnerClassNode elements
0423:                 */
0424:                ClassNode cn = dc.getClassNode();
0425:                List/*<InnerClassNode>*/innerClasses = cn.innerClasses;
0426:
0427:                for (int i = 0; i < innerClasses.size(); i++) {
0428:                    InnerClassNode in = (InnerClassNode) innerClasses.get(i);
0429:                    if (typeSignature.equals(in.name)) {
0430:                        int idx = typeSignature.lastIndexOf(PACKAGE_SEPARATOR);
0431:                        String className = typeSignature;
0432:                        if (idx > 0) {
0433:                            className = typeSignature.substring(idx + 1,
0434:                                    typeSignature.length());
0435:                        }
0436:                        className += ".class";
0437:                        return cf.getType().getPackageFragment().getClassFile(
0438:                                className);
0439:                    }
0440:                }
0441:                return null;
0442:            }
0443:
0444:            /**
0445:             * Modified copy from org.eclipse.jdt.internal.ui.actions.SelectionConverter
0446:             * @param input
0447:             * @param selection
0448:             * @return null, if selection is null or could not be resolved to java element
0449:             * @throws JavaModelException
0450:             */
0451:            public static IJavaElement getElementAtOffset(IJavaElement input,
0452:                    ITextSelection selection) throws JavaModelException {
0453:                if (selection == null) {
0454:                    return null;
0455:                }
0456:                ICompilationUnit workingCopy = null;
0457:                if (input instanceof  ICompilationUnit) {
0458:                    workingCopy = (ICompilationUnit) input;
0459:                    // be in-sync with model
0460:                    // instead of using internal JavaModelUtil.reconcile(workingCopy);
0461:                    synchronized (workingCopy) {
0462:                        workingCopy
0463:                                .reconcile(
0464:                                        ICompilationUnit.NO_AST,
0465:                                        false /* don't force problem detection */,
0466:                                        null /* use primary owner */, null /* no progress monitor */);
0467:                    }
0468:                    IJavaElement ref = workingCopy.getElementAt(selection
0469:                            .getOffset());
0470:                    if (ref != null) {
0471:                        return ref;
0472:                    }
0473:                } else if (input instanceof  IClassFile) {
0474:                    IClassFile iClass = (IClassFile) input;
0475:                    IJavaElement ref = iClass.getElementAt(selection
0476:                            .getOffset());
0477:                    if (ref != null) {
0478:                        // If we are in the inner class, try to refine search result now
0479:                        if (ref instanceof  IType) {
0480:                            IType type = (IType) ref;
0481:                            IClassFile classFile = type.getClassFile();
0482:                            if (classFile != iClass) {
0483:                                /*
0484:                                 * WORKAROUND it seems that source range for constructors from
0485:                                 * bytecode with source attached from zip files is not computed
0486:                                 * in Eclipse (SourceMapper returns nothing useful).
0487:                                 * Example: HashMap$Entry class with constructor
0488:                                 * <init>(ILjava/lang/Object;Ljava/lang/Object;Ljava/util/HashMap$Entry;)V
0489:                                 * We will get here at least the inner class...
0490:                                 * see https://bugs.eclipse.org/bugs/show_bug.cgi?id=137847
0491:                                 */
0492:                                ref = classFile.getElementAt(selection
0493:                                        .getOffset());
0494:                            }
0495:                        }
0496:                        return ref;
0497:                    }
0498:                }
0499:                return null;
0500:            }
0501:
0502:            /**
0503:             * Modified copy from JavaModelUtil.
0504:             * @param javaElt
0505:             * @return true, if corresponding java project has compiler setting to generate
0506:             * bytecode for jdk 1.5 and above
0507:             */
0508:            public static boolean is50OrHigher(IJavaElement javaElt) {
0509:                IJavaProject project = javaElt.getJavaProject();
0510:                String option = project.getOption(JavaCore.COMPILER_COMPLIANCE,
0511:                        true);
0512:                boolean result = JavaCore.VERSION_1_5.equals(option);
0513:                if (result) {
0514:                    return result;
0515:                }
0516:                // probably > 1.5?
0517:                result = JavaCore.VERSION_1_4.equals(option);
0518:                if (result) {
0519:                    return false;
0520:                }
0521:                result = JavaCore.VERSION_1_3.equals(option);
0522:                if (result) {
0523:                    return false;
0524:                }
0525:                result = JavaCore.VERSION_1_2.equals(option);
0526:                if (result) {
0527:                    return false;
0528:                }
0529:                result = JavaCore.VERSION_1_1.equals(option);
0530:                if (result) {
0531:                    return false;
0532:                }
0533:                // unknown = > 1.5
0534:                return true;
0535:            }
0536:
0537:            /**
0538:             * Cite: jdk1.1.8/docs/guide/innerclasses/spec/innerclasses.doc10.html: For the sake
0539:             * of tools, there are some additional requirements on the naming of an inaccessible
0540:             * class N. Its bytecode name must consist of the bytecode name of an enclosing class
0541:             * (the immediately enclosing class, if it is a member), followed either by `$' and a
0542:             * positive decimal numeral chosen by the compiler, or by `$' and the simple name of
0543:             * N, or else by both (in that order). Moreover, the bytecode name of a block-local N
0544:             * must consist of its enclosing package member T, the characters `$1$', and N, if the
0545:             * resulting name would be unique.
0546:             * <br>
0547:             * Note, that this rule was changed for static blocks after 1.5 jdk.
0548:             * @param javaElement
0549:             * @return simply element name
0550:             */
0551:            public static String getElementName(IJavaElement javaElement) {
0552:                if (isAnonymousType(javaElement)) {
0553:                    IType anonType = (IType) javaElement;
0554:                    List allAnonymous = new ArrayList();
0555:                    /*
0556:                     * in order to resolve anon. class name we need to know about all other
0557:                     * anonymous classes in declaring class, therefore we need to collect all here
0558:                     */
0559:                    collectAllAnonymous(allAnonymous, anonType);
0560:                    int idx = getAnonimousIndex(anonType,
0561:                            (IType[]) allAnonymous
0562:                                    .toArray(new IType[allAnonymous.size()]));
0563:                    return Integer.toString(idx);
0564:                }
0565:                String name = javaElement.getElementName();
0566:                if (isLocal(javaElement)) {
0567:                    /*
0568:                     * Compiler have different naming conventions for inner non-anon. classes in
0569:                     * static blocks or any methods, this difference was introduced with 1.5 JDK.
0570:                     * The problem is, that we could have projects with classes, generated
0571:                     * with both 1.5 and earlier settings. One could not see on particular
0572:                     * java element, for which jdk version the existing bytecode was generated.
0573:                     * If we could have a *.class file, but we are just searching for one...
0574:                     * So there could be still a chance, that this code fails, if java element
0575:                     * is not compiled with comiler settings from project, but with different
0576:                     */
0577:                    if (is50OrHigher(javaElement)) {
0578:                        name = "1" + name; // compiler output changed for > 1.5 code
0579:                    } else {
0580:                        name = "1$" + name; // see method comment, this was the case for older code
0581:                    }
0582:                }
0583:
0584:                if (name.endsWith(".java")) { //$NON-NLS-1$
0585:                    name = name.substring(0, name.lastIndexOf(".java")); //$NON-NLS-1$
0586:                } else if (name.endsWith(".class")) { //$NON-NLS-1$
0587:                    name = name.substring(0, name.lastIndexOf(".class")); //$NON-NLS-1$
0588:                }
0589:                return name;
0590:            }
0591:
0592:            /**
0593:             * @param javaElement
0594:             * @return null, if javaElement is top level class
0595:             */
0596:            static IType getFirstAncestor(IJavaElement javaElement) {
0597:                IJavaElement parent = javaElement;
0598:                if (javaElement.getElementType() == IJavaElement.TYPE) {
0599:                    parent = javaElement.getParent();
0600:                }
0601:                if (parent != null) {
0602:                    return (IType) parent.getAncestor(IJavaElement.TYPE);
0603:                }
0604:                return null;
0605:            }
0606:
0607:            static IJavaElement getLastAncestor(IJavaElement javaElement,
0608:                    int elementType) {
0609:                IJavaElement lastFound = null;
0610:                if (elementType == javaElement.getElementType()) {
0611:                    lastFound = javaElement;
0612:                }
0613:                IJavaElement parent = javaElement.getParent();
0614:                if (parent == null) {
0615:                    return lastFound;
0616:                }
0617:                IJavaElement ancestor = parent.getAncestor(elementType);
0618:                if (ancestor != null) {
0619:                    return getLastAncestor(ancestor, elementType);
0620:                }
0621:                return lastFound;
0622:            }
0623:
0624:            /**
0625:             * @param javaElement
0626:             * @return distance to given ancestor, 0 if it is the same, -1 if ancestor with type
0627:             * IJavaElement.TYPE does not exist
0628:             */
0629:            static int getTopAncestorDistance(IJavaElement javaElement,
0630:                    IJavaElement topAncestor) {
0631:                if (topAncestor == javaElement) {
0632:                    return 0;
0633:                }
0634:                IJavaElement ancestor = getFirstAncestor(javaElement);
0635:                if (ancestor != null) {
0636:                    return 1 + getTopAncestorDistance(ancestor, topAncestor);
0637:                }
0638:                // this is not possible, if ancestor exist - which return value we should use?
0639:                return -1;
0640:            }
0641:
0642:            /**
0643:             * @param javaElement
0644:             * @return first non-anonymous ancestor
0645:             */
0646:            static IJavaElement getFirstNonAnonymous(IJavaElement javaElement,
0647:                    IJavaElement topAncestor) {
0648:                if (javaElement.getElementType() == IJavaElement.TYPE
0649:                        && !isAnonymousType(javaElement)) {
0650:                    return javaElement;
0651:                }
0652:                IJavaElement parent = javaElement.getParent();
0653:                if (parent == null) {
0654:                    return topAncestor;
0655:                }
0656:                IJavaElement ancestor = parent.getAncestor(IJavaElement.TYPE);
0657:                if (ancestor != null) {
0658:                    return getFirstNonAnonymous(ancestor, topAncestor);
0659:                }
0660:                return topAncestor;
0661:            }
0662:
0663:            /**
0664:             * @param javaElement
0665:             * @return true, if given element is anonymous inner class
0666:             */
0667:            private static boolean isAnonymousType(IJavaElement javaElement) {
0668:                try {
0669:                    return javaElement instanceof  IType
0670:                            && ((IType) javaElement).isAnonymous();
0671:                } catch (JavaModelException e) {
0672:                    BytecodeOutlinePlugin.log(e, IStatus.ERROR);
0673:                }
0674:                return false;
0675:            }
0676:
0677:            /**
0678:             * @param innerType should be inner type.
0679:             * @return true, if given element is inner class from initializer block or method body
0680:             */
0681:            private static boolean isLocal(IJavaElement innerType) {
0682:                try {
0683:                    return innerType instanceof  IType
0684:                            && ((IType) innerType).isLocal();
0685:                } catch (JavaModelException e) {
0686:                    BytecodeOutlinePlugin.log(e, IStatus.ERROR);
0687:                }
0688:                return false;
0689:            }
0690:
0691:            /**
0692:             * @param type
0693:             * @return true, if given element is non static inner class
0694:             * @throws JavaModelException
0695:             */
0696:            private static boolean isNonStaticInner(IType type)
0697:                    throws JavaModelException {
0698:                if (type.isMember()) {
0699:                    return !Flags.isStatic(type.getFlags());
0700:                }
0701:                return false;
0702:            }
0703:
0704:            /**
0705:             * @param innerType should be inner type.
0706:             * @return true, if given element is inner class from initializer block
0707:             */
0708:            private static boolean isInnerFromBlock(IType type) {
0709:                try {
0710:                    IJavaElement ancestor = type
0711:                            .getAncestor(IJavaElement.INITIALIZER);
0712:                    return type.isLocal() && ancestor != null;
0713:                } catch (JavaModelException e) {
0714:                    BytecodeOutlinePlugin.log(e, IStatus.ERROR);
0715:                }
0716:                return false;
0717:            }
0718:
0719:            /**
0720:             * @param javaElement
0721:             * @return absolute path of generated bytecode package for given element
0722:             * @throws JavaModelException
0723:             */
0724:            private static String getPackageOutputPath(IJavaElement javaElement)
0725:                    throws JavaModelException {
0726:                String dir = ""; //$NON-NLS-1$
0727:                if (javaElement == null) {
0728:                    return dir;
0729:                }
0730:
0731:                IJavaProject project = javaElement.getJavaProject();
0732:
0733:                if (project == null) {
0734:                    return dir;
0735:                }
0736:                // default bytecode location
0737:                IPath path = project.getOutputLocation();
0738:
0739:                IResource resource = javaElement.getUnderlyingResource();
0740:                if (resource == null) {
0741:                    return dir;
0742:                }
0743:                // resolve multiple output locations here
0744:                if (project.exists() && project.getProject().isOpen()) {
0745:                    IClasspathEntry entries[] = project.getRawClasspath();
0746:                    for (int i = 0; i < entries.length; i++) {
0747:                        IClasspathEntry classpathEntry = entries[i];
0748:                        if (classpathEntry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
0749:                            IPath outputPath = classpathEntry
0750:                                    .getOutputLocation();
0751:                            if (outputPath != null
0752:                                    && classpathEntry.getPath().isPrefixOf(
0753:                                            resource.getFullPath())) {
0754:                                path = outputPath;
0755:                                break;
0756:                            }
0757:                        }
0758:                    }
0759:                }
0760:
0761:                if (path == null) {
0762:                    // check the default location if not already included
0763:                    IPath def = project.getOutputLocation();
0764:                    if (def != null && def.isPrefixOf(resource.getFullPath())) {
0765:                        path = def;
0766:                    }
0767:                }
0768:
0769:                if (path == null) {
0770:                    return dir;
0771:                }
0772:
0773:                IWorkspace workspace = ResourcesPlugin.getWorkspace();
0774:
0775:                if (!project.getPath().equals(path)) {
0776:                    IFolder outputFolder = workspace.getRoot().getFolder(path);
0777:                    if (outputFolder != null) {
0778:                        // linked resources will be resolved here!
0779:                        IPath rawPath = outputFolder.getRawLocation();
0780:                        if (rawPath != null) {
0781:                            path = rawPath;
0782:                        }
0783:                    }
0784:                } else {
0785:                    path = project.getProject().getLocation();
0786:                }
0787:
0788:                // here we should resolve path variables,
0789:                // probably existing at first place of path
0790:                IPathVariableManager pathManager = workspace
0791:                        .getPathVariableManager();
0792:                path = pathManager.resolvePath(path);
0793:
0794:                if (path == null) {
0795:                    return dir;
0796:                }
0797:
0798:                if (isPackageRoot(project, resource)) {
0799:                    dir = path.toOSString();
0800:                } else {
0801:                    String packPath = EclipseUtils.getJavaPackageName(
0802:                            javaElement).replace(Signature.C_DOT,
0803:                            PACKAGE_SEPARATOR);
0804:                    dir = path.append(packPath).toOSString();
0805:                }
0806:                return dir;
0807:            }
0808:
0809:            /**
0810:             * @param project
0811:             * @param pack
0812:             * @return true if 'pack' argument is package root
0813:             * @throws JavaModelException
0814:             */
0815:            private static boolean isPackageRoot(IJavaProject project,
0816:                    IResource pack) throws JavaModelException {
0817:                boolean isRoot = false;
0818:                if (project == null || pack == null) {
0819:                    return isRoot;
0820:                }
0821:                IPackageFragmentRoot root = project
0822:                        .getPackageFragmentRoot(pack);
0823:                IClasspathEntry clPathEntry = null;
0824:                if (root != null) {
0825:                    clPathEntry = root.getRawClasspathEntry();
0826:                }
0827:                isRoot = clPathEntry != null;
0828:                return isRoot;
0829:            }
0830:
0831:            /**
0832:             * Works only for eclipse - managed/generated bytecode, ergo not with imported
0833:             * classes/jars
0834:             * @param javaElement
0835:             * @return full os-specific file path to .class resource, containing given element
0836:             */
0837:            public static String getByteCodePath(IJavaElement javaElement) {
0838:                if (javaElement == null) {
0839:                    return "";//$NON-NLS-1$
0840:                }
0841:                String packagePath = ""; //$NON-NLS-1$
0842:                try {
0843:                    packagePath = getPackageOutputPath(javaElement);
0844:                } catch (JavaModelException e) {
0845:                    BytecodeOutlinePlugin.log(e, IStatus.ERROR);
0846:                    return "";
0847:                }
0848:                IJavaElement ancestor = getLastAncestor(javaElement,
0849:                        IJavaElement.TYPE);
0850:                StringBuffer sb = new StringBuffer(packagePath);
0851:                sb.append(File.separator);
0852:                sb.append(getClassName(javaElement, ancestor));
0853:                sb.append(".class"); //$NON-NLS-1$
0854:                return sb.toString();
0855:            }
0856:
0857:            /**
0858:             * @param javaElement
0859:             * @return new generated input stream for given element bytecode class file, or null
0860:             * if class file cannot be found or this element is not from java source path
0861:             */
0862:            public static InputStream createInputStream(IJavaElement javaElement) {
0863:                IClassFile classFile = (IClassFile) javaElement
0864:                        .getAncestor(IJavaElement.CLASS_FILE);
0865:                InputStream is = null;
0866:
0867:                // existing read-only class files
0868:                if (classFile != null) {
0869:                    IJavaElement jarParent = classFile.getParent();
0870:                    // TODO dirty hack to be sure, that package is from jar -
0871:                    // because JarPackageFragment is not public class, we cannot
0872:                    // use instanceof here
0873:                    boolean isJar = jarParent != null
0874:                            && jarParent.getClass().getName().endsWith(
0875:                                    "JarPackageFragment"); //$NON-NLS-1$
0876:                    if (isJar) {
0877:                        is = createStreamFromJar(classFile);
0878:                    } else {
0879:                        is = createStreamFromClass(classFile);
0880:                    }
0881:                } else {
0882:                    // usual eclipse - generated bytecode
0883:
0884:                    boolean inJavaPath = isOnClasspath(javaElement);
0885:                    if (!inJavaPath) {
0886:                        return null;
0887:                    }
0888:                    String classPath = getByteCodePath(javaElement);
0889:
0890:                    try {
0891:                        is = new FileInputStream(classPath);
0892:                    } catch (FileNotFoundException e) {
0893:                        // if autobuild is disabled, we get tons of this errors.
0894:                        // but I think we cannot ignore them, therefore WARNING and not
0895:                        // ERROR status
0896:                        BytecodeOutlinePlugin.log(e, IStatus.WARNING);
0897:                    }
0898:                }
0899:                return is;
0900:            }
0901:
0902:            /**
0903:             * Creates stream from external class file from Eclipse classpath (means, that this
0904:             * class file is read-only)
0905:             * @param classFile
0906:             * @return new generated input stream from external class file, or null, if class file
0907:             * for this element cannot be found
0908:             */
0909:            private static InputStream createStreamFromClass(
0910:                    IClassFile classFile) {
0911:                IResource underlyingResource = null;
0912:                try {
0913:                    // to tell the truth, I don't know why that different methods
0914:                    // are not working in a particular case. But it seems to be better
0915:                    // to use getResource() with non-java elements (not in model)
0916:                    // and getUnderlyingResource() with java elements.
0917:                    if (classFile.exists()) {
0918:                        underlyingResource = classFile.getUnderlyingResource();
0919:                    } else {
0920:                        // this is a class file that is not in java model
0921:                        underlyingResource = classFile.getResource();
0922:                    }
0923:                } catch (JavaModelException e) {
0924:                    BytecodeOutlinePlugin.log(e, IStatus.ERROR);
0925:                    return null;
0926:                }
0927:                IPath rawLocation = underlyingResource.getRawLocation();
0928:                // here we should resolve path variables,
0929:                // probably existing at first place of "rawLocation" path
0930:                IWorkspace workspace = ResourcesPlugin.getWorkspace();
0931:                IPathVariableManager pathManager = workspace
0932:                        .getPathVariableManager();
0933:                rawLocation = pathManager.resolvePath(rawLocation);
0934:                try {
0935:                    return new FileInputStream(rawLocation.toOSString());
0936:                } catch (FileNotFoundException e) {
0937:                    BytecodeOutlinePlugin.log(e, IStatus.ERROR);
0938:                }
0939:                return null;
0940:            }
0941:
0942:            /**
0943:             * Creates stream from external class file that is stored in jar file
0944:             * @param classFile
0945:             * @param javaElement
0946:             * @return new generated input stream from external class file that is stored in jar
0947:             * file, or null, if class file for this element cannot be found
0948:             */
0949:            private static InputStream createStreamFromJar(IClassFile classFile) {
0950:                IPath path = null;
0951:                IResource resource = classFile.getResource();
0952:                // resource == null => this is a external archive
0953:                if (resource != null) {
0954:                    path = resource.getRawLocation();
0955:                } else {
0956:                    path = classFile.getPath();
0957:                }
0958:                if (path == null) {
0959:                    return null;
0960:                }
0961:                // here we should resolve path variables,
0962:                // probably existing at first place of path
0963:                IWorkspace workspace = ResourcesPlugin.getWorkspace();
0964:                IPathVariableManager pathManager = workspace
0965:                        .getPathVariableManager();
0966:                path = pathManager.resolvePath(path);
0967:
0968:                JarFile jar = null;
0969:                try {
0970:                    jar = new JarFile(path.toOSString());
0971:                } catch (IOException e) {
0972:                    BytecodeOutlinePlugin.log(e, IStatus.ERROR);
0973:                    return null;
0974:                }
0975:                String fullClassName = getFullBytecodeName(classFile);
0976:                if (fullClassName == null) {
0977:                    return null;
0978:                }
0979:                JarEntry jarEntry = jar.getJarEntry(fullClassName);
0980:                if (jarEntry != null) {
0981:                    try {
0982:                        return jar.getInputStream(jarEntry);
0983:                    } catch (IOException e) {
0984:                        BytecodeOutlinePlugin.log(e, IStatus.ERROR);
0985:                    }
0986:                }
0987:                return null;
0988:            }
0989:
0990:            private static boolean isOnClasspath(IJavaElement javaElement) {
0991:                IJavaProject project = javaElement.getJavaProject();
0992:                if (project != null) {
0993:                    boolean result = project.isOnClasspath(javaElement);
0994:                    return result;
0995:                }
0996:                return false;
0997:            }
0998:
0999:            /**
1000:             * @param classFile
1001:             * @return full qualified bytecode name of given class
1002:             */
1003:            public static String getFullBytecodeName(IClassFile classFile) {
1004:                IPackageFragment packageFr = (IPackageFragment) classFile
1005:                        .getAncestor(IJavaElement.PACKAGE_FRAGMENT);
1006:                if (packageFr == null) {
1007:                    return null;
1008:                }
1009:                String packageName = packageFr.getElementName();
1010:                // switch to java bytecode naming conventions
1011:                packageName = packageName.replace(Signature.C_DOT,
1012:                        PACKAGE_SEPARATOR);
1013:
1014:                String className = classFile.getElementName();
1015:                if (packageName != null && packageName.length() > 0) {
1016:                    return packageName + PACKAGE_SEPARATOR + className;
1017:                }
1018:                return className;
1019:            }
1020:
1021:            /**
1022:             * @param javaElement
1023:             * @param topAncestor
1024:             * @param sb
1025:             */
1026:            private static String getClassName(IJavaElement javaElement,
1027:                    IJavaElement topAncestor) {
1028:                StringBuffer sb = new StringBuffer();
1029:                if (!javaElement.equals(topAncestor)) {
1030:                    int elementType = javaElement.getElementType();
1031:                    if (elementType == IJavaElement.FIELD
1032:                            || elementType == IJavaElement.METHOD
1033:                            || elementType == IJavaElement.INITIALIZER) {
1034:                        // it's field or method
1035:                        javaElement = getFirstAncestor(javaElement);
1036:                    } else {
1037:                        boolean is50OrHigher = is50OrHigher(javaElement);
1038:                        if (!is50OrHigher
1039:                                && (isAnonymousType(javaElement) || isLocal(javaElement))) {
1040:                            // it's inner type
1041:                            sb.append(getElementName(topAncestor));
1042:                            sb.append(TYPE_SEPARATOR);
1043:                        } else {
1044:                            /*
1045:                             * TODO there is an issue with < 1.5 compiler setting and with inner
1046:                             * classes with the same name but defined in different methods in the same
1047:                             * source file. Then compiler needs to generate *different* content for
1048:                             *  A$1$B and A$1$B, which is not possible so therefore compiler generates
1049:                             *  A$1$B and A$2$B. The naming order is the source range order of inner
1050:                             *  classes, so the first inner B class will get A$1$B and the second
1051:                             *  inner B class A$2$B etc.
1052:                             */
1053:
1054:                            // override top ancestor with immediate ancestor
1055:                            topAncestor = getFirstAncestor(javaElement);
1056:                            while (topAncestor != null) {
1057:                                sb.insert(0, getElementName(topAncestor)
1058:                                        + TYPE_SEPARATOR);
1059:                                topAncestor = getFirstAncestor(topAncestor);
1060:                            }
1061:                        }
1062:                    }
1063:                }
1064:                sb.append(getElementName(javaElement));
1065:                return sb.toString();
1066:            }
1067:
1068:            /**
1069:             * Collect all anonymous classes which are on the same "name shema level"
1070:             * as the given element for the compiler. The list could contain different set of
1071:             * elements for the same source code, depends on the compiler and jdk version
1072:             * @param list for the found anon. classes, elements instanceof IType.
1073:             * @param anonType the anon. type
1074:             */
1075:            private static void collectAllAnonymous(List list, IType anonType) {
1076:                /*
1077:                 * For JDK >= 1.5 in Eclipse 3.1+ the naming shema for nested anonymous
1078:                 * classes was changed from A$1, A$2, A$3, A$4, ..., A$n
1079:                 * to A$1, A$1$1, A$1$2, A$1$2$1, ..., A$2, A$2$1, A$2$2, ..., A$x$y
1080:                 */
1081:                boolean allowNested = !is50OrHigher(anonType);
1082:
1083:                IParent declaringType;
1084:                if (allowNested) {
1085:                    declaringType = (IType) getLastAncestor(anonType,
1086:                            IJavaElement.TYPE);
1087:                } else {
1088:                    declaringType = anonType.getDeclaringType();
1089:                }
1090:
1091:                try {
1092:                    collectAllAnonymous(list, declaringType, allowNested);
1093:                } catch (JavaModelException e) {
1094:                    BytecodeOutlinePlugin.log(e, IStatus.ERROR);
1095:                }
1096:            }
1097:
1098:            /**
1099:             * Traverses down the children tree of this parent and collect all child anon. classes
1100:             * @param list
1101:             * @param parent
1102:             * @param allowNested true to search in IType child elements too
1103:             * @throws JavaModelException
1104:             */
1105:            private static void collectAllAnonymous(List list, IParent parent,
1106:                    boolean allowNested) throws JavaModelException {
1107:                IJavaElement[] children = parent.getChildren();
1108:                for (int i = 0; i < children.length; i++) {
1109:                    IJavaElement childElem = children[i];
1110:                    if (isAnonymousType(childElem)) {
1111:                        list.add(childElem);
1112:                    }
1113:                    if (childElem instanceof  IParent) {
1114:                        if (allowNested || !(childElem instanceof  IType)) {
1115:                            collectAllAnonymous(list, (IParent) childElem,
1116:                                    allowNested);
1117:                        }
1118:                    }
1119:                }
1120:            }
1121:
1122:            /**
1123:             * @param anonType
1124:             * @param anonymous
1125:             * @return the index of given java element in the anon. classes list, which was used
1126:             *  by compiler to generate bytecode name for given element. If the given type is not
1127:             *  in the list, then return value is '-1'
1128:             */
1129:            private static int getAnonimousIndex(IType anonType,
1130:                    IType[] anonymous) {
1131:                sortAnonymous(anonymous, anonType);
1132:                for (int i = 0; i < anonymous.length; i++) {
1133:                    if (anonymous[i] == anonType) {
1134:                        // +1 because compiler starts generated classes always with 1
1135:                        return i + 1;
1136:                    }
1137:                }
1138:                return -1;
1139:            }
1140:
1141:            /**
1142:             * Sort given anonymous classes in order like java compiler would generate output
1143:             * classes, in context of given anonymous type
1144:             * @param anonymous
1145:             */
1146:            private static void sortAnonymous(IType[] anonymous, IType anonType) {
1147:                SourceOffsetComparator sourceComparator = new SourceOffsetComparator();
1148:                Arrays.sort(anonymous, new AnonymClassComparator(anonType,
1149:                        sourceComparator));
1150:            }
1151:
1152:            /**
1153:             * 1) from instance init 2) from deepest inner from instance init (deepest first) 3) from
1154:             * static init 4) from deepest inner from static init (deepest first) 5) from deepest inner
1155:             * (deepest first) 6) regular anon classes from main class
1156:             *
1157:             * <br>
1158:             * Note, that nested inner anon. classes which do not have different non-anon. inner class
1159:             * ancestors, are compiled in they nesting order, opposite to rule 2)
1160:             *
1161:             * @param javaElement
1162:             * @return priority - lesser mean wil be compiled later, a value > 0
1163:             * @throws JavaModelException
1164:             */
1165:            static int getAnonCompilePriority(IJavaElement javaElement,
1166:                    IJavaElement firstAncestor, IJavaElement topAncestor,
1167:                    boolean is50OrHigher) {
1168:
1169:                // search for initializer block
1170:                IJavaElement lastAncestor = getLastAncestor(javaElement,
1171:                        IJavaElement.INITIALIZER);
1172:                // test is for anon. classes from initializer blocks
1173:                if (lastAncestor != null) {
1174:                    IInitializer init = (IInitializer) lastAncestor;
1175:                    int initFlags = 0;
1176:                    try {
1177:                        initFlags = init.getFlags();
1178:                    } catch (JavaModelException e) {
1179:                        BytecodeOutlinePlugin.log(e, IStatus.ERROR);
1180:                    }
1181:
1182:                    if (is50OrHigher) {
1183:                        if (!Flags.isStatic(initFlags)) {
1184:                            if (firstAncestor == topAncestor) {
1185:                                return 10; // instance init
1186:                            }
1187:                            return 9; // from inner from instance init
1188:                        }
1189:
1190:                        if (firstAncestor == topAncestor) {
1191:                            return 8; // class init
1192:                        }
1193:                        return 7; // from inner from class init
1194:                    }
1195:
1196:                    /*
1197:                     * crazy 1.4 logic
1198:                     */
1199:                    if (Flags.isStatic(initFlags)) {
1200:                        return 10;
1201:                    }
1202:                    IJavaElement firstNonAnon = getFirstNonAnonymous(
1203:                            javaElement, topAncestor);
1204:                    if (isStatic((IMember) firstNonAnon)) {
1205:                        return 9;
1206:                    }
1207:
1208:                    return 6; // class init
1209:                }
1210:                // from inner from main type
1211:                if (!is50OrHigher) {
1212:                    /*
1213:                     * crazy 1.4 logic
1214:                     */
1215:                    int topFlags = 0;
1216:                    IJavaElement firstNonAnon = getFirstNonAnonymous(
1217:                            javaElement, topAncestor);
1218:                    try {
1219:                        topFlags = ((IType) firstNonAnon).getFlags();
1220:                    } catch (JavaModelException e) {
1221:                        BytecodeOutlinePlugin.log(e, IStatus.ERROR);
1222:                    }
1223:                    if (Flags.isStatic(topFlags)) {
1224:                        return 8;
1225:                    }
1226:
1227:                    if (firstAncestor == topAncestor) {
1228:                        return 6; // regular anonyme classes
1229:                    }
1230:                    return 6; // the same prio as regular anonyme classes
1231:                }
1232:
1233:                // test for anon. classes from "regular" code
1234:                if (firstAncestor == topAncestor) {
1235:                    return 5; // regular anonyme classes
1236:                }
1237:                return 6;
1238:            }
1239:
1240:            private static boolean isStatic(IMember firstNonAnon) {
1241:                int topFlags = 0;
1242:                try {
1243:                    topFlags = firstNonAnon.getFlags();
1244:                } catch (JavaModelException e) {
1245:                    BytecodeOutlinePlugin.log(e, IStatus.ERROR);
1246:                }
1247:                return Flags.isStatic(topFlags);
1248:            }
1249:
1250:            /**
1251:             * @param type
1252:             * @return
1253:             */
1254:            public static ClassLoader getClassLoader(IJavaElement type) {
1255:                ClassLoader cl;
1256:
1257:                IJavaProject javaProject = type.getJavaProject();
1258:                List urls = new ArrayList();
1259:
1260:                getClassURLs(javaProject, urls);
1261:
1262:                if (urls.isEmpty()) {
1263:                    cl = JdtUtils.class.getClassLoader();
1264:                } else {
1265:                    cl = new URLClassLoader((URL[]) urls.toArray(new URL[urls
1266:                            .size()]));
1267:                }
1268:                return cl;
1269:            }
1270:
1271:            private static void getClassURLs(IJavaProject javaProject, List urls) {
1272:                IProject project = javaProject.getProject();
1273:                IWorkspaceRoot workspaceRoot = project.getWorkspace().getRoot();
1274:
1275:                IClasspathEntry[] paths = null;
1276:                IPath defaultOutputLocation = null;
1277:                try {
1278:                    paths = javaProject.getResolvedClasspath(true);
1279:                    defaultOutputLocation = javaProject.getOutputLocation();
1280:                } catch (JavaModelException e) {
1281:                    // don't show message to user neither log it
1282:                    // BytecodeOutlinePlugin.log(e, IStatus.ERROR);
1283:                }
1284:                if (paths != null) {
1285:                    IPath projectPath = javaProject.getProject().getLocation();
1286:                    for (int i = 0; i < paths.length; ++i) {
1287:                        IClasspathEntry cpEntry = paths[i];
1288:                        IPath p = null;
1289:                        if (cpEntry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
1290:                            // filter out source container - there are unused for class
1291:                            // search - add bytecode output location instead
1292:                            p = cpEntry.getOutputLocation();
1293:                            if (p == null) {
1294:                                // default output used:
1295:                                p = defaultOutputLocation;
1296:                            }
1297:                        } else if (cpEntry.getEntryKind() == IClasspathEntry.CPE_PROJECT) {
1298:                            String projName = cpEntry.getPath()
1299:                                    .toPortableString().substring(1);
1300:                            IProject proj = workspaceRoot.getProject(projName);
1301:                            IJavaProject projj = JavaCore.create(proj);
1302:                            getClassURLs(projj, urls);
1303:                            continue;
1304:                        } else {
1305:                            p = cpEntry.getPath();
1306:                        }
1307:
1308:                        if (p == null) {
1309:                            continue;
1310:                        }
1311:                        if (!p.toFile().exists()) {
1312:                            // removeFirstSegments: remove project from relative path
1313:                            p = projectPath.append(p.removeFirstSegments(1));
1314:                            if (!p.toFile().exists()) {
1315:                                continue;
1316:                            }
1317:                        }
1318:                        try {
1319:                            urls.add(p.toFile().toURL());
1320:                        } catch (MalformedURLException e) {
1321:                            // don't show message to user
1322:                            BytecodeOutlinePlugin.log(e, IStatus.ERROR);
1323:                        }
1324:                    }
1325:                }
1326:            }
1327:
1328:            /**
1329:             * Check if java element is an interface or abstract method or a method from
1330:             * interface.
1331:             */
1332:            public static boolean isAbstractOrInterface(IJavaElement javaEl) {
1333:                if (javaEl == null) {
1334:                    return true;
1335:                }
1336:                boolean abstractOrInterface = false;
1337:                try {
1338:                    switch (javaEl.getElementType()) {
1339:                    case IJavaElement.CLASS_FILE:
1340:                        IClassFile classFile = (IClassFile) javaEl;
1341:                        if (isOnClasspath(javaEl)) {
1342:                            abstractOrInterface = classFile.isInterface();
1343:                        } /*else {
1344:                                              this is the case for eclipse-generated class files.
1345:                                              if we do not perform the check in if, then we will have java model
1346:                                              exception on classFile.isInterface() call.
1347:                                           }*/
1348:                        break;
1349:                    case IJavaElement.COMPILATION_UNIT:
1350:                        ICompilationUnit cUnit = (ICompilationUnit) javaEl;
1351:                        IType type = cUnit.findPrimaryType();
1352:                        abstractOrInterface = type != null
1353:                                && type.isInterface();
1354:                        break;
1355:                    case IJavaElement.TYPE:
1356:                        abstractOrInterface = ((IType) javaEl).isInterface();
1357:                        break;
1358:                    case IJavaElement.METHOD:
1359:                        // test for "abstract" flag on method in a class
1360:                        abstractOrInterface = Flags
1361:                                .isAbstract(((IMethod) javaEl).getFlags());
1362:                        // "abstract" flags could be not exist on interface methods
1363:                        if (!abstractOrInterface) {
1364:                            IType ancestor = (IType) javaEl
1365:                                    .getAncestor(IJavaElement.TYPE);
1366:                            abstractOrInterface = ancestor != null
1367:                                    && ancestor.isInterface();
1368:                        }
1369:                        break;
1370:                    default:
1371:                        IType ancestor1 = (IType) javaEl
1372:                                .getAncestor(IJavaElement.TYPE);
1373:                        abstractOrInterface = ancestor1 != null
1374:                                && ancestor1.isInterface();
1375:                        break;
1376:                    }
1377:                } catch (JavaModelException e) {
1378:                    // No point to log it here
1379:                    // BytecodeOutlinePlugin.log(e, IStatus.ERROR);
1380:                }
1381:                return abstractOrInterface;
1382:            }
1383:
1384:            static class SourceOffsetComparator implements  Comparator {
1385:
1386:                /**
1387:                 * First source occurence win.
1388:                 * @param o1 should be IType
1389:                 * @param o2 should be IType
1390:                 * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
1391:                 */
1392:                public int compare(Object o1, Object o2) {
1393:                    IType m1 = (IType) o1;
1394:                    IType m2 = (IType) o2;
1395:                    int idx1, idx2;
1396:                    try {
1397:                        ISourceRange sr1 = m1.getSourceRange();
1398:                        ISourceRange sr2 = m2.getSourceRange();
1399:                        if (sr1 == null || sr2 == null) {
1400:                            return 0;
1401:                        }
1402:                        idx1 = sr1.getOffset();
1403:                        idx2 = sr2.getOffset();
1404:                    } catch (JavaModelException e) {
1405:                        BytecodeOutlinePlugin.log(e, IStatus.ERROR);
1406:                        return 0;
1407:                    }
1408:                    if (idx1 < idx2) {
1409:                        return -1;
1410:                    } else if (idx1 > idx2) {
1411:                        return 1;
1412:                    }
1413:                    return 0;
1414:                }
1415:            }
1416:
1417:            static class AnonymClassComparator implements  Comparator {
1418:
1419:                private IType topAncestorType;
1420:                private SourceOffsetComparator sourceComparator;
1421:                private boolean is50OrHigher;
1422:
1423:                /**
1424:                 * @param javaElement
1425:                 * @param sourceComparator
1426:                 */
1427:                public AnonymClassComparator(IType javaElement,
1428:                        SourceOffsetComparator sourceComparator) {
1429:                    this .sourceComparator = sourceComparator;
1430:                    is50OrHigher = is50OrHigher(javaElement);
1431:                    topAncestorType = (IType) getLastAncestor(javaElement,
1432:                            IJavaElement.TYPE);
1433:                }
1434:
1435:                /**
1436:                 * If "deep" is the same, then source order win. 1) from instance init 2) from
1437:                 * deepest inner from instance init (deepest first) 3) from static init 4) from
1438:                 * deepest inner from static init (deepest first) 5) from deepest inner (deepest
1439:                 * first) 7) regular anon classes from main class
1440:                 *
1441:                 * <br>
1442:                 * Note, that nested inner anon. classes which do not have different
1443:                 * non-anon. inner class ancestors, are compiled in they nesting order, opposite
1444:                 * to rule 2)
1445:                 *
1446:                 * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
1447:                 */
1448:                public int compare(Object o1, Object o2) {
1449:                    if (o1 == o2) {
1450:                        return 0;
1451:                    }
1452:                    IType m1 = (IType) o1;
1453:                    IType m2 = (IType) o2;
1454:                    // both have the same ancestor as immediate ancestor
1455:                    if (!is50OrHigher && isInnerFromBlock(m1)
1456:                            && isInnerFromBlock(m2)) {
1457:                        return sourceComparator.compare(o1, o2);
1458:                    }
1459:                    IJavaElement firstAncestor1 = getFirstAncestor(m1);
1460:                    IJavaElement firstAncestor2 = getFirstAncestor(m2);
1461:                    int compilePrio1 = getAnonCompilePriority(m1,
1462:                            firstAncestor1, topAncestorType, is50OrHigher);
1463:                    int compilePrio2 = getAnonCompilePriority(m2,
1464:                            firstAncestor2, topAncestorType, is50OrHigher);
1465:
1466:                    //            System.out.println("" + compilePrio1 + " -> " + o1.toString().substring(0, 50));
1467:                    //            System.out.println("" + compilePrio2 + " -> " + o2.toString().substring(0, 50));
1468:
1469:                    if (compilePrio1 > compilePrio2) {
1470:                        return -1;
1471:                    } else if (compilePrio1 < compilePrio2) {
1472:                        return 1;
1473:                    } else {
1474:                        firstAncestor1 = getFirstNonAnonymous(m1,
1475:                                topAncestorType);
1476:                        firstAncestor2 = getFirstNonAnonymous(m2,
1477:                                topAncestorType);
1478:
1479:                        if (firstAncestor1 == firstAncestor2) {
1480:                            /*
1481:                             * for anonymous classes from same chain and same first common ancestor,
1482:                             * the order is the definition order
1483:                             */
1484:                            int topAncestorDistance1 = getTopAncestorDistance(
1485:                                    m1, topAncestorType);
1486:                            int topAncestorDistance2 = getTopAncestorDistance(
1487:                                    m2, topAncestorType);
1488:                            if (topAncestorDistance1 < topAncestorDistance2) {
1489:                                return -1;
1490:                            } else if (topAncestorDistance1 > topAncestorDistance2) {
1491:                                return 1;
1492:                            } else {
1493:                                return sourceComparator.compare(o1, o2);
1494:                            }
1495:                        }
1496:
1497:                        //                if(!is50OrHigher && !isStatic(m1) && !isStatic(m2)){
1498:                        //                    return sourceComparator.compare(o1, o2);
1499:                        //                }
1500:
1501:                        /*
1502:                         * for anonymous classes which have first non-common non-anonymous ancestor,
1503:                         * the order is the reversed definition order
1504:                         */
1505:                        int topAncestorDistance1 = getTopAncestorDistance(
1506:                                firstAncestor1, topAncestorType);
1507:                        int topAncestorDistance2 = getTopAncestorDistance(
1508:                                firstAncestor2, topAncestorType);
1509:                        if (topAncestorDistance1 > topAncestorDistance2) {
1510:                            return -1;
1511:                        } else if (topAncestorDistance1 < topAncestorDistance2) {
1512:                            return 1;
1513:                        } else {
1514:                            return sourceComparator.compare(o1, o2);
1515:                        }
1516:                    }
1517:                }
1518:            }
1519:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.