All Downloads are FREE. Search and download functionalities are using the official Maven repository.

eu.toolchain.serializer.processor.ClassProcessor Maven / Gradle / Ivy

The newest version!
package eu.toolchain.serializer.processor;

import static eu.toolchain.serializer.processor.Exceptions.brokenElement;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.TypeName;
import eu.toolchain.serializer.processor.annotation.AutoSerializeMirror;
import eu.toolchain.serializer.processor.annotation.SubTypeMirror;
import eu.toolchain.serializer.processor.field.Field;
import eu.toolchain.serializer.processor.field.FieldBuilder;
import eu.toolchain.serializer.processor.field.FieldSet;
import eu.toolchain.serializer.processor.field.SubType;
import eu.toolchain.serializer.processor.field.Value;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Modifier;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public class ClassProcessor {
  final Types types;
  final Elements elements;
  final AutoSerializeUtils utils;

  public JavaFile process(final Element element) {
    return buildSpec(element)
      .orElseThrow(() -> brokenElement("@AutoSerialize expected on class or interface", element))
      .toSerializer();
  }

  public Optional buildSpec(final Element element) {
    final Optional annotation = utils.autoSerialize(element);

    if (!annotation.isPresent()) {
      return Optional.empty();
    }

    final AutoSerializeMirror autoSerialize = annotation.get();

    if (element.getKind() == ElementKind.INTERFACE) {
      if (autoSerialize.getBuilder().isPresent()) {
        return Optional.of(conreteClass(element));
      } else {
        return Optional.of(abstractClass(element));
      }
    }

    if (element.getKind() == ElementKind.CLASS) {
      if (element.getModifiers().contains(Modifier.ABSTRACT) &&
        !autoSerialize.getBuilder().isPresent()) {
        return Optional.of(abstractClass(element));
      }

      return Optional.of(conreteClass(element));
    }

    return Optional.empty();
  }

  public AbstractClassSpec abstractClass(final Element element) {
    final String packageName = elements.getPackageOf(element).getQualifiedName().toString();
    final String serializerName = utils.serializerName(element);

    final TypeName elementType = TypeName.get(element.asType());
    final TypeName superType = TypeName.get(utils.serializerFor(element.asType()));

    final List subTypes = subTypes(element);

    return new AbstractClassSpec(utils, packageName, serializerName, elementType, superType,
      subTypes);
  }

  private ConcreteClassSpec conreteClass(final Element element) {
    final Optional annotation = utils.autoSerialize(element);

    if (!annotation.isPresent()) {
      throw brokenElement("@AutoSerialize annotation not present", element);
    }

    final AutoSerializeMirror autoSerialize = annotation.get();

    final String packageName = elements.getPackageOf(element).getQualifiedName().toString();
    final Set kinds = getKinds(element);

    final FieldSet fieldSet =
      new FieldSet(this, utils, element, kinds, autoSerialize.isUseGetter());

    for (final Element child : element.getEnclosedElements()) {
      fieldSet.add(child);
    }

    final ClassName elementType = (ClassName) TypeName.get(element.asType());
    final TypeName superType = TypeName.get(utils.serializerFor(element.asType()));
    final String serializerName = utils.serializerName(element);

    final boolean fieldBased = autoSerialize.isFieldBased();
    final boolean failOnMissing = autoSerialize.isFailOnMissing();

    final Optional fieldTypeBuilder =
      utils.builder(element).map(Optional::of).orElseGet(autoSerialize::getBuilder).map(method -> {
        return new FieldBuilder(method, method.shouldUseConstructor(), method.isUseSetter(),
          method.getMethodName());
      });

    final List fields = ImmutableList.copyOf(fieldSet.getFields());
    final List values = ImmutableList.copyOf(fieldSet.getValues());

    return new ConcreteClassSpec(this, utils, elements, packageName, fields, values, elementType,
      superType, serializerName, fieldBased, failOnMissing, fieldTypeBuilder);
  }

  /**
   * Get the set of supported element kinds that make up the total set of fields for this type.
   *
   * @param element
   * @return
   */
  Set getKinds(Element element) {
    final ImmutableSet.Builder kinds = ImmutableSet.builder();

    if (element.getKind() == ElementKind.INTERFACE) {
      kinds.add(ElementKind.METHOD);
    }

    if (element.getKind() == ElementKind.CLASS) {
      kinds.add(ElementKind.FIELD);

      if (element.getModifiers().contains(Modifier.ABSTRACT)) {
        kinds.add(ElementKind.METHOD);
      }
    }

    return kinds.build();
  }

  List subTypes(Element element) {
    return utils.subTypes(element).map(subTypes -> {
      final Set seenIds = new HashSet<>();

      int offset = 0;
      final ShortIterator index = new ShortIterator();

      final ImmutableList.Builder results = ImmutableList.builder();

      for (final SubTypeMirror s : subTypes.getSubTypes()) {
        final DeclaredType type = (DeclaredType) s.getValue().get();
        final short id = s.getId().orElseGet(index::next);

        if (!seenIds.add(id)) {
          throw brokenElement(
            String.format("Conflicting subtype id (%d) defined for definition #%d", id, offset),
            element);
        }

        final Optional maybeSpec = buildSpec(type.asElement());
        final List fields = maybeSpec.map(ClassSpec::getFields).orElseGet(ImmutableList::of);
        final List values = maybeSpec.map(ClassSpec::getValues).orElseGet(ImmutableList::of);

        results.add(new SubType(type, id, fields, values));
        offset++;
      }

      return results.build();
    }).orElseGet(ImmutableList::of);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy