private List<FieldNode> getAnnotatedFieldOfClass(ClassNode target, ClassNode annotation) {
    List<FieldNode> result = new ArrayList<FieldNode>();

    for (FieldNode fieldNode : target.getFields())
      if (!fieldNode.getAnnotations(annotation).isEmpty()) result.add(fieldNode);

    return result;
  }
 private Expression findConstant(FieldNode fn) {
   if (fn != null && !fn.isEnum() && fn.isStatic() && fn.isFinal()) {
     if (fn.getInitialValueExpression() instanceof ConstantExpression) {
       return fn.getInitialValueExpression();
     }
   }
   return null;
 }
 private Expression findStaticField(ClassNode staticImportType, String fieldName) {
   if (staticImportType.isPrimaryClassNode() || staticImportType.isResolved()) {
     FieldNode field = staticImportType.getField(fieldName);
     if (field != null && field.isStatic())
       return new PropertyExpression(new ClassExpression(staticImportType), fieldName);
   }
   return null;
 }
 public void addFieldFirst(FieldNode node) {
   final ClassNode r = redirect();
   node.setDeclaringClass(r);
   node.setOwner(r);
   if (r.fields == null) r.fields = new LinkedList<FieldNode>();
   if (r.fieldIndex == null) r.fieldIndex = new HashMap<String, FieldNode>();
   r.fields.addFirst(node);
   r.fieldIndex.put(node.getName(), node);
 }
 @SuppressWarnings("RedundantIfStatement")
 boolean shouldFieldBeIgnored(FieldNode fieldNode) {
   if (fieldNode == keyField) return true;
   if (fieldNode == ownerField) return true;
   if (getAnnotation(fieldNode, IGNORE_ANNOTATION) != null) return true;
   if (fieldNode.isFinal()) return true;
   if (fieldNode.getName().startsWith("$")) return true;
   if ((fieldNode.getModifiers() & ACC_TRANSIENT) != 0) return true;
   return false;
 }
  private void createMethodsForSingleField(FieldNode fieldNode) {
    if (shouldFieldBeIgnored(fieldNode)) return;

    if (hasAnnotation(fieldNode.getType(), DSL_CONFIG_ANNOTATION)) {
      createSingleDSLObjectClosureMethod(fieldNode);
      createSingleFieldSetterMethod(fieldNode);
    } else if (ASTHelper.isMap(fieldNode.getType())) createMapMethod(fieldNode);
    else if (ASTHelper.isList(fieldNode.getType())) createListMethod(fieldNode);
    else createSingleFieldSetterMethod(fieldNode);
  }
  private void preventOwnerOverride() {

    MethodBuilder.createPublicMethod(setterName(ownerField))
        .param(OBJECT_TYPE, "value")
        .statement(
            ifS(
                andX(
                    isInstanceOfX(varX("value"), ownerField.getType()),
                    notX(propX(varX("this"), ownerField.getName()))),
                assignX(propX(varX("this"), ownerField.getName()), varX("value"))))
        .addTo(annotatedClass);
  }
  private static boolean isDirectAccessAllowed(
      FieldNode a, ClassNode receiver, boolean isSamePackage) {
    ClassNode declaringClass = a.getDeclaringClass().redirect();
    ClassNode receiverType = receiver.redirect();

    // first, direct access from within the class or inner class nodes
    if (declaringClass.equals(receiverType)) return true;
    if (receiverType instanceof InnerClassNode) {
      while (receiverType != null && receiverType instanceof InnerClassNode) {
        if (declaringClass.equals(receiverType)) return true;
        receiverType = receiverType.getOuterClass();
      }
    }

    // no getter
    return a.isPublic() || (a.isProtected() && isSamePackage);
  }
  @SuppressWarnings("ConstantConditions")
  private GenericsType[] getGenericsTypes(FieldNode fieldNode) {
    GenericsType[] types = fieldNode.getType().getGenericsTypes();

    if (types == null)
      addCompileError("Lists and Maps need to be assigned an explicit Generic Type", fieldNode);
    return types;
  }
  private void createMapOfSimpleElementsMethods(
      FieldNode fieldNode, ClassNode keyType, ClassNode valueType) {
    String methodName = fieldNode.getName();

    MethodBuilder.createPublicMethod(methodName)
        .param(fieldNode.getType(), "values")
        .callMethod(propX(varX("this"), fieldNode.getName()), "putAll", varX("values"))
        .addTo(annotatedClass);

    String singleElementMethod = getElementNameForCollectionField(fieldNode);

    MethodBuilder.createPublicMethod(singleElementMethod)
        .param(keyType, "key")
        .param(valueType, "value")
        .callMethod(propX(varX("this"), fieldNode.getName()), "put", args("key", "value"))
        .addTo(annotatedClass);
  }
  private void assertMembersNamesAreUnique() {
    Map<String, FieldNode> allDslCollectionFieldNodesOfHierarchy = new HashMap<String, FieldNode>();

    for (ClassNode level : ASTHelper.getHierarchyOfDSLObjectAncestors(annotatedClass)) {
      for (FieldNode field : level.getFields()) {
        if (!ASTHelper.isListOrMap(field.getType())) continue;

        String memberName = getElementNameForCollectionField(field);

        FieldNode conflictingField = allDslCollectionFieldNodesOfHierarchy.get(memberName);

        if (conflictingField != null) {
          addCompileError(
              String.format(
                  "Member name %s is used more than once: %s:%s and %s:%s",
                  memberName,
                  field.getOwner().getName(),
                  field.getName(),
                  conflictingField.getOwner().getName(),
                  conflictingField.getName()),
              field);
          return;
        }

        allDslCollectionFieldNodesOfHierarchy.put(memberName, field);
      }
    }
  }
  private void validateFieldAnnotations() {
    for (FieldNode fieldNode : annotatedClass.getFields()) {
      if (shouldFieldBeIgnored(fieldNode)) continue;

      AnnotationNode annotation = getAnnotation(fieldNode, DSL_FIELD_ANNOTATION);

      if (annotation == null) continue;

      if (ASTHelper.isListOrMap(fieldNode.getType())) return;

      if (annotation.getMember("members") != null) {
        addCompileError(
            String.format(
                "@Field.members is only valid for List or Map fields, but field %s is of type %s",
                fieldNode.getName(), fieldNode.getType().getName()),
            annotation);
      }
    }
  }
 boolean makeGetField(
     final Expression receiver,
     final ClassNode receiverType,
     final String fieldName,
     final boolean implicitThis,
     final boolean samePackage) {
   FieldNode field = receiverType.getField(fieldName);
   // direct access is allowed if we are in the same class as the declaring class
   // or we are in an inner class
   if (field != null && isDirectAccessAllowed(field, controller.getClassNode(), samePackage)) {
     CompileStack compileStack = controller.getCompileStack();
     MethodVisitor mv = controller.getMethodVisitor();
     if (field.isStatic()) {
       mv.visitFieldInsn(
           GETSTATIC,
           BytecodeHelper.getClassInternalName(field.getOwner()),
           fieldName,
           BytecodeHelper.getTypeDescription(field.getOriginType()));
       controller.getOperandStack().push(field.getOriginType());
     } else {
       if (implicitThis) {
         compileStack.pushImplicitThis(implicitThis);
       }
       receiver.visit(controller.getAcg());
       if (implicitThis) compileStack.popImplicitThis();
       if (!controller.getOperandStack().getTopOperand().isDerivedFrom(field.getOwner())) {
         mv.visitTypeInsn(CHECKCAST, BytecodeHelper.getClassInternalName(field.getOwner()));
       }
       mv.visitFieldInsn(
           GETFIELD,
           BytecodeHelper.getClassInternalName(field.getOwner()),
           fieldName,
           BytecodeHelper.getTypeDescription(field.getOriginType()));
     }
     controller.getOperandStack().replace(field.getOriginType());
     return true;
   }
   ClassNode superClass = receiverType.getSuperClass();
   if (superClass != null) {
     return makeGetField(receiver, superClass, fieldName, implicitThis, false);
   }
   return false;
 }
  private FieldNode getKeyField(ClassNode target) {

    List<FieldNode> annotatedFields = getAnnotatedFieldsOfHierarchy(target, KEY_ANNOTATION);

    if (annotatedFields.isEmpty()) return null;

    if (annotatedFields.size() > 1) {
      addCompileError(
          String.format(
              "Found more than one key fields, only one is allowed in hierarchy (%s, %s)",
              getQualifiedName(annotatedFields.get(0)), getQualifiedName(annotatedFields.get(1))),
          annotatedFields.get(0));
      return null;
    }

    FieldNode result = annotatedFields.get(0);

    if (!result.getType().equals(ClassHelper.STRING_TYPE)) {
      addCompileError(
          String.format(
              "Key field '%s' must be of type String, but is '%s' instead",
              result.getName(), result.getType().getName()),
          result);
      return null;
    }

    ClassNode ancestor = ASTHelper.getHighestAncestorDSLObject(target);

    if (target.equals(ancestor)) return result;

    FieldNode firstKey = getKeyField(ancestor);

    if (firstKey == null) {
      addCompileError(
          String.format(
              "Inconsistent hierarchy: Toplevel class %s has no key, but child class %s defines '%s'.",
              ancestor.getName(), target.getName(), result.getName()),
          result);
      return null;
    }

    return result;
  }
  private void validateFields(BlockStatement block) {
    Validation.Option mode =
        getEnumMemberValue(
            getAnnotation(annotatedClass, VALIDATION_ANNOTATION),
            "option",
            Validation.Option.class,
            Validation.Option.IGNORE_UNMARKED);
    for (FieldNode fieldNode : annotatedClass.getFields()) {
      if (shouldFieldBeIgnoredForValidation(fieldNode)) continue;

      ClosureExpression validationClosure =
          createGroovyTruthClosureExpression(block.getVariableScope());
      String message = null;

      AnnotationNode validateAnnotation = getAnnotation(fieldNode, VALIDATE_ANNOTATION);
      if (validateAnnotation != null) {
        message =
            getMemberStringValue(
                validateAnnotation, "message", "'" + fieldNode.getName() + "' must be set!");
        Expression member = validateAnnotation.getMember("value");
        if (member instanceof ClassExpression) {
          ClassNode memberType = member.getType();
          if (memberType.equals(ClassHelper.make(Validate.Ignore.class))) continue;
          else if (!memberType.equals(ClassHelper.make(Validate.GroovyTruth.class))) {
            addError(
                "value of Validate must be either Validate.GroovyTruth, Validate.Ignore or a closure.",
                fieldNode);
          }
        } else if (member instanceof ClosureExpression) {
          validationClosure = (ClosureExpression) member;
        }
      }

      if (validateAnnotation != null || mode == Validation.Option.VALIDATE_UNMARKED) {
        block.addStatement(
            new AssertStatement(
                new BooleanExpression(
                    callX(validationClosure, "call", args(varX(fieldNode.getName())))),
                message == null ? ConstantExpression.NULL : new ConstantExpression(message)));
      }
    }
  }
 private void createKeyConstructor() {
   annotatedClass.addConstructor(
       ACC_PUBLIC,
       params(param(STRING_TYPE, "key")),
       NO_EXCEPTIONS,
       block(
           ASTHelper.isDSLObject(annotatedClass.getSuperClass())
               ? ctorSuperS(args("key"))
               : ctorSuperS(),
           assignS(propX(varX("this"), keyField.getName()), varX("key"))));
 }
 @SuppressWarnings("unchecked")
 private boolean makeGetPrivateFieldWithBridgeMethod(
     final Expression receiver,
     final ClassNode receiverType,
     final String fieldName,
     final boolean safe,
     final boolean implicitThis) {
   FieldNode field = receiverType.getField(fieldName);
   ClassNode classNode = controller.getClassNode();
   if (field != null
       && Modifier.isPrivate(field.getModifiers())
       && (StaticInvocationWriter.isPrivateBridgeMethodsCallAllowed(receiverType, classNode)
           || StaticInvocationWriter.isPrivateBridgeMethodsCallAllowed(classNode, receiverType))
       && !receiverType.equals(classNode)) {
     Map<String, MethodNode> accessors =
         (Map<String, MethodNode>)
             receiverType
                 .redirect()
                 .getNodeMetaData(StaticCompilationMetadataKeys.PRIVATE_FIELDS_ACCESSORS);
     if (accessors != null) {
       MethodNode methodNode = accessors.get(fieldName);
       if (methodNode != null) {
         MethodCallExpression mce =
             new MethodCallExpression(
                 receiver,
                 methodNode.getName(),
                 new ArgumentListExpression(
                     field.isStatic() ? new ConstantExpression(null) : receiver));
         mce.setMethodTarget(methodNode);
         mce.setSafe(safe);
         mce.setImplicitThis(implicitThis);
         mce.visit(controller.getAcg());
         return true;
       }
     }
   }
   return false;
 }
  private String getElementNameForCollectionField(FieldNode fieldNode) {
    AnnotationNode fieldAnnotation = getAnnotation(fieldNode, DSL_FIELD_ANNOTATION);

    String result = getNullSafeMemberStringValue(fieldAnnotation, "members", null);

    if (result != null && result.length() > 0) return result;

    String collectionMethodName = fieldNode.getName();

    if (collectionMethodName.endsWith("s"))
      return collectionMethodName.substring(0, collectionMethodName.length() - 1);

    return collectionMethodName;
  }
  /** Adds special accessors for private constants so that inner classes can retrieve them. */
  @SuppressWarnings("unchecked")
  private void addPrivateFieldsAccessors(ClassNode node) {
    Set<ASTNode> accessedFields =
        (Set<ASTNode>) node.getNodeMetaData(StaticTypesMarker.PV_FIELDS_ACCESS);
    if (accessedFields == null) return;
    Map<String, MethodNode> privateConstantAccessors =
        (Map<String, MethodNode>) node.getNodeMetaData(PRIVATE_FIELDS_ACCESSORS);
    if (privateConstantAccessors != null) {
      // already added
      return;
    }
    int acc = -1;
    privateConstantAccessors = new HashMap<String, MethodNode>();
    final int access = Opcodes.ACC_STATIC | Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC;
    for (FieldNode fieldNode : node.getFields()) {
      if (accessedFields.contains(fieldNode)) {

        acc++;
        Parameter param = new Parameter(node.getPlainNodeReference(), "$that");
        Expression receiver =
            fieldNode.isStatic() ? new ClassExpression(node) : new VariableExpression(param);
        Statement stmt =
            new ExpressionStatement(new PropertyExpression(receiver, fieldNode.getName()));
        MethodNode accessor =
            node.addMethod(
                "pfaccess$" + acc,
                access,
                fieldNode.getOriginType(),
                new Parameter[] {param},
                ClassNode.EMPTY_ARRAY,
                stmt);
        privateConstantAccessors.put(fieldNode.getName(), accessor);
      }
    }
    node.setNodeMetaData(PRIVATE_FIELDS_ACCESSORS, privateConstantAccessors);
  }
 private void createCanonicalMethods() {
   if (!hasAnnotation(annotatedClass, EQUALS_HASHCODE_ANNOT)) {
     createHashCode(annotatedClass, false, false, true, null, null);
     createEquals(annotatedClass, false, true, true, null, null);
   }
   if (!hasAnnotation(annotatedClass, TOSTRING_ANNOT)) {
     if (ownerField == null) createToString(annotatedClass, false, false, null, null, false);
     else
       createToString(
           annotatedClass,
           false,
           false,
           Collections.singletonList(ownerField.getName()),
           null,
           false);
   }
 }
  private void createSingleFieldSetterMethod(FieldNode fieldNode) {
    MethodBuilder.createPublicMethod(fieldNode.getName())
        .param(fieldNode.getType(), "value")
        .assignToProperty(fieldNode.getName(), varX("value"))
        .addTo(annotatedClass);

    if (fieldNode.getType().equals(ClassHelper.boolean_TYPE)) {
      MethodBuilder.createPublicMethod(fieldNode.getName())
          .callThis(fieldNode.getName(), constX(true))
          .addTo(annotatedClass);
    }
  }
  private void createListOfSimpleElementsMethods(FieldNode fieldNode, ClassNode elementType) {

    MethodBuilder.createPublicMethod(fieldNode.getName())
        .arrayParam(elementType, "values")
        .statement(callX(propX(varX("this"), fieldNode.getName()), "addAll", varX("values")))
        .addTo(annotatedClass);

    MethodBuilder.createPublicMethod(fieldNode.getName())
        .param(fieldNode.getType(), "values")
        .statement(callX(propX(varX("this"), fieldNode.getName()), "addAll", varX("values")))
        .addTo(annotatedClass);

    MethodBuilder.createPublicMethod(getElementNameForCollectionField(fieldNode))
        .param(elementType, "value")
        .statement(callX(propX(varX("this"), fieldNode.getName()), "add", varX("value")))
        .addTo(annotatedClass);
  }
 private String getQualifiedName(FieldNode node) {
   return node.getOwner().getName() + "." + node.getName();
 }
 private void initializeField(FieldNode fieldNode, Expression init) {
   if (!fieldNode.hasInitialExpression()) fieldNode.setInitialValueExpression(init);
 }
 protected Object scrubField(ClassNode cnOrig, ClassNode cnRepl, FieldNode fnRepl) {
   if (fnRepl.desc.equals(cnRepl.name)) {
     fnRepl.desc = fnRepl.desc.replace(cnRepl.name, cnOrig.name);
   }
   return fnRepl;
 }
  private void createListOfDSLObjectMethods(FieldNode fieldNode, ClassNode elementType) {
    String methodName = getElementNameForCollectionField(fieldNode);

    FieldNode fieldKey = getKeyField(elementType);
    String targetOwner = getOwnerFieldName(elementType);

    MethodBuilder.createPublicMethod(fieldNode.getName())
        .closureParam("closure")
        .assignS(propX(varX("closure"), "delegate"), varX("this"))
        .assignS(
            propX(varX("closure"), "resolveStrategy"),
            propX(classX(ClassHelper.CLOSURE_TYPE), "DELEGATE_FIRST"))
        .callMethod("closure", "call")
        .addTo(annotatedClass);

    if (!ASTHelper.isAbstract(elementType)) {
      MethodBuilder.createPublicMethod(methodName)
          .returning(elementType)
          .namedParams("values")
          .optionalStringParam("key", fieldKey)
          .delegatingClosureParam(elementType)
          .declareVariable(
              "created", callX(classX(elementType), "newInstance", optionalKeyArg(fieldKey)))
          .callMethod("created", "copyFromTemplate")
          .optionalAssignThisToPropertyS("created", targetOwner, targetOwner)
          .callMethod(
              fieldNode.getName(),
              "add",
              callX(varX("created"), "apply", args("values", "closure")))
          .callValidationOn("created")
          .doReturn("created")
          .addTo(annotatedClass);
      MethodBuilder.createPublicMethod(methodName)
          .returning(elementType)
          .optionalStringParam("key", fieldKey)
          .delegatingClosureParam(elementType)
          .declareVariable(
              "created", callX(classX(elementType), "newInstance", optionalKeyArg(fieldKey)))
          .callMethod("created", "copyFromTemplate")
          .optionalAssignThisToPropertyS("created", targetOwner, targetOwner)
          .callMethod(fieldNode.getName(), "add", callX(varX("created"), "apply", varX("closure")))
          .callValidationOn("created")
          .doReturn("created")
          .addTo(annotatedClass);
    }

    if (!isFinal(elementType)) {
      MethodBuilder.createPublicMethod(methodName)
          .returning(elementType)
          .namedParams("values")
          .classParam("typeToCreate", elementType)
          .optionalStringParam("key", fieldKey)
          .delegatingClosureParam(elementType)
          .declareVariable(
              "created", callX(varX("typeToCreate"), "newInstance", optionalKeyArg(fieldKey)))
          .callMethod("created", "copyFromTemplate")
          .optionalAssignThisToPropertyS("created", targetOwner, targetOwner)
          .callMethod(
              fieldNode.getName(),
              "add",
              callX(varX("created"), "apply", args("values", "closure")))
          .callValidationOn("created")
          .doReturn("created")
          .addTo(annotatedClass);
      MethodBuilder.createPublicMethod(methodName)
          .returning(elementType)
          .classParam("typeToCreate", elementType)
          .optionalStringParam("key", fieldKey)
          .delegatingClosureParam(elementType)
          .declareVariable(
              "created", callX(varX("typeToCreate"), "newInstance", optionalKeyArg(fieldKey)))
          .callMethod("created", "copyFromTemplate")
          .optionalAssignThisToPropertyS("created", targetOwner, targetOwner)
          .callMethod(fieldNode.getName(), "add", callX(varX("created"), "apply", varX("closure")))
          .callValidationOn("created")
          .doReturn("created")
          .addTo(annotatedClass);
    }

    MethodBuilder.createPublicMethod(methodName)
        .param(elementType, "value")
        .callMethod(fieldNode.getName(), "add", varX("value"))
        .optionalAssignThisToPropertyS("value", targetOwner, targetOwner)
        .addTo(annotatedClass);
  }
 private String setterName(FieldNode node) {
   char[] name = node.getName().toCharArray();
   name[0] = Character.toUpperCase(name[0]);
   return "set" + new String(name);
 }
  private void createMapOfDSLObjectMethods(
      FieldNode fieldNode, ClassNode keyType, ClassNode elementType) {
    if (getKeyField(elementType) == null) {
      addCompileError(
          String.format(
              "Value type of map %s (%s) has no key field",
              fieldNode.getName(), elementType.getName()),
          fieldNode);
      return;
    }

    MethodBuilder.createPublicMethod(fieldNode.getName())
        .closureParam("closure")
        .assignS(propX(varX("closure"), "delegate"), varX("this"))
        .assignS(
            propX(varX("closure"), "resolveStrategy"),
            propX(classX(ClassHelper.CLOSURE_TYPE), "DELEGATE_FIRST"))
        .callMethod("closure", "call")
        .addTo(annotatedClass);

    String methodName = getElementNameForCollectionField(fieldNode);
    String targetOwner = getOwnerFieldName(elementType);

    if (!ASTHelper.isAbstract(elementType)) {
      MethodBuilder.createPublicMethod(methodName)
          .returning(elementType)
          .namedParams("values")
          .param(keyType, "key")
          .delegatingClosureParam(elementType)
          .declareVariable("created", callX(classX(elementType), "newInstance", args("key")))
          .callMethod("created", "copyFromTemplate")
          .optionalAssignThisToPropertyS("created", targetOwner, targetOwner)
          .callMethod(fieldNode.getName(), "put", args(varX("key"), varX("created")))
          .callMethod("created", "apply", args("values", "closure"))
          .callValidationOn("created")
          .doReturn("created")
          .addTo(annotatedClass);
      MethodBuilder.createPublicMethod(methodName)
          .returning(elementType)
          .param(keyType, "key")
          .delegatingClosureParam(elementType)
          .declareVariable("created", callX(classX(elementType), "newInstance", args("key")))
          .callMethod("created", "copyFromTemplate")
          .optionalAssignThisToPropertyS("created", targetOwner, targetOwner)
          .callMethod(fieldNode.getName(), "put", args(varX("key"), varX("created")))
          .callMethod("created", "apply", varX("closure"))
          .callValidationOn("created")
          .doReturn("created")
          .addTo(annotatedClass);
    }

    if (!isFinal(elementType)) {
      MethodBuilder.createPublicMethod(methodName)
          .returning(elementType)
          .namedParams("values")
          .classParam("typeToCreate", elementType)
          .param(keyType, "key")
          .delegatingClosureParam(elementType)
          .declareVariable("created", callX(varX("typeToCreate"), "newInstance", args("key")))
          .callMethod("created", "copyFromTemplate")
          .callMethod(fieldNode.getName(), "put", args(varX("key"), varX("created")))
          .optionalAssignThisToPropertyS("created", targetOwner, targetOwner)
          .callMethod("created", "apply", args("values", "closure"))
          .callValidationOn("created")
          .doReturn("created")
          .addTo(annotatedClass);
      MethodBuilder.createPublicMethod(methodName)
          .returning(elementType)
          .classParam("typeToCreate", elementType)
          .param(keyType, "key")
          .delegatingClosureParam(elementType)
          .declareVariable("created", callX(varX("typeToCreate"), "newInstance", args("key")))
          .callMethod("created", "copyFromTemplate")
          .callMethod(fieldNode.getName(), "put", args(varX("key"), varX("created")))
          .optionalAssignThisToPropertyS("created", targetOwner, targetOwner)
          .callMethod("created", "apply", varX("closure"))
          .callValidationOn("created")
          .doReturn("created")
          .addTo(annotatedClass);
    }

    //noinspection ConstantConditions
    MethodBuilder.createPublicMethod(methodName)
        .param(elementType, "value")
        .callMethod(
            fieldNode.getName(),
            "put",
            args(propX(varX("value"), getKeyField(elementType).getName()), varX("value")))
        .optionalAssignThisToPropertyS("value", targetOwner, targetOwner)
        .addTo(annotatedClass);
  }
 private String getOwnerFieldName(ClassNode target) {
   FieldNode ownerFieldOfElement = getOwnerField(target);
   return ownerFieldOfElement != null ? ownerFieldOfElement.getName() : null;
 }
  private void createSingleDSLObjectClosureMethod(FieldNode fieldNode) {
    String methodName = fieldNode.getName();

    ClassNode targetFieldType = fieldNode.getType();
    FieldNode targetTypeKeyField = getKeyField(targetFieldType);
    String targetOwnerFieldName = getOwnerFieldName(targetFieldType);

    if (!ASTHelper.isAbstract(targetFieldType)) {
      MethodBuilder.createPublicMethod(methodName)
          .returning(targetFieldType)
          .namedParams("values")
          .optionalStringParam("key", targetTypeKeyField)
          .delegatingClosureParam(targetFieldType)
          .declareVariable(
              "created",
              callX(classX(targetFieldType), "newInstance", optionalKeyArg(targetTypeKeyField)))
          .callMethod("created", "copyFromTemplate")
          .optionalAssignThisToPropertyS("created", targetOwnerFieldName, targetOwnerFieldName)
          .assignToProperty(
              fieldNode.getName(), callX(varX("created"), "apply", args("values", "closure")))
          .callValidationOn("created")
          .doReturn("created")
          .addTo(annotatedClass);

      MethodBuilder.createPublicMethod(methodName)
          .returning(targetFieldType)
          .optionalStringParam("key", targetTypeKeyField)
          .delegatingClosureParam(targetFieldType)
          .declareVariable(
              "created",
              callX(classX(targetFieldType), "newInstance", optionalKeyArg(targetTypeKeyField)))
          .callMethod("created", "copyFromTemplate")
          .optionalAssignThisToPropertyS("created", targetOwnerFieldName, targetOwnerFieldName)
          .assignToProperty(fieldNode.getName(), callX(varX("created"), "apply", varX("closure")))
          .callValidationOn("created")
          .doReturn("created")
          .addTo(annotatedClass);
    }

    if (!isFinal(targetFieldType)) {
      MethodBuilder.createPublicMethod(methodName)
          .returning(targetFieldType)
          .namedParams("values")
          .classParam("typeToCreate", targetFieldType)
          .optionalStringParam("key", targetTypeKeyField)
          .delegatingClosureParam(targetFieldType)
          .declareVariable(
              "created",
              callX(varX("typeToCreate"), "newInstance", optionalKeyArg(targetTypeKeyField)))
          .callMethod("created", "copyFromTemplate")
          .optionalAssignThisToPropertyS("created", targetOwnerFieldName, targetOwnerFieldName)
          .assignToProperty(
              fieldNode.getName(), callX(varX("created"), "apply", args("values", "closure")))
          .callValidationOn("created")
          .doReturn("created")
          .addTo(annotatedClass);

      MethodBuilder.createPublicMethod(methodName)
          .returning(targetFieldType)
          .classParam("typeToCreate", targetFieldType)
          .optionalStringParam("key", targetTypeKeyField)
          .delegatingClosureParam(targetFieldType)
          .declareVariable(
              "created",
              callX(varX("typeToCreate"), "newInstance", optionalKeyArg(targetTypeKeyField)))
          .callMethod("created", "copyFromTemplate")
          .optionalAssignThisToPropertyS("created", targetOwnerFieldName, targetOwnerFieldName)
          .assignToProperty(fieldNode.getName(), callX(varX("created"), "apply", varX("closure")))
          .callValidationOn("created")
          .doReturn("created")
          .addTo(annotatedClass);
    }
  }