Skip to content

Commit

Permalink
Some limited support for extends and implements clauses, following pu…
Browse files Browse the repository at this point in the history
…ll request merging
  • Loading branch information
sviperll committed Apr 29, 2015
1 parent e0aa144 commit e98914a
Show file tree
Hide file tree
Showing 12 changed files with 369 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
import com.github.sviperll.adt4j.Getter;
import com.github.sviperll.meta.Visitor;

@GenerateValueClassForVisitor(baseClass = "com.github.sviperll.adt4j.examples.BaseOptionalSupport<T>")
@GenerateValueClassForVisitor(extendsClass = BaseOptionalSupport.class)
@Visitor(resultVariableName = "R", exceptionVariableName = "E")
public interface BaseOptionalVisitor<T, R, E extends Exception> {
R missing() throws E;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,24 +81,24 @@
String className() default ":auto";

/**
* Name of an interface for value classes to implement.
* Interfaces for value classes to implement.
* <p>
* You can leave the baseInterface parameter out.
* Generated classes will implement this interface, allowing the developer to work with abstractions,
* You can leave the implementsInterfaces parameter out.
* Generated classes will implement these interfaces, allowing the developer to work with abstractions,
* and make use of Java 8 default methods.
* @return Class name of marker interface.
*/
String baseInterface() default "";
Class<?>[] implementsInterfaces() default {};

/**
* Name of an class for value classes to extend.
* Class for value classes to extend.
* <p>
* You can leave the baseClass parameter out.
* Generated classes will extends this interface, allowing the developer to work with abstractions,
* You can leave the extendsClass parameter out.
* Generated classes will extends this class, allowing the developer to work with abstractions,
* and make use of Java 8 default methods.
* @return Class name of marker interface.
*/
String baseClass() default "";
Class<?> extendsClass() default Object.class;

/**
* Java's access for generated class.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
Expand Down Expand Up @@ -95,10 +96,9 @@ private void processElements(Set<? extends TypeElement> elements) {
Visitor visitorAnnotation = element.getAnnotation(Visitor.class);
if (visitorAnnotation == null)
throw new SourceCodeValidationException("No " + Visitor.class.getName() + " annotation for " + element.getQualifiedName() + " class annotated with " + GenerateValueClassForVisitor.class.getName() + " annotation");
GenerateValueClassForVisitor generateAnnotation = element.getAnnotation(GenerateValueClassForVisitor.class);
JCodeModelJavaxLangModelAdapter adapter = new JCodeModelJavaxLangModelAdapter(jCodeModel, processingEnv.getElementUtils());
JDefinedClass visitorModel = adapter.getClassWithErrorTypes(element);
JDefinedClass valueClass = ValueClassModelFactory.createValueClass(jCodeModel, visitorModel, visitorAnnotation, generateAnnotation);
JDefinedClass valueClass = ValueClassModelFactory.createValueClass(visitorModel, visitorAnnotation);
if (jCodeModel.buildsErrorTypeRefs()) {
remainingElements.add(element.getQualifiedName().toString());
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,9 @@ void read(JMethod interfaceMethod, JAnnotationUse annotationUsage) throws Source
MemberAccess accessLevel = Source.getAnnotationArgument(annotationUsage, "access", MemberAccess.class);
read(interfaceMethod, predicateName, accessLevel);
} else if (annotationClassName.equals(GeneratePredicates.class.getName())) {
JAnnotationArrayMember values = (JAnnotationArrayMember)annotationUsage.getParam("value");
if (values != null && values.size() > 0) {
Collection<JAnnotationUse> annotations = values.annotations();
for (JAnnotationUse annotation: annotations) {
read(interfaceMethod, annotation);
}
JAnnotationUse[] annotations = Source.getAnnotationArgument(annotationUsage, "value", JAnnotationUse[].class);
for (JAnnotationUse annotation: annotations) {
read(interfaceMethod, annotation);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
import com.github.sviperll.adt4j.GenerateValueClassForVisitor;
import com.github.sviperll.adt4j.GenerateValueClassForVisitorProcessor;
import com.github.sviperll.meta.CodeModelBuildingException;
import com.github.sviperll.adt4j.model.util.Serialization;
import com.github.sviperll.adt4j.model.util.Types;
import com.github.sviperll.adt4j.model.util.ValueVisitorInterfaceModel;
import com.github.sviperll.meta.SourceCodeValidationException;
Expand All @@ -42,7 +41,6 @@
import com.helger.jcodemodel.EClassType;
import com.helger.jcodemodel.JAnnotationUse;
import com.helger.jcodemodel.JClassAlreadyExistsException;
import com.helger.jcodemodel.JCodeModel;
import com.helger.jcodemodel.JDefinedClass;
import com.helger.jcodemodel.JExpr;
import com.helger.jcodemodel.JMethod;
Expand All @@ -56,51 +54,24 @@
import java.util.Map;

public class ValueClassModelFactory {
private static final String VISITOR_SUFFIX = "Visitor";
private static final String VALUE_SUFFIX = "Value";

public static JDefinedClass createValueClass(JCodeModel jCodeModel, JDefinedClass jVisitorModel, Visitor visitorAnnotation, GenerateValueClassForVisitor annotation) throws SourceCodeValidationException, CodeModelBuildingException {
public static JDefinedClass createValueClass(JDefinedClass jVisitorModel, Visitor visitorAnnotation) throws SourceCodeValidationException, CodeModelBuildingException {
JAnnotationUse annotation = null;
for (JAnnotationUse anyAnnotation: jVisitorModel.annotations()) {
if (anyAnnotation.getAnnotationClass().fullName().equals(GenerateValueClassForVisitor.class.getName()))
annotation = anyAnnotation;
}
if (annotation == null)
throw new IllegalStateException("ValueClassModelFactory can't be run for interface without " + GenerateValueClassForVisitor.class + " annotation");
ValueVisitorInterfaceModel visitorModel = ValueVisitorInterfaceModel.createInstance(jVisitorModel, visitorAnnotation, annotation);
Serialization serialization = serialization(annotation);
String valueClassName = valueClassName(jVisitorModel, annotation);
ValueClassModelFactory factory = new ValueClassModelFactory(jVisitorModel._package(), valueClassName, serialization, annotation);
ValueClassModel valueClassModel = factory.createValueClass(jCodeModel, visitorModel);
ValueClassModelFactory factory = new ValueClassModelFactory(jVisitorModel._package());
ValueClassModel valueClassModel = factory.createValueClass(visitorModel);
return valueClassModel.getJDefinedClass();
}

private static String valueClassName(JDefinedClass jVisitorModel, GenerateValueClassForVisitor annotation) {
if (!annotation.className().equals(":auto")) {
return annotation.className();
} else {
String visitorName = jVisitorModel.name();
if (visitorName == null)
throw new IllegalStateException("Visitor interface without a name: " + jVisitorModel);
else {
if (visitorName.endsWith(VISITOR_SUFFIX))
return visitorName.substring(0, visitorName.length() - VISITOR_SUFFIX.length());
else
return visitorName + VALUE_SUFFIX;
}
}
}

private static Serialization serialization(GenerateValueClassForVisitor annotation) {
if (!annotation.isSerializable())
return Serialization.notSerializable();
else
return Serialization.serializable(annotation.serialVersionUID());
}

private final Serialization serialization;
private final GenerateValueClassForVisitor annotation;
private final JPackage jpackage;
private final String className;

ValueClassModelFactory(JPackage jpackage, String className, Serialization serialization, GenerateValueClassForVisitor annotation) {
this.serialization = serialization;
this.annotation = annotation;
ValueClassModelFactory(JPackage jpackage) {
this.jpackage = jpackage;
this.className = className;
}

private JDefinedClass createAcceptingInterface(JDefinedClass valueClass,
Expand Down Expand Up @@ -136,10 +107,10 @@ private JDefinedClass createAcceptingInterface(JDefinedClass valueClass,
return acceptingInterface;
}

ValueClassModel createValueClass(JCodeModel jCodeModel, ValueVisitorInterfaceModel visitorInterface) throws SourceCodeValidationException, CodeModelBuildingException {
ValueClassModel createValueClass(ValueVisitorInterfaceModel visitorInterface) throws SourceCodeValidationException, CodeModelBuildingException {
try {
Types types = Types.createInstance(jpackage.owner());
if (annotation.isSerializable()) {
if (visitorInterface.isValueClassSerializable()) {
for (JMethod interfaceMethod: visitorInterface.methods()) {
for (JVar param: interfaceMethod.params()) {
AbstractJType type = param.type();
Expand All @@ -155,7 +126,7 @@ ValueClassModel createValueClass(JCodeModel jCodeModel, ValueVisitorInterfaceMod
}
}

if (annotation.isComparable()) {
if (visitorInterface.isValueClassComparable()) {
for (JMethod interfaceMethod: visitorInterface.methods()) {
for (JVar param: interfaceMethod.params()) {
AbstractJType type = param.type();
Expand All @@ -171,43 +142,40 @@ ValueClassModel createValueClass(JCodeModel jCodeModel, ValueVisitorInterfaceMod
}
}

int mods = annotation.isPublic() ? JMod.PUBLIC: JMod.NONE;
JDefinedClass valueClass = jpackage._class(mods, className, EClassType.CLASS);
if (!annotation.baseInterface().equals("")) {
AbstractJClass marker = jCodeModel.ref(annotation.baseInterface());
valueClass._implements(marker);
}
if (!annotation.baseClass().equals("")) {
AbstractJClass marker = jCodeModel.ref(annotation.baseClass());
valueClass._extends(marker);
}
int mods = visitorInterface.isValueClassPublic() ? JMod.PUBLIC: JMod.NONE;
JDefinedClass valueClass = jpackage._class(mods, visitorInterface.valueClassName(), EClassType.CLASS);
JAnnotationUse generatedAnnotation = valueClass.annotate(Generated.class);
generatedAnnotation.param("value", GenerateValueClassForVisitorProcessor.class.getName());
valueClass.annotate(ParametersAreNonnullByDefault.class);
for (JTypeVar visitorTypeParameter: visitorInterface.getValueTypeParameters()) {
Types.generifyWithBoundsFrom(valueClass, visitorTypeParameter.name(), visitorTypeParameter);
}
if (annotation.isSerializable()) {
for (AbstractJClass iface: visitorInterface.implementsInterfaces()) {
valueClass._implements(iface.typeParams().length == 0 ? iface : iface.narrow(valueClass.typeParams()));
}
AbstractJClass extendsClass = visitorInterface.valueClassExtends();
valueClass._extends(extendsClass.typeParams().length == 0 ? extendsClass : extendsClass.narrow(valueClass.typeParams()));
if (visitorInterface.isValueClassSerializable()) {
valueClass._implements(types._Serializable);
valueClass.field(JMod.PRIVATE | JMod.FINAL | JMod.STATIC, types._long, "serialVersionUID", JExpr.lit(annotation.serialVersionUID()));
valueClass.field(JMod.PRIVATE | JMod.FINAL | JMod.STATIC, types._long, "serialVersionUID", JExpr.lit(visitorInterface.serialVersionUIDForGeneratedCode()));
}

if (annotation.isComparable()) {
if (visitorInterface.isValueClassComparable()) {
valueClass._implements(types._Comparable.narrow(valueClass.narrow(valueClass.typeParams())));
}

JDefinedClass acceptingInterface = createAcceptingInterface(valueClass, visitorInterface, types);
if (annotation.isSerializable()) {
if (visitorInterface.isValueClassSerializable()) {
acceptingInterface._extends(types._Serializable);
}

ValueClassModel result = new ValueClassModel(valueClass, acceptingInterface, visitorInterface, types);
ValueClassModel.MethodBuilder methodBuilder = result.createMethodBuilder(serialization);
Map<String, JMethod> constructorMethods = methodBuilder.buildConstructorMethods(serialization);
ValueClassModel.MethodBuilder methodBuilder = result.createMethodBuilder(visitorInterface.serialization());
Map<String, JMethod> constructorMethods = methodBuilder.buildConstructorMethods(visitorInterface.serialization());
methodBuilder.buildPrivateConstructor();
if (serialization.isSerializable())
if (visitorInterface.isValueClassSerializable())
methodBuilder.buildReadObjectMethod();
methodBuilder.buildProtectedConstructor(serialization);
methodBuilder.buildProtectedConstructor(visitorInterface.serialization());
methodBuilder.buildAcceptMethod();
Map<String, FieldConfiguration> gettersConfigutation = result.getGettersConfigutation();
for (FieldConfiguration getter: gettersConfigutation.values()) {
Expand All @@ -221,11 +189,11 @@ ValueClassModel createValueClass(JCodeModel jCodeModel, ValueVisitorInterfaceMod
for (Map.Entry<String, PredicateConfigutation> predicate: predicates.entrySet()) {
methodBuilder.generatePredicate(predicate.getKey(), predicate.getValue());
}
if (annotation.isComparable()) {
if (visitorInterface.isValueClassComparable()) {
methodBuilder.buildCompareTo();
}
methodBuilder.buildEqualsMethod();
methodBuilder.buildHashCodeMethod(annotation.hashCodeBase());
methodBuilder.buildHashCodeMethod(visitorInterface.hashCodeBase());
methodBuilder.buildToStringMethod();
result.buildFactory(constructorMethods);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,31 +30,52 @@
package com.github.sviperll.adt4j.model.util;

import com.github.sviperll.meta.MemberAccess;
import com.helger.jcodemodel.AbstractJClass;

/**
*
* @author Victor Nazarov <asviraspossible@gmail.com>
*/
class APICustomization {
private final String acceptMethodName;
private final boolean isValueClassPublic;
private final MemberAccess acceptMethodAccessLevel;
public APICustomization(String acceptMethodName, MemberAccess acceptMethodAccessLevel, boolean isValueClassPublic) {
this.acceptMethodName = acceptMethodName;
private final AcceptMethodCustomization acceptMethod;
private final InterfacesCustomization interfaces;
public APICustomization(boolean isValueClassPublic, AcceptMethodCustomization acceptMethod, InterfacesCustomization interfaces) {
this.isValueClassPublic = isValueClassPublic;
this.acceptMethodAccessLevel = acceptMethodAccessLevel;
this.acceptMethod = acceptMethod;
this.interfaces = interfaces;
}

public String acceptMethodName() {
return acceptMethodName;
return acceptMethod.acceptMethodName();
}

public boolean isValueClassPublic() {
public boolean isPublic() {
return isValueClassPublic;
}

MemberAccess acceptMethodAccessLevel() {
return acceptMethodAccessLevel;
return acceptMethod.acceptMethodAccessLevel();
}

public boolean isSerializable() {
return interfaces.isSerializable();
}

boolean isComparable() {
return interfaces.isComparable();
}

AbstractJClass[] interfaces() {
return interfaces.interfaces();
}

Serialization serialization() {
return interfaces.serialization();
}

long serialVersionUIDForGeneratedCode() {
return interfaces.serialVersionUIDForGeneratedCode();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright (c) 2015, Victor Nazarov <asviraspossible@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.github.sviperll.adt4j.model.util;

import com.github.sviperll.meta.MemberAccess;

/**
*
* @author Victor Nazarov <asviraspossible@gmail.com>
*/
class AcceptMethodCustomization {
private final String acceptMethodName;
private final MemberAccess acceptMethodAccessLevel;
public AcceptMethodCustomization(String acceptMethodName, MemberAccess acceptMethodAccessLevel) {
this.acceptMethodName = acceptMethodName;
this.acceptMethodAccessLevel = acceptMethodAccessLevel;
}

public String acceptMethodName() {
return acceptMethodName;
}

MemberAccess acceptMethodAccessLevel() {
return acceptMethodAccessLevel;
}

}
Loading

0 comments on commit e98914a

Please sign in to comment.