private static <T> void replaceInstantiatedTypeParams(CtClass<T> toMerge, CtClass<T> mergeInto) { List<CtTypeReference<?>> typeArgs = mergeInto.getSuperclass().getActualTypeArguments(); typeArgs .stream() .filter(ta -> !(ta instanceof CtTypeParameterReference)) .forEach( ta -> { int instantiatedParamIndex = typeArgs.indexOf(ta); CtTypeReference<?> instantiatedTypeParam = toMerge.getFormalTypeParameters().get(instantiatedParamIndex); toMerge.accept( new CtScanner() { @Override public void scan(CtReference ref) { if (ref instanceof CtGenericElementReference) { CtGenericElementReference gRef = (CtGenericElementReference) ref; replaceInList(ref.getFactory(), gRef.getActualTypeArguments()); } if (ref instanceof CtTypeParameterReference) { replaceInList( ref.getFactory(), ((CtTypeParameterReference) ref).getBounds()); } super.scan(ref); } @Override public void scan(CtElement element) { if (element instanceof CtTypedElement) { CtTypedElement typed = (CtTypedElement) element; CtTypeReference type = typed.getType(); if (type != null && instantiatedTypeParam.getSimpleName().equals(type.getSimpleName())) { typed.setType(element.getFactory().Core().clone(ta)); } } if (element instanceof CtExpression) { replaceInList( element.getFactory(), ((CtExpression) element).getTypeCasts()); } super.scan(element); } private void replaceInList(Factory f, List<CtTypeReference<?>> types) { for (int i = 0; i < types.size(); i++) { CtTypeReference<?> arg = types.get(i); if (instantiatedTypeParam.getSimpleName().equals(arg.getSimpleName())) { types.set(i, f.Core().clone(ta)); } } } }); }); }
private static boolean checkOnlyDefaultConstructor(CtClass<?> ctClass) { Set<? extends CtConstructor<?>> constructors = ctClass.getConstructors(); if (constructors.size() > 1) return false; if (constructors.isEmpty()) return true; CtConstructor<?> constructor = constructors.iterator().next(); return constructor.isImplicit(); }
private static <T> void processOverridden( CtClass<?> mergeInto, CtClass<?> toMerge, final CtMethod<T> methodToMerge) { List<CtInvocation<T>> superInvocations = mergeInto.getElements( new Filter<CtInvocation<T>>() { @Override public boolean matches(CtInvocation<T> invocation) { if (!(invocation.getTarget() instanceof CtSuperAccess)) return false; CtExecutable<?> m = invocation.getExecutable().getDeclaration(); return m != null && MethodNode.overrides((CtMethod<?>) m, methodToMerge); } }); methodToMerge.setSimpleName(classPrefixedName(toMerge, methodToMerge)); methodToMerge.setVisibility(PRIVATE); removeAnnotation(methodToMerge, Override.class); for (CtInvocation<T> superInvocation : superInvocations) { superInvocation.setTarget(null); superInvocation.setExecutable(methodToMerge.getReference()); } add(mergeInto, methodToMerge, mergeInto::addMethod); }
private static <T> void processMethod( CtClass<?> mergeInto, CtClass<?> toMerge, CtMethod<T> methodToMerge) { if (methodToMerge.hasModifier(STATIC)) { add(mergeInto, methodToMerge, mergeInto::addMethod); return; } if (methodToMerge.hasModifier(ABSTRACT)) return; Optional<CtMethod<?>> overridingMethod = mergeInto .getMethods() .stream() .filter(m -> MethodNode.overrides(m, methodToMerge)) .findFirst(); if (overridingMethod.isPresent()) { @SuppressWarnings("unchecked") CtMethod<T> overriding = (CtMethod<T>) overridingMethod.get(); boolean shouldRemoveAnn = methodToMerge.getAnnotation(Override.class) == null; if (!overriding.hasModifier(ABSTRACT)) processOverridden(mergeInto, toMerge, methodToMerge); if (shouldRemoveAnn) removeAnnotation(overriding, Override.class); } else { add(mergeInto, methodToMerge, mergeInto::addMethod); } }
private static <T> void mergeStagedChainInner(List<CtClass<T>> chain) { if (chain.size() == 1) return; reverse(chain); CtClass<T> toMerge = chain.get(0); for (int i = 1; i < chain.size(); i++) { CtClass<T> mergeInto = chain.get(i); replaceInstantiatedTypeParams(toMerge, mergeInto); toMerge .getAnnotations() .stream() .forEach( (CtAnnotation<? extends Annotation> a) -> { if (mergeInto.getAnnotation(a.getActualAnnotation().getClass()) == null) add(mergeInto, a, mergeInto::addAnnotation); }); toMerge.getSuperInterfaces().forEach(mergeInto::addSuperInterface); toMerge .getAnonymousExecutables() .forEach(b -> add(mergeInto, b, mergeInto::addAnonymousExecutable)); toMerge.getNestedTypes().forEach(nt -> add(mergeInto, nt, mergeInto::addNestedType)); toMerge.getFields().forEach(f -> add(mergeInto, f, mergeInto::addField)); for (CtMethod<?> methodToMerge : toMerge.getMethods()) { processMethod(mergeInto, toMerge, methodToMerge); } final CtClass<T> finalToMerge = toMerge; mergeInto.getConstructors().forEach(c -> processConstructor(c, finalToMerge)); mergeInto.setSuperclass(toMerge.getSuperclass()); toMerge = mergeInto; } }
static String classPrefixedName(CtClass<?> ctClass, CtMethod<?> method) { return "_" + ctClass.getSimpleName() + "_" + method.getSimpleName(); }