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

de.rhocas.featuregen.ap.FeatureIDEFeaturesProcessor Maven / Gradle / Ivy

There is a newer version: 2.0.0
Show newest version
package de.rhocas.featuregen.ap;

import com.google.common.base.Objects;
import de.rhocas.featuregen.ap.FeatureIDEFeatures;
import de.rhocas.featuregen.ap.FeatureNameConverter;
import de.rhocas.featuregen.ap.NameProvider;
import de.rhocas.featuregen.featureide.model.feature.BranchedFeatureType;
import de.rhocas.featuregen.featureide.model.feature.FeatureModel;
import de.rhocas.featuregen.featureide.model.feature.FeatureModelType;
import de.rhocas.featuregen.featureide.model.feature.FeatureType;
import de.rhocas.featuregen.featureide.model.feature.StructType;
import java.io.InputStream;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Unmarshaller;
import org.eclipse.xtend.lib.macro.AbstractClassProcessor;
import org.eclipse.xtend.lib.macro.RegisterGlobalsContext;
import org.eclipse.xtend.lib.macro.TransformationContext;
import org.eclipse.xtend.lib.macro.declaration.AnnotationReference;
import org.eclipse.xtend.lib.macro.declaration.ClassDeclaration;
import org.eclipse.xtend.lib.macro.declaration.EnumerationTypeDeclaration;
import org.eclipse.xtend.lib.macro.declaration.MutableAnnotationTypeDeclaration;
import org.eclipse.xtend.lib.macro.declaration.MutableAnnotationTypeElementDeclaration;
import org.eclipse.xtend.lib.macro.declaration.MutableClassDeclaration;
import org.eclipse.xtend.lib.macro.declaration.MutableConstructorDeclaration;
import org.eclipse.xtend.lib.macro.declaration.MutableEnumerationTypeDeclaration;
import org.eclipse.xtend.lib.macro.declaration.MutableEnumerationValueDeclaration;
import org.eclipse.xtend.lib.macro.declaration.MutableFieldDeclaration;
import org.eclipse.xtend.lib.macro.declaration.MutableInterfaceDeclaration;
import org.eclipse.xtend.lib.macro.declaration.MutableMethodDeclaration;
import org.eclipse.xtend.lib.macro.declaration.Type;
import org.eclipse.xtend.lib.macro.declaration.TypeReference;
import org.eclipse.xtend.lib.macro.declaration.Visibility;
import org.eclipse.xtend.lib.macro.file.FileLocations;
import org.eclipse.xtend.lib.macro.file.FileSystemSupport;
import org.eclipse.xtend.lib.macro.file.Path;
import org.eclipse.xtend.lib.macro.services.AnnotationReferenceBuildContext;
import org.eclipse.xtend2.lib.StringConcatenation;
import org.eclipse.xtend2.lib.StringConcatenationClient;
import org.eclipse.xtext.xbase.lib.Exceptions;
import org.eclipse.xtext.xbase.lib.Extension;
import org.eclipse.xtext.xbase.lib.Functions.Function0;
import org.eclipse.xtext.xbase.lib.Procedures.Procedure1;

/**
 * This is the annotation processor for {@link FeatureIDEFeatures}.
 * 
 * @author Nils Christian Ehmke
 * 
 * @since 1.0.0
 */
@SuppressWarnings("all")
public final class FeatureIDEFeaturesProcessor extends AbstractClassProcessor {
  @Extension
  private final NameProvider _nameProvider = new NameProvider();
  
  @Extension
  private final FeatureNameConverter _featureNameConverter = new FeatureNameConverter();
  
  private final JAXBContext jaxbContext = new Function0() {
    public JAXBContext apply() {
      try {
        JAXBContext _newInstance = JAXBContext.newInstance(FeatureModel.class);
        return _newInstance;
      } catch (Throwable _e) {
        throw Exceptions.sneakyThrow(_e);
      }
    }
  }.apply();
  
  @Override
  public void doRegisterGlobals(final ClassDeclaration annotatedClass, @Extension final RegisterGlobalsContext context) {
    final AnnotationReference annotationReference = annotatedClass.findAnnotation(context.findUpstreamType(FeatureIDEFeatures.class));
    final Path modelFilePath = this.findModelFilePath(annotatedClass, annotationReference, context);
    boolean _isFile = context.isFile(modelFilePath);
    if (_isFile) {
      final FeatureModelType featureModel = this.readFeatureModel(modelFilePath, context);
      this.registerFeatureCheckService(annotatedClass, featureModel, context);
      this.registerSelectedFeatures(annotatedClass, featureModel, context);
      this.registerFeature(annotatedClass, featureModel, context);
      this.registerVariant(annotatedClass, featureModel, context);
    }
  }
  
  private Path findModelFilePath(final ClassDeclaration annotatedClass, final AnnotationReference annotationReference, @Extension final FileLocations context) {
    String value = annotationReference.getStringValue("value");
    boolean _equals = Objects.equal(value, "");
    if (_equals) {
      value = "model.xml";
    }
    Path path = null;
    boolean _startsWith = value.startsWith("/");
    if (_startsWith) {
      path = context.getProjectFolder(annotatedClass.getCompilationUnit().getFilePath());
    } else {
      path = annotatedClass.getCompilationUnit().getFilePath().getParent();
    }
    return path.append("/").append(value);
  }
  
  private FeatureModelType readFeatureModel(final Path modelFilePath, @Extension final FileSystemSupport fileSystemSupport) {
    try {
      final Unmarshaller unmarshaller = this.jaxbContext.createUnmarshaller();
      final InputStream stream = fileSystemSupport.getContentsAsStream(modelFilePath);
      try {
        Object _unmarshal = unmarshaller.unmarshal(stream);
        return ((FeatureModelType) _unmarshal);
      } finally {
        stream.close();
      }
    } catch (Throwable _e) {
      throw Exceptions.sneakyThrow(_e);
    }
  }
  
  private void registerFeatureCheckService(final ClassDeclaration annotatedClass, final FeatureModelType featureModel, @Extension final RegisterGlobalsContext context) {
    final FeatureType root = this.getRoot(featureModel);
    if ((root != null)) {
      context.registerClass(this._nameProvider.getFullQualifiedFeatureCheckServiceClassName(annotatedClass, root.getName()));
    }
  }
  
  private FeatureType getRoot(final FeatureModelType model) {
    FeatureType _xblockexpression = null;
    {
      final StructType struct = model.getStruct();
      FeatureType _xifexpression = null;
      FeatureType _feature = struct.getFeature();
      boolean _tripleNotEquals = (_feature != null);
      if (_tripleNotEquals) {
        _xifexpression = struct.getFeature();
      } else {
        BranchedFeatureType _xifexpression_1 = null;
        BranchedFeatureType _and = struct.getAnd();
        boolean _tripleNotEquals_1 = (_and != null);
        if (_tripleNotEquals_1) {
          _xifexpression_1 = struct.getAnd();
        } else {
          BranchedFeatureType _xifexpression_2 = null;
          BranchedFeatureType _or = struct.getOr();
          boolean _tripleNotEquals_2 = (_or != null);
          if (_tripleNotEquals_2) {
            _xifexpression_2 = struct.getOr();
          } else {
            _xifexpression_2 = struct.getAlt();
          }
          _xifexpression_1 = _xifexpression_2;
        }
        _xifexpression = _xifexpression_1;
      }
      _xblockexpression = _xifexpression;
    }
    return _xblockexpression;
  }
  
  private void registerSelectedFeatures(final ClassDeclaration annotatedClass, final FeatureModelType featureModel, @Extension final RegisterGlobalsContext context) {
    final FeatureType root = this.getRoot(featureModel);
    if ((root != null)) {
      context.registerAnnotationType(this._nameProvider.getFullQualifiedSelectedFeaturesAnnotationName(annotatedClass, root.getName()));
    }
  }
  
  private void registerFeature(final ClassDeclaration annotatedClass, final FeatureModelType featureModel, @Extension final RegisterGlobalsContext context) {
    final FeatureType root = this.getRoot(featureModel);
    if ((root != null)) {
      context.registerEnumerationType(this._nameProvider.getFullQualifiedFeaturesEnumName(annotatedClass, root.getName()));
    }
  }
  
  private void registerVariant(final ClassDeclaration annotatedClass, final FeatureModelType featureModel, @Extension final RegisterGlobalsContext context) {
    final FeatureType root = this.getRoot(featureModel);
    if ((root != null)) {
      context.registerInterface(this.getVariantName(annotatedClass, root));
    }
  }
  
  private String getVariantName(final ClassDeclaration annotatedClass, final FeatureType root) {
    StringConcatenation _builder = new StringConcatenation();
    String _packageName = annotatedClass.getCompilationUnit().getPackageName();
    _builder.append(_packageName);
    _builder.append(".");
    String _name = root.getName();
    _builder.append(_name);
    _builder.append("Variant");
    return _builder.toString();
  }
  
  @Override
  public void doTransform(final MutableClassDeclaration annotatedClass, @Extension final TransformationContext context) {
    this.makeFinal(annotatedClass);
    final AnnotationReference annotationReference = annotatedClass.findAnnotation(context.findTypeGlobally(FeatureIDEFeatures.class));
    final Path modelFilePath = this.findModelFilePath(annotatedClass, annotationReference, context);
    boolean _isFile = context.isFile(modelFilePath);
    if (_isFile) {
      final FeatureModelType featureModel = this.readFeatureModel(modelFilePath, context);
      this.transformFeature(annotatedClass, featureModel, context);
      this.transformSelectedFeatures(annotatedClass, featureModel, context);
      this.transformVariant(annotatedClass, featureModel, context);
      this.transformFeatureCheckService(annotatedClass, featureModel, context);
    } else {
      StringConcatenation _builder = new StringConcatenation();
      _builder.append("The model file could not be found (Assumed path was: \'");
      _builder.append(modelFilePath);
      _builder.append("\').");
      context.addError(annotatedClass, _builder.toString());
    }
  }
  
  private void makeFinal(final MutableClassDeclaration annotatedClass) {
    annotatedClass.setFinal(true);
  }
  
  private MutableMethodDeclaration transformFeatureCheckService(final MutableClassDeclaration annotatedClass, final FeatureModelType featureModel, @Extension final TransformationContext context) {
    MutableMethodDeclaration _xblockexpression = null;
    {
      final FeatureType root = this.getRoot(featureModel);
      final MutableClassDeclaration featureCheckService = context.findClass(this._nameProvider.getFullQualifiedFeatureCheckServiceClassName(annotatedClass, root.getName()));
      final MutableEnumerationTypeDeclaration featureEnum = context.findEnumerationType(this._nameProvider.getFullQualifiedFeaturesEnumName(annotatedClass, root.getName()));
      featureCheckService.setFinal(true);
      StringConcatenation _builder = new StringConcatenation();
      _builder.append("This service allows to check which features are currently active.
"); _builder.newLine(); _builder.append("
"); _builder.newLine(); _builder.append("Note that instances of this class are immutable and thus inherent thread safe.
"); _builder.newLine(); _builder.append("
"); _builder.newLine(); _builder.append("This service is generated."); _builder.newLine(); featureCheckService.setDocComment(_builder.toString()); final Procedure1 _function = (MutableFieldDeclaration it) -> { it.setFinal(true); it.setType(context.newTypeReference(Set.class)); }; featureCheckService.addField("activeFeatures", _function); final Procedure1 _function_1 = (MutableFieldDeclaration it) -> { it.setFinal(true); it.setType(context.newTypeReference(String.class)); }; featureCheckService.addField("description", _function_1); final Procedure1 _function_2 = (MutableConstructorDeclaration it) -> { it.setVisibility(Visibility.PRIVATE); it.addParameter("selectedFeatures", context.newTypeReference(List.class, context.newSelfTypeReference(featureEnum))); it.addParameter("variantName", context.newTypeReference(String.class)); StringConcatenationClient _client = new StringConcatenationClient() { @Override protected void appendTo(StringConcatenationClient.TargetStringConcatenation _builder) { _builder.append("activeFeatures = "); TypeReference _newTypeReference = context.newTypeReference(EnumSet.class); _builder.append(_newTypeReference); _builder.append(".noneOf( "); TypeReference _newTypeReference_1 = context.newTypeReference(featureEnum); _builder.append(_newTypeReference_1); _builder.append(".class );"); _builder.newLineIfNotEmpty(); _builder.append("activeFeatures.addAll( selectedFeatures );"); _builder.newLine(); _builder.newLine(); _builder.append("description = \""); String _simpleName = featureCheckService.getSimpleName(); _builder.append(_simpleName); _builder.append(" [\" + variantName + \"]\";"); _builder.newLineIfNotEmpty(); } }; it.setBody(_client); }; featureCheckService.addConstructor(_function_2); final Procedure1 _function_3 = (MutableMethodDeclaration it) -> { StringConcatenation _builder_1 = new StringConcatenation(); _builder_1.append("Checks whether the given feature is currently active or not."); _builder_1.newLine(); _builder_1.newLine(); _builder_1.append("@param feature"); _builder_1.newLine(); _builder_1.append("\t"); _builder_1.append("The feature to check. Must not be {@code null}."); _builder_1.newLine(); _builder_1.append("\t"); _builder_1.newLine(); _builder_1.append("@return true if and only if the given feature is active."); _builder_1.newLine(); _builder_1.newLine(); _builder_1.append("@throws NullPointerException"); _builder_1.newLine(); _builder_1.append("\t"); _builder_1.append("If the given feature is {@code null}."); _builder_1.newLine(); it.setDocComment(_builder_1.toString()); it.addParameter("feature", context.newTypeReference(context.findTypeGlobally(this._nameProvider.getFullQualifiedFeaturesEnumName(annotatedClass, root.getName())))); it.setReturnType(context.getPrimitiveBoolean()); StringConcatenationClient _client = new StringConcatenationClient() { @Override protected void appendTo(StringConcatenationClient.TargetStringConcatenation _builder) { TypeReference _newTypeReference = context.newTypeReference(java.util.Objects.class); _builder.append(_newTypeReference); _builder.append(".requireNonNull( feature, \"The feature must not be null.\" );"); _builder.newLineIfNotEmpty(); _builder.newLine(); _builder.append("return activeFeatures.contains( feature );"); _builder.newLine(); } }; it.setBody(_client); }; featureCheckService.addMethod("isFeatureActive", _function_3); final Procedure1 _function_4 = (MutableMethodDeclaration it) -> { it.addAnnotation(context.newAnnotationReference(Override.class)); it.setReturnType(context.newTypeReference(String.class)); StringConcatenationClient _client = new StringConcatenationClient() { @Override protected void appendTo(StringConcatenationClient.TargetStringConcatenation _builder) { _builder.append("return description;"); _builder.newLine(); } }; it.setBody(_client); }; featureCheckService.addMethod("toString", _function_4); final MutableInterfaceDeclaration variant = context.findInterface(this.getVariantName(annotatedClass, root)); final MutableAnnotationTypeDeclaration selectedFeaturesAnnotation = context.findAnnotationType(this._nameProvider.getFullQualifiedSelectedFeaturesAnnotationName(annotatedClass, root.getName())); final Procedure1 _function_5 = (MutableMethodDeclaration it) -> { it.setReturnType(context.newSelfTypeReference(featureCheckService)); it.setStatic(true); StringConcatenation _builder_1 = new StringConcatenation(); _builder_1.append("Creates a new instance of this service with the features of the given variant."); _builder_1.newLine(); _builder_1.newLine(); _builder_1.append("@param variant"); _builder_1.newLine(); _builder_1.append("\t"); _builder_1.append("The new variant. Must not be {@code null} and must be annotated with {@link "); TypeReference _newTypeReference = context.newTypeReference(selectedFeaturesAnnotation); _builder_1.append(_newTypeReference, "\t"); _builder_1.append("}."); _builder_1.newLineIfNotEmpty(); _builder_1.newLine(); _builder_1.append("@return A new feature check service."); _builder_1.newLine(); _builder_1.append("\t"); _builder_1.newLine(); _builder_1.append("@throws NullPointerException"); _builder_1.newLine(); _builder_1.append("\t"); _builder_1.append("If the given variant is {@code null} or not annotated with {@link "); TypeReference _newTypeReference_1 = context.newTypeReference(selectedFeaturesAnnotation); _builder_1.append(_newTypeReference_1, "\t"); _builder_1.append("}."); _builder_1.newLineIfNotEmpty(); it.setDocComment(_builder_1.toString()); it.addParameter("variant", context.newTypeReference(Class.class, context.newWildcardTypeReference(context.newSelfTypeReference(variant)))); StringConcatenationClient _client = new StringConcatenationClient() { @Override protected void appendTo(StringConcatenationClient.TargetStringConcatenation _builder) { TypeReference _newTypeReference = context.newTypeReference(java.util.Objects.class); _builder.append(_newTypeReference); _builder.append(".requireNonNull( variant, \"The variant must not be null.\" );"); _builder.newLineIfNotEmpty(); _builder.newLine(); _builder.append("final "); TypeReference _newTypeReference_1 = context.newTypeReference(selectedFeaturesAnnotation); _builder.append(_newTypeReference_1); _builder.append(" selectedFeaturesAnnotation = variant.getAnnotation( "); TypeReference _newTypeReference_2 = context.newTypeReference(selectedFeaturesAnnotation); _builder.append(_newTypeReference_2); _builder.append(".class );"); _builder.newLineIfNotEmpty(); TypeReference _newTypeReference_3 = context.newTypeReference(java.util.Objects.class); _builder.append(_newTypeReference_3); _builder.append(".requireNonNull( selectedFeaturesAnnotation, \"The variant must be annotated with "); String _simpleName = selectedFeaturesAnnotation.getSimpleName(); _builder.append(_simpleName); _builder.append(".\" );"); _builder.newLineIfNotEmpty(); _builder.append("final "); TypeReference _newTypeReference_4 = context.newTypeReference(List.class, context.newSelfTypeReference(featureEnum)); _builder.append(_newTypeReference_4); _builder.append(" selectedFeatures = "); TypeReference _newTypeReference_5 = context.newTypeReference(Arrays.class); _builder.append(_newTypeReference_5); _builder.append(".asList( selectedFeaturesAnnotation.value( ) );"); _builder.newLineIfNotEmpty(); _builder.newLine(); _builder.append("return new "); TypeReference _newSelfTypeReference = context.newSelfTypeReference(featureCheckService); _builder.append(_newSelfTypeReference); _builder.append("( selectedFeatures, variant.getSimpleName( ) );"); _builder.newLineIfNotEmpty(); } }; it.setBody(_client); }; featureCheckService.addMethod("of", _function_5); final Procedure1 _function_6 = (MutableMethodDeclaration it) -> { it.setReturnType(context.newSelfTypeReference(featureCheckService)); it.setStatic(true); StringConcatenation _builder_1 = new StringConcatenation(); _builder_1.append("Creates a new instance of this service without any active features."); _builder_1.newLine(); _builder_1.newLine(); _builder_1.append("@return A new feature check service."); _builder_1.newLine(); it.setDocComment(_builder_1.toString()); StringConcatenationClient _client = new StringConcatenationClient() { @Override protected void appendTo(StringConcatenationClient.TargetStringConcatenation _builder) { _builder.append("return new "); TypeReference _newSelfTypeReference = context.newSelfTypeReference(featureCheckService); _builder.append(_newSelfTypeReference); _builder.append("( "); TypeReference _newSelfTypeReference_1 = context.newSelfTypeReference(context.findTypeGlobally(Collections.class)); _builder.append(_newSelfTypeReference_1); _builder.append(".emptyList( ), \"Empty\" );"); _builder.newLineIfNotEmpty(); } }; it.setBody(_client); }; _xblockexpression = featureCheckService.addMethod("empty", _function_6); } return _xblockexpression; } private MutableAnnotationTypeElementDeclaration transformSelectedFeatures(final MutableClassDeclaration annotatedClass, final FeatureModelType featureModel, @Extension final TransformationContext context) { MutableAnnotationTypeElementDeclaration _xblockexpression = null; { final FeatureType root = this.getRoot(featureModel); final MutableAnnotationTypeDeclaration selectedFeatures = context.findAnnotationType(this._nameProvider.getFullQualifiedSelectedFeaturesAnnotationName(annotatedClass, root.getName())); StringConcatenation _builder = new StringConcatenation(); _builder.append("This annotation is used to mark which features the annotated variant provides.
"); _builder.newLine(); _builder.append("
"); _builder.newLine(); _builder.append("This annotation is generated."); _builder.newLine(); selectedFeatures.setDocComment(_builder.toString()); final Procedure1 _function = (AnnotationReferenceBuildContext it) -> { Type _findTypeGlobally = context.findTypeGlobally(RetentionPolicy.class); it.setEnumValue("value", ((EnumerationTypeDeclaration) _findTypeGlobally).findDeclaredValue(RetentionPolicy.RUNTIME.name())); }; selectedFeatures.addAnnotation(context.newAnnotationReference(Retention.class, _function)); final Procedure1 _function_1 = (AnnotationReferenceBuildContext it) -> { Type _findTypeGlobally = context.findTypeGlobally(ElementType.class); it.setEnumValue("value", ((EnumerationTypeDeclaration) _findTypeGlobally).findDeclaredValue(ElementType.TYPE.name())); }; selectedFeatures.addAnnotation(context.newAnnotationReference(Target.class, _function_1)); final Procedure1 _function_2 = (MutableAnnotationTypeElementDeclaration it) -> { it.setDocComment("The selected features."); it.setType(context.newArrayTypeReference(context.newSelfTypeReference(context.findEnumerationType(this._nameProvider.getFullQualifiedFeaturesEnumName(annotatedClass, root.getName()))))); }; _xblockexpression = selectedFeatures.addAnnotationTypeElement("value", _function_2); } return _xblockexpression; } private void transformFeature(final MutableClassDeclaration annotatedClass, final FeatureModelType featureModel, @Extension final TransformationContext context) { final FeatureType root = this.getRoot(featureModel); final MutableEnumerationTypeDeclaration feature = context.findEnumerationType(this._nameProvider.getFullQualifiedFeaturesEnumName(annotatedClass, root.getName())); final AnnotationReference annotation = annotatedClass.findAnnotation(context.findTypeGlobally(FeatureIDEFeatures.class)); StringConcatenation _builder = new StringConcatenation(); _builder.append("This enumeration contains all available features.
"); _builder.newLine(); _builder.append("
"); _builder.newLine(); _builder.append("This enumeration is generated."); _builder.newLine(); feature.setDocComment(_builder.toString()); this.addFeaturesToEnum(feature, annotation, root); } private void addFeaturesToEnum(final MutableEnumerationTypeDeclaration enumeration, final AnnotationReference annotationReference, final FeatureType type) { if ((type != null)) { boolean _equals = Boolean.TRUE.equals(type.isAbstract()); boolean _not = (!_equals); if (_not) { final Procedure1 _function = (MutableEnumerationValueDeclaration it) -> { it.setDocComment(type.getDescription()); }; enumeration.addValue(this._featureNameConverter.convertToValidSimpleFeatureName(type.getName(), annotationReference), _function); } if ((type instanceof BranchedFeatureType)) { List> _andOrOrOrAlt = ((BranchedFeatureType)type).getAndOrOrOrAlt(); for (final JAXBElement feature : _andOrOrOrAlt) { this.addFeaturesToEnum(enumeration, annotationReference, feature.getValue()); } } } } private void transformVariant(final MutableClassDeclaration annotatedClass, final FeatureModelType featureModel, @Extension final TransformationContext context) { final FeatureType root = this.getRoot(featureModel); final MutableInterfaceDeclaration variant = context.findInterface(this.getVariantName(annotatedClass, root)); StringConcatenation _builder = new StringConcatenation(); _builder.append("This is a marker interface for all variants.
"); _builder.newLine(); _builder.append("
"); _builder.newLine(); _builder.append("This interface is generated."); _builder.newLine(); variant.setDocComment(_builder.toString()); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy