Search in sources :

Example 1 with BuilderContext

use of io.sundr.builder.internal.BuilderContext in project sundrio by sundrio.

the class BuildableProcessor method process.

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
    Elements elements = processingEnv.getElementUtils();
    Types types = processingEnv.getTypeUtils();
    Filer filer = processingEnv.getFiler();
    BuilderContext ctx = null;
    // First pass register all buildables
    Set<TypeDef> buildables = new HashSet<>();
    for (TypeElement typeElement : annotations) {
        for (Element element : env.getElementsAnnotatedWith(typeElement)) {
            Buildable buildable = element.getAnnotation(Buildable.class);
            if (buildable == null) {
                continue;
            }
            AptContext aptContext = AptContext.create(elements, types, DefinitionRepository.getRepository());
            ctx = BuilderContextManager.create(elements, types, buildable.validationEnabled(), buildable.generateBuilderPackage(), buildable.builderPackage());
            TypeDef b = new TypeDefBuilder(Adapters.adaptType(Apt.getClassElement(element), aptContext)).addToAttributes(BUILDABLE, buildable).addToAttributes(EDITABLE_ENABLED, buildable.editableEnabled()).addToAttributes(VALIDATION_ENABLED, buildable.validationEnabled()).accept(new Visitor<PropertyBuilder>() {

                @Override
                public void visit(PropertyBuilder builder) {
                    builder.addToAttributes(LAZY_COLLECTIONS_INIT_ENABLED, buildable.lazyCollectionInitEnabled());
                    builder.addToAttributes(LAZY_MAP_INIT_ENABLED, buildable.lazyMapInitEnabled());
                }
            }).build();
            ctx.getDefinitionRepository().register(b);
            ctx.getBuildableRepository().register(b);
            buildables.add(b);
            for (TypeElement ref : BuilderUtils.getBuildableReferences(ctx, buildable)) {
                TypeDef r = new TypeDefBuilder(Adapters.adaptType(Apt.getClassElement(ref), aptContext)).addToAttributes(BUILDABLE, buildable).addToAttributes(EDITABLE_ENABLED, buildable.editableEnabled()).addToAttributes(VALIDATION_ENABLED, buildable.validationEnabled()).accept(new Visitor<PropertyBuilder>() {

                    @Override
                    public void visit(PropertyBuilder builder) {
                        builder.addToAttributes(LAZY_COLLECTIONS_INIT_ENABLED, buildable.lazyCollectionInitEnabled());
                        builder.addToAttributes(LAZY_MAP_INIT_ENABLED, buildable.lazyMapInitEnabled());
                    }
                }).build();
                ctx.getDefinitionRepository().register(r);
                ctx.getBuildableRepository().register(r);
                buildables.add(r);
            }
        }
    }
    if (ctx == null) {
        return true;
    }
    generateLocalDependenciesIfNeeded();
    ctx.getDefinitionRepository().updateReferenceMap();
    generateBuildables(ctx, buildables);
    generatePojos(ctx, buildables);
    processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, String.format("%-120s", "100%: Builder generation complete."));
    return false;
}
Also used : SupportedAnnotationTypes(javax.annotation.processing.SupportedAnnotationTypes) Types(javax.lang.model.util.Types) AptContext(io.sundr.adapter.apt.AptContext) Visitor(io.sundr.builder.Visitor) TypeElement(javax.lang.model.element.TypeElement) TypeElement(javax.lang.model.element.TypeElement) Element(javax.lang.model.element.Element) Elements(javax.lang.model.util.Elements) TypeDefBuilder(io.sundr.model.TypeDefBuilder) TypeDef(io.sundr.model.TypeDef) BuilderContext(io.sundr.builder.internal.BuilderContext) Filer(javax.annotation.processing.Filer) Buildable(io.sundr.builder.annotations.Buildable) HashSet(java.util.HashSet) PropertyBuilder(io.sundr.model.PropertyBuilder)

Example 2 with BuilderContext

use of io.sundr.builder.internal.BuilderContext in project sundrio by sundrio.

the class ExternalBuildableProcessor method process.

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
    Elements elements = processingEnv.getElementUtils();
    Types types = processingEnv.getTypeUtils();
    Filer filer = processingEnv.getFiler();
    BuilderContext ctx = null;
    Set<TypeDef> buildables = new HashSet<>();
    // First pass register all externals
    for (TypeElement annotation : annotations) {
        for (Element element : env.getElementsAnnotatedWith(annotation)) {
            final ExternalBuildables generated = element.getAnnotation(ExternalBuildables.class);
            if (generated == null) {
                continue;
            }
            ctx = BuilderContextManager.create(elements, types, generated.validationEnabled(), generated.generateBuilderPackage(), generated.builderPackage());
            for (String name : generated.value()) {
                PackageElement packageElement = elements.getPackageElement(name);
                List<TypeElement> typeElements = new ArrayList<>();
                if (packageElement != null) {
                    for (Element e : packageElement.getEnclosedElements()) {
                        if (e instanceof TypeElement) {
                            typeElements.add((TypeElement) e);
                        }
                    }
                } else {
                    TypeElement e = elements.getTypeElement(name);
                    if (e != null) {
                        typeElements.add(e);
                    }
                }
                for (TypeElement typeElement : typeElements) {
                    final boolean isLazyCollectionInitEnabled = generated.lazyCollectionInitEnabled();
                    final boolean isLazyMapInitEnabled = generated.lazyMapInitEnabled();
                    final boolean includeInterfaces = generated.includeInterfaces();
                    final boolean includeAbstractClasses = generated.includeAbstractClasses();
                    AptContext aptContext = AptContext.create(ctx.getElements(), ctx.getTypes(), ctx.getDefinitionRepository());
                    TypeDef original = Adapters.adaptType(typeElement, aptContext);
                    String fqcn = original.getFullyQualifiedName();
                    boolean isBuildable = original.getKind() != Kind.ENUM && (includeAbstractClasses || !original.isAbstract()) && (includeInterfaces || original.getKind() != Kind.INTERFACE) && isIncluded(fqcn, generated.includes()) && !isExcluded(fqcn, generated.excludes());
                    TypeDef b = new TypeDefBuilder(original).accept(new Visitor<PropertyBuilder>() {

                        @Override
                        public void visit(PropertyBuilder builder) {
                            if (isBuildable) {
                                builder.addToAttributes(EXTERNAL_BUILDABLE, generated);
                                builder.addToAttributes(EDITABLE_ENABLED, generated.editableEnabled());
                                builder.addToAttributes(VALIDATION_ENABLED, generated.validationEnabled());
                                builder.addToAttributes(LAZY_COLLECTIONS_INIT_ENABLED, isLazyCollectionInitEnabled);
                                builder.addToAttributes(LAZY_MAP_INIT_ENABLED, isLazyMapInitEnabled);
                            }
                        }
                    }).build();
                    if (b.getKind() == Kind.ENUM) {
                        continue;
                    }
                    if (b.isAbstract() && !includeAbstractClasses) {
                        continue;
                    }
                    if (b.getKind() == Kind.INTERFACE && !includeInterfaces) {
                        continue;
                    }
                    if (!isIncluded(b.getFullyQualifiedName(), generated.includes())) {
                        continue;
                    }
                    if (isExcluded(b.getFullyQualifiedName(), generated.excludes())) {
                        continue;
                    }
                    ctx.getDefinitionRepository().register(b);
                    ctx.getBuildableRepository().register(b);
                    buildables.add(b);
                }
            }
            for (TypeElement ref : BuilderUtils.getBuildableReferences(ctx, generated)) {
                final boolean isLazyCollectionInitEnabled = generated.lazyCollectionInitEnabled();
                final boolean isLazyMapInitEnabled = generated.lazyMapInitEnabled();
                final boolean includeInterfaces = generated.includeInterfaces();
                final boolean includeAbstractClasses = generated.includeAbstractClasses();
                AptContext aptContext = AptContext.create(ctx.getElements(), ctx.getTypes(), ctx.getDefinitionRepository());
                TypeDef original = Adapters.adaptType(Apt.getClassElement(ref), aptContext);
                String fqcn = original.getFullyQualifiedName();
                boolean isBuildable = original.getKind() != Kind.ENUM && !original.isAbstract() && isIncluded(fqcn, generated.includes()) && !isExcluded(fqcn, generated.excludes());
                TypeDef r = new TypeDefBuilder(original).accept(new Visitor<PropertyBuilder>() {

                    @Override
                    public void visit(PropertyBuilder builder) {
                        if (isBuildable) {
                            builder.addToAttributes(EXTERNAL_BUILDABLE, generated);
                            builder.addToAttributes(EDITABLE_ENABLED, generated.editableEnabled());
                            builder.addToAttributes(VALIDATION_ENABLED, generated.validationEnabled());
                            builder.addToAttributes(LAZY_COLLECTIONS_INIT_ENABLED, isLazyCollectionInitEnabled);
                            builder.addToAttributes(LAZY_MAP_INIT_ENABLED, isLazyMapInitEnabled);
                        }
                    }
                }).build();
                if (r.getKind() == Kind.ENUM || r.isAbstract()) {
                    continue;
                }
                if (r.getKind() == Kind.ENUM) {
                    continue;
                }
                if (r.isAbstract() && !includeAbstractClasses) {
                    continue;
                }
                if (r.getKind() == Kind.INTERFACE && !includeInterfaces) {
                    continue;
                }
                if (!isIncluded(r.getFullyQualifiedName(), generated.includes())) {
                    continue;
                }
                if (isExcluded(r.getFullyQualifiedName(), generated.excludes())) {
                    continue;
                }
                ctx.getDefinitionRepository().register(r);
                ctx.getBuildableRepository().register(r);
                buildables.add(r);
            }
        }
    }
    if (ctx == null) {
        return true;
    }
    generateLocalDependenciesIfNeeded();
    ctx.getDefinitionRepository().updateReferenceMap();
    generateBuildables(ctx, buildables);
    generatePojos(ctx, buildables);
    processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, String.format("%-120s", "100%: Builder generation complete."));
    return true;
}
Also used : SupportedAnnotationTypes(javax.annotation.processing.SupportedAnnotationTypes) Types(javax.lang.model.util.Types) AptContext(io.sundr.adapter.apt.AptContext) Visitor(io.sundr.builder.Visitor) ExternalBuildables(io.sundr.builder.annotations.ExternalBuildables) TypeElement(javax.lang.model.element.TypeElement) PackageElement(javax.lang.model.element.PackageElement) TypeElement(javax.lang.model.element.TypeElement) Element(javax.lang.model.element.Element) ArrayList(java.util.ArrayList) Elements(javax.lang.model.util.Elements) TypeDefBuilder(io.sundr.model.TypeDefBuilder) TypeDef(io.sundr.model.TypeDef) BuilderContext(io.sundr.builder.internal.BuilderContext) PackageElement(javax.lang.model.element.PackageElement) Filer(javax.annotation.processing.Filer) HashSet(java.util.HashSet) PropertyBuilder(io.sundr.model.PropertyBuilder)

Example 3 with BuilderContext

use of io.sundr.builder.internal.BuilderContext in project sundrio by sundrio.

the class BuilderUtils method toEquals.

public static List<Statement> toEquals(TypeDef type, Collection<Property> properties) {
    List<Statement> statements = new ArrayList<>();
    String simpleName = type.getName();
    ClassRef superClass = type.getExtendsList().isEmpty() ? TypeDef.OBJECT_REF : type.getExtendsList().iterator().next();
    statements.add(new StringStatement("if (this == o) return true;"));
    statements.add(new StringStatement("if (o == null || getClass() != o.getClass()) return false;"));
    // If base fluent is the superclass just skip.
    final BuilderContext context = BuilderContextManager.getContext();
    final String superClassFQN = superClass.getFullyQualifiedName();
    if (!context.getBaseFluentClass().getFullyQualifiedName().equals(superClassFQN) && !OBJECT_FULLY_QUALIFIED_NAME.equals(superClassFQN)) {
        statements.add(new StringStatement("if (!super.equals(o)) return false;"));
    }
    statements.add(new StringStatement(new StringBuilder().append(simpleName).append(" that = (").append(simpleName).append(") o;").toString()));
    for (Property property : properties) {
        String name = property.getName();
        if (Types.isPrimitive(property.getTypeRef())) {
            statements.add(new StringStatement(new StringBuilder().append("if (").append(name).append(" != ").append("that.").append(name).append(") return false;").toString()));
        } else if (property.getTypeRef() instanceof ClassRef && Descendants.isDescendant(type, GetDefinition.of((ClassRef) property.getTypeRef()))) {
            statements.add(new StringStatement(new StringBuilder().append("if (").append(name).append(" != null &&").append(name).append(" != this ? !").append(name).append(".equals(that.").append(name).append(") :").append("that.").append(name).append(" != null &&").append(name).append(" != this ) return false;").append("\n").toString()));
        } else {
            statements.add(new StringStatement(new StringBuilder().append("if (").append(name).append(" != null ? !").append(name).append(".equals(that.").append(name).append(") :").append("that.").append(name).append(" != null) return false;").toString()));
        }
    }
    statements.add(new StringStatement("return true;"));
    return statements;
}
Also used : ClassRef(io.sundr.model.ClassRef) StringStatement(io.sundr.model.StringStatement) Statement(io.sundr.model.Statement) ArrayList(java.util.ArrayList) BuilderContext(io.sundr.builder.internal.BuilderContext) StringStatement(io.sundr.model.StringStatement) Property(io.sundr.model.Property)

Example 4 with BuilderContext

use of io.sundr.builder.internal.BuilderContext in project sundrio by sundrio.

the class ClazzAs method toBuild.

private static List<Statement> toBuild(final TypeDef clazz, final TypeDef instanceType) {
    Method constructor = findBuildableConstructor(clazz);
    List<Statement> statements = new ArrayList<Statement>();
    statements.add(new StringStatement(new StringBuilder().append(instanceType.getName()).append(" buildable = new ").append(instanceType.getName()).append("(").append(Strings.join(constructor.getArguments(), new Function<Property, String>() {

        public String apply(Property item) {
            return "fluent." + Getter.name(item) + "()";
        }
    }, ",")).append(");").toString()));
    TypeDef target = clazz;
    List<TypeDef> parents = new ArrayList<TypeDef>();
    Types.visitParents(target, parents);
    for (TypeDef c : parents) {
        if (!isBuildable(c)) {
            continue;
        }
        for (Property property : c.getProperties()) {
            Method setter;
            try {
                setter = Setter.find(clazz, property);
            } catch (SundrException e) {
                // no setter found nothing to set
                continue;
            }
            if (!hasBuildableConstructorWithArgument(c, property)) {
                String getterName = Getter.name(property);
                statements.add(new StringStatement(new StringBuilder().append("buildable.").append(setter.getName()).append("(fluent.").append(getterName).append("());").toString()));
            }
        }
    }
    BuilderContext context = BuilderContextManager.getContext();
    if (context.isExternalvalidatorSupported()) {
        statements.add(new StringStatement("if (validationEnabled) {" + context.getBuilderPackage() + ".ValidationUtils.validate(buildable, validator);}"));
    } else if (context.isValidationEnabled()) {
        statements.add(new StringStatement("if (validationEnabled) {" + context.getBuilderPackage() + ".ValidationUtils.validate(buildable);}"));
    }
    statements.add(new StringStatement("return buildable;"));
    return statements;
}
Also used : StringStatement(io.sundr.model.StringStatement) Statement(io.sundr.model.Statement) ArrayList(java.util.ArrayList) Method(io.sundr.model.Method) StringStatement(io.sundr.model.StringStatement) Function(java.util.function.Function) RichTypeDef(io.sundr.model.RichTypeDef) TypeDef(io.sundr.model.TypeDef) BuilderContext(io.sundr.builder.internal.BuilderContext) Property(io.sundr.model.Property) SundrException(io.sundr.SundrException)

Example 5 with BuilderContext

use of io.sundr.builder.internal.BuilderContext in project sundrio by sundrio.

the class ToPojo method apply.

public TypeDef apply(RichTypeDef item) {
    List<Property> arguments = new CopyOnWriteArrayList<>();
    List<Property> fields = new CopyOnWriteArrayList<>();
    Map<String, Property> superClassFields = new HashMap<>();
    List<Method> getters = new CopyOnWriteArrayList<>();
    List<Method> additionalMethods = new ArrayList<>();
    List<Property> constructorArgs = new ArrayList<>();
    List<TypeDef> types = new ArrayList<TypeDef>();
    Types.visitParents(item, types);
    TypeDef superClass = null;
    Set<ClassRef> extendsList = new HashSet<>();
    Set<ClassRef> implementsList = new HashSet<>();
    List<ClassRef> additionalImports = new ArrayList<>();
    List<AnnotationRef> annotationRefs = new ArrayList<>();
    String pojoName = Strings.toPojoName(item.getName(), "Default", "");
    String relativePath = ".";
    AnnotationRef pojoRef = null;
    boolean enableStaticBuilder = true;
    boolean enableStaticAdapter = true;
    boolean enableStaticMapAdapter = false;
    boolean autobox = false;
    boolean initialize = false;
    boolean mutable = false;
    final List adapters = new ArrayList();
    for (AnnotationRef r : item.getAnnotations()) {
        if (r.getClassRef() != null) {
            if (r.getClassRef().getFullyQualifiedName().equals(Buildable.class.getTypeName())) {
                if (!annotationRefs.contains(r)) {
                    annotationRefs.add(r);
                }
            }
            if (r.getClassRef().getFullyQualifiedName().equals(Pojo.class.getTypeName())) {
                pojoRef = r;
                Map<String, Object> params = r.getParameters();
                if (params.containsKey("mutable")) {
                    mutable = Boolean.parseBoolean(String.valueOf(r.getParameters().getOrDefault("mutable", false)));
                }
                if (params.containsKey("autobox")) {
                    autobox = Boolean.parseBoolean(String.valueOf(r.getParameters().getOrDefault("autobox", false)));
                }
                if (params.containsKey("initialize")) {
                    initialize = Boolean.parseBoolean(String.valueOf(r.getParameters().getOrDefault("initialize", false)));
                }
                if (params.containsKey("name")) {
                    pojoName = String.valueOf(r.getParameters().getOrDefault("name", pojoName));
                } else if (params.containsKey("prefix") || params.containsKey("suffix")) {
                    String prefix = String.valueOf(r.getParameters().getOrDefault("prefix", ""));
                    String suffix = String.valueOf(r.getParameters().getOrDefault("suffix", ""));
                    pojoName = Strings.toPojoName(item.getName(), prefix, suffix);
                } else if (params.containsKey("relativePath")) {
                    // When the package is different and there is no name clash, just use the same name unless explicitly specified.
                    pojoName = item.getName();
                }
                if (params.containsKey("adapter")) {
                    Object adapter = params.get("adapter");
                    if (adapter != null && adapter.getClass().isArray()) {
                        int length = Array.getLength(adapter);
                        for (int i = 0; i < length; i++) {
                            adapters.add(Array.get(adapter, i));
                        }
                    }
                }
                String superClassName = Types.toClassName(r.getParameters().getOrDefault("superClass", ""));
                if (!superClassName.isEmpty()) {
                    superClassName = superClassName.replaceAll("\\.class$", "");
                    superClass = DefinitionRepository.getRepository().getDefinition(superClassName);
                    if (superClass == null) {
                        BuilderContext context = BuilderContextManager.getContext();
                        AptContext aptContext = AptContext.create(context.getElements(), context.getTypes(), context.getDefinitionRepository());
                        superClass = new TypeDefBuilder(Adapters.adaptType(aptContext.getElements().getTypeElement(superClassName), aptContext.getAdapterContext())).build();
                        BuilderContextManager.getContext().getDefinitionRepository().register(superClass);
                        BuilderContextManager.getContext().getBuildableRepository().register(superClass);
                    }
                    if (superClass != null) {
                        ClassRef superClassRef = superClass.toInternalReference();
                        extendsList.add(superClassRef);
                        BuilderContextManager.getContext().getBuildableRepository().register(GetDefinition.of(superClassRef));
                        BuilderUtils.findBuildableReferences(superClassRef).stream().forEach(b -> BuilderContextManager.getContext().getBuildableRepository().register(GetDefinition.of(b)));
                    }
                }
                if (item.isInterface()) {
                    implementsList.add(item.toInternalReference());
                }
                Arrays.asList(r.getParameters().getOrDefault("interfaces", new Object[] {})).stream().map(String::valueOf).map(s -> s.replaceAll("\\.class$", "")).map(n -> DefinitionRepository.getRepository().getDefinition(n)).filter(d -> d != null).map(d -> d.toInternalReference()).forEach(ref -> implementsList.add(ref));
                if (params.containsKey("relativePath")) {
                    relativePath = String.valueOf(r.getParameters().getOrDefault("relativePath", "."));
                }
                enableStaticBuilder = !"false".equals(String.valueOf(params.get("withStaticBuilderMethod")));
                enableStaticAdapter = !"false".equals(String.valueOf(params.get("withStaticAdapterMethod")));
                enableStaticMapAdapter = !"false".equals(String.valueOf(params.get("withStaticMapAdapterMethod")));
            }
        }
    }
    Set<TypeDef> additionalBuildables = new HashSet<>();
    Set<TypeDef> additionalTypes = new HashSet<>();
    boolean shouldBeAbstract = false;
    for (TypeDef t : types) {
        if (superClass != null) {
            Method constructor = findBuildableConstructor(superClass);
            if (constructor != null) {
                for (Property p : constructor.getArguments()) {
                    String name = Strings.toFieldName(p.getName());
                    superClassFields.put(p.getName(), p);
                }
            }
        }
        if (t.isInterface() && !Annotation.class.getName().equals(t.getFullyQualifiedName())) {
            implementsList.add(t.toInternalReference());
        }
        for (Method method : t.getMethods()) {
            // Ignore static methods and methods with arguments.
            if (method.isStatic() || !method.getArguments().isEmpty()) {
                continue;
            }
            // We need all getters and all annotation methods.
            boolean isAnnotation = t.isAnnotation() && t.equals(item);
            if (Getter.is(method) || isAnnotation) {
                String name = isAnnotation ? method.getName() : Getter.propertyName(method);
                TypeRef returnType = method.getReturnType();
                if (autobox) {
                    returnType = Types.box(returnType);
                }
                // If return type is an annotation also convert the annotation.
                if (method.getReturnType() instanceof ClassRef) {
                    ClassRef ref = (ClassRef) method.getReturnType();
                    if (GetDefinition.of(ref).isAnnotation()) {
                        AnnotationRef inheritedPojoRef = (pojoRef != null ? new AnnotationRefBuilder(pojoRef) : new AnnotationRefBuilder()).removeFromParameters("name").removeFromParameters("superClass").removeFromParameters("interfaces").build();
                        TypeDef p = hasPojoAnnotation(GetDefinition.of(ref)) ? POJO.apply(TypeArguments.apply(GetDefinition.of(ref))) : POJO.apply(TypeArguments.apply(new TypeDefBuilder(GetDefinition.of(ref)).withAnnotations(annotationRefs).addToAnnotations(inheritedPojoRef).withAttributes(item.getAttributes()).build()));
                        additionalBuildables.add(p);
                        // create a reference and apply dimension
                        returnType = new ClassRefBuilder(p.toInternalReference()).withDimensions(ref.getDimensions()).build();
                    }
                }
                Map<AttributeKey, Object> fieldAttributes = new HashMap<>();
                if (method.hasAttribute(DEFAULT_VALUE)) {
                    if (returnType.getDimensions() > 0 || (mutable && initialize)) {
                        fieldAttributes.put(DEFAULT_VALUE, method.getAttribute(DEFAULT_VALUE));
                        fieldAttributes.put(INIT, getDefaultValue(new PropertyBuilder().withTypeRef(returnType).withAttributes(fieldAttributes).build()));
                    }
                }
                // For arguments we need to retain all the original attributes as they affect adapters.
                Property arg = new PropertyBuilder().withName(name).withTypeRef(returnType).withModifiers(Types.modifiersToInt()).withAttributes(method.getAttributes()).build();
                arguments.add(arg);
                // Let's also update superClassFields, so that we reatins default values.
                if (superClassFields.containsKey(name)) {
                    superClassFields.put(name, arg);
                }
                if (!superClassFields.containsKey(Strings.toFieldName(name))) {
                    Property field = new PropertyBuilder().withName(Strings.toFieldName(name)).withTypeRef(returnType).withModifiers(mutable ? Types.modifiersToInt(Modifier.PRIVATE) : Types.modifiersToInt(Modifier.PRIVATE, Modifier.FINAL)).withAttributes(fieldAttributes).build();
                    Method getter = new MethodBuilder(Getter.forProperty(field)).withAnnotations(method.getAnnotations()).withComments(method.getComments()).withModifiers(modifiersToInt(Modifier.PUBLIC)).withNewBlock().withStatements(new StringStatement("return this." + Strings.toFieldName(name) + ";")).endBlock().build();
                    fields.add(field);
                    getters.add(getter);
                    if (field.getTypeRef().equals(Types.BOOLEAN_REF)) {
                        Method primitiveGetter = new MethodBuilder(getter).withName("is" + getter.getName().replaceAll("^get", "")).withReturnType(Types.PRIMITIVE_BOOLEAN_REF).withNewBlock().withStatements(new StringStatement("return this." + Strings.toFieldName(name) + " != null &&  this." + Strings.toFieldName(name) + ";")).endBlock().build();
                        getters.add(primitiveGetter);
                    }
                }
            // Let's try to identify methods that we can't possibly implement and mark the type as abstract if such method is found.
            } else if (method.isDefaultMethod()) {
            // nothing special
            } else if (method.getBlock() != null && method.getBlock().getClass() != null && !method.getBlock().getStatements().isEmpty()) {
            // actual method, nothing special
            } else if (method.getExceptions() != null && !method.getExceptions().isEmpty()) {
                shouldBeAbstract = true;
            }
        }
    }
    List<Statement> statements = new ArrayList<Statement>();
    if (superClass != null) {
        Method constructor = findBuildableConstructor(superClass);
        if (constructor != null) {
            constructorArgs.addAll(constructor.getArguments());
        }
        StringBuilder sb = new StringBuilder();
        sb.append("super(");
        sb.append(Strings.join(constructor.getArguments(), new Function<Property, String>() {

            @Override
            public String apply(Property item) {
                return Strings.toFieldName(item.getName());
            }
        }, ", "));
        sb.append(");");
        statements.add(new StringStatement(sb.toString()));
        for (Property p : fields) {
            statements.add(new StringStatement(fieldIntializer(p, initialize)));
        }
    } else {
        for (Property p : fields) {
            statements.add(new StringStatement(fieldIntializer(p, initialize)));
        }
    }
    List<Method> constructors = new ArrayList();
    Method emptyConstructor = new MethodBuilder().withModifiers(modifiersToInt(Modifier.PUBLIC)).build();
    // We NEED to make sure that the superclass constructor arguments are in place and then add everything else.
    for (Property p : arguments) {
        if (!constructorArgs.contains(p)) {
            constructorArgs.add(p);
        }
    }
    // We don't want to annotate the POJO as @Buildable, as this is likely to re-trigger the processor multiple times.
    // The processor instead explicitly generates fluent and builder for the new pojo.
    Method buildableConstructor = new MethodBuilder().withModifiers(modifiersToInt(Modifier.PUBLIC)).withArguments(constructorArgs).withNewBlock().withStatements(statements).endBlock().accept(new TypedVisitor<PropertyBuilder>() {

        @Override
        public void visit(PropertyBuilder b) {
            String name = b.getName();
            b.withName(Strings.toFieldName(name));
            // That piece of information is lost (as is the case with our super-classs). Let's work-around it.
            if (superClassFields.containsKey(name)) {
                Property f = superClassFields.get(name);
                if (f.getAttributes().containsKey(DEFAULT_VALUE)) {
                    b.addToAttributes(DEFAULT_VALUE, f.getAttribute(DEFAULT_VALUE));
                }
            }
        }
    }).build();
    if (mutable) {
        constructors.add(emptyConstructor);
    }
    constructors.add(buildableConstructor);
    int modifiers = shouldBeAbstract ? modifiersToInt(Modifier.PUBLIC, Modifier.ABSTRACT) : modifiersToInt(Modifier.PUBLIC);
    TypeDef generatedPojo = new TypeDefBuilder().withPackageName(relativePackage(item.getPackageName(), relativePath)).withModifiers(modifiers).withName(pojoName).withAnnotations(annotationRefs).withProperties(fields).withConstructors(constructors).withMethods(getters).withImplementsList(new ArrayList<>(implementsList)).withExtendsList(new ArrayList<>(extendsList)).addToAttributes(item.getAttributes()).build();
    TypeDef pojoBuilder = BUILDER.apply(TypeArguments.apply(generatedPojo));
    if (!shouldBeAbstract) {
        if (enableStaticBuilder) {
            Method staticBuilder = new MethodBuilder().withModifiers(modifiersToInt(Modifier.PUBLIC, Modifier.STATIC)).withName(// avoid clashes in case of inheritance
            extendsList.isEmpty() ? "newBuilder" : "new" + pojoBuilder.getName()).withReturnType(pojoBuilder.toInternalReference()).withNewBlock().addNewStringStatementStatement("return new " + pojoBuilder.getFullyQualifiedName() + "();").endBlock().build();
            additionalMethods.add(staticBuilder);
            StringBuilder sb = new StringBuilder().append("return new " + pojoBuilder.getFullyQualifiedName() + "()");
            for (Method m : item.getMethods()) {
                if (m.hasAttribute(DEFAULT_VALUE)) {
                    if (m.getReturnType().getDimensions() > 0) {
                        continue;
                    }
                    String defaultValue = getDefaultValue(m);
                    if (defaultValue == null || defaultValue.trim().isEmpty() || defaultValue.equals("\"\"") || defaultValue.equals("null")) {
                        continue;
                    }
                    sb.append(".with" + Strings.capitalizeFirst(Strings.toFieldName(m.getName())) + "(" + defaultValue + ")");
                }
            }
            sb.append(";");
            Method staticBuilderFromDefaults = new MethodBuilder().withModifiers(modifiersToInt(Modifier.PUBLIC, Modifier.STATIC)).withName(// avoid clashes in case of inheritance
            extendsList.isEmpty() ? "newBuilderFromDefaults" : "new" + pojoBuilder.getName() + "FromDefaults").withReturnType(pojoBuilder.toInternalReference()).withNewBlock().addNewStringStatementStatement(sb.toString()).endBlock().build();
            additionalMethods.add(staticBuilderFromDefaults);
        }
        Method staticAdapter = new MethodBuilder().withModifiers(modifiersToInt(Modifier.PUBLIC, Modifier.STATIC)).withName("adapt").addNewArgument().withName("instance").withTypeRef(item.toInternalReference()).endArgument().withReturnType(generatedPojo.toInternalReference()).withNewBlock().addNewStringStatementStatement("return newBuilder(instance).build();").endBlock().build();
        Method staticAdaptingBuilder = new MethodBuilder().withModifiers(modifiersToInt(Modifier.PUBLIC, Modifier.STATIC)).withName("newBuilder").addNewArgument().withName("instance").withTypeRef(item.toInternalReference()).endArgument().withReturnType(pojoBuilder.toInternalReference()).withNewBlock().addToStatements(new StringStatement(() -> "return " + convertReference("instance", item, generatedPojo, pojoBuilder) + ";")).endBlock().build();
        Method staticMapAdapterWithDefaults = new MethodBuilder().withModifiers(modifiersToInt(Modifier.PUBLIC, Modifier.STATIC)).withName("adaptWithDefaults").addNewArgument().withName("map").withTypeRef(Collections.MAP.toUnboundedReference()).endArgument().withReturnType(generatedPojo.toInternalReference()).withNewBlock().addToStatements(new StringStatement(() -> "return " + convertMap("map", item, generatedPojo) + ";")).endBlock().build();
        Method staticMapAdapter = new MethodBuilder().withModifiers(modifiersToInt(Modifier.PUBLIC, Modifier.STATIC)).withName("adapt").addNewArgument().withName("map").withTypeRef(Collections.MAP.toUnboundedReference()).endArgument().withReturnType(generatedPojo.toInternalReference()).withNewBlock().addToStatements(new StringStatement(() -> "return " + convertMap("map", item, withoutDefaults(generatedPojo)) + ";")).endBlock().build();
        Method staticMapAdaptingBuilder = new MethodBuilder().withModifiers(modifiersToInt(Modifier.PUBLIC, Modifier.STATIC)).withName("newBuilder").addNewArgument().withName("map").withTypeRef(Collections.MAP.toUnboundedReference()).endArgument().withReturnType(pojoBuilder.toInternalReference()).withNewBlock().addToStatements(new StringStatement(() -> "return " + convertMap("map", item, withoutDefaults(generatedPojo), withoutDefaults(pojoBuilder)) + ";")).endBlock().build();
        Method staticMapAdaptingBuilderWithDefaults = new MethodBuilder().withModifiers(modifiersToInt(Modifier.PUBLIC, Modifier.STATIC)).withName("newBuilderWithDefaults").addNewArgument().withName("map").withTypeRef(Collections.MAP.toUnboundedReference()).endArgument().withReturnType(pojoBuilder.toInternalReference()).withNewBlock().addToStatements(new StringStatement(() -> "return " + convertMap("map", item, generatedPojo, pojoBuilder) + ";")).endBlock().build();
        Method staticToStringArray = new MethodBuilder().withModifiers(modifiersToInt(Modifier.PUBLIC, Modifier.STATIC)).withName("toStringArray").addNewArgument().withName("o").withTypeRef(Types.OBJECT.toInternalReference()).endArgument().withReturnType(Types.STRING_REF.withDimensions(1)).withNewBlock().addToStatements(new StringStatement(TO_STRING_ARRAY_TEXT)).endBlock().build();
        if (enableStaticAdapter && hasArrayFields(item)) {
            item.getMethods().stream().filter(m -> m.getReturnType() instanceof ClassRef && ((ClassRef) m.getReturnType()).getDimensions() > 0).findAny().ifPresent(m -> {
                additionalImports.add(ARRAYS);
                additionalImports.add(COLLECTORS);
            });
            additionalMethods.add(staticAdapter);
            additionalMethods.add(staticAdaptingBuilder);
            additionalMethods.add(staticMapAdapter);
            additionalMethods.add(staticMapAdaptingBuilderWithDefaults);
            additionalMethods.add(staticMapAdapterWithDefaults);
            if (enableStaticMapAdapter) {
                additionalMethods.add(staticMapAdaptingBuilder);
            }
        }
        Method equals = new MethodBuilder().withModifiers(Types.modifiersToInt(Modifier.PUBLIC)).withReturnType(Types.PRIMITIVE_BOOLEAN_REF).addNewArgument().withName("o").withTypeRef(Types.OBJECT.toReference()).endArgument().withName("equals").withNewBlock().withStatements(BuilderUtils.toEquals(generatedPojo, fields)).endBlock().build();
        Method hashCode = new MethodBuilder().withModifiers(Types.modifiersToInt(Modifier.PUBLIC)).withReturnType(Types.PRIMITIVE_INT_REF).withName("hashCode").withNewBlock().withStatements(BuilderUtils.toHashCode(fields)).endBlock().build();
        additionalMethods.add(equals);
        additionalMethods.add(hashCode);
        for (Object o : adapters) {
            if (o instanceof AnnotationRef) {
                AnnotationRef r = (AnnotationRef) o;
                String name = String.valueOf(r.getParameters().getOrDefault("name", ""));
                String prefix = String.valueOf(r.getParameters().getOrDefault("prefix", ""));
                String suffix = String.valueOf(r.getParameters().getOrDefault("suffix", ""));
                String mapperRelativePath = "";
                boolean enableMapAdapter = !"false".equals(String.valueOf(r.getParameters().getOrDefault("withMapAdapterMethod", "false")));
                List<Method> adapterMethods = new ArrayList<>();
                if (Strings.isNullOrEmpty(name) && Strings.isNullOrEmpty(prefix) && Strings.isNullOrEmpty(suffix)) {
                    suffix = "Adapter";
                }
                if (r.getParameters().containsKey("relativePath")) {
                    mapperRelativePath = String.valueOf(r.getParameters().getOrDefault("relativePath", "."));
                }
                String adapterPackage = relativePackage(item.getPackageName(), mapperRelativePath);
                List<ClassRef> adapterImports = new ArrayList<>();
                adapterImports.add(Collections.LIST.toInternalReference());
                if (hasArrayFields(item)) {
                    adapterImports.add(ARRAYS);
                    adapterImports.add(COLLECTORS);
                }
                List<ClassRef> generatedRefs = new ArrayList<>();
                Types.allProperties(generatedPojo).stream().map(i -> i.getTypeRef()).filter(i -> i instanceof ClassRef).forEach(i -> populateReferences((ClassRef) i, generatedRefs));
                adapterImports.addAll(generatedRefs);
                adapterImports.add(TypeAs.SHALLOW_BUILDER.apply(generatedPojo).toInternalReference());
                Types.allProperties(generatedPojo).stream().filter(p -> p.getTypeRef() instanceof ClassRef).map(p -> (ClassRef) p.getTypeRef()).filter(c -> !adapterPackage.equals(GetDefinition.of(c).getPackageName())).collect(toList());
                adapterImports.addAll(Types.allProperties(generatedPojo).stream().filter(p -> p.getTypeRef() instanceof ClassRef).map(p -> (ClassRef) p.getTypeRef()).filter(c -> !adapterPackage.equals(GetDefinition.of(c).getPackageName())).collect(toList()));
                adapterMethods.add(staticAdapter);
                adapterMethods.add(staticAdaptingBuilder);
                adapterMethods.add(staticMapAdapter);
                if (enableMapAdapter) {
                    adapterMethods.add(staticMapAdaptingBuilder);
                }
                adapterMethods.add(staticToStringArray);
                TypeDef mapper = new TypeDefBuilder().withComments("Generated").withModifiers(modifiersToInt(Modifier.PUBLIC)).withPackageName(adapterPackage).withName(!Strings.isNullOrEmpty(name) ? name : Strings.toPojoName(generatedPojo.getName(), prefix, suffix)).withMethods(adapterMethods).addToAttributes(ALSO_IMPORT, adapterImports).build();
                additionalTypes.add(mapper);
            }
        }
    }
    return DefinitionRepository.getRepository().register(new TypeDefBuilder(generatedPojo).withComments("Generated").addAllToMethods(additionalMethods).addToAttributes(ALSO_IMPORT, additionalImports).addToAttributes(ADDITIONAL_BUILDABLES, additionalBuildables).addToAttributes(ADDITIONAL_TYPES, additionalTypes).build());
}
Also used : INIT(io.sundr.model.Attributeable.INIT) Arrays(java.util.Arrays) BuilderUtils.findBuildableConstructor(io.sundr.builder.internal.utils.BuilderUtils.findBuildableConstructor) Array(java.lang.reflect.Array) Modifier(javax.lang.model.element.Modifier) Buildable(io.sundr.builder.annotations.Buildable) AnnotationRefBuilder(io.sundr.model.AnnotationRefBuilder) TO_STRING_ARRAY_SNIPPET(io.sundr.builder.Constants.TO_STRING_ARRAY_SNIPPET) Pojo(io.sundr.builder.annotations.Pojo) BuilderUtils(io.sundr.builder.internal.utils.BuilderUtils) Attributeable(io.sundr.model.Attributeable) ClassRef(io.sundr.model.ClassRef) Getter(io.sundr.model.utils.Getter) PropertyFluent(io.sundr.model.PropertyFluent) Map(java.util.Map) Path(java.nio.file.Path) COLLECTORS(io.sundr.builder.Constants.COLLECTORS) Collections(io.sundr.model.utils.Collections) Strings(io.sundr.utils.Strings) BuilderContext(io.sundr.builder.internal.BuilderContext) DefinitionRepository(io.sundr.model.repo.DefinitionRepository) Strings.loadResourceQuietly(io.sundr.utils.Strings.loadResourceQuietly) Set(java.util.Set) Element(javax.lang.model.element.Element) Method(io.sundr.model.Method) ADDITIONAL_BUILDABLES(io.sundr.builder.Constants.ADDITIONAL_BUILDABLES) Collectors(java.util.stream.Collectors) ALSO_IMPORT(io.sundr.model.Attributeable.ALSO_IMPORT) List(java.util.List) ClassRefBuilder(io.sundr.model.ClassRefBuilder) PrimitiveRef(io.sundr.model.PrimitiveRef) MethodBuilder(io.sundr.model.MethodBuilder) ARRAYS(io.sundr.builder.Constants.ARRAYS) Annotation(java.lang.annotation.Annotation) Optional(java.util.Optional) Pattern(java.util.regex.Pattern) BuilderContextManager(io.sundr.builder.internal.BuilderContextManager) ADDITIONAL_TYPES(io.sundr.builder.Constants.ADDITIONAL_TYPES) CopyOnWriteArrayList(java.util.concurrent.CopyOnWriteArrayList) StringStatement(io.sundr.model.StringStatement) BUILDER(io.sundr.builder.internal.functions.ClazzAs.BUILDER) GetDefinition(io.sundr.model.functions.GetDefinition) HashMap(java.util.HashMap) TypeDefBuilder(io.sundr.model.TypeDefBuilder) Function(java.util.function.Function) Stack(java.util.Stack) ArrayList(java.util.ArrayList) AttributeKey(io.sundr.model.AttributeKey) HashSet(java.util.HashSet) RichTypeDef(io.sundr.model.RichTypeDef) Assignable(io.sundr.model.functions.Assignable) Types(io.sundr.model.utils.Types) POJO(io.sundr.builder.internal.functions.ClazzAs.POJO) AptContext(io.sundr.adapter.apt.AptContext) TypedVisitor(io.sundr.builder.TypedVisitor) Types.modifiersToInt(io.sundr.model.utils.Types.modifiersToInt) ElementKind(javax.lang.model.element.ElementKind) Adapters(io.sundr.adapter.api.Adapters) TypeRef(io.sundr.model.TypeRef) AnnotationRef(io.sundr.model.AnnotationRef) Property(io.sundr.model.Property) TypeArguments(io.sundr.model.utils.TypeArguments) Paths(java.nio.file.Paths) Statement(io.sundr.model.Statement) TypeDef(io.sundr.model.TypeDef) DEFAULT_VALUE(io.sundr.model.Attributeable.DEFAULT_VALUE) PropertyBuilder(io.sundr.model.PropertyBuilder) TypedVisitor(io.sundr.builder.TypedVisitor) Pojo(io.sundr.builder.annotations.Pojo) ClassRef(io.sundr.model.ClassRef) HashMap(java.util.HashMap) TypeRef(io.sundr.model.TypeRef) CopyOnWriteArrayList(java.util.concurrent.CopyOnWriteArrayList) ArrayList(java.util.ArrayList) TypeDefBuilder(io.sundr.model.TypeDefBuilder) MethodBuilder(io.sundr.model.MethodBuilder) Function(java.util.function.Function) RichTypeDef(io.sundr.model.RichTypeDef) TypeDef(io.sundr.model.TypeDef) AnnotationRefBuilder(io.sundr.model.AnnotationRefBuilder) List(java.util.List) CopyOnWriteArrayList(java.util.concurrent.CopyOnWriteArrayList) ArrayList(java.util.ArrayList) Property(io.sundr.model.Property) AnnotationRef(io.sundr.model.AnnotationRef) Buildable(io.sundr.builder.annotations.Buildable) HashSet(java.util.HashSet) PropertyBuilder(io.sundr.model.PropertyBuilder) AptContext(io.sundr.adapter.apt.AptContext) StringStatement(io.sundr.model.StringStatement) Statement(io.sundr.model.Statement) ClassRefBuilder(io.sundr.model.ClassRefBuilder) Method(io.sundr.model.Method) StringStatement(io.sundr.model.StringStatement) Annotation(java.lang.annotation.Annotation) AttributeKey(io.sundr.model.AttributeKey) BuilderContext(io.sundr.builder.internal.BuilderContext) CopyOnWriteArrayList(java.util.concurrent.CopyOnWriteArrayList)

Aggregations

BuilderContext (io.sundr.builder.internal.BuilderContext)6 TypeDef (io.sundr.model.TypeDef)4 ArrayList (java.util.ArrayList)4 AptContext (io.sundr.adapter.apt.AptContext)3 Property (io.sundr.model.Property)3 PropertyBuilder (io.sundr.model.PropertyBuilder)3 Statement (io.sundr.model.Statement)3 StringStatement (io.sundr.model.StringStatement)3 TypeDefBuilder (io.sundr.model.TypeDefBuilder)3 Visitor (io.sundr.builder.Visitor)2 Buildable (io.sundr.builder.annotations.Buildable)2 ClassRef (io.sundr.model.ClassRef)2 Method (io.sundr.model.Method)2 RichTypeDef (io.sundr.model.RichTypeDef)2 HashSet (java.util.HashSet)2 Function (java.util.function.Function)2 Filer (javax.annotation.processing.Filer)2 SupportedAnnotationTypes (javax.annotation.processing.SupportedAnnotationTypes)2 Element (javax.lang.model.element.Element)2 TypeElement (javax.lang.model.element.TypeElement)2