Unverified Commit b0b41691 authored by Daniel Bluhm's avatar Daniel Bluhm Committed by GitHub

Merge pull request #476 from dbluhm/feature/writer-improvements

Use metadata classes in writers directly
parents 4ddedec2 fa187fbd
......@@ -13,7 +13,7 @@ import org.slf4j.LoggerFactory;
/**
* Abstract base class for AnnotationExtractors providing common methods to
* all extractors.
*
*
* @author Daniel Bluhm
*
* @param <T> Type of data extracted from element.
......@@ -78,11 +78,11 @@ public abstract class AbstractAnnotationExtractor<T> implements AnnotationExtrac
/**
* Find and return annotation mirror of a given type if present.
*
*
* When an annotation value is a Class, Annotation Mirrors must be used to
* retrieve a TypeMirror to the class (using {@link
* Elements#getElementValuesWithDefaults(AnnotationMirror)}).
*
*
* If the annotation in question has no Class values, it is recommended to
* use the Annotation directly with {@link #getAnnotation(Element, Class)}.
*
......
......@@ -11,7 +11,13 @@
package org.eclipse.ice.dev.annotations.processors;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
/**
......@@ -20,21 +26,68 @@ import lombok.Getter;
*
* @author Daniel Bluhm
*/
@Getter
@Builder
@Getter
@EqualsAndHashCode
@JsonDeserialize(builder = DataElementMetadata.DataElementMetadataBuilder.class)
@JsonAutoDetect(
fieldVisibility = Visibility.ANY,
getterVisibility = Visibility.NONE,
isGetterVisibility = Visibility.NONE,
setterVisibility = Visibility.NONE
)
public class DataElementMetadata {
/**
* Suffix for Implementation name.
*/
private static final String IMPL_SUFFIX = "Implementation";
/**
* Base name of classes to be generated.
*/
protected String name;
@JsonProperty
private String name;
/**
* Package of classes to be generated.
*/
protected String packageName;
@JsonProperty("package")
private String packageName;
/**
* Collected fields of the DataElement.
*/
protected Fields fields;
@JsonProperty
private Fields fields;
/**
* Fully qualified name of this DataElement.
* @return fully qualified name.
*/
public String getFullyQualifiedName() {
String value = null;
if (packageName != null) {
value = String.format("%s.%s", packageName, name);
} else {
value = name;
}
return value;
}
/**
* Name of implementation for this DataElement.
* @return Implementation name.
*/
public String getImplementationName() {
return name + IMPL_SUFFIX;
}
/**
* Fully qualified name of this DataElement's implementation.
* @return fully qualified implementation name.
*/
public String getFullyQualifiedImplName() {
return getFullyQualifiedName() + IMPL_SUFFIX;
}
}
\ No newline at end of file
......@@ -42,26 +42,10 @@ public class DataElementWriterGenerator implements WriterGenerator {
@Override
public List<GeneratedFileWriter> generate() {
List<GeneratedFileWriter> writers = new ArrayList<>();
Fields nonDefaults = data.getFields().getNonDefaultFields();
writers.add(InterfaceWriter.builder()
.packageName(data.getPackageName())
.interfaceName(data.getName())
.fields(nonDefaults)
.types(nonDefaults.getTypes())
.build());
writers.add(ImplementationWriter.builder()
.packageName(data.getPackageName())
.interfaceName(data.getName())
.className(data.getName() + "Implementation")
.fields(data.getFields())
.types(data.getFields().getTypes())
.build());
writers.add(new InterfaceWriter(data));
writers.add(new ImplementationWriter(data));
try {
writers.add(TypeScriptWriter.builder()
.name(data.getName())
.fields(nonDefaults)
.types(nonDefaults.getTypes())
.build());
writers.add(new TypeScriptWriter(data));
} catch (UnsupportedOperationException e) {
logger.info("Failed to create typescript writer for element:", e);
}
......
......@@ -17,20 +17,27 @@ import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.EqualsAndHashCode;
/**
* A collection of Field objects to be used especially in template rendering.
*
* @author Daniel Bluhm
*/
@EqualsAndHashCode
public class Fields implements Iterable<Field> {
/**
* The collection of fields on which views will be retrieved.
*/
private List<Field> fields;
@JsonValue
private List<Field> list;
public Fields() {
this.fields = new ArrayList<>();
this.list = new ArrayList<>();
}
/**
......@@ -38,9 +45,10 @@ public class Fields implements Iterable<Field> {
*
* @param fields initial fields
*/
@JsonCreator
public Fields(Collection<Field> fields) {
this.fields = new ArrayList<>();
this.fields.addAll(fields);
this.list = new ArrayList<>();
this.list.addAll(fields);
}
/**
......@@ -49,7 +57,7 @@ public class Fields implements Iterable<Field> {
* @param fields to add
*/
public void collect(Collection<Field> fields) {
this.fields.addAll(fields);
this.list.addAll(fields);
}
/**
......@@ -59,7 +67,7 @@ public class Fields implements Iterable<Field> {
* @see Field#isConstant()
*/
public Iterator<Field> getConstants() {
return fields.stream()
return list.stream()
.filter(Field::isConstant)
.iterator();
}
......@@ -71,7 +79,7 @@ public class Fields implements Iterable<Field> {
* @see Field#isConstant()
*/
public Iterator<Field> getMutable() {
return fields.stream()
return list.stream()
.filter(field -> !field.isConstant())
.iterator();
}
......@@ -83,7 +91,7 @@ public class Fields implements Iterable<Field> {
* @see org.eclipse.ice.data.IDataElement#matches(Object)
*/
public Iterator<Field> getMatch() {
return fields.stream()
return list.stream()
.filter(Field::isMatch)
.iterator();
}
......@@ -96,7 +104,7 @@ public class Fields implements Iterable<Field> {
* @see Field#isVarDifferent()
*/
public Iterator<Field> getVarNamesDiffer() {
return fields.stream()
return list.stream()
.filter(Field::isVarNameDifferent)
.iterator();
}
......@@ -106,7 +114,7 @@ public class Fields implements Iterable<Field> {
* @return Iterable of fields needed for interface.
*/
public Iterable<Field> getInterfaceFields() {
return fields.stream()
return list.stream()
.filter(field -> !field.isDefaultField())
.collect(Collectors.toList());
}
......@@ -117,7 +125,7 @@ public class Fields implements Iterable<Field> {
*/
public Fields getNonDefaultFields() {
return new Fields(
fields.stream()
list.stream()
.filter(field -> !field.isDefaultField())
.collect(Collectors.toList())
);
......
......@@ -12,9 +12,6 @@
package org.eclipse.ice.dev.annotations.processors;
import lombok.Builder;
import lombok.NonNull;
import java.io.IOException;
import java.io.Writer;
......@@ -74,22 +71,14 @@ public class ImplementationWriter
* @param fields
* @param generatedFile
*/
@Builder
public ImplementationWriter(
String packageName, String interfaceName, String className,
@NonNull Fields fields, @NonNull Types types
) {
public ImplementationWriter(DataElementMetadata data) {
super(IMPL_TEMPLATE);
if (packageName != null) {
this.fqn = String.format("%s.%s", packageName, className);
} else {
this.fqn = className;
}
this.context.put(PACKAGE, packageName);
this.context.put(INTERFACE, interfaceName);
this.context.put(CLASS, className);
this.context.put(FIELDS, fields);
this.context.put(TYPES, types);
this.fqn = data.getFullyQualifiedImplName();
this.context.put(PACKAGE, data.getPackageName());
this.context.put(INTERFACE, data.getName());
this.context.put(CLASS, data.getImplementationName());
this.context.put(FIELDS, data.getFields());
this.context.put(TYPES, data.getFields().getTypes());
}
@Override
......
......@@ -17,9 +17,6 @@ import java.io.Writer;
import javax.annotation.processing.Filer;
import lombok.Builder;
import lombok.NonNull;
/**
* Writer for DataElement Interfaces.
*
......@@ -72,21 +69,16 @@ public class InterfaceWriter
* @param fields
* @param generatedFile
*/
@Builder
public InterfaceWriter(
String packageName, String interfaceName, @NonNull Fields fields,
@NonNull Types types
DataElementMetadata data
) {
super(TEMPLATE);
if (packageName != null) {
this.fqn = String.format("%s.%s", packageName, interfaceName);
} else {
this.fqn = interfaceName;
}
context.put(PACKAGE, packageName);
context.put(INTERFACE, interfaceName);
this.fqn = data.getFullyQualifiedName();
Fields fields = data.getFields().getNonDefaultFields();
context.put(PACKAGE, data.getPackageName());
context.put(INTERFACE, data.getName());
context.put(FIELDS, fields);
context.put(TYPES, types);
context.put(TYPES, fields.getTypes());
}
@Override
......
......@@ -17,9 +17,6 @@ import java.io.Writer;
import javax.annotation.processing.Filer;
import lombok.Builder;
import lombok.NonNull;
/**
* Writer for DataElement Persistence classes.
*
......@@ -45,11 +42,6 @@ public class PersistenceHandlerWriter
*/
private static final String CLASS = "class";
/**
* Context key for interface of PersistenceHandlers
*/
private static final String INTERFACE = "interface";
/**
* Context key for collection.
*/
......@@ -79,38 +71,27 @@ public class PersistenceHandlerWriter
* Fully qualified name of the class for file output.
*/
private String fqn;
/**
* Constructor
*
* @param packageName
* @param elementInterface
* @param className
* @param interfaceName
* @param implementation
* @param collection
* @param fields
* @param generatedFile
* Create instance of persistence handler writer from metadata.
* @param dataElement DataElementMetadata
* @param persistence PersistenceMetadata
*/
@Builder
public PersistenceHandlerWriter(
String packageName, String elementInterface, String className, String
interfaceName, String implementation, String collection, @NonNull Fields
fields, @NonNull Types types
DataElementMetadata dataElement,
PersistenceMetadata persistence
) {
super(TEMPLATE);
if (packageName != null) {
this.fqn = String.format("%s.%s", packageName, className);
} else {
this.fqn = className;
}
this.context.put(PACKAGE, packageName);
this.context.put(ELEMENT_INTERFACE, elementInterface);
this.context.put(CLASS, className);
this.context.put(INTERFACE, interfaceName);
this.context.put(COLLECTION, collection);
this.context.put(IMPLEMENTATION, implementation);
this.context.put(FIELDS, fields);
this.context.put(TYPES, types);
this.fqn = persistence.getHandlerName(
dataElement.getFullyQualifiedName()
);
this.context.put(PACKAGE, dataElement.getPackageName());
this.context.put(ELEMENT_INTERFACE, dataElement.getName());
this.context.put(CLASS, persistence.getHandlerName(dataElement.getName()));
this.context.put(COLLECTION, persistence.getCollection());
this.context.put(IMPLEMENTATION, dataElement.getImplementationName());
this.context.put(FIELDS, dataElement.getFields());
this.context.put(TYPES, dataElement.getFields().getTypes());
}
@Override
......
......@@ -21,8 +21,22 @@ import lombok.Getter;
@Getter
@Builder
public class PersistenceMetadata {
/**
* Suffix to PersistenceHandler class names.
*/
private static final String SUFFIX = "PersistenceHandler";
/**
* Collection name for persistence.
*/
private String collection;
/**
* Get Persistence Handler class name using data element metadata.
* @param dataElementData DataElementMetadata
* @return persistence handler class name.
*/
public String getHandlerName(String dataElementName) {
return dataElementName + SUFFIX;
}
}
......@@ -35,17 +35,7 @@ public class PersistenceWriterGenerator implements WriterGenerator {
@Override
public List<GeneratedFileWriter> generate() {
List<GeneratedFileWriter> writers = List.of(
PersistenceHandlerWriter.builder()
.packageName(dataElement.getPackageName())
.elementInterface(dataElement.getName())
.className(dataElement.getName() + "PersistenceHandler")
// TODO Just move interface name into template (it's static)
.interfaceName("IPersistenceHandler")
.implementation(dataElement.getName()+ "Implementation")
.collection(persistence.getCollection())
.fields(dataElement.getFields())
.types(dataElement.getFields().getTypes())
.build()
new PersistenceHandlerWriter(dataElement, persistence)
);
return writers;
}
......
......@@ -18,9 +18,6 @@ import java.util.Map;
import javax.annotation.processing.Filer;
import javax.tools.StandardLocation;
import lombok.Builder;
import lombok.NonNull;
/**
* Writer for TypeScript representation of DataElement.
* @author Daniel Bluhm
......@@ -80,11 +77,11 @@ public class TypeScriptWriter
* @param types of fields.
* @throws UnsupportedOperationException When any field is not supported.
*/
@Builder
public TypeScriptWriter(
String name, @NonNull Fields fields, @NonNull Types types
DataElementMetadata data
) {
super(TYPESCRIPT_TEMPLATE);
Fields fields = data.getFields().getNonDefaultFields();
for (Field field : fields) {
if (!primitiveMap.containsKey(field.getType())) {
throw new UnsupportedOperationException(String.format(
......@@ -93,10 +90,10 @@ public class TypeScriptWriter
));
}
}
this.filename = name;
this.context.put(NAME, name);
this.filename = data.getName();
this.context.put(NAME, data.getName());
this.context.put(FIELDS, fields);
this.context.put(TYPES, types);
this.context.put(TYPES, fields.getTypes());
this.context.put(PRIMITIVE_MAP, primitiveMap);
}
......
......@@ -66,7 +66,7 @@ public abstract class VelocitySourceWriter implements FileWriter {
/**
* Template for writing. Should be filled by concrete classes.
*/
protected String template;
private String template;
/**
* Context for template. Should be filled by concrete classes.
......
......@@ -13,11 +13,11 @@ import com.mongodb.client.MongoDatabase;
#imports
/**
* This is an implementation of $interface<$elementInterface>
* This is an implementation of IPersistenceHandler<$elementInterface>
* that satisfies the dependencies of the {@code @Persisted} Annotation and was
* auto-generated by the ICE Framework.
*/
public class $class implements $interface<$elementInterface> {
public class $class implements IPersistenceHandler<$elementInterface> {
/**
* The name of the collection backing this PersistenceHandler.
......
......@@ -9,25 +9,29 @@
* Daniel Bluhm - Initial implementation
*******************************************************************************/
package org.eclipse.ice.tests.dev.pojofromjson;
package org.eclipse.ice.tests.dev.annotations.processors;
import org.eclipse.ice.dev.annotations.processors.DataElementMetadata;
import org.eclipse.ice.dev.annotations.processors.Field;
import org.eclipse.ice.dev.pojofromjson.PojoOutline;
import org.eclipse.ice.dev.annotations.processors.Fields;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import static org.junit.jupiter.api.Assertions.*;
import java.util.List;
import org.junit.jupiter.api.Test;
public class PojoOutlineTest {
class DataElementMetadataTest {
@Test
public void testSerialization() throws JsonProcessingException {
PojoOutline pojo = PojoOutline.builder()
void testSerialization() throws JsonProcessingException {
DataElementMetadata pojo = DataElementMetadata.builder()
.packageName("testpackage")
.element("TestElement")
.field(
.name("TestElement")
.fields(new Fields(List.of(
Field.builder()
.name("test")
.type(String.class)
......@@ -36,21 +40,21 @@ public class PojoOutlineTest {
.primitive(true)
.nullable(true)
.build()
).build();