@Override public void visitClass(ClassNode node) { addNode(classes, node.getNameWithoutPackage(), node); super.visitClass(node); }
public static String createErrorMessage(String className, String fieldName, String typeName, String mode) { return "Unsupported type (" + prettyTypeName(typeName) + ") found for field '" + fieldName + "' while " + mode + " immutable class " + className + ".\n" + "Immutable classes only support properties with effectively immutable types including:\n" + "- Strings, primitive types, wrapper types, Class, BigInteger and BigDecimal, enums\n" + "- classes annotated with @KnownImmutable and known immutables (java.awt.Color, java.net.URI)\n" + "- Cloneable classes, collections, maps and arrays, and other classes with special handling\n" + " (java.util.Date and various java.time.* classes and interfaces)\n" + "Other restrictions apply, please see the groovydoc for " + IMMUTABLE_OPTIONS_TYPE.getNameWithoutPackage() + " for further details"; }
private static String getBuilderClassName(ClassNode buildee, AnnotationNode anno) { return getMemberStringValue(anno, "builderClassName", buildee.getNameWithoutPackage() + "Initializer"); }
private boolean findClassWithMatchingBasename(String nameWithoutPackage) { // For performance reasons test against classNamePattern first if (classNamePattern != null && classNamePattern.matcher(nameWithoutPackage).matches()) { return true; } if (classesToNewify != null) { @SuppressWarnings("unchecked") final List<ClassExpression> classes = (List) classesToNewify.getExpressions(); for (ClassExpression ce : classes) { if (ce.getType().getNameWithoutPackage().equals(nameWithoutPackage)) { return true; } } } return false; }
@Override public void visitConstructor(ConstructorNode node) { addNode(constructors, node.getDeclaringClass().getNameWithoutPackage(), node); super.visitConstructor(node); }
private void addImport(final String className) { final ClassNode node = ClassHelper.make(className); imports.add(new Import(ImportType.regular, node.getNameWithoutPackage(), node)); }
private void checkDuplicateNameClashes(ListExpression list) { final Set<String> seen = new HashSet<String>(); @SuppressWarnings("unchecked") final List<ClassExpression> classes = (List) list.getExpressions(); for (ClassExpression ce : classes) { final String name = ce.getType().getNameWithoutPackage(); if (seen.contains(name)) { addError("Duplicate name '" + name + "' found during @" + MY_NAME + " processing.", ce); } seen.add(name); } }
private static String getFullName(AnnotationNode anno, ClassNode buildee) { String builderClassName = getMemberStringValue(anno, "builderClassName", buildee.getNameWithoutPackage() + "Builder"); return buildee.getName() + "$" + builderClassName; }
private ClassNode getSourceUnitClass(VariableExpression ve) { List<ClassNode> classes = source.getAST().getClasses(); for (ClassNode classNode : classes) { if (classNode.getNameWithoutPackage().equals(ve.getName())) return classNode; } return null; }
private static String buildTypeName(ClassNode type) { if (type.isArray()) { return String.format("%sArray", buildTypeName(type.getComponentType())); } return type.getNameWithoutPackage(); }
private static SyntaxException createException(ClassNode trait, ClassNode targetNode, MethodNode forwarder, MethodNode existingMethod) { String middle; ASTNode errorTarget; if (existingMethod.getLineNumber() == -1) { // came from a trait errorTarget = targetNode; List<AnnotationNode> allAnnos = existingMethod.getAnnotations(Traits.TRAITBRIDGE_CLASSNODE); AnnotationNode bridgeAnno = allAnnos == null ? null : allAnnos.get(0); String fromTrait = null; if (bridgeAnno != null) { Expression traitClass = bridgeAnno.getMember("traitClass"); if (traitClass instanceof ClassExpression) { ClassExpression ce = (ClassExpression) traitClass; fromTrait = ce.getType().getNameWithoutPackage(); } } middle = "in '" + targetNode.getNameWithoutPackage(); if (fromTrait != null) { middle += "' from trait '" + fromTrait; } } else { errorTarget = existingMethod; middle = "declared in '" + targetNode.getNameWithoutPackage(); } String message = "The static '" + forwarder.getName() + "' method " + middle + "' conflicts with the instance method having the same signature from trait '" + trait.getNameWithoutPackage() + "'"; return new SyntaxException(message, errorTarget); }
private void printConstructor(PrintWriter out, ClassNode clazz, ConstructorNode constructorNode) { printAnnotations(out, constructorNode); // printModifiers(out, constructorNode.getModifiers()); out.print("public "); // temporary hack String className = clazz.getNameWithoutPackage(); if (clazz instanceof InnerClassNode) className = className.substring(className.lastIndexOf("$") + 1); out.println(className); printParams(out, constructorNode); ConstructorCallExpression constrCall = getConstructorCallExpression(constructorNode); if (constrCall == null || !constrCall.isSpecialCall()) { out.println(" {}"); } else { out.println(" {"); printSpecialConstructorArgs(out, constructorNode, constrCall); out.println("}"); } }
private void checkClassLevelClashes(ListExpression list) { @SuppressWarnings("unchecked") final List<ClassExpression> classes = (List) list.getExpressions(); for (ClassExpression ce : classes) { final String name = ce.getType().getNameWithoutPackage(); if (findClassWithMatchingBasename(name)) { addError("Error during @" + MY_NAME + " processing. Class '" + name + "' can't appear at " + "method/constructor/field level if it already appears at the class level.", ce); } } }
@Override @SuppressWarnings("unchecked") public void visitAnnotations(AnnotatedNode node) { for (AnnotationNode an : node.getAnnotations()) { ClassNode cn = an.getClassNode(); // this comparison should be good enough, and also works in phase conversion if (cn.getNameWithoutPackage().equals(Inspect.class.getSimpleName())) { ConstantExpression name = (ConstantExpression)an.getMember("value"); if (name == null || !(name.getValue() instanceof String)) throw new AstInspectorException("@Inspect must have a String argument"); addNode(markedNodes, (String)name.getValue(), node); break; } } super.visitAnnotations(node); }
/** * Determine if an explicit (non-generated) constructor is in the class. * * @param xform if non null, add an error if an explicit constructor is found * @param cNode the type of the containing class * @return true if an explicit (non-generated) constructor was found */ public static boolean hasExplicitConstructor(AbstractASTTransformation xform, ClassNode cNode) { List<ConstructorNode> declaredConstructors = cNode.getDeclaredConstructors(); for (ConstructorNode constructorNode : declaredConstructors) { // allow constructors added by other transforms if flagged as Generated if (hasAnnotation(constructorNode, GENERATED_TYPE)) { continue; } if (xform != null) { xform.addError("Error during " + xform.getAnnotationName() + " processing. Explicit constructors not allowed for class: " + cNode.getNameWithoutPackage(), constructorNode); } return true; } return false; }
private static Expression initializeInstance(ClassNode sourceClass, List<PropertyInfo> props, BlockStatement body) { Expression instance = varX("_the" + sourceClass.getNameWithoutPackage(), sourceClass); body.addStatement(declS(instance, ctorX(sourceClass))); for (PropertyInfo prop : props) { body.addStatement(stmt(assignX(propX(instance, prop.getName()), varX(prop.getName().equals("class") ? "clazz" : prop.getName(), newClass(prop.getType()))))); } return instance; }
private static void addHolderClassIdiomBody(BlockStatement body, FieldNode fieldNode, Expression initExpr) { final ClassNode declaringClass = fieldNode.getDeclaringClass(); final ClassNode fieldType = fieldNode.getType(); final int visibility = ACC_PRIVATE | ACC_STATIC; final String fullName = declaringClass.getName() + "$" + fieldType.getNameWithoutPackage() + "Holder_" + fieldNode.getName().substring(1); final InnerClassNode holderClass = new InnerClassNode(declaringClass, fullName, visibility, ClassHelper.OBJECT_TYPE); final String innerFieldName = "INSTANCE"; // we have two options: // (1) embed initExpr within holder class but redirect field access/method calls to declaring class members // (2) keep initExpr within a declaring class method that is only called by the holder class // currently we have gone with (2) for simplicity with only a slight memory footprint increase in the declaring class final String initializeMethodName = (fullName + "_initExpr").replace('.', '_'); addGeneratedMethod(declaringClass, initializeMethodName, ACC_PRIVATE | ACC_STATIC | ACC_FINAL, fieldType, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, returnS(initExpr)); holderClass.addField(innerFieldName, ACC_PRIVATE | ACC_STATIC | ACC_FINAL, fieldType, callX(declaringClass, initializeMethodName)); final Expression innerField = propX(classX(holderClass), innerFieldName); declaringClass.getModule().addClass(holderClass); body.addStatement(returnS(innerField)); }
/** * Given a class node, if this class node implements a trait, then generate all the appropriate * code which delegates calls to the trait. It is safe to call this method on a class node which * does not implement a trait. * @param cNode a class node * @param unit the source unit */ public static void doExtendTraits(final ClassNode cNode, final SourceUnit unit, final CompilationUnit cu) { if (cNode.isInterface()) return; boolean isItselfTrait = Traits.isTrait(cNode); SuperCallTraitTransformer superCallTransformer = new SuperCallTraitTransformer(unit); if (isItselfTrait) { checkTraitAllowed(cNode, unit); return; } if (!cNode.getNameWithoutPackage().endsWith(Traits.TRAIT_HELPER)) { List<ClassNode> traits = findTraits(cNode); for (ClassNode trait : traits) { TraitHelpersTuple helpers = Traits.findHelpers(trait); applyTrait(trait, cNode, helpers, unit); superCallTransformer.visitClass(cNode); if (unit!=null) { ASTTransformationCollectorCodeVisitor collector = new ASTTransformationCollectorCodeVisitor(unit, cu.getTransformLoader()); collector.visitClass(cNode); } } } }
private static Expression initializeInstance(ClassNode buildee, List<PropertyInfo> props, BlockStatement body) { Expression instance = varX("_the" + buildee.getNameWithoutPackage(), buildee); body.addStatement(declS(instance, ctorX(buildee))); for (PropertyInfo pi : props) { body.addStatement(stmt(assignX(propX(instance, pi.getName()), varX(pi.getName(), pi.getType())))); } return instance; } }
private boolean processDelegateParam(MethodNode mNode, Parameter mapParam, ArgumentListExpression args, List<String> propNames, Parameter fromParam) { if (isInnerClass(fromParam.getType())) { if (mNode.isStatic()) { addError("Error during " + MY_TYPE_NAME + " processing. Delegate type '" + fromParam.getType().getNameWithoutPackage() + "' is an inner class which is not supported.", mNode); return false; } } Set<String> names = new HashSet<String>(); List<PropertyNode> props = getAllProperties(names, fromParam.getType(), true, false, false, true, false, true); for (String next : names) { if (hasDuplicates(mNode, propNames, next)) return false; } List<MapEntryExpression> entries = new ArrayList<MapEntryExpression>(); for (PropertyNode pNode : props) { String name = pNode.getName(); entries.add(new MapEntryExpression(constX(name), propX(varX(mapParam), name))); AnnotationNode namedParam = new AnnotationNode(NAMED_PARAM_TYPE); namedParam.addMember("value", constX(name)); namedParam.addMember("type", classX(pNode.getType())); mapParam.addAnnotation(namedParam); } Expression delegateMap = new MapExpression(entries); args.addExpression(castX(fromParam.getType(), delegateMap)); return true; }