diff options
author | Roel Spilker <r.spilker@gmail.com> | 2015-01-20 03:00:02 +0100 |
---|---|---|
committer | Reinier Zwitserloot <reinier@zwitserloot.com> | 2015-01-20 22:37:06 +0100 |
commit | 296d1c2ed8a6aa29fb2a1ce7f16ea9e39b49d7d5 (patch) | |
tree | 0b34161fabfbbe44ad7e1b5409b52efb0a9ea4a8 /src/eclipseAgent/lombok/launch/PatchFixesHider.java | |
parent | e97e0cc23eca1b34d657f382d123ce2298ceb5a8 (diff) | |
download | lombok-296d1c2ed8a6aa29fb2a1ce7f16ea9e39b49d7d5.tar.gz lombok-296d1c2ed8a6aa29fb2a1ce7f16ea9e39b49d7d5.tar.bz2 lombok-296d1c2ed8a6aa29fb2a1ce7f16ea9e39b49d7d5.zip |
[shadowloader] all the patching we do for ecj are ALL entrypoints and need shadowloader treatment. This commit makes it happen.
Diffstat (limited to 'src/eclipseAgent/lombok/launch/PatchFixesHider.java')
-rw-r--r-- | src/eclipseAgent/lombok/launch/PatchFixesHider.java | 587 |
1 files changed, 587 insertions, 0 deletions
diff --git a/src/eclipseAgent/lombok/launch/PatchFixesHider.java b/src/eclipseAgent/lombok/launch/PatchFixesHider.java new file mode 100644 index 00000000..23774d0f --- /dev/null +++ b/src/eclipseAgent/lombok/launch/PatchFixesHider.java @@ -0,0 +1,587 @@ +/* + * Copyright (C) 2010-2015 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.launch; + +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +import lombok.eclipse.EclipseAugments; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jdt.core.IAnnotatable; +import org.eclipse.jdt.core.IAnnotation; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.dom.MethodDeclaration; +import org.eclipse.jdt.core.dom.SimpleName; +import org.eclipse.jdt.internal.compiler.ast.Annotation; +import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; +import org.eclipse.jdt.internal.compiler.ast.Expression; +import org.eclipse.jdt.internal.compiler.ast.ForeachStatement; +import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; +import org.eclipse.jdt.internal.compiler.ast.MessageSend; +import org.eclipse.jdt.internal.compiler.lookup.BlockScope; +import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; +import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; +import org.eclipse.jdt.internal.compiler.parser.Parser; +import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; +import org.eclipse.jdt.internal.core.dom.rewrite.NodeRewriteEvent; +import org.eclipse.jdt.internal.core.dom.rewrite.RewriteEvent; +import org.eclipse.jdt.internal.core.dom.rewrite.TokenScanner; +import org.eclipse.jdt.internal.corext.refactoring.structure.ASTNodeSearchUtil; + +/** These contain a mix of the following: + * <ul> + * <li> 'dependency free' method wrappers that cross the shadowloader barrier. + * <li> methods that directly patch, <em>but</em>, these should ALWAYS be transplanted. + * </ul> + * + * <strong>This class lives on the outside of the shadowloader barrier, and as a consequence, cannot access any other lombok code except other + * code in the {@code lombok.launch} package!</strong>. + * <p> + * This class is package private with lots of public inner static classes. This hides all of them from IDE autocomplete dialogs and such but at the JVM + * level the inner static class are just plain public, which is important, because calls to the contents of these inner static classes are injected into + * various eclipse classes verbatim, and if they weren't public, the verifier wouldn't accept it. + */ +final class PatchFixesHider { + + /** These utility methods are only used 'internally', but because of transplant methods, the class (and its methods) still have to be public! */ + public static final class Util { + public static Class<?> shadowLoadClass(String name) { + try { + return Class.forName(name, true, Main.createShadowClassLoader()); + } catch (ClassNotFoundException e) { + throw sneakyThrow(e); + } + } + + public static Method findMethod(Class<?> type, String name, Class<?>... parameterTypes) { + try { + return type.getDeclaredMethod(name, parameterTypes); + } catch (NoSuchMethodException e) { + throw sneakyThrow(e); + } + } + + public static Object invokeMethod(Method method, Object... args) { + try { + return method.invoke(null, args); + } catch (IllegalAccessException e) { + throw sneakyThrow(e); + } catch (InvocationTargetException e) { + throw sneakyThrow(e.getCause()); + } + } + + private static RuntimeException sneakyThrow(Throwable t) { + if (t == null) throw new NullPointerException("t"); + Util.<RuntimeException>sneakyThrow0(t); + return null; + } + + @SuppressWarnings("unchecked") + private static <T extends Throwable> void sneakyThrow0(Throwable t) throws T { + throw (T)t; + } + } + + /** Contains patch fixes that are dependent on lombok internals. */ + public static final class LombokDeps { + public static final Method ADD_LOMBOK_NOTES; + public static final Method POST_COMPILER_BYTES_STRING; + public static final Method POST_COMPILER_OUTPUTSTREAM; + public static final Method POST_COMPILER_BUFFEREDOUTPUTSTREAM_STRING_STRING; + + static { + Class<?> shadowed = Util.shadowLoadClass("lombok.eclipse.agent.PatchFixesShadowLoaded"); + ADD_LOMBOK_NOTES = Util.findMethod(shadowed, "addLombokNotesToEclipseAboutDialog", String.class, String.class); + POST_COMPILER_BYTES_STRING = Util.findMethod(shadowed, "runPostCompiler", byte[].class, String.class); + POST_COMPILER_OUTPUTSTREAM = Util.findMethod(shadowed, "runPostCompiler", OutputStream.class); + POST_COMPILER_BUFFEREDOUTPUTSTREAM_STRING_STRING = Util.findMethod(shadowed, "runPostCompiler", BufferedOutputStream.class, String.class, String.class); + } + + public static String addLombokNotesToEclipseAboutDialog(String origReturnValue, String key) { + return (String) Util.invokeMethod(LombokDeps.ADD_LOMBOK_NOTES, origReturnValue, key); + } + + public static byte[] runPostCompiler(byte[] bytes, String fileName) { + return (byte[]) Util.invokeMethod(LombokDeps.POST_COMPILER_BYTES_STRING, bytes, fileName); + } + + public static OutputStream runPostCompiler(OutputStream out) throws IOException { + return (OutputStream) Util.invokeMethod(LombokDeps.POST_COMPILER_OUTPUTSTREAM, out); + } + + public static BufferedOutputStream runPostCompiler(BufferedOutputStream out, String path, String name) throws IOException { + return (BufferedOutputStream) Util.invokeMethod(LombokDeps.POST_COMPILER_BUFFEREDOUTPUTSTREAM_STRING_STRING, out, path, name); + } + } + + public static final class Transform { + private static final Method TRANSFORM; + private static final Method TRANSFORM_SWAPPED; + + static { + Class<?> shadowed = Util.shadowLoadClass("lombok.eclipse.TransformEclipseAST"); + TRANSFORM = Util.findMethod(shadowed, "transform", Parser.class, CompilationUnitDeclaration.class); + TRANSFORM_SWAPPED = Util.findMethod(shadowed, "transform_swapped", CompilationUnitDeclaration.class, Parser.class); + } + + public static void transform(Parser parser, CompilationUnitDeclaration ast) throws IOException { + Util.invokeMethod(TRANSFORM, parser, ast); + } + + public static void transform_swapped(CompilationUnitDeclaration ast, Parser parser) throws IOException { + Util.invokeMethod(TRANSFORM_SWAPPED, ast, parser); + } + } + + /** Contains patch code to support {@code @Delegate} */ + public static final class Delegate { + private static final Method HANDLE_DELEGATE_FOR_TYPE; + + static { + Class<?> shadowed = Util.shadowLoadClass("lombok.eclipse.agent.PatchDelegatePortal"); + HANDLE_DELEGATE_FOR_TYPE = Util.findMethod(shadowed, "handleDelegateForType", Object.class); + } + + public static boolean handleDelegateForType(Object classScope) { + return (Boolean) Util.invokeMethod(HANDLE_DELEGATE_FOR_TYPE, classScope); + } + } + + /** Contains patch code to support {@code val} (eclipse specific) */ + public static final class ValPortal { + private static final Method COPY_INITIALIZATION_OF_FOR_EACH_ITERABLE; + private static final Method COPY_INITIALIZATION_OF_LOCAL_DECLARATION; + private static final Method ADD_FINAL_AND_VAL_ANNOTATION_TO_VARIABLE_DECLARATION_STATEMENT; + private static final Method ADD_FINAL_AND_VAL_ANNOTATION_TO_SINGLE_VARIABLE_DECLARATION; + + static { + Class<?> shadowed = Util.shadowLoadClass("lombok.eclipse.agent.PatchValEclipsePortal"); + COPY_INITIALIZATION_OF_FOR_EACH_ITERABLE = Util.findMethod(shadowed, "copyInitializationOfForEachIterable", Object.class); + COPY_INITIALIZATION_OF_LOCAL_DECLARATION = Util.findMethod(shadowed, "copyInitializationOfLocalDeclaration", Object.class); + ADD_FINAL_AND_VAL_ANNOTATION_TO_VARIABLE_DECLARATION_STATEMENT = Util.findMethod(shadowed, "addFinalAndValAnnotationToVariableDeclarationStatement", Object.class, Object.class, Object.class); + ADD_FINAL_AND_VAL_ANNOTATION_TO_SINGLE_VARIABLE_DECLARATION = Util.findMethod(shadowed, "addFinalAndValAnnotationToSingleVariableDeclaration", Object.class, Object.class, Object.class); + } + + public static void copyInitializationOfForEachIterable(Object parser) { + Util.invokeMethod(COPY_INITIALIZATION_OF_FOR_EACH_ITERABLE, parser); + } + + public static void copyInitializationOfLocalDeclaration(Object parser) { + Util.invokeMethod(COPY_INITIALIZATION_OF_LOCAL_DECLARATION, parser); + } + + public static void addFinalAndValAnnotationToVariableDeclarationStatement(Object converter, Object out, Object in) { + Util.invokeMethod(ADD_FINAL_AND_VAL_ANNOTATION_TO_VARIABLE_DECLARATION_STATEMENT, converter, out, in); + } + + public static void addFinalAndValAnnotationToSingleVariableDeclaration(Object converter, Object out, Object in) { + Util.invokeMethod(ADD_FINAL_AND_VAL_ANNOTATION_TO_SINGLE_VARIABLE_DECLARATION, converter, out, in); + } + } + + /** Contains patch code to support {@code val} (eclipse and ecj) */ + public static final class Val { + private static final Method SKIP_RESOLVE_INITIALIZER_IF_ALREADY_CALLED; + private static final Method SKIP_RESOLVE_INITIALIZER_IF_ALREADY_CALLED2; + private static final Method HANDLE_VAL_FOR_LOCAL_DECLARATION; + private static final Method HANDLE_VAL_FOR_FOR_EACH; + + static { + Class<?> shadowed = Util.shadowLoadClass("lombok.eclipse.agent.PatchVal"); + SKIP_RESOLVE_INITIALIZER_IF_ALREADY_CALLED = Util.findMethod(shadowed, "skipResolveInitializerIfAlreadyCalled", Expression.class, BlockScope.class); + SKIP_RESOLVE_INITIALIZER_IF_ALREADY_CALLED2 = Util.findMethod(shadowed, "skipResolveInitializerIfAlreadyCalled2", Expression.class, BlockScope.class, LocalDeclaration.class); + HANDLE_VAL_FOR_LOCAL_DECLARATION = Util.findMethod(shadowed, "handleValForLocalDeclaration", LocalDeclaration.class, BlockScope.class); + HANDLE_VAL_FOR_FOR_EACH = Util.findMethod(shadowed, "handleValForForEach", ForeachStatement.class, BlockScope.class); + } + + public static TypeBinding skipResolveInitializerIfAlreadyCalled(Expression expr, BlockScope scope) { + return (TypeBinding) Util.invokeMethod(SKIP_RESOLVE_INITIALIZER_IF_ALREADY_CALLED, expr, scope); + } + + public static TypeBinding skipResolveInitializerIfAlreadyCalled2(Expression expr, BlockScope scope, LocalDeclaration decl) { + return (TypeBinding) Util.invokeMethod(SKIP_RESOLVE_INITIALIZER_IF_ALREADY_CALLED2, expr, scope, decl); + } + + public static boolean handleValForLocalDeclaration(LocalDeclaration local, BlockScope scope) { + return (Boolean) Util.invokeMethod(HANDLE_VAL_FOR_LOCAL_DECLARATION, local, scope); + } + + public static boolean handleValForForEach(ForeachStatement forEach, BlockScope scope) { + return (Boolean) Util.invokeMethod(HANDLE_VAL_FOR_FOR_EACH, forEach, scope); + } + } + + /** Contains patch code to support {@code @ExtensionMethod} */ + public static final class ExtensionMethod { + private static final Method RESOLVE_TYPE; + private static final Method ERROR_NO_METHOD_FOR; + private static final Method INVALID_METHOD; + + static { + Class<?> shadowed = Util.shadowLoadClass("lombok.eclipse.agent.PatchExtensionMethod"); + RESOLVE_TYPE = Util.findMethod(shadowed, "resolveType", TypeBinding.class, MessageSend.class, BlockScope.class); + ERROR_NO_METHOD_FOR = Util.findMethod(shadowed, "errorNoMethodFor", ProblemReporter.class, MessageSend.class, TypeBinding.class, TypeBinding[].class); + INVALID_METHOD = Util.findMethod(shadowed, "invalidMethod", ProblemReporter.class, MessageSend.class, MethodBinding.class); + } + + public static TypeBinding resolveType(TypeBinding resolvedType, MessageSend methodCall, BlockScope scope) { + return (TypeBinding) Util.invokeMethod(RESOLVE_TYPE, resolvedType, methodCall, scope); + } + + public static void errorNoMethodFor(ProblemReporter problemReporter, MessageSend messageSend, TypeBinding recType, TypeBinding[] params) { + Util.invokeMethod(ERROR_NO_METHOD_FOR, problemReporter, messageSend, recType, params); + } + + public static void invalidMethod(ProblemReporter problemReporter, MessageSend messageSend, MethodBinding method) { + Util.invokeMethod(INVALID_METHOD, problemReporter, messageSend, method); + } + } + + /** + * Contains a mix of methods: ecj only, ecj+eclipse, and eclipse only. As a consequence, _EVERY_ method from here used for ecj MUST be + * transplanted, as ecj itself cannot load this class (signatures refer to things that don't exist in ecj-only mode). + * <p> + * Because of usage of transplant(), a bunch of these contain direct code and don't try to cross the shadowloader barrier. + */ + public static final class PatchFixes { + public static boolean isGenerated(org.eclipse.jdt.core.dom.ASTNode node) { + boolean result = false; + try { + result = ((Boolean)node.getClass().getField("$isGenerated").get(node)).booleanValue(); + if (!result && node.getParent() != null && node.getParent() instanceof org.eclipse.jdt.core.dom.QualifiedName) { + result = isGenerated(node.getParent()); + } + } catch (Exception e) { + // better to assume it isn't generated + } + return result; + } + + public static boolean isListRewriteOnGeneratedNode(org.eclipse.jdt.core.dom.rewrite.ListRewrite rewrite) { + return isGenerated(rewrite.getParent()); + } + + public static boolean returnFalse(java.lang.Object object) { + return false; + } + + public static boolean returnTrue(java.lang.Object object) { + return true; + } + + @java.lang.SuppressWarnings({"unchecked", "rawtypes"}) public static java.util.List removeGeneratedNodes(java.util.List list) { + try { + java.util.List realNodes = new java.util.ArrayList(list.size()); + for (java.lang.Object node : list) { + if(!isGenerated(((org.eclipse.jdt.core.dom.ASTNode)node))) { + realNodes.add(node); + } + } + return realNodes; + } catch (Exception e) { + } + return list; + } + + public static java.lang.String getRealMethodDeclarationSource(java.lang.String original, Object processor, org.eclipse.jdt.core.dom.MethodDeclaration declaration) throws Exception { + if (!isGenerated(declaration)) return original; + + List<org.eclipse.jdt.core.dom.Annotation> annotations = new ArrayList<org.eclipse.jdt.core.dom.Annotation>(); + for (Object modifier : declaration.modifiers()) { + if (modifier instanceof org.eclipse.jdt.core.dom.Annotation) { + org.eclipse.jdt.core.dom.Annotation annotation = (org.eclipse.jdt.core.dom.Annotation)modifier; + String qualifiedAnnotationName = annotation.resolveTypeBinding().getQualifiedName(); + if (!"java.lang.Override".equals(qualifiedAnnotationName) && !"java.lang.SuppressWarnings".equals(qualifiedAnnotationName)) annotations.add(annotation); + } + } + + StringBuilder signature = new StringBuilder(); + addAnnotations(annotations, signature); + + if ((Boolean)processor.getClass().getDeclaredField("fPublic").get(processor)) signature.append("public "); + if ((Boolean)processor.getClass().getDeclaredField("fAbstract").get(processor)) signature.append("abstract "); + + signature + .append(declaration.getReturnType2().toString()) + .append(" ").append(declaration.getName().getFullyQualifiedName()) + .append("("); + + boolean first = true; + for (Object parameter : declaration.parameters()) { + if (!first) signature.append(", "); + first = false; + // We should also add the annotations of the parameters + signature.append(parameter); + } + + signature.append(");"); + return signature.toString(); + } + + // part of getRealMethodDeclarationSource(...) + public static void addAnnotations(List<org.eclipse.jdt.core.dom.Annotation> annotations, StringBuilder signature) { + /* + * We SHOULD be able to handle the following cases: + * @Override + * @Override() + * @SuppressWarnings("all") + * @SuppressWarnings({"all", "unused"}) + * @SuppressWarnings(value = "all") + * @SuppressWarnings(value = {"all", "unused"}) + * @EqualsAndHashCode(callSuper=true, of="id") + * + * Currently, we only seem to correctly support: + * @Override + * @Override() N.B. We lose the parentheses here, since there are no values. No big deal. + * @SuppressWarnings("all") + */ + for (org.eclipse.jdt.core.dom.Annotation annotation : annotations) { + List<String> values = new ArrayList<String>(); + if (annotation.isSingleMemberAnnotation()) { + org.eclipse.jdt.core.dom.SingleMemberAnnotation smAnn = (org.eclipse.jdt.core.dom.SingleMemberAnnotation) annotation; + values.add(smAnn.getValue().toString()); + } else if (annotation.isNormalAnnotation()) { + org.eclipse.jdt.core.dom.NormalAnnotation normalAnn = (org.eclipse.jdt.core.dom.NormalAnnotation) annotation; + for (Object value : normalAnn.values()) values.add(value.toString()); + } + + signature.append("@").append(annotation.resolveTypeBinding().getQualifiedName()); + if (!values.isEmpty()) { + signature.append("("); + boolean first = true; + for (String string : values) { + if (!first) signature.append(", "); + first = false; + signature.append('"').append(string).append('"'); + } + signature.append(")"); + } + signature.append(" "); + } + } + + public static org.eclipse.jdt.core.dom.MethodDeclaration getRealMethodDeclarationNode(org.eclipse.jdt.core.IMethod sourceMethod, org.eclipse.jdt.core.dom.CompilationUnit cuUnit) throws JavaModelException { + MethodDeclaration methodDeclarationNode = ASTNodeSearchUtil.getMethodDeclarationNode(sourceMethod, cuUnit); + if (isGenerated(methodDeclarationNode)) { + IType declaringType = sourceMethod.getDeclaringType(); + Stack<IType> typeStack = new Stack<IType>(); + while (declaringType != null) { + typeStack.push(declaringType); + declaringType = declaringType.getDeclaringType(); + } + + IType rootType = typeStack.pop(); + org.eclipse.jdt.core.dom.AbstractTypeDeclaration typeDeclaration = findTypeDeclaration(rootType, cuUnit.types()); + while (!typeStack.isEmpty() && typeDeclaration != null) { + typeDeclaration = findTypeDeclaration(typeStack.pop(), typeDeclaration.bodyDeclarations()); + } + + if (typeStack.isEmpty() && typeDeclaration != null) { + String methodName = sourceMethod.getElementName(); + for (Object declaration : typeDeclaration.bodyDeclarations()) { + if (declaration instanceof org.eclipse.jdt.core.dom.MethodDeclaration) { + org.eclipse.jdt.core.dom.MethodDeclaration methodDeclaration = (org.eclipse.jdt.core.dom.MethodDeclaration) declaration; + if (methodDeclaration.getName().toString().equals(methodName)) { + return methodDeclaration; + } + } + } + } + } + return methodDeclarationNode; + } + + // part of getRealMethodDeclarationNode + public static org.eclipse.jdt.core.dom.AbstractTypeDeclaration findTypeDeclaration(IType searchType, List<?> nodes) { + for (Object object : nodes) { + if (object instanceof org.eclipse.jdt.core.dom.AbstractTypeDeclaration) { + org.eclipse.jdt.core.dom.AbstractTypeDeclaration typeDeclaration = (org.eclipse.jdt.core.dom.AbstractTypeDeclaration) object; + if (typeDeclaration.getName().toString().equals(searchType.getElementName())) + return typeDeclaration; + } + } + return null; + } + + public static int getSourceEndFixed(int sourceEnd, org.eclipse.jdt.internal.compiler.ast.ASTNode node) throws Exception { + if (sourceEnd == -1) { + org.eclipse.jdt.internal.compiler.ast.ASTNode object = (org.eclipse.jdt.internal.compiler.ast.ASTNode)node.getClass().getField("$generatedBy").get(node); + if (object != null) { + return object.sourceEnd; + } + } + return sourceEnd; + } + + public static int fixRetrieveStartingCatchPosition(int original, int start) { + return original == -1 ? start : original; + } + + public static int fixRetrieveIdentifierEndPosition(int original, int end) { + return original == -1 ? end : original; + } + + public static int fixRetrieveEllipsisStartPosition(int original, int end) { + return original == -1 ? end : original; + } + + public static int fixRetrieveRightBraceOrSemiColonPosition(int original, int end) { + return original == -1 ? end : original; // Need to fix: see issue 325. + } + + public static final int ALREADY_PROCESSED_FLAG = 0x800000; //Bit 24 + + public static boolean checkBit24(Object node) throws Exception { + int bits = (Integer)(node.getClass().getField("bits").get(node)); + return (bits & ALREADY_PROCESSED_FLAG) != 0; + } + + public static boolean skipRewritingGeneratedNodes(org.eclipse.jdt.core.dom.ASTNode node) throws Exception { + return ((Boolean) node.getClass().getField("$isGenerated").get(node)).booleanValue(); + } + + public static void setIsGeneratedFlag(org.eclipse.jdt.core.dom.ASTNode domNode, + org.eclipse.jdt.internal.compiler.ast.ASTNode internalNode) throws Exception { + + if (internalNode == null || domNode == null) return; + boolean isGenerated = EclipseAugments.ASTNode_generatedBy.get(internalNode) != null; + if (isGenerated) domNode.getClass().getField("$isGenerated").set(domNode, true); + } + + public static void setIsGeneratedFlagForName(org.eclipse.jdt.core.dom.Name name, Object internalNode) throws Exception { + if (internalNode instanceof org.eclipse.jdt.internal.compiler.ast.ASTNode) { + boolean isGenerated = EclipseAugments.ASTNode_generatedBy.get((org.eclipse.jdt.internal.compiler.ast.ASTNode) internalNode) != null; + if (isGenerated) name.getClass().getField("$isGenerated").set(name, true); + } + } + + public static RewriteEvent[] listRewriteHandleGeneratedMethods(RewriteEvent parent) { + RewriteEvent[] children = parent.getChildren(); + List<RewriteEvent> newChildren = new ArrayList<RewriteEvent>(); + List<RewriteEvent> modifiedChildren = new ArrayList<RewriteEvent>(); + for (int i = 0; i < children.length; i++) { + RewriteEvent child = children[i]; + boolean isGenerated = isGenerated((org.eclipse.jdt.core.dom.ASTNode) child.getOriginalValue()); + if (isGenerated) { + boolean isReplacedOrRemoved = child.getChangeKind() == RewriteEvent.REPLACED || child.getChangeKind() == RewriteEvent.REMOVED; + boolean convertingFromMethod = child.getOriginalValue() instanceof org.eclipse.jdt.core.dom.MethodDeclaration; + if (isReplacedOrRemoved && convertingFromMethod && child.getNewValue() != null) { + modifiedChildren.add(new NodeRewriteEvent(null, child.getNewValue())); + } + } else { + newChildren.add(child); + } + } + // Since Eclipse doesn't honor the "insert at specified location" for already existing members, + // we'll just add them last + newChildren.addAll(modifiedChildren); + return newChildren.toArray(new RewriteEvent[newChildren.size()]); + } + + public static int getTokenEndOffsetFixed(TokenScanner scanner, int token, int startOffset, Object domNode) throws CoreException { + boolean isGenerated = false; + try { + isGenerated = (Boolean) domNode.getClass().getField("$isGenerated").get(domNode); + } catch (Exception e) { + // If this fails, better to break some refactor scripts than to crash eclipse. + } + if (isGenerated) return -1; + return scanner.getTokenEndOffset(token, startOffset); + } + + public static IMethod[] removeGeneratedMethods(IMethod[] methods) throws Exception { + List<IMethod> result = new ArrayList<IMethod>(); + for (IMethod m : methods) { + if (m.getNameRange().getLength() > 0 && !m.getNameRange().equals(m.getSourceRange())) result.add(m); + } + return result.size() == methods.length ? methods : result.toArray(new IMethod[result.size()]); + } + + public static SimpleName[] removeGeneratedSimpleNames(SimpleName[] in) throws Exception { + Field f = SimpleName.class.getField("$isGenerated"); + + int count = 0; + for (int i = 0; i < in.length; i++) { + if (in[i] == null || !((Boolean)f.get(in[i])).booleanValue()) count++; + } + if (count == in.length) return in; + SimpleName[] newSimpleNames = new SimpleName[count]; + count = 0; + for (int i = 0; i < in.length; i++) { + if (in[i] == null || !((Boolean)f.get(in[i])).booleanValue()) newSimpleNames[count++] = in[i]; + } + return newSimpleNames; + } + + public static Annotation[] convertAnnotations(Annotation[] out, IAnnotatable annotatable) { + IAnnotation[] in; + + try { + in = annotatable.getAnnotations(); + } catch (Exception e) { + return out; + } + + if (out == null) return null; + int toWrite = 0; + + for (int idx = 0; idx < out.length; idx++) { + String oName = new String(out[idx].type.getLastToken()); + boolean found = false; + for (IAnnotation i : in) { + String name = i.getElementName(); + int li = name.lastIndexOf('.'); + if (li > -1) name = name.substring(li + 1); + if (name.equals(oName)) { + found = true; + break; + } + } + if (!found) out[idx] = null; + else toWrite++; + } + + Annotation[] replace = out; + if (toWrite < out.length) { + replace = new Annotation[toWrite]; + int idx = 0; + for (int i = 0; i < out.length; i++) { + if (out[i] == null) continue; + replace[idx++] = out[i]; + } + } + + return replace; + } + } +} |