private void processMethods(List<ExecutableElement> methodsElt) { Map<String, ExecutableElement> getters = new HashMap<>(); Map<String, ExecutableElement> setters = new HashMap<>(); Map<String, ExecutableElement> adders = new HashMap<>(); while (methodsElt.size() > 0) { ExecutableElement methodElt = methodsElt.remove(0); if (((TypeElement) methodElt.getEnclosingElement()) .getQualifiedName() .toString() .equals("java.lang.Object")) { continue; } String methodName = methodElt.getSimpleName().toString(); if (methodName.startsWith("get") && methodName.length() > 3 && Character.isUpperCase(methodName.charAt(3)) && methodElt.getParameters().isEmpty() && methodElt.getReturnType().getKind() != TypeKind.VOID) { String name = Helper.normalizePropertyName(methodName.substring(3)); getters.put(name, methodElt); } else if (methodName.startsWith("is") && methodName.length() > 2 && Character.isUpperCase(methodName.charAt(2)) && methodElt.getParameters().isEmpty() && methodElt.getReturnType().getKind() != TypeKind.VOID) { String name = Helper.normalizePropertyName(methodName.substring(2)); getters.put(name, methodElt); } else if ((methodName.startsWith("set") || methodName.startsWith("add")) && methodName.length() > 3 && Character.isUpperCase(methodName.charAt(3))) { String prefix = methodName.substring(0, 3); String name = Helper.normalizePropertyName(methodName.substring(3)); int numParams = methodElt.getParameters().size(); if ("add".equals(prefix)) { if (name.endsWith("s")) { throw new GenException(methodElt, "Option adder name must not terminate with 's' char"); } else { name += "s"; } TypeMirror t = methodElt.getParameters().get(0).asType(); if (numParams == 1 || (numParams == 2 && t.getKind() == TypeKind.DECLARED && ((TypeElement) ((DeclaredType) t).asElement()) .getQualifiedName() .toString() .equals("java.lang.String"))) { adders.put(name, methodElt); } } else { if (numParams == 1) { setters.put(name, methodElt); } } } } Set<String> names = new HashSet<>(); names.addAll(getters.keySet()); names.addAll(setters.keySet()); names.addAll(adders.keySet()); for (String name : names) { processMethod(name, getters.get(name), setters.get(name), adders.get(name)); } }
private void processMethod( String name, ExecutableElement getterElt, ExecutableElement setterElt, ExecutableElement adderElt) { PropertyKind propKind = null; TypeInfo propType = null; TypeMirror propTypeMirror = null; // if (setterElt != null) { VariableElement paramElt = setterElt.getParameters().get(0); propTypeMirror = paramElt.asType(); propType = typeFactory.create(propTypeMirror); propKind = PropertyKind.forType(propType.getKind()); switch (propKind) { case LIST: case SET: propType = ((ParameterizedTypeInfo) propType).getArgs().get(0); propTypeMirror = ((DeclaredType) propTypeMirror).getTypeArguments().get(0); break; case MAP: propType = ((ParameterizedTypeInfo) propType).getArgs().get(1); propTypeMirror = ((DeclaredType) propTypeMirror).getTypeArguments().get(1); break; } } // if (getterElt != null) { TypeMirror getterTypeMirror = getterElt.getReturnType(); TypeInfo getterType = typeFactory.create(getterTypeMirror); PropertyKind getterKind = PropertyKind.forType(getterType.getKind()); switch (getterKind) { case LIST: case SET: getterType = ((ParameterizedTypeInfo) getterType).getArgs().get(0); getterTypeMirror = ((DeclaredType) getterTypeMirror).getTypeArguments().get(0); break; case MAP: getterType = ((ParameterizedTypeInfo) getterType).getArgs().get(1); getterTypeMirror = ((DeclaredType) getterTypeMirror).getTypeArguments().get(1); break; } if (propType != null) { if (propKind != getterKind) { throw new GenException( getterElt, name + " getter " + getterKind + " does not match the setter " + propKind); } if (!getterType.equals(propType)) { throw new GenException( getterElt, name + " getter type " + getterType + " does not match the setter type " + propType); } } else { propTypeMirror = getterTypeMirror; propType = getterType; propKind = getterKind; } } // if (adderElt != null) { switch (adderElt.getParameters().size()) { case 1: { VariableElement paramElt = adderElt.getParameters().get(0); TypeMirror adderTypeMirror = paramElt.asType(); TypeInfo adderType = typeFactory.create(adderTypeMirror); if (propTypeMirror != null) { if (propKind != PropertyKind.LIST && propKind != PropertyKind.SET) { throw new GenException( adderElt, name + "adder does not correspond to non list/set"); } if (!adderType.equals(propType)) { throw new GenException( adderElt, name + " adder type " + adderType + " does not match the property type " + propType); } } else { propTypeMirror = adderTypeMirror; propType = adderType; propKind = PropertyKind.LIST; } break; } case 2: { VariableElement paramElt = adderElt.getParameters().get(1); TypeMirror adderTypeMirror = paramElt.asType(); TypeInfo adderType = typeFactory.create(adderTypeMirror); if (propTypeMirror != null) { if (propKind != PropertyKind.MAP) { throw new GenException(adderElt, name + "adder does not correspond to non map"); } if (!adderType.equals(propType)) { throw new GenException( adderElt, name + " adder type " + adderType + " does not match the property type " + propType); } } else { propTypeMirror = adderTypeMirror; propType = adderType; propKind = PropertyKind.MAP; } break; } } } // boolean jsonifiable; switch (propType.getKind()) { case OBJECT: if (propKind == PropertyKind.VALUE) { return; } case PRIMITIVE: case BOXED_PRIMITIVE: case STRING: case API: case JSON_OBJECT: case JSON_ARRAY: case ENUM: jsonifiable = true; break; case DATA_OBJECT: Element propTypeElt = typeUtils.asElement(propTypeMirror); jsonifiable = propTypeElt.getAnnotation(DataObject.class) == null || Helper.isJsonifiable(elementUtils, typeUtils, (TypeElement) propTypeElt); break; default: return; } boolean declared = false; Doc doc = null; for (ExecutableElement methodElt : Arrays.asList(setterElt, adderElt, getterElt)) { if (methodElt != null) { // A stream that list all overriden methods from super types // the boolean control whether or not we want to filter only annotated // data objects Function<Boolean, Stream<ExecutableElement>> overridenMeths = (annotated) -> { Set<DeclaredType> ancestorTypes = Helper.resolveAncestorTypes(modelElt, true, true); return ancestorTypes .stream() .map(DeclaredType::asElement) .filter(elt -> !annotated || elt.getAnnotation(DataObject.class) != null) .flatMap(Helper.cast(TypeElement.class)) .flatMap(elt -> elementUtils.getAllMembers(elt).stream()) .flatMap(Helper.instanceOf(ExecutableElement.class)) .filter( executableElt -> executableElt.getKind() == ElementKind.METHOD && elementUtils.overrides(methodElt, executableElt, modelElt)); }; // if (doc == null) { doc = docFactory.createDoc(methodElt); if (doc == null) { Optional<Doc> first = overridenMeths .apply(false) .map(docFactory::createDoc) .filter(d -> d != null) .findFirst(); doc = first.orElse(null); } } // if (!declared) { Element ownerElt = methodElt.getEnclosingElement(); if (ownerElt.equals(modelElt)) { Object[] arr = overridenMeths .apply(true) .limit(1) .filter(elt -> !elt.getModifiers().contains(Modifier.ABSTRACT)) .toArray(); // Handle the case where this methods overrides from another data object declared = arr.length == 0; } else { declared = ownerElt.getAnnotation(DataObject.class) == null; } } } } PropertyInfo property = new PropertyInfo( declared, name, doc, propType, setterElt != null ? setterElt.getSimpleName().toString() : null, adderElt != null ? adderElt.getSimpleName().toString() : null, getterElt != null ? getterElt.getSimpleName().toString() : null, propKind, jsonifiable); propertyMap.put(property.name, property); }
private void traverse() { DataObject ann = modelElt.getAnnotation(DataObject.class); this.generateConverter = ann.generateConverter(); this.inheritConverter = ann.inheritConverter(); this.isClass = modelElt.getKind() == ElementKind.CLASS; this.concrete = isClass && !modelElt.getModifiers().contains(Modifier.ABSTRACT); try { this.type = (ClassTypeInfo) typeFactory.create(modelElt.asType()); } catch (ClassCastException e) { throw new GenException( modelElt, "Data object must be a plain java class with no type parameters"); } Helper.checkUnderModule(this, "@VertxGen"); doc = docFactory.createDoc(modelElt); if (getModule() == null) { throw new GenException( modelElt, "Data object must have an ancestor package annotated with @ModuleGen"); } modelElt .getInterfaces() .stream() .filter( superTM -> superTM instanceof DeclaredType && ((DeclaredType) superTM).asElement().getAnnotation(DataObject.class) != null) .map(e -> (ClassTypeInfo) typeFactory.create(e)) .forEach(abstractSuperTypes::add); superTypes.addAll(abstractSuperTypes); TypeMirror superClass = modelElt.getSuperclass(); if (superClass instanceof DeclaredType && ((DeclaredType) superClass).asElement().getAnnotation(DataObject.class) != null) { superType = (ClassTypeInfo) typeFactory.create(superClass); superTypes.add(superType); } int result = 0; List<ExecutableElement> methodsElt = new ArrayList<>(); for (Element enclosedElt : elementUtils.getAllMembers(modelElt)) { switch (enclosedElt.getKind()) { case CONSTRUCTOR: ExecutableElement constrElt = (ExecutableElement) enclosedElt; result |= processConstructor(constrElt); break; case METHOD: { ExecutableElement methodElt = (ExecutableElement) enclosedElt; if (methodElt.getSimpleName().toString().equals("toJson") && methodElt.getParameters().isEmpty() && typeFactory.create(methodElt.getReturnType()).getKind() == ClassKind.JSON_OBJECT) { jsonifiable = true; } if (methodElt.getAnnotation(GenIgnore.class) == null) { methodsElt.add(methodElt); } break; } } } processMethods(methodsElt); boolean hasJsonConstructor = (result & 2) == 2; if (concrete && !hasJsonConstructor) { throw new GenException( modelElt, "Data object " + modelElt + " class does not have a constructor " + modelElt.getSimpleName() + "(" + JsonObject.class.getSimpleName() + ")"); } // Sort the properties so we do have a consistent order ArrayList<PropertyInfo> props = new ArrayList<>(propertyMap.values()); Collections.sort(props, (p1, p2) -> p1.name.compareTo(p2.name)); propertyMap.clear(); props.forEach(prop -> propertyMap.put(prop.name, prop)); }