//	private AnnotationValue get(Map<String, AnnotationValue> values, String name, AnnotationValue
  // def) {
  //		AnnotationValue value = values.get(name);
  //		if (value != null)
  //			return value;
  //		return def;
  //	}
  private void processProperties(Thing data, Map<String, AnnotationValue> beanValues) {
    boolean atLeastOneBound = false;
    boolean atLeastOneDouble = false;
    boolean atLeastOneObject = false;
    String firstPropertyName = null;
    AnnotationValue propertiesValue = beanValues.get("properties");
    if (propertiesValue == null) {
      data.setEmpty("properties");
    } else {
      Set<String> propertyNames = new HashSet<String>();
      AnnotationValue defaultType = null;
      AnnotationValue defaultTypeString = null;
      AnnotationValue defaultKeyType = null;
      AnnotationValue defaultKeyTypeString = null;
      AnnotationValue defaultReader = null;
      AnnotationValue defaultWriter = null;
      AnnotationValue defaultBound = null;
      AnnotationValue defaultKind = null;
      AnnotationValue defaultOmitFromToString = null;
      AnnotationValue defaultNotNull = null;
      AnnotationValue defaultIsStatic = null;
      AnnotationValue defaultIsSynchronized = null;

      @SuppressWarnings("unchecked")
      List<AnnotationValue> properties = (List<AnnotationValue>) propertiesValue.getValue();
      for (AnnotationValue annotationValue : properties) {
        AnnotationMirror propertyMirror = (AnnotationMirror) annotationValue.getValue();
        Map<String, AnnotationValue> propertyValues = getAnnotationValues(propertyMirror);
        AnnotationValue name = propertyValues.get("name");
        AnnotationValue plural = propertyValues.get("plural");
        AnnotationValue type = propertyValues.get("type");
        AnnotationValue typeString = propertyValues.get("typeString");
        AnnotationValue keyType = propertyValues.get("keyType");
        AnnotationValue keyTypeString = propertyValues.get("keyTypeString");
        AnnotationValue reader = propertyValues.get("reader");
        AnnotationValue writer = propertyValues.get("writer");
        AnnotationValue bound = propertyValues.get("bound");
        AnnotationValue kind = propertyValues.get("kind");
        AnnotationValue omitFromToString = propertyValues.get("omitFromToString");
        AnnotationValue notNull = propertyValues.get("notNull");
        AnnotationValue isStatic = propertyValues.get("isStatic");
        AnnotationValue isSynchronized = propertyValues.get("isSynchronized");

        if (Property.DEFAULTS.equals(name.getValue())) {
          defaultType = type;
          defaultTypeString = typeString;
          defaultKeyType = keyType;
          defaultKeyTypeString = keyTypeString;
          defaultReader = reader;
          defaultWriter = writer;
          defaultBound = bound;
          defaultKind = kind;
          defaultOmitFromToString = omitFromToString;
          defaultNotNull = notNull;
          defaultIsStatic = isStatic;
          defaultIsSynchronized = isSynchronized;
          continue;
        }

        // plugin the default values
        if (type == null && typeString == null) {
          type = defaultType;
          typeString = defaultTypeString;
        }
        if (keyType == null && keyTypeString == null) {
          keyType = defaultKeyType;
          keyTypeString = defaultKeyTypeString;
        }
        if (reader == null) {
          reader = defaultReader;
        }
        if (writer == null) {
          writer = defaultWriter;
        }
        if (bound == null) {
          bound = defaultBound;
        }
        if (kind == null) {
          kind = defaultKind;
        }
        if (omitFromToString == null) {
          omitFromToString = defaultOmitFromToString;
        }
        if (notNull == null) {
          notNull = defaultNotNull;
        }
        if (isStatic == null) {
          isStatic = defaultIsStatic;
        }
        if (isSynchronized == null) {
          isSynchronized = defaultIsSynchronized;
        }

        Thing property = new Thing("property");
        property.put("name", name.getValue());

        if (typeString == null) {
          if (type == null) {
            property.put("type", "java.lang.String");
          } else {
            if (type.getValue() instanceof TypeDeclaration) {
              property.put("type", ((TypeDeclaration) type.getValue()).getQualifiedName());
            } else {
              property.put("type", ((PrimitiveType) type.getValue()).toString());
            }
          }
        } else {
          if (type != null) {
            String message = "@Property cannot have both type and typeString attributes specified";
            error(typeString, message);
            error(type, message);
            property.put("type", "<ERROR>");
          } else {
            property.put("type", typeString.getValue());
          }
        }

        PropertyKind propertyKind = PropertyKind.SIMPLE;
        if (kind != null) {
          propertyKind = PropertyKind.valueOf(kind.getValue().toString());
        }

        // check for duplicate keytype specifications
        if (propertyKind.isMap()) {
          if (keyTypeString == null) {
            if (keyType == null) {
              property.put("keyType", "java.lang.String");
            } else {
              property.put("keyType", ((TypeDeclaration) keyType.getValue()).getQualifiedName());
            }
          } else if (keyType != null) {
            String message =
                "@Property cannot have both keyType and keyTypeString attributes specified";
            error(keyType, message);
            error(keyTypeString, message);
            property.put("keyType", "<ERROR>");
          }
        } else {
          if (keyTypeString != null) {
            error(
                keyTypeString,
                "@Property can only have a keyTypeString attribute if kind is MAP or UNMODIFIABLE_MAP");
          }
          if (keyType != null) {
            error(
                keyType,
                "@Property can only have a keyType attribute if kind is MAP or UNMODIFIABLE_MAP");
          }
          property.put("keyType", "<ERROR>");
        }

        // check for plural names
        if (propertyKind.isSimple()) {
          if (plural != null) {
            error(plural, "@Property cannot have plural specified if kind is SIMPLE");
          }
          property.put("pluralName", null);
        } else {
          if (plural == null) {
            property.put("pluralName", name.getValue() + "s");
          } else {
            property.put("pluralName", plural.getValue());
          }
        }

        property.put("simple", propertyKind.isSimple());
        property.put("list", propertyKind.isList());
        property.put("map", propertyKind.isMap());
        property.put("set", propertyKind.isSet());
        String typeName = (String) property.get("type");

        property.put("float", "float".equals(typeName));
        property.put("double", "double".equals(typeName));
        property.put("boolean", "boolean".equals(typeName));
        property.put("char", "char".equals(typeName));
        property.put("byte", "byte".equals(typeName));
        property.put("long", "long".equals(typeName));
        property.put("int", "int".equals(typeName));
        property.put("short", "short".equals(typeName));

        if (property.containsKey("firstPropertyName")) {
          property.put("firstPropertyName", name.getValue());
        }
        if (propertyNames.contains(name.getValue())) {
          error(
              name,
              "Duplicate property name '" + name + "' specified for @Bean properties definition");
        } else {
          propertyNames.add((String) name.getValue());
        }

        if (bound != null) {
          if (isStatic != null) {
            error(bound, "Static properties cannot be declared bound");
            error(isStatic, "Static properties cannot be declared bound");
          } else {
            atLeastOneBound = true;
          }
        }

        if ("double".equals(property.get("type"))) {
          data.put("atLeastOneDouble", true);
        }
        property.put("kind", propertyKind);
        property.put("omitFromToString", b(omitFromToString));
        data.add("properties", property);

        // evil hack to get the type, which is a Class

        boolean isPrimitive =
            BeanAnnotationProcessor.PRIMITIVE_TYPES.contains(property.get("type"));
        property.put("primitive", isPrimitive);

        if (!isPrimitive) {
          data.put("atLeastOneObject", true);
        }

        property.put("bound", b(bound));
        if (writer == null) {
          property.put("writerAccess", Access.PUBLIC.getModifier());
          property.put("writeable", true);
        } else {
          EnumConstantDeclaration writerValue = (EnumConstantDeclaration) writer.getValue();
          Access writerAccess = Access.valueOf(writerValue.toString());
          property.put("writerAccess", writerAccess.getModifier());
          property.put("writeable", writerAccess != Access.NONE);
        }
        if (reader == null) {
          property.put("readerAccess", Access.PUBLIC.getModifier());
          property.put("readable", true);
        } else {
          EnumConstantDeclaration readerValue = (EnumConstantDeclaration) reader.getValue();
          Access readerAccess = Access.valueOf(readerValue.toString());
          property.put("readerAccess", readerAccess.getModifier());
          property.put("readable", readerAccess != Access.NONE);
        }

        boolean bNotNull = b(notNull);
        boolean bIsStatic = b(isStatic);
        boolean bIsSynchronized = b(isSynchronized);
        property.put("notNull", bNotNull);

        if (bNotNull && isPrimitive) {
          error(
              notNull,
              "Cannot specify notNull for primitive-typed property "
                  + name.getValue()
                  + " in @Property");
        }
        String extraFieldKeywords = "";
        String extraMethodKeywords = "";
        if (bIsStatic) {
          extraFieldKeywords = "static ";
          extraMethodKeywords = "static ";
        }
        if (bIsSynchronized) {
          if (bIsStatic) {
            extraMethodKeywords += "synchronized ";
          } else {
            extraMethodKeywords = "synchronized ";
          }
        }
        property.put("extraFieldKeywords", extraFieldKeywords);
        property.put("extraMethodKeywords", extraMethodKeywords);
        property.checkAllValuesSet(propertiesValue, this);
      }
    }
    data.put(
        "definePropertyChangeSupport",
        !((Boolean) data.get("getPropertyChangeSupportInherited")) && atLeastOneBound);
    data.put("atLeastOneDouble", atLeastOneDouble);
    data.put("atLeastOneObject", atLeastOneObject);
    data.put("firstPropertyName", firstPropertyName);
  }
 private Thing setupMethod(ExecutableDeclaration declaration, boolean forceNonAbstract) {
   String name = null;
   String returnType = null;
   boolean isAbstract = false;
   String nullBody = null;
   String throwsClause = "";
   if (declaration instanceof ConstructorDeclaration) {
     name = "<init>";
   } else {
     MethodDeclaration methodDeclaration = (MethodDeclaration) declaration;
     name = methodDeclaration.getSimpleName();
     returnType = methodDeclaration.getReturnType().toString();
     if (!forceNonAbstract) {
       isAbstract = methodDeclaration.getModifiers().contains(Modifier.ABSTRACT);
     }
     if ("void".equals(returnType)) {
       nullBody = "// null object implementation; do nothing";
     } else if ("boolean".equals(returnType)) {
       nullBody = "false; // null object implementation";
     } else if ("char".equals(returnType)) {
       nullBody = "' '; // null object implementation";
     } else if (BeanAnnotationProcessor.PRIMITIVE_TYPES.contains(returnType)) {
       nullBody = "0; // null object implementation";
     } else {
       nullBody = "null; // null object implementation";
     }
   }
   Collection<ReferenceType> thrownTypes = declaration.getThrownTypes();
   if (!thrownTypes.isEmpty()) {
     throwsClause = "throws " + commaSeparate(thrownTypes);
   }
   String genericDecls = null;
   String argDecls = null;
   String args = null;
   String modifiers = "";
   Collection<ParameterDeclaration> parameters = declaration.getParameters();
   for (ParameterDeclaration parameterDeclaration : parameters) {
     argDecls =
         addWithCommasBetween(
             argDecls,
             parameterDeclaration.getType() + " " + parameterDeclaration.getSimpleName());
     args = addWithCommasBetween(args, parameterDeclaration.getSimpleName());
   }
   Collection<TypeParameterDeclaration> formalTypeParameters =
       declaration.getFormalTypeParameters();
   for (TypeParameterDeclaration typeParameterDeclaration : formalTypeParameters) {
     genericDecls = addWithCommasBetween(genericDecls, typeParameterDeclaration);
   }
   Collection<Modifier> modifiers2 = declaration.getModifiers();
   for (Modifier modifier : modifiers2) {
     if (!"abstract".equals(modifier.toString())) {
       modifiers += modifier.toString() + ' ';
     }
   }
   if (genericDecls == null) {
     genericDecls = "";
   } else {
     genericDecls = '<' + genericDecls + "> ";
   }
   Thing method = new Thing("method");
   method.put("name", name);
   method.put("returnType", returnType);
   method.put("abstract", isAbstract);
   method.put("nullBody", nullBody);
   method.put("modifiers", modifiers); // do not normalize; already "" or has ' ' at end
   method.put("argDecls", normalizeList(argDecls));
   method.put("args", normalizeList(args));
   method.put("genericDecls", genericDecls);
   method.put("throwsClause", throwsClause);
   return method;
 }