diff --git a/org.eclipse.ice.dev.annotations/.classpath b/org.eclipse.ice.dev.annotations/.classpath
index 6e0021feb..90dbf04f5 100644
--- a/org.eclipse.ice.dev.annotations/.classpath
+++ b/org.eclipse.ice.dev.annotations/.classpath
@@ -37,17 +37,11 @@
-
-
-
-
-
-
diff --git a/org.eclipse.ice.dev.annotations/pom.xml b/org.eclipse.ice.dev.annotations/pom.xml
index bf6305502..a0bb33669 100644
--- a/org.eclipse.ice.dev.annotations/pom.xml
+++ b/org.eclipse.ice.dev.annotations/pom.xml
@@ -18,8 +18,7 @@
maven-compiler-plugin3.8.1
-
- 1.8
+ 11com.google.auto.service
diff --git a/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/DataField.java b/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/DataField.java
index 6d004adbd..745ee6d88 100644
--- a/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/DataField.java
+++ b/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/DataField.java
@@ -12,4 +12,5 @@
public @interface DataField {
String fieldName();
Class> fieldType() default String.class;
+ String docString() default "";
}
diff --git a/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/IDataElement.java b/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/IDataElement.java
index b7e0a98bd..3a9d4a269 100644
--- a/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/IDataElement.java
+++ b/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/IDataElement.java
@@ -1,9 +1,153 @@
package org.eclipse.ice.dev.annotations;
+import java.util.UUID;
+
/**
* Marker interface for DataElements.
*/
public interface IDataElement {
+
+ /**
+ * Get the public identifier of the data element. This is a common id that may
+ * or may not be unique to this data element.
+ *
+ * @return the public id
+ */
+ public long getId();
+
+ /**
+ * Set the public identifier of the data element. This is a common id that may
+ * or may not be unique to this data element.
+ *
+ * @param public_id the public id
+ * @throws Exception An exception is thrown if the value is null, which is
+ * unallowable.
+ */
+ public void setId(final long public_id) throws Exception;
+
+ /**
+ * This operation returns the simple name of the data
+ *
+ * @return the name of the data, i.e. - "CORD-19" or "Steve"
+ */
+ public String getName();
+
+ /**
+ * Set the simple name of the element
+ *
+ * @param elemName a simple name
+ * @throws Exception An exception is thrown if the value is null, which is
+ * unallowable.
+ */
+ public void setName(final String name) throws Exception;
+
+ /**
+ * Get the simple description of the data, i.e. - "Machine readable data for
+ * COVID-19 research."
+ *
+ * @return the description of the data
+ */
+ public String getDescription();
+
+ /**
+ * Set the description of the data
+ *
+ * @param desc the description
+ * @throws Exception An exception is thrown if the value is null, which is
+ * unallowable.
+ */
+ public void setDescription(final String desc) throws Exception;
+
+ /**
+ * Get the comment/tag that annotates this data. This value is different than
+ * the description in that it provides commentary or a secondary designation in
+ * the form of an annotation for the data. For example, where the description
+ * should generally be useful, this value could simply be "2020Data" or any
+ * other tag of convenience used during processing.
+ *
+ * @return the comment
+ */
+ public String getComment();
+
+ /**
+ * Return the comment or tag that annotates the data
+ *
+ * @param comment the comment to set
+ * @throws Exception An exception is thrown if the value is null, which is
+ * unallowable.
+ */
+ public void setComment(final String comment) throws Exception;
+
+ /**
+ * Get the context of the data in its present state. For example, a single
+ * physical sample may be used across multiple experiments and the context in
+ * one case may be "x-ray scattering" whereas in another it could be "neutron
+ * scattering." Another example is when the same data is being used by two
+ * clients and this value changes from "ornl.gov" to "lbnl.gov" to indicate that
+ * a client should dynamically adapt without changing the data otherwise.
+ *
+ * @return the context
+ */
+ public String getContext();
+
+ /**
+ * Return the context in which the data exists.
+ *
+ * @param context the context to set
+ * @throws Exception An exception is thrown if the value is null, which is
+ * unallowable.
+ */
+ public void setContext(final String context) throws Exception;
+
+ /**
+ * True if the element is required by the client, false otherwise. This is only
+ * for client tracking and may make no sense for different clients.
+ *
+ * @return true if required, false if not
+ */
+ public boolean isRequired();
+
+ /**
+ * True if the element is required by the client, false otherwise. This is only
+ * for client side tracking and may make no sense for different clients.
+ *
+ * @param required true if required, false if not
+ */
+ public void setRequired(final boolean required);
+
+ /**
+ * True if the element is something that should be secret by the client, false
+ * otherwise. This is only for client tracking and may make no sense for
+ * different clients.
+ *
+ * @return true if secret, false if not
+ */
+ public boolean isSecret();
+
+ /**
+ * True if the element is something that should be secret by the client, false
+ * otherwise. This is only for client tracking and may make no sense for
+ * different clients.
+ *
+ * @param secret true if the element should be treated as a secret, false
+ * otherwise
+ */
+ public void setSecret(final boolean secret);
+
+ /**
+ * This operation returns the UUID of the data element. The UUID is a private
+ * unique identifier assigned to all data elements.
+ *
+ * @return the UUID for this element
+ */
+ public UUID getUUID();
+
+ /**
+ * Format the DataElement as an output friendly string.
+ * @return String representation of Data
+ */
+ public String toString();
+
/**
* This function checks deep equality of DataElements to see if all members are
* equal ("match") with the exception of fields with match set to false (such
diff --git a/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/processors/DataElementProcessor.java b/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/processors/DataElementProcessor.java
index 86ed16e76..d2f07c292 100644
--- a/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/processors/DataElementProcessor.java
+++ b/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/processors/DataElementProcessor.java
@@ -6,7 +6,9 @@
import java.io.StringWriter;
import java.io.Writer;
import java.util.Arrays;
+import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
@@ -72,6 +74,21 @@ public static List getAnnotationValuesForMirror(
.collect(Collectors.toList());
}
+ /**
+ * Get a list of annotation values from an annotation mirror.
+ * @param mirror the mirror from which to grab values.
+ * @return list of AnnotationValue
+ */
+ public static Map getAnnotationValueMapForMirror(
+ final Elements elementUtils, final AnnotationMirror mirror
+ ) {
+ return (Map) elementUtils.getElementValuesWithDefaults(mirror).entrySet().stream()
+ .collect(Collectors.toMap(
+ entry -> entry.getKey().getSimpleName().toString(),
+ entry -> entry.getValue().getValue()
+ ));
+ }
+
/**
* Return stack trace as string.
* @param e subject exception
@@ -100,14 +117,11 @@ private static void unwrap(final Optional e) throws T {
protected Elements elementUtils;
protected DataFieldsVisitor fieldsVisitor;
- protected DataFieldVisitor fieldVisitor;
-
@Override
public void init(final ProcessingEnvironment env) {
messager = env.getMessager();
elementUtils = env.getElementUtils();
- fieldVisitor = new DataFieldVisitor();
- fieldsVisitor = new DataFieldsVisitor(elementUtils, fieldVisitor);
+ fieldsVisitor = new DataFieldsVisitor(elementUtils);
// Set up Velocity using the Singleton approach; ClasspathResourceLoader allows
// us to load templates from src/main/resources
@@ -130,7 +144,7 @@ public boolean process(final Set extends TypeElement> annotations, final Round
return false;
}
- final Fields fields = new Fields();
+ List fields = new ArrayList();
fields.addAll(DefaultFields.get());
try {
@@ -158,9 +172,9 @@ public boolean process(final Set extends TypeElement> annotations, final Round
* @return discovered fields
* @throws IOException
*/
- private Fields collectFromDataFieldJson(Element element) throws IOException {
+ private List collectFromDataFieldJson(Element element) throws IOException {
final List extends AnnotationMirror> mirrors = element.getAnnotationMirrors();
- Fields fields = new Fields();
+ List fields = new ArrayList<>();
// Iterate through AnnotationValues of AnnotationMirrors for DataFieldJson
for (
final AnnotationValue value : mirrors.stream()
@@ -197,40 +211,38 @@ private Fields collectFromDataFieldJson(Element element) throws IOException {
* @return discovered fields
* @throws UnexpectedValueError
*/
- private Fields collectFromDataFields(Element element) throws UnexpectedValueError {
+ private List collectFromDataFields(Element element) throws UnexpectedValueError {
final List extends AnnotationMirror> mirrors = element.getAnnotationMirrors();
- Fields fields = new Fields();
+ List fields = new ArrayList<>();
// Iterate over the AnnotationValues of AnnotationMirrors of type DataFields.
// DataFields present when more than one DataField annotation is used.
for (
final AnnotationValue value : mirrors.stream()
- .filter(
- mirror -> mirror.getAnnotationType().toString().equals(
- DataFields.class.getCanonicalName()
+ .filter(
+ mirror -> mirror.getAnnotationType().toString().equals(
+ DataFields.class.getCanonicalName()
)
)
- .map(mirror -> getAnnotationValuesForMirror(elementUtils, mirror))
- .flatMap(List::stream) // Flatten List to List
- .collect(Collectors.toList())
- ) {
+ .map(mirror -> getAnnotationValuesForMirror(elementUtils, mirror))
+ .flatMap(List::stream) // Flatten List to List
+ .collect(Collectors.toList())
+ ) {
// Traditional for-loop used to allow raising an exception with unwrap if the
// field visitor returns an error result
unwrap(value.accept(fieldsVisitor, fields));
}
- // Iterate over the AnnotationValues of AnnotationMirrors of type DataField.
- // Only present when only one DataField annotation is used.
+ // Iterate over any DataField Annotations. Only present when only one DataField
+ // annotation is used.
for (
- final AnnotationValue value : mirrors.stream()
- .filter(
- mirror -> mirror.getAnnotationType().toString().equals(
- DataField.class.getCanonicalName()
+ final AnnotationMirror dataFieldMirror : mirrors.stream()
+ .filter(
+ mirror -> mirror.getAnnotationType().toString().equals(
+ DataField.class.getCanonicalName()
)
)
- .map(mirror -> getAnnotationValuesForMirror(elementUtils, mirror))
- .flatMap(List::stream)
- .collect(Collectors.toList())
- ) {
- unwrap(value.accept(fieldVisitor, fields));
+ .collect(Collectors.toList())
+ ) {
+ unwrap(fieldsVisitor.visitAnnotation(dataFieldMirror, fields));
}
return fields;
}
@@ -242,7 +254,7 @@ private Fields collectFromDataFields(Element element) throws UnexpectedValueErro
* @param fields the fields extracted from DataField annotations on interface
* @throws IOException
*/
- private void writeClass(final String interfaceName, final Fields fields) throws IOException {
+ private void writeClass(final String interfaceName, final List fields) throws IOException {
// Determine package, class name from annotated interface name
String packageName = null;
final int lastDot = interfaceName.lastIndexOf('.');
@@ -258,8 +270,7 @@ private void writeClass(final String interfaceName, final Fields fields) throws
context.put(ContextProperty.PACKAGE.key(), packageName);
context.put(ContextProperty.INTERFACE.key(), simpleName);
context.put(ContextProperty.CLASS.key(), generatedSimpleClassName);
- // Hand over the list directly so template can #foreach on fields
- context.put(ContextProperty.FIELDS.key(), fields.getFields());
+ context.put(ContextProperty.FIELDS.key(), fields);
// Write to file
final JavaFileObject generatedClassFile = processingEnv.getFiler().createSourceFile(generatedClassName);
diff --git a/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/processors/DataFieldVisitor.java b/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/processors/DataFieldVisitor.java
deleted file mode 100644
index 58047f593..000000000
--- a/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/processors/DataFieldVisitor.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package org.eclipse.ice.dev.annotations.processors;
-
-import java.util.Optional;
-
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.SimpleAnnotationValueVisitor8;
-
-import org.eclipse.ice.dev.annotations.processors.Fields;
-
-/**
- * Visitor that accumulates DataField information from AnnotationValues. This
- * Visitor is only intended for use on the AnnotationValues of DataField
- * AnnotationMirrors.
- *
- * Returns an Optional to report errors to the caller
- * while conforming to the AnnotationValueVisitor Interface which does not throw
- * any exceptions. Return value should be checked.
- *
- * Additional Parameter Fields acts as the data accumulator, appending visited
- * data to its list of fields.
- */
-class DataFieldVisitor extends SimpleAnnotationValueVisitor8, Fields> {
-
- /**
- * Check if the given type mirror represents a primitive type.
- * @param t type to check
- * @return true if primitive, false otherwise
- */
- private static boolean isPrimitiveType(TypeMirror t) {
- TypeKind kind = t.getKind();
- return
- kind == TypeKind.BOOLEAN ||
- kind == TypeKind.BYTE ||
- kind == TypeKind.CHAR ||
- kind == TypeKind.DOUBLE ||
- kind == TypeKind.FLOAT ||
- kind == TypeKind.INT ||
- kind == TypeKind.LONG ||
- kind == TypeKind.SHORT;
- }
-
- /**
- * Return error as default action for unhandled annotation values.
- */
- @Override
- protected Optional defaultAction(final Object o, final Fields f) {
- return Optional.of(
- new UnexpectedValueError(
- "An unexpected annotation value was encountered: " + o.getClass().getCanonicalName()
- )
- );
- }
-
- /**
- * Visit AnnotationValues of type String. For DataField annotations, this is
- * expected to be the value of DataField.fieldName.
- */
- @Override
- public Optional visitString(final String s, final Fields f) {
- if (!f.isBuilding()) {
- f.begin();
- }
- f.setName(s);
- if (f.isComplete()) {
- f.finish();
- }
- return Optional.empty();
- }
-
- /**
- * Visit AnnotationValues of type Type. For DataField annotations, this is
- * expected to be the value of DataField.fieldType.
- */
- @Override
- public Optional visitType(final TypeMirror t, final Fields f) {
- if (!f.isBuilding()) {
- f.begin();
- }
- f.setType(t.toString());
- f.setPrimitive(isPrimitiveType(t));
- if (f.isComplete()) {
- f.finish();
- }
- return Optional.empty();
- }
-}
\ No newline at end of file
diff --git a/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/processors/DataFieldsVisitor.java b/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/processors/DataFieldsVisitor.java
index 52eaa9928..22b450b6f 100644
--- a/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/processors/DataFieldsVisitor.java
+++ b/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/processors/DataFieldsVisitor.java
@@ -1,15 +1,17 @@
package org.eclipse.ice.dev.annotations.processors;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.SimpleAnnotationValueVisitor8;
+import org.apache.commons.lang3.ClassUtils;
import org.eclipse.ice.dev.annotations.DataField;
-import org.eclipse.ice.dev.annotations.processors.Fields;
/**
* Visitor that accumulates DataField information from AnnotationValues. This
@@ -27,21 +29,19 @@
* DataField Array then pass the AnnotationMirror of each DataField to
* visitAnnotation by recursively visiting the values.
*/
-class DataFieldsVisitor extends SimpleAnnotationValueVisitor8, Fields> {
+class DataFieldsVisitor extends SimpleAnnotationValueVisitor8, List> {
protected Elements elementUtils;
- protected DataFieldVisitor fieldVisitor;
- public DataFieldsVisitor(Elements elementUtils, DataFieldVisitor fieldVisitor) {
+ public DataFieldsVisitor(Elements elementUtils) {
super();
this.elementUtils = elementUtils;
- this.fieldVisitor = fieldVisitor;
}
/**
* Return error as default action for unhandled annotation values.
*/
@Override
- protected Optional defaultAction(final Object o, final Fields f) {
+ protected Optional defaultAction(final Object o, final List f) {
return Optional.of(
new UnexpectedValueError(
"An unexpected annotation value was encountered: " + o.getClass().getCanonicalName()
@@ -50,11 +50,11 @@ protected Optional defaultAction(final Object o, final Fie
}
/**
- * Visit AnnotationValues of type Annotation (as an AnnotationMirror), expected
- * to visit DataField AnnotationMirrors.
+ * Visit AnnotationValues of type Annotation. AnnotationMirror for DataField is
+ * expected.
*/
@Override
- public Optional visitAnnotation(final AnnotationMirror a, final Fields f) {
+ public Optional visitAnnotation(final AnnotationMirror a, final List f) {
if (!a.getAnnotationType().toString().equals(DataField.class.getCanonicalName())) {
return Optional.of(
new UnexpectedValueError(
@@ -63,12 +63,23 @@ public Optional visitAnnotation(final AnnotationMirror a,
);
}
- for (final AnnotationValue value : DataElementProcessor.getAnnotationValuesForMirror(elementUtils, a)) {
- final Optional result = value.accept(fieldVisitor, f);
- if (result.isPresent()) {
- return result;
+ Field.FieldBuilder builder = Field.builder();
+ Map valueMap = DataElementProcessor.getAnnotationValueMapForMirror(elementUtils, a);
+ if (valueMap.containsKey("fieldName")) {
+ builder.name((String) valueMap.get("fieldName"));
+ }
+ if (valueMap.containsKey("fieldType")) {
+ TypeMirror type = (TypeMirror) valueMap.get("fieldType");
+ try {
+ builder.type(ClassUtils.getClass(type.toString()));
+ } catch (ClassNotFoundException e) {
+ builder.type(Field.raw(type.toString()));
}
}
+ if (valueMap.containsKey("docString")) {
+ builder.docString((String) valueMap.get("docString"));
+ }
+ f.add(builder.build());
return Optional.empty();
}
@@ -76,7 +87,7 @@ public Optional visitAnnotation(final AnnotationMirror a,
* Visit AnnotationValues of type Array, visiting the Array of DataField AnnotationMirrors.
*/
@Override
- public Optional visitArray(final List extends AnnotationValue> vals, final Fields f) {
+ public Optional visitArray(final List extends AnnotationValue> vals, final List f) {
for (final AnnotationValue val : vals) {
final Optional result = val.accept(this, f);
if (result.isPresent()) {
diff --git a/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/processors/DefaultFields.java b/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/processors/DefaultFields.java
index 58d716f96..e440fe8d6 100644
--- a/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/processors/DefaultFields.java
+++ b/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/processors/DefaultFields.java
@@ -18,8 +18,17 @@ public class DefaultFields {
private static Field privateId = Field.builder()
.name("privateId")
.type(UUID.class)
+ .docString("The private UUID of this element. This field is left out of matches().")
.defaultValue(Field.raw("UUID.randomUUID()"))
.match(false)
+ .getter(false)
+ .setter(false)
+ .alias(
+ FieldAlias.builder()
+ .alias("UUID")
+ .getter(true)
+ .build()
+ )
.build();
/**
@@ -28,6 +37,7 @@ public class DefaultFields {
private static Field id = Field.builder()
.name("id")
.type(long.class)
+ .docString("A unique identifier for this element.")
.defaultValue(0L)
.build();
@@ -37,6 +47,7 @@ public class DefaultFields {
private static Field name = Field.builder()
.name("name")
.type(String.class)
+ .docString("A simple name for the data.")
.defaultValue("name")
.build();
@@ -46,6 +57,7 @@ public class DefaultFields {
private static Field description = Field.builder()
.name("description")
.type(String.class)
+ .docString("A simple description of the data")
.defaultValue("description")
.build();
@@ -55,6 +67,7 @@ public class DefaultFields {
private static Field comment = Field.builder()
.name("comment")
.type(String.class)
+ .docString("A comment that annotates the data in a meaningful way.")
.defaultValue("no comment")
.build();
@@ -64,6 +77,7 @@ public class DefaultFields {
private static Field context = Field.builder()
.name("context")
.type(String.class)
+ .docString("The context (a tag) in which the data should be considered.")
.defaultValue("default")
.build();
@@ -73,6 +87,7 @@ public class DefaultFields {
private static Field required = Field.builder()
.name("required")
.type(boolean.class)
+ .docString("This value is true if the element should be regarded by the client as required.")
.defaultValue(false)
.build();
@@ -83,6 +98,7 @@ public class DefaultFields {
private static Field secret = Field.builder()
.name("secret")
.type(boolean.class)
+ .docString("This value is true if the element should be regarded as a secret by the client, such as for passwords.")
.defaultValue(false)
.build();
@@ -92,6 +108,7 @@ public class DefaultFields {
private static Field validator = Field.builder()
.name("validator")
.type(Field.raw("JavascriptValidator<$class>"))
+ .docString("The validator used to check the correctness of the data.")
.nullable(true)
.build();
diff --git a/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/processors/Field.java b/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/processors/Field.java
index 34266c2c7..f8cdf2256 100644
--- a/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/processors/Field.java
+++ b/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/processors/Field.java
@@ -1,5 +1,7 @@
package org.eclipse.ice.dev.annotations.processors;
+import java.util.List;
+
import com.fasterxml.jackson.annotation.JsonAlias;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
@@ -7,6 +9,7 @@
import lombok.Builder;
import lombok.Data;
import lombok.NonNull;
+import lombok.Singular;
/**
* Container for Field information, taken from DataField Annotations, in
@@ -33,6 +36,11 @@ public class Field {
*/
String defaultValue;
+ /**
+ * Comment to add to the field declaration.
+ */
+ String docString;
+
/**
* Whether or not this field can be null.
*
@@ -53,6 +61,21 @@ public class Field {
*/
@Builder.Default boolean match = true;
+ /**
+ * Generate a getter for this field.
+ */
+ @Builder.Default boolean getter = true;
+
+ /**
+ * Generate a setter for this field.
+ */
+ @Builder.Default boolean setter = true;
+
+ /**
+ * A list of alternate names for this field.
+ */
+ @Singular("alias") List aliases;
+
/**
* Builder class for Field. This class must be a static inner class of Field in
* order to take advantage of Lombok's @Builder annotation. The methods defined
@@ -237,4 +260,4 @@ private static boolean stringRepresentsPrimitive(String type) {
type.equals("boolean") ||
type.equals("char");
}
-}
\ No newline at end of file
+}
diff --git a/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/processors/FieldAlias.java b/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/processors/FieldAlias.java
new file mode 100644
index 000000000..1d395af17
--- /dev/null
+++ b/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/processors/FieldAlias.java
@@ -0,0 +1,22 @@
+package org.eclipse.ice.dev.annotations.processors;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Getter;
+
+/**
+ * An alternate name for a field. This may help identify another JSON attribute
+ * name for a field, provide alternate Getters and Setters, etc.
+ */
+@AllArgsConstructor
+@Getter
+@Builder
+public class FieldAlias {
+ String alias;
+ boolean getter;
+ boolean setter;
+
+ public String getGetterName() {
+ return alias.substring(0, 1).toUpperCase() + alias.substring(1);
+ }
+}
diff --git a/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/processors/Fields.java b/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/processors/Fields.java
deleted file mode 100644
index a82a52117..000000000
--- a/org.eclipse.ice.dev.annotations/src/main/java/org/eclipse/ice/dev/annotations/processors/Fields.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package org.eclipse.ice.dev.annotations.processors;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A simple container for fields discovered on a DataElement.
- */
-class Fields {
- protected List fields;
- protected Field.FieldBuilder building;
-
- public Fields() {
- this.fields = new ArrayList<>();
- this.building = null;
- }
-
- public void begin() {
- this.building = Field.builder();
- }
-
- public void finish() {
- this.fields.add(this.building.build());
- this.building = null;
- }
-
- public List getFields() {
- return fields;
- }
-
- public boolean isBuilding() {
- return this.building != null;
- }
-
- public boolean isComplete() {
- Field partial = this.building.build();
- return (partial.getType() != null) && (partial.getName() != null);
- }
-
- public void setType(final String type) {
- this.building.type(Field.raw(type));
- }
-
- public void setName(final String name) {
- this.building.name(name);
- }
-
- public void setPrimitive(boolean primitive) {
- this.building.primitive(primitive);
- }
-
- @Override
- public String toString() {
- return fields.toString();
- }
-
- public void add(Field field) {
- this.fields.add(field);
- }
-
- public void addAll(Fields fields) {
- this.fields.addAll(fields.getFields());
- }
-
- public void addAll(List fields) {
- this.fields.addAll(fields);
- }
-
- public void addAll(Field[] fields) {
- if (fields == null) {
- return;
- }
- for (int i = 0; i < fields.length; i++) {
- this.fields.add(fields[i]);
- }
- }
-}
\ No newline at end of file
diff --git a/org.eclipse.ice.dev.annotations/src/main/resources/templates/DataElement.vm b/org.eclipse.ice.dev.annotations/src/main/resources/templates/DataElement.vm
index 174b57eb9..a9969c45e 100644
--- a/org.eclipse.ice.dev.annotations/src/main/resources/templates/DataElement.vm
+++ b/org.eclipse.ice.dev.annotations/src/main/resources/templates/DataElement.vm
@@ -1,3 +1,11 @@
+## Whitespace helpers
+#set($blank = "")
+#set($newline = "
+")
+#macro(noop)#end
+##
+## Begin Template
+##
#if($package)
package $package;
#end
@@ -16,9 +24,12 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.AccessLevel;
import lombok.Data;
+import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.NonNull;
+import lombok.Setter;
/**
* This is an implementation of $interface that satisfies the dependencies of
@@ -34,7 +45,47 @@ public class ${class} implements ${interface}, Serializable, IDataElement {
private static final Logger logger = LoggerFactory.getLogger(${class}.class);
#foreach($field in $fields)
-#if(!${field.Nullable} && !${field.Primitive}) @NonNull #else #{end}protected #evaluate(${field.Type}) ${field.Name}#if(${field.DefaultValue}) = #evaluate(${field.DefaultValue})#end;
+
+ #if(${field.DocString})
+ /**
+ * ${field.DocString}
+ */
+ #end
+ #macro(fieldtype)#evaluate(${field.Type})#end
+ #macro(fielddecl)protected #evaluate(${field.Type}) ${field.Name}#if(${field.DefaultValue}) = #evaluate(${field.DefaultValue})#end;#end
+ #if(!${field.Nullable} && !${field.Primitive})
+ @NonNull
+ #end
+ #if(!${field.Getter})
+ @Getter(AccessLevel.NONE)
+ #end
+ #if(!${field.Setter})
+ @Setter(AccessLevel.NONE)
+ #end
+ $blank#fielddecl()
+ #end
+
+ #foreach($field in $fields)
+ #foreach($alias in ${field.aliases})
+ #if(${alias.Getter})
+ /**
+ * Get ${field.Name} by alias ${alias.Alias}.
+ * @return ${field.Name}
+ */
+ public #fieldtype() get${alias.GetterName}() {
+ return ${field.Name};
+ }
+ #end
+ #if(${alias.Setter})
+ /**
+ * Set ${field.Name} by alias ${alias.Alias}.
+ * @return ${field.Name}
+ */
+ public void set${alias.SetterName}(#if(!${field.Nullable} && !${field.Primitive})@NonNull #end#fieldtype() ${field.Name}) {
+ this.${$field.Name} = ${field.Name};
+ }
+ #end
+ #end
#end
/**
@@ -81,28 +132,50 @@ public class ${class} implements ${interface}, Serializable, IDataElement {
* this element.
*/
public boolean matches(Object o) {
- if (o == this) return true;
- if (!(o instanceof $class)) return false;
- $class other = ($class) o;
- #foreach($field in $fields)
- #if(${field.Match})
- #if(${field.Nullable})
- if (this.${field.Name} != null) {
- if (!this.${field.Name}.equals(other.${field.Name})) return false;
- } else {
- if (this.${field.Name} != other.${field.Name}) return false;
+ boolean retval = false;
+
+ // Outer check for null comparisons
+ if (o != null) {
+ // Next check for shallow comparison
+ if (this != o) {
+ if (o instanceof $class) {
+ $class other = ($class) o;
+
+ // Separate boolean checks used to enable better catching
+ // by debuggers.
+ #foreach($field in $fields)
+ #if(${field.Match})
+
+ boolean ${field.Name}Match =
+ #if(${field.Nullable})
+ this.${field.Name} == null ?
+ this.${field.Name} == other.${field.Name} :
+ this.${field.Name}.equals(other.${field.Name});
+ #elseif(${field.Primitive})
+ this.${field.Name} == other.${field.Name};
+ #else
+ this.${field.Name}.equals(other.${field.Name});
+ #end## if nullable
+ #else
+ // Not checking ${field.Name}
+ #end## if match
+ #end## foreach
+
+ #set($count = 0)
+ retval =
+ $blank#foreach($field in $fields)
+ #if(${field.Match})
+ #set($count = $count + 1)
+ #set($third = $count % 3)
+ #noop()${field.Name}Match#if($foreach.hasNext) &if($third == 0)$newline #else #end#else;$newline#end#end#end
+ }
+ } else {
+ // This should be true if they are the same because the deep comparison is
+ // performed otherwise.
+ retval = true;
+ }
}
- #elseif(${field.Primitive})
- if (this.${field.Name} != other.${field.Name}) return false;
- #else
- if (!this.${field.Name}.equals(other.${field.Name})) return false;
- #end## if nullable
- #else
- // Not checking ${field.Name}
- #end## if match
- #end## foreach
-
- return true;
+ return retval;
}
/**
diff --git a/org.eclipse.ice.renderer/src/main/java/org/eclipse/ice/renderer/Person.java b/org.eclipse.ice.renderer/src/main/java/org/eclipse/ice/renderer/Person.java
index d96b20104..1e1d1f38d 100644
--- a/org.eclipse.ice.renderer/src/main/java/org/eclipse/ice/renderer/Person.java
+++ b/org.eclipse.ice.renderer/src/main/java/org/eclipse/ice/renderer/Person.java
@@ -4,9 +4,9 @@
import org.eclipse.ice.dev.annotations.DataField;
@DataElement
-@DataField(fieldName = "age", fieldType = int.class)
-@DataField(fieldName = "firstName", fieldType = String.class)
-@DataField(fieldName = "lastName", fieldType = String.class)
+@DataField(fieldName = "age", fieldType = int.class, docString = "The age of the person.")
+@DataField(fieldName = "firstName", fieldType = String.class, docString = "The first name of the person.")
+@DataField(fieldName = "lastName", fieldType = String.class, docString = "The last name of the Person.")
public interface Person {
//void foo();
}
diff --git a/org.eclipse.ice.renderer/src/test/java/org/eclipse/ice/tests/renderer/GeneratedDataElementTest.java b/org.eclipse.ice.renderer/src/test/java/org/eclipse/ice/tests/renderer/GeneratedDataElementTest.java
index c9b8e6c06..cd60c3a5a 100644
--- a/org.eclipse.ice.renderer/src/test/java/org/eclipse/ice/tests/renderer/GeneratedDataElementTest.java
+++ b/org.eclipse.ice.renderer/src/test/java/org/eclipse/ice/tests/renderer/GeneratedDataElementTest.java
@@ -104,7 +104,7 @@ void testDefaultProps() {
assertEquals(element.getValidator(), validator);
// Make sure that the UUID is not null
- assertNotNull(element.getPrivateId());
+ assertNotNull(element.getUUID());
return;
}