private Statement createConstructorStatement(
     ClassNode cNode,
     PropertyNode pNode,
     List<String> knownImmutableClasses,
     List<String> knownImmutables) {
   FieldNode fNode = pNode.getField();
   final ClassNode fieldType = fNode.getType();
   Statement statement = null;
   if (fieldType.isArray() || isOrImplements(fieldType, CLONEABLE_TYPE)) {
     statement = createConstructorStatementArrayOrCloneable(fNode);
   } else if (isKnownImmutableClass(fieldType, knownImmutableClasses)
       || isKnownImmutable(pNode.getName(), knownImmutables)) {
     statement = createConstructorStatementDefault(fNode);
   } else if (fieldType.isDerivedFrom(DATE_TYPE)) {
     statement = createConstructorStatementDate(fNode);
   } else if (isOrImplements(fieldType, COLLECTION_TYPE)
       || fieldType.isDerivedFrom(COLLECTION_TYPE)
       || isOrImplements(fieldType, MAP_TYPE)
       || fieldType.isDerivedFrom(MAP_TYPE)) {
     statement = createConstructorStatementCollection(fNode);
   } else if (fieldType.isResolved()) {
     addError(
         createErrorMessage(cNode.getName(), fNode.getName(), fieldType.getName(), "compiling"),
         fNode);
     statement = EmptyStatement.INSTANCE;
   } else {
     statement = createConstructorStatementGuarded(cNode, fNode);
   }
   return statement;
 }
 private Statement createConstructorStatementMapSpecial(FieldNode fNode) {
   final Expression fieldExpr = new VariableExpression(fNode);
   Expression initExpr = fNode.getInitialValueExpression();
   if (initExpr == null) initExpr = new ConstantExpression(null);
   Expression namedArgs = findArg(fNode.getName());
   Expression baseArgs = new VariableExpression("args");
   return new IfStatement(
       equalsNullExpr(baseArgs),
       new IfStatement(
           equalsNullExpr(initExpr),
           new EmptyStatement(),
           assignStatement(fieldExpr, cloneCollectionExpr(initExpr))),
       new IfStatement(
           equalsNullExpr(namedArgs),
           new IfStatement(
               isTrueExpr(
                   new MethodCallExpression(
                       baseArgs, "containsKey", new ConstantExpression(fNode.getName()))),
               assignStatement(fieldExpr, namedArgs),
               assignStatement(fieldExpr, cloneCollectionExpr(baseArgs))),
           new IfStatement(
               isOneExpr(
                   new MethodCallExpression(baseArgs, "size", MethodCallExpression.NO_ARGUMENTS)),
               assignStatement(fieldExpr, cloneCollectionExpr(namedArgs)),
               assignStatement(fieldExpr, cloneCollectionExpr(baseArgs)))));
 }
 private Statement createConstructorStatementDate(FieldNode fNode) {
   final Expression fieldExpr = new VariableExpression(fNode);
   Expression initExpr = fNode.getInitialValueExpression();
   if (initExpr == null) initExpr = new ConstantExpression(null);
   final Expression date = findArg(fNode.getName());
   return new IfStatement(
       equalsNullExpr(date),
       new IfStatement(
           equalsNullExpr(initExpr),
           assignStatement(fieldExpr, new ConstantExpression(null)),
           assignStatement(fieldExpr, cloneDateExpr(initExpr))),
       assignStatement(fieldExpr, cloneDateExpr(date)));
 }
 private Statement createConstructorStatementGuarded(ClassNode cNode, FieldNode fNode) {
   final Expression fieldExpr = new VariableExpression(fNode);
   Expression initExpr = fNode.getInitialValueExpression();
   if (initExpr == null) initExpr = new ConstantExpression(null);
   Expression unknown = findArg(fNode.getName());
   return new IfStatement(
       equalsNullExpr(unknown),
       new IfStatement(
           equalsNullExpr(initExpr),
           new EmptyStatement(),
           assignStatement(fieldExpr, checkUnresolved(cNode, fNode, initExpr))),
       assignStatement(fieldExpr, checkUnresolved(cNode, fNode, unknown)));
 }
 private Statement createConstructorStatementArrayOrCloneable(FieldNode fNode) {
   final Expression fieldExpr = new VariableExpression(fNode);
   Expression initExpr = fNode.getInitialValueExpression();
   ClassNode fieldType = fNode.getType();
   if (initExpr == null) initExpr = new ConstantExpression(null);
   final Expression array = findArg(fNode.getName());
   return new IfStatement(
       equalsNullExpr(array),
       new IfStatement(
           equalsNullExpr(initExpr),
           assignStatement(fieldExpr, new ConstantExpression(null)),
           assignStatement(fieldExpr, cloneArrayOrCloneableExpr(initExpr, fieldType))),
       assignStatement(fieldExpr, cloneArrayOrCloneableExpr(array, fieldType)));
 }
 private void addProperty(ClassNode cNode, PropertyNode pNode) {
   final FieldNode fn = pNode.getField();
   cNode.getFields().remove(fn);
   cNode.addProperty(
       pNode.getName(),
       pNode.getModifiers() | ACC_FINAL,
       pNode.getType(),
       pNode.getInitialExpression(),
       pNode.getGetterBlock(),
       pNode.getSetterBlock());
   final FieldNode newfn = cNode.getField(fn.getName());
   cNode.getFields().remove(newfn);
   cNode.addField(fn);
 }
 private void ensureNotPublic(String cNode, FieldNode fNode) {
   String fName = fNode.getName();
   // TODO: do we need to lock down things like: $ownClass
   if (fNode.isPublic() && !fName.contains("$") && !(fNode.isStatic() && fNode.isFinal())) {
     addError(
         "Public field '"
             + fName
             + "' not allowed for "
             + MY_TYPE_NAME
             + " class '"
             + cNode
             + "'.",
         fNode);
   }
 }
 private Statement createConstructorStatementCollection(FieldNode fNode) {
   final Expression fieldExpr = new VariableExpression(fNode);
   Expression initExpr = fNode.getInitialValueExpression();
   if (initExpr == null) initExpr = new ConstantExpression(null);
   Expression collection = findArg(fNode.getName());
   ClassNode fieldType = fieldExpr.getType();
   return new IfStatement(
       equalsNullExpr(collection),
       new IfStatement(
           equalsNullExpr(initExpr),
           new EmptyStatement(),
           assignStatement(fieldExpr, cloneCollectionExpr(initExpr))),
       new IfStatement(
           isInstanceOf(collection, CLONEABLE_TYPE),
           assignStatement(
               fieldExpr, cloneCollectionExpr(cloneArrayOrCloneableExpr(collection, fieldType))),
           assignStatement(fieldExpr, cloneCollectionExpr(collection))));
 }
 private Expression checkUnresolved(ClassNode cNode, FieldNode fNode, Expression value) {
   Expression args =
       new TupleExpression(
           new MethodCallExpression(
               new VariableExpression("this"), "getClass", ArgumentListExpression.EMPTY_ARGUMENTS),
           new ConstantExpression(fNode.getName()),
           value);
   return new StaticMethodCallExpression(SELF_TYPE, "checkImmutable", args);
 }
 private Statement checkFinalArgNotOverridden(ClassNode cNode, FieldNode fNode) {
   final String name = fNode.getName();
   Expression value = findArg(name);
   return new IfStatement(
       equalsNullExpr(value),
       new EmptyStatement(),
       new ThrowStatement(
           new ConstructorCallExpression(
               READONLYEXCEPTION_TYPE,
               new ArgumentListExpression(
                   new ConstantExpression(name), new ConstantExpression(cNode.getName())))));
 }
 private Statement createGetterBody(FieldNode fNode) {
   BlockStatement body = new BlockStatement();
   final ClassNode fieldType = fNode.getType();
   final Statement statement;
   if (fieldType.isArray() || isOrImplements(fieldType, CLONEABLE_TYPE)) {
     statement = createGetterBodyArrayOrCloneable(fNode);
   } else if (fieldType.isDerivedFrom(DATE_TYPE)) {
     statement = createGetterBodyDate(fNode);
   } else {
     statement = createGetterBodyDefault(fNode);
   }
   body.addStatement(statement);
   return body;
 }
 private void createConstructorMapCommon(ClassNode cNode, BlockStatement body) {
   final List<FieldNode> fList = cNode.getFields();
   for (FieldNode fNode : fList) {
     if (fNode.isPublic()) continue; // public fields will be rejected elsewhere
     if (cNode.getProperty(fNode.getName()) != null) continue; // a property
     if (fNode.isFinal() && fNode.isStatic()) continue;
     if (fNode.getName().contains("$")) continue; // internal field
     if (fNode.isFinal() && fNode.getInitialExpression() != null)
       body.addStatement(checkFinalArgNotOverridden(cNode, fNode));
     body.addStatement(createConstructorStatementDefault(fNode));
   }
   final Parameter[] params = new Parameter[] {new Parameter(HASHMAP_TYPE, "args")};
   doAddConstructor(
       cNode,
       new ConstructorNode(
           ACC_PUBLIC,
           params,
           ClassNode.EMPTY_ARRAY,
           new IfStatement(
               equalsNullExpr(new VariableExpression("args")), new EmptyStatement(), body)));
 }
 private Statement createGetterBodyArrayOrCloneable(FieldNode fNode) {
   final Expression fieldExpr = new VariableExpression(fNode);
   final Expression expression = cloneArrayOrCloneableExpr(fieldExpr, fNode.getType());
   return safeExpression(fieldExpr, expression);
 }
 private void adjustPropertyForImmutability(PropertyNode pNode, List<PropertyNode> newNodes) {
   final FieldNode fNode = pNode.getField();
   fNode.setModifiers((pNode.getModifiers() & (~ACC_PUBLIC)) | ACC_FINAL | ACC_PRIVATE);
   adjustPropertyNode(pNode, createGetterBody(fNode));
   newNodes.add(pNode);
 }