Unverified Commit ff18c728 authored by Gregory Cage's avatar Gregory Cage Committed by GitHub
Browse files

Merge pull request #3 from eclipse/next

Next
parents 1ae7dae1 dd3d5f82
......@@ -43,7 +43,7 @@ public class Field {
*
* This handles cases like the default field {@link DefaultFields#privateId}
* being accessible through the method
* {@link org.eclipse.ice.dev.annotations.IDataElement#getUUID()} rather than
* {@link org.eclipse.ice.data.IDataElement#getUUID()} rather than
* {@code getPrivateId()}. In that example, the variable name would be
* {@code privateId} and the field name would be {@code UUID}.
*/
......@@ -103,7 +103,7 @@ public class Field {
/**
* Whether this field should be searchable with PersistenceHandler.
*/
@Builder.Default boolean search = true;
@Builder.Default boolean searchable = true;
/**
* Whether this field should return only one from PersistenceHandler.
......
......@@ -65,7 +65,7 @@ public class Fields implements Iterable<Field> {
* Return iterator over fields that are set to be used in matches.
*
* @return iterator over the fields
* @see org.eclipse.ice.dev.annotations.IDataElement#matches(Object)
* @see org.eclipse.ice.data.IDataElement#matches(Object)
*/
public Iterator<Field> getMatch() {
return fields.stream()
......
......@@ -43,6 +43,11 @@ public class PersistenceHandlerWriter extends VelocitySourceWriter {
*/
private static final String CLASS = "class";
/**
* Context key for interface of PersistenceHandlers
*/
private static final String INTERFACE = "interface";
/**
* Context key for collection.
*/
......@@ -60,14 +65,16 @@ public class PersistenceHandlerWriter extends VelocitySourceWriter {
@Builder
public PersistenceHandlerWriter(
String packageName, String elementInterface, String className,
String implementation, String collection, @NonNull Fields fields
String packageName, String elementInterface, String interfaceName,
String className, String implementation, String collection,
@NonNull Fields fields
) {
super();
this.template = PERSISTENCE_HANDLER_TEMPLATE;
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);
......
/******************************************************************************
* Copyright (c) 2020- UT-Battelle, LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Initial API and implementation and/or initial documentation -
* Jay Jay Billings, Daniel Bluhm
*****************************************************************************/
package org.eclipse.ice.dev.annotations.webform;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
* @author Jay Jay Billings and Daniel Bluhm
*
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface FinishLoadHook {
}
/******************************************************************************
* Copyright (c) 2020- UT-Battelle, LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Initial API and implementation and/or initial documentation -
* Jay Jay Billings, Daniel Bluhm
*****************************************************************************/
package org.eclipse.ice.dev.annotations.webform;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
* @author Jay Jay Billings and Daniel Bluhm
*
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface StartLoadHook {
}
/******************************************************************************
* Copyright (c) 2020- UT-Battelle, LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Initial API and implementation and/or initial documentation -
* Jay Jay Billings, Daniel Bluhm
*****************************************************************************/
package org.eclipse.ice.dev.annotations.webform;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;
/**
* Mark a class as a WebForm Specification.
*
* Classes marked as {@code @WebForm} are expected to ....
* * need to have required methods annotated
* * declare hooks
*
* Discuss lifecycle when appropriate and injection of dependencies
*
*
* For example:
*
* <pre>
* {@literal @WebForm}(name = "Talk Submission Page")
* public class TalkFormSpec {
* ...
* }
* </pre>
*
* Will generate an interface like the following:
*
* <pre>
* public interface TalkForm extends ... {
* .... FIXME
* }
* </pre>
*
* And an associated implementing class (in this case, the class would be called
* {@code TalkFormImplementation}) that implements that interface.
*
* @author Jay Jay Billings and Daniel Bluhm
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface WebForm {
/**
* Name of the DataElement to generate
* @return name annotation value
*/
String name();
}
......@@ -22,6 +22,8 @@ import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.Setter;
import org.eclipse.ice.data.JavascriptValidator;
/**
* This is an implementation of $interface that satisfies the dependencies of
* the @DataElement Annotation and was auto-generated by the ICE Framework.
......
......@@ -3,7 +3,7 @@
package $package;
#end
import org.eclipse.ice.dev.annotations.IDataElement;
import org.eclipse.ice.data.IDataElement;
/**
* This interface satisfies the dependencies of the @DataElement Annotation and
......
## Declarations
#set($interface = "IPersistenceHandler")
##
#if($package)
package $package;
#end
import java.util.Map;
import java.util.UUID;
import org.bson.Document;
import org.eclipse.ice.dev.annotations.IDataElement;
import org.eclipse.ice.dev.annotations.IPersistenceHandler;
import org.eclipse.ice.data.IPersistenceHandler;
import org.eclipse.ice.dev.annotations.PersistenceFilters;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
......@@ -46,7 +38,6 @@ public class $class implements $interface<$elementInterface> {
/**
* Save the $elementInterface.
* @param <T> Object extending IDataElement
* @param element
* @throws Exception
*/
......@@ -57,7 +48,6 @@ public class $class implements $interface<$elementInterface> {
/**
* Find and retrieve all $elementInterface from the collection.
* @param <T>
* @return an iterable of the retrieved elements.
* @throws Exception
*/
......@@ -74,26 +64,26 @@ public class $class implements $interface<$elementInterface> {
*/
@Override
public long clear() throws Exception {
long count = this.collection.count();
long count = this.collection.countDocuments();
this.collection.drop();
return count;
}
#macro(findmethod $var $type $docName)
#if(${var.Getter} && ${var.Search})
#if(${var.Unique})
#foreach($field in $fields)
#if(${field.Getter} && ${field.Searchable})
#if(${field.Unique})
/**
* Find $elementInterface by ${var.Name}.
* @param ${var.VarName}
* Find $elementInterface by ${field.Name}.
* @param ${field.VarName}
* @return found element or null
*/
#if(${var.DefaultField})
#if(${field.DefaultField})
@Override
#end
public $elementInterface findBy${var.NameForMethod}($type ${var.VarName}) throws Exception {
public $elementInterface findBy${field.NameForMethod}(#fieldtype ${field.VarName}) throws Exception {
Document doc = this.collection
.find(PersistenceFilters.eq("${docName}", ${var.VarName}))
.find(PersistenceFilters.eq("${field.VarName}", ${field.VarName}))
.first();
if (doc == null) {
return null;
......@@ -104,21 +94,18 @@ public class $class implements $interface<$elementInterface> {
#else
/**
* Find $elementInterface by ${var.Name}.
* @param ${var.VarName}
* Find $elementInterface by ${field.Name}.
* @param ${field.VarName}
* @return Iterator of results
*/
#if(${var.DefaultField})
#if(${field.DefaultField})
@Override
#end
public Iterable<$elementInterface> findBy${var.NameForMethod}(${var.Type} ${var.VarName}) throws Exception {
return this.collection.find(PersistenceFilters.eq("${var.VarName}", ${var.VarName}))
public Iterable<$elementInterface> findBy${field.NameForMethod}(#fieldtype ${field.VarName}) throws Exception {
return this.collection.find(PersistenceFilters.eq("${field.VarName}", ${field.VarName}))
.map(doc -> mapper.convertValue(doc, ${implementation}.class));
}
#end## if unique
#end## if getter and search
#end## macro findmethod
#foreach($field in $fields)
#findmethod($field ${field.Type} ${field.VarName})
#end
#end## foreach
}
\ No newline at end of file
/*******************************************************************************
* Copyright (c) 2020- UT-Battelle, LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Daniel Bluhm - Initial implementation
*******************************************************************************/
package org.eclipse.ice.tests.dev.annotations.processors;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import javax.annotation.processing.Processor;
import javax.tools.JavaFileObject;
import org.eclipse.ice.dev.annotations.processors.DataElementProcessor;
import com.google.testing.compile.Compilation;
import static com.google.testing.compile.Compiler.*;
/**
* Helper class for testing DataElement related annotations.
* @author Daniel Bluhm
*/
public class DataElementAnnotationTestHelper {
/**
* Retrieve an instance of Lombok's Annotation Processor.
*
* This is a nasty method that violates the accessibility of the Processor by
* reflection but is necessary to correctly process and test the generated code.
* @return lombok annotation processor
*/
private Processor getLombokAnnotationProcessor() {
Processor p = null;
try {
Class<?> c = Class.forName("lombok.launch.AnnotationProcessorHider$AnnotationProcessor");
Constructor<?> constructor = c.getConstructor();
constructor.setAccessible(true);
p = (Processor) constructor.newInstance();
} catch (
ClassNotFoundException | InstantiationException |
IllegalAccessException | IllegalArgumentException |
InvocationTargetException | NoSuchMethodException |
SecurityException e
) {
System.err.println("Failed to get Lombok AnnotationProcessor!");
e.printStackTrace();
}
return p;
}
/**
* Compile the sources with needed processors.
* @param sources to compile
* @return Compilation result
*/
public Compilation compile(JavaFileObject... sources) {
return javac()
.withProcessors(
getLombokAnnotationProcessor(),
new DataElementProcessor()
).compile(sources);
}
}
package org.eclipse.ice.tests.dev.annotations.processors;
import static org.junit.jupiter.api.Assertions.fail;
/*******************************************************************************
* Copyright (c) 2020- UT-Battelle, LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Daniel Bluhm - Initial implementation
*******************************************************************************/
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
package org.eclipse.ice.tests.dev.annotations.processors;
import static com.google.testing.compile.Compiler.*;
import static com.google.testing.compile.CompilationSubject.*;
import javax.annotation.processing.Processor;
import javax.tools.JavaFileObject;
import org.eclipse.ice.dev.annotations.processors.DataElementProcessor;
import org.junit.jupiter.api.Test;
import com.google.testing.compile.Compilation;
import com.google.testing.compile.JavaFileObjects;
import lombok.AllArgsConstructor;
......@@ -38,6 +40,12 @@ import lombok.AllArgsConstructor;
*/
class DataElementProcessorTest {
/**
* Helper for testing DataElement related annotations.
*/
private static DataElementAnnotationTestHelper helper =
new DataElementAnnotationTestHelper();
/**
* Fully qualified name of the generated interface.
*/
......@@ -53,7 +61,7 @@ class DataElementProcessorTest {
* @author Daniel Bluhm
*/
@AllArgsConstructor
private static enum Inputs {
private static enum Inputs implements JavaFileObjectResource{
HELLO_WORLD("HelloWorld.java"),
NAME_MISSING("DataElementNameMissing.java"),
ON_ENUM("DataElementOnEnum.java"),
......@@ -75,19 +83,16 @@ class DataElementProcessorTest {
/**
* Parent directory of inputs. Prepended to all paths.
*/
private static final String PARENT = "input/";
private static final String PARENT = "input/DataElement/";
/**
* Path to inputs.
*/
private String path;
private String filename;
/**
* Retrieve the JavaFileObject corresponding to this input.
* @return input as a JavaFileObject
*/
public JavaFileObject get() {
return JavaFileObjects.forResource(PARENT + this.path);
@Override
public String getPath() {
return PARENT + this.filename;
}
}
......@@ -96,7 +101,7 @@ class DataElementProcessorTest {
* @author Daniel Bluhm
*/
@AllArgsConstructor
private static enum Patterns {
private static enum Patterns implements JavaFileObjectResource {
DEFAULTS_INT("Defaults.java"),
DEFAULTS_IMPL("DefaultsImplementation.java"),
SINGLE_INT("Single.java"),
......@@ -116,61 +121,19 @@ class DataElementProcessorTest {
/**
* Parent directory of inputs. Prepended to all paths.
*/
private static final String PARENT = "patterns/";
private static final String PARENT = "patterns/DataElement/";
/**
* Path to inputs.
*/
private String path;
private String filename;
/**
* Retrieve the JavaFileObject corresponding to this pattern.
* @return input as a JavaFileObject
*/
public JavaFileObject get() {
return JavaFileObjects.forResource(PARENT + this.path);
@Override
public String getPath() {
return PARENT + this.filename;
}
}
/**
* Retrieve an instance of Lombok's Annotation Processor.
*
* This is a nasty method that violates the accessibility of the Processor by
* reflection but is necessary to correctly process and test the generated code.
* @return lombok annotation processor
*/
private static Processor getLombokAnnotationProcessor() {
Processor p = null;
try {
Class<?> c = Class.forName("lombok.launch.AnnotationProcessorHider$AnnotationProcessor");
Constructor<?> constructor = c.getConstructor();
constructor.setAccessible(true);
p = (Processor) constructor.newInstance();
} catch (
ClassNotFoundException | InstantiationException |
IllegalAccessException | IllegalArgumentException |
InvocationTargetException | NoSuchMethodException |
SecurityException e
) {
System.err.println("Failed to get Lombok AnnotationProcessor!");
e.printStackTrace();
}
return p;
}
/**
* Compile the sources with needed processors.
* @param sources to compile
* @return Compilation result
*/
private static Compilation compile(JavaFileObject... sources) {
return javac()
.withProcessors(
getLombokAnnotationProcessor(),
new DataElementProcessor()
).compile(sources);
}
/**
* Assert that the interface generated in this compilation matches the given
* pattern.
......@@ -213,7 +176,7 @@ class DataElementProcessorTest {
*/
@Test
void testNoAnnotationsToProcessSucceeds() {
Compilation compilation = compile(Inputs.HELLO_WORLD.get());
Compilation compilation = helper.compile(Inputs.HELLO_WORLD.get());
assertThat(compilation).succeeded();
}
......@@ -223,7 +186,7 @@ class DataElementProcessorTest {
*/
@Test
void testMissingNameFails() {
Compilation compilation = compile(Inputs.NAME_MISSING.get());
Compilation compilation = helper.compile(Inputs.NAME_MISSING.get());
assertThat(compilation)
.hadErrorContaining(
"missing a default value for the element 'name'"
......@@ -235,7 +198,7 @@ class DataElementProcessorTest {
*/
@Test
void testAnnotateInterfaceFails() {
Compilation compilation = compile(Inputs.ON_INTERFACE.get());
Compilation compilation = helper.compile(Inputs.ON_INTERFACE.get());
assertThat(compilation)
.hadErrorContaining("DataElementSpec must be class");
}
......@@ -245,7 +208,7 @@ class DataElementProcessorTest {
*/
@Test
void testAnnotateEnumFails() {