Commit 29426b7a authored by Ryan Berkheimer's avatar Ryan Berkheimer
Browse files

MAJOR COMMIT - completed addition of direct specification condition mode...

MAJOR COMMIT - completed addition of direct specification condition mode strategy. This allows either factory or spec strategy depending on what the parser sees during session setup.
parent 27bff43f
......@@ -6,10 +6,11 @@
"constructor": {
"metadata": "{}/resources/test/metadata/file-reader/schema.json",
"fields": "{}/resources/test/file-reader/parameters.json",
"conditions": {
"conditionsblahblah": {
"map": "{}/resources/test/file-reader/parameters.json",
"factory": "gov.noaa.messageapi.factories.SimpleConditionFactory"
}
},
"conditions": "{}/resources/test/file-reader/parameters_with_conds.json"
}
},
"container": {
......
......@@ -5,7 +5,6 @@
"conditions": [{"id": "is-relative-path",
"type": "comparison",
"operator": "contains",
"factory": "dummy",
"field": "file-path",
"value": "{}"}],
"collections": [{"id": "coll-1",
......
{
"fields": [{"id": "file-path",
"type": "string",
"required": true}],
"conditions": [{"id": "is-relative-path",
"type": "comparison",
"operator": "gov.noaa.messageapi.operators.conditions.StringConditionOperator",
"constructor": {"comparison": "contains"},
"field": "file-path",
"value": "{}"}],
"collections": [{"id": "coll-1",
"classifiers": {},
"fields": ["file-path"],
"conditions": ["is-relative-path"]}],
"transformations": [{"id": "trans-1",
"operator": "gov.noaa.messageapi.test.transformations.FixRelativePathsTransformation",
"constructor": {"transform-key": "file-collection",
"fields": ["file-path"]},
"records": {"file-collection": {"COLLECTION": "coll-1"}}}],
"connections": [{"id": "conn-1",
"transformations": ["trans-1"],
"constructor": {"file-fields": "file-path"},
"fields" : ["value","number", "length"]}]
}
\ No newline at end of file
......@@ -95,6 +95,7 @@ typedef struct rejection_list
int count;
jobject jrejections;
} rejection_list;
typedef struct rejection
{
jobject jrejection;
......
package gov.noaa.messageapi.definitions;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.stream.Collectors;
import gov.noaa.messageapi.interfaces.IConditionFactory;
import gov.noaa.messageapi.interfaces.IConditionOperator;
import gov.noaa.messageapi.parsers.schemas.MetadataParser;
import gov.noaa.messageapi.parsers.schemas.FieldParser;
import gov.noaa.messageapi.parsers.schemas.ConditionParser;
import gov.noaa.messageapi.parsers.schemas.ConditionOperatorParser;
import gov.noaa.messageapi.enums.ConditionStrategy;
import gov.noaa.messageapi.exceptions.ConfigurationParsingException;
import gov.noaa.messageapi.exceptions.MessageApiException;
/**
* A SchemaDefinition is used to parse a specified
* configuration and create a blueprint for how the user interacts with
......@@ -27,6 +34,8 @@ public class SchemaDefinition {
private List<Map<String,Object>> fieldMaps = null;
private List<Map<String,Object>> conditionMaps = null;
private IConditionFactory conditionOperatorFactory = null;
private Enum<ConditionStrategy> conditionStrategy = null;
private Map<String,Map<String,Object>> conditionOperatorMap = null;
/**
* This is the operator factory provided by the service. If users
......@@ -45,7 +54,6 @@ public class SchemaDefinition {
* @param properties A map containing specific schema-blueprint-related info
* @throws Exception An exception is thrown if there is an issue parsing the schema definition.
*/
@SuppressWarnings("unchecked")
public SchemaDefinition(final Map<String, Object> properties) throws Exception {
if (properties.containsKey("metadata")) {
this.parseMetadataSpec((String) properties.get("metadata"));
......@@ -55,10 +63,10 @@ public class SchemaDefinition {
if (properties.containsKey("fields")) {
this.parseFieldSpec((String) properties.get("fields"));
} else {
throw new Exception("Missing necessary 'fields' key when parsing schema definition.");
throw new ConfigurationParsingException("Missing necessary 'fields' key when parsing schema definition.");
}
if (properties.containsKey("conditions")) {
this.parseConditionSpec((Map<String, String>) properties.get("conditions"));
this.parseConditions(properties.get("conditions"));
} else {
this.setEmptyConditions();
}
......@@ -79,7 +87,12 @@ public class SchemaDefinition {
this.metadataMap = new HashMap<String, Object>(definition.getMetadataMap());
this.fieldMaps = new ArrayList<Map<String, Object>>(definition.getFieldMaps());
this.conditionMaps = new ArrayList<Map<String, Object>>(definition.getConditionMaps());
this.conditionOperatorFactory = definition.getOperatorFactory().getCopy();
this.conditionStrategy = definition.getConditionStrategy();
if (definition.getConditionStrategy().equals(ConditionStrategy.FACTORY)) {
this.conditionOperatorFactory = definition.getOperatorFactory().getCopy();
} else if (definition.getConditionStrategy().equals(ConditionStrategy.SPEC)) {
this.conditionOperatorMap = definition.getConditionOperatorMap();
}
}
/**
......@@ -94,7 +107,32 @@ public class SchemaDefinition {
* operator class.
*/
private void createOperatorFactory(final String operatorClass) throws Exception {
this.conditionOperatorFactory = ConditionOperatorParser.build(operatorClass);
this.conditionOperatorFactory = ConditionOperatorParser.buildFactory(operatorClass);
}
@SuppressWarnings("unchecked")
private void createConditionOperatorMap(final List<Map<String,Object>> conditionMaps) throws Exception {
try {
List<Map<String,Object>> conditionOperatorList = conditionMaps.stream().map(cmap -> {
Map<String,Object> externalMap = new HashMap<String,Object>();
Map<String,Object> internalMap = new HashMap<String,Object>();
try {
internalMap.put("class", ConditionOperatorParser.buildOperatorClass((String)cmap.get("operator")));
} catch (Exception e) {
return null;
}
if (cmap.containsKey("constructor")) {
internalMap.put("constructor", (Map<String,Object>)cmap.get("constructor"));
}
externalMap.put("id", (String)cmap.get("id"));
externalMap.put("map", internalMap);
return externalMap;
}).collect(Collectors.toList());
this.conditionOperatorMap = conditionOperatorList.stream()
.collect(Collectors.toMap(m -> (String) m.get("id"), m -> (Map<String, Object>) m.get("map")));
} catch (Exception e) {
throw new MessageApiException("Condition Operator Map creation failed. Check your configuration.");
}
}
/**
......@@ -125,6 +163,26 @@ public class SchemaDefinition {
this.fieldMaps = parser.getFieldMaps();
}
@SuppressWarnings("unchecked")
private void parseConditions(Object conditionValue) throws Exception {
if (conditionValue instanceof String) {
this.parseConditionsFromSpec((String) conditionValue);
} else if (conditionValue instanceof Map) {
this.parseConditionsFromManifest((Map<String, String>) conditionValue);
}
}
private void parseConditionsFromSpec(String conditionSpec) throws Exception {
try {
final ConditionParser parser = new ConditionParser(conditionSpec);
this.conditionMaps = parser.getConditionMaps();
this.setConditionStrategy(ConditionStrategy.SPEC);
this.createConditionOperatorMap(this.conditionMaps);
} catch (Exception e) {
throw new ConfigurationParsingException("Attempted to parse conditions (spec type) but failed.");
}
}
/**
* Parses a condition map from a location specified by a string and a factory
* from a given class string. The condition map holds a flat list of conditions
......@@ -141,14 +199,19 @@ public class SchemaDefinition {
* @throws Exception Throws an exception in the case that the map could not be
* parsed.
*/
private void parseConditionSpec(final Map<String, String> conditionMap) throws Exception {
private void parseConditionsFromManifest(final Map<String, String> conditionMap) throws Exception {
if (conditionMap.containsKey("map")) {
final ConditionParser parser = new ConditionParser((String) conditionMap.get("map"));
this.conditionMaps = parser.getConditionMaps();
if (conditionMap.containsKey("factory")) {
this.createOperatorFactory((String) conditionMap.get("factory"));
} else {
this.createOperatorFactory(DEFAULT_OPERATOR_FACTORY);
try {
final ConditionParser parser = new ConditionParser((String) conditionMap.get("map"));
this.conditionMaps = parser.getConditionMaps();
if (conditionMap.containsKey("factory")) {
this.createOperatorFactory((String) conditionMap.get("factory"));
} else {
this.createOperatorFactory(DEFAULT_OPERATOR_FACTORY);
}
this.setConditionStrategy(ConditionStrategy.FACTORY);
} catch (Exception e) {
throw new ConfigurationParsingException("Attempted to parse conditions (factory type) but failed.");
}
} else {
this.setEmptyConditions();
......@@ -187,12 +250,24 @@ public class SchemaDefinition {
return this.conditionOperatorFactory;
}
/**
* Returns a new operator instance corresponding to the class referenced by the given methodId.
* This method is used in the case that the ConditionStrategy is SPEC (i.e., classes are directly
* specified in the parameter map.)
* @param methodId
* @return
*/
public IConditionOperator getOperator(String methodId) throws Exception {
return ConditionOperatorParser.constructOperatorInstance(this.conditionOperatorMap.get(methodId));
}
/**
* sets empty conditions on the definition, in the case that we aren't using conditions.
*/
private void setEmptyConditions() throws Exception {
this.conditionMaps = new ArrayList<Map<String,Object>>();
this.createOperatorFactory(DEFAULT_OPERATOR_FACTORY);
this.setConditionStrategy(ConditionStrategy.FACTORY);
}
/**
......@@ -202,4 +277,16 @@ public class SchemaDefinition {
this.metadataMap = new HashMap<String,Object>();
}
private void setConditionStrategy(Enum<ConditionStrategy> conditionStrategy) {
this.conditionStrategy = conditionStrategy;
}
public Enum<ConditionStrategy> getConditionStrategy() {
return this.conditionStrategy;
}
public Map<String,Map<String,Object>> getConditionOperatorMap() {
return this.conditionOperatorMap;
}
}
package gov.noaa.messageapi.enums;
public enum ConditionStrategy {
FACTORY,SPEC
}
......@@ -3,12 +3,12 @@ package gov.noaa.messageapi.factories;
import gov.noaa.messageapi.interfaces.IConditionOperator;
import gov.noaa.messageapi.interfaces.IConditionFactory;
import gov.noaa.messageapi.operators.conditions.BooleanConditionOperator;
import gov.noaa.messageapi.operators.conditions.FloatConditionOperator;
import gov.noaa.messageapi.operators.conditions.DoubleConditionOperator;
import gov.noaa.messageapi.operators.conditions.IntegerConditionOperator;
import gov.noaa.messageapi.operators.conditions.StringConditionOperator;
import gov.noaa.messageapi.operators.conditions.DateTimeConditionOperator;
import gov.noaa.messageapi.operators.conditions.FactoryBooleanConditionOperator;
import gov.noaa.messageapi.operators.conditions.FactoryFloatConditionOperator;
import gov.noaa.messageapi.operators.conditions.FactoryDoubleConditionOperator;
import gov.noaa.messageapi.operators.conditions.FactoryIntegerConditionOperator;
import gov.noaa.messageapi.operators.conditions.FactoryStringConditionOperator;
import gov.noaa.messageapi.operators.conditions.FactoryDateTimeConditionOperator;
/**
* Creates new Operators for use in comparison between conditions and fields attached to
......@@ -45,17 +45,17 @@ public class SimpleConditionFactory implements IConditionFactory {
public IConditionOperator getOperator(final String type) {
switch (type) {
case "boolean":
return new BooleanConditionOperator();
return new FactoryBooleanConditionOperator();
case "float":
return new FloatConditionOperator();
return new FactoryFloatConditionOperator();
case "double":
return new DoubleConditionOperator();
return new FactoryDoubleConditionOperator();
case "integer":
return new IntegerConditionOperator();
return new FactoryIntegerConditionOperator();
case "string":
return new StringConditionOperator();
return new FactoryStringConditionOperator();
case "datetime":
return new DateTimeConditionOperator();
return new FactoryDateTimeConditionOperator();
}
return null;
}
......
package gov.noaa.messageapi.interfaces;
import gov.noaa.messageapi.definitions.SchemaDefinition;
import gov.noaa.messageapi.enums.ConditionStrategy;
/**
* @author Ryan Berkheimer
......@@ -10,6 +11,7 @@ public interface ISchema extends IComponent {
public SchemaDefinition getDefinition();
public void initialize(IContainer c, IProtocol p);
public IRecord createRecord();
public IConditionOperator getOperator(String type);
public IConditionOperator getOperator(Enum<ConditionStrategy> strategy, String type);
public Enum<ConditionStrategy> getConditionStrategy();
}
......@@ -7,7 +7,7 @@ import gov.noaa.messageapi.interfaces.ICondition;
/**
* @author Ryan Berkheimer
*/
public class BooleanConditionOperator extends SimpleConditionOperator implements IConditionOperator {
public class FactoryBooleanConditionOperator extends FactorySimpleConditionOperator implements IConditionOperator {
public boolean compare(final IField field, final ICondition condition) {
return super.compare(field, condition);
......
......@@ -9,7 +9,7 @@ import gov.noaa.messageapi.interfaces.ICondition;
/**
* @author Ryan Berkheimer
*/
public class DateTimeConditionOperator extends SimpleConditionOperator implements IConditionOperator {
public class FactoryDateTimeConditionOperator extends FactorySimpleConditionOperator implements IConditionOperator {
public boolean compare(final IField field, final ICondition condition) {
return super.compare(field, condition);
......
......@@ -7,7 +7,7 @@ import gov.noaa.messageapi.interfaces.ICondition;
/**
* @author Ryan Berkheimer
*/
public class DoubleConditionOperator extends SimpleConditionOperator implements IConditionOperator {
public class FactoryDoubleConditionOperator extends FactorySimpleConditionOperator implements IConditionOperator {
public boolean compare(final IField field, final ICondition condition) {
return super.compare(field, condition);
......
......@@ -7,7 +7,7 @@ import gov.noaa.messageapi.interfaces.ICondition;
/**
* @author Ryan Berkheimer
*/
public class FloatConditionOperator extends SimpleConditionOperator implements IConditionOperator {
public class FactoryFloatConditionOperator extends FactorySimpleConditionOperator implements IConditionOperator {
public boolean compare(final IField field, final ICondition condition) {
return super.compare(field, condition);
......
......@@ -7,7 +7,7 @@ import gov.noaa.messageapi.interfaces.ICondition;
/**
* @author Ryan Berkheimer
*/
public class IntegerConditionOperator extends SimpleConditionOperator implements IConditionOperator {
public class FactoryIntegerConditionOperator extends FactorySimpleConditionOperator implements IConditionOperator {
public boolean compare(final IField field, final ICondition condition) {
return super.compare(field, condition);
......
......@@ -6,7 +6,7 @@ import gov.noaa.messageapi.interfaces.ICondition;
/**
* @author Ryan Berkheimer
*/
public abstract class SimpleConditionOperator {
public abstract class FactorySimpleConditionOperator {
protected abstract Integer getCompareValue(IField field, ICondition condition);
......
package gov.noaa.messageapi.operators.conditions;
import gov.noaa.messageapi.interfaces.IConditionOperator;
import gov.noaa.messageapi.interfaces.IField;
import gov.noaa.messageapi.interfaces.ICondition;
/**
* @author Ryan Berkheimer
*/
public class FactoryStringConditionOperator implements IConditionOperator {
public boolean compare(final IField field, final ICondition condition) {
switch (condition.getOperator()) {
case ">":
return false;
case "<":
return false;
case ">=":
return false;
case "<=":
return false;
case "=":
return isEqual((String) field.getValue(), (String) condition.getValue());
case "/=":
return isNotEqual((String) field.getValue(), (String) condition.getValue());
case "contains":
return contains((String) field.getValue(), (String) condition.getValue());
}
return false;
}
private boolean isEqual(final String fieldValue, final String conditionValue) {
if (fieldValue.equals(conditionValue)) {
return true;
}
return false;
}
private boolean isNotEqual(final String fieldValue, final String conditionValue) {
if (!fieldValue.equals(conditionValue)) {
return true;
}
return false;
}
private boolean contains(final String fieldValue, final String conditionValue) {
if (fieldValue.contains(conditionValue)) {
return true;
} else {
return false;
}
}
}
package gov.noaa.messageapi.operators.conditions;
import java.util.Map;
import gov.noaa.messageapi.interfaces.IConditionOperator;
import gov.noaa.messageapi.interfaces.IField;
import gov.noaa.messageapi.exceptions.MessageApiException;
import gov.noaa.messageapi.interfaces.ICondition;
/**
* This comparison condition class contains various string comparisons.
* Currently supported are '=', '/=', and 'contains'. These must be specified
* in the constructor with the 'comparison' key (e.g., {'comparison':'='}).
* @author Ryan Berkheimer
*/
public class StringConditionOperator implements IConditionOperator {
private String comparison = null;
public StringConditionOperator(Map<String,Object> params) throws Exception {
try {
this.setComparison((String)params.get("comparison"));
} catch (Exception e) {
throw new MessageApiException("The StringConditionOperator operator class needs a constructor parameter 'comparison'." +
"Failed to initialize this condition.");
}
}
public boolean compare(final IField field, final ICondition condition) {
switch (condition.getOperator()) {
case ">":
return false;
case "<":
return false;
case ">=":
return false;
case "<=":
return false;
switch (this.comparison) {
case "=":
return isEqual((String) field.getValue(), (String) condition.getValue());
return this.isEqual((String) field.getValue(), (String) condition.getValue());
case "/=":
return isNotEqual((String) field.getValue(), (String) condition.getValue());
return this.isNotEqual((String) field.getValue(), (String) condition.getValue());
case "contains":
return contains((String) field.getValue(), (String) condition.getValue());
return this.contains((String) field.getValue(), (String) condition.getValue());
}
return false;
}
......@@ -51,4 +60,8 @@ public class StringConditionOperator implements IConditionOperator {
}
}
private void setComparison(String comparison) {
this.comparison = comparison;
}
}
package gov.noaa.messageapi.parsers.schemas;
import java.lang.reflect.Constructor;
import java.util.Map;
import gov.noaa.messageapi.interfaces.IConditionFactory;
import gov.noaa.messageapi.interfaces.IConditionOperator;
/**
* @author Ryan Berkheimer
*/
public class ConditionOperatorParser {
public static IConditionFactory build(final String operatorClass) throws Exception {
public static IConditionFactory buildFactory(final String operatorClass) throws Exception {
try {
return (IConditionFactory) constructPlugin(Class.forName(operatorClass));
return (IConditionFactory) constructPluginNoArgs(Class.forName(operatorClass));
} catch (final Exception e) {
System.err.println(String.format(
"Could not construct the ConditionOperatorFactory using the given class %s", operatorClass));
......@@ -17,8 +21,49 @@ public class ConditionOperatorParser {
}
}
private static Object constructPlugin(final Class<?> pluginClass) throws Exception {
@SuppressWarnings("unchecked")
public static Class<IConditionOperator> buildOperatorClass(final String operatorClassName) throws Exception {
try {
return (Class<IConditionOperator>) Class.forName(operatorClassName);
} catch (final Exception e) {
System.err.println(String.format(
"Could not construct the ConditionOperatorFactory using the given class %s", operatorClassName));
return null;
}
}
@SuppressWarnings("unchecked")
public static IConditionOperator constructOperatorInstance(final Map<String, Object> operatorMap) throws Exception {
if (operatorMap.containsKey("constructor")) {
try {
final Class<?>[] ctrClasses = { Map.class };
final Object[] args = { operatorMap.get("constructor") };
return (IConditionOperator) constructPluginWithArgs(
(Class<IConditionOperator>) operatorMap.get("class"), ctrClasses, args);
} catch (final Exception e) {
System.err.println("Exception thrown while building a condition operator with args: " + e.getMessage());
System.exit(1);
return null;
}
} else {
try {
return (IConditionOperator) constructPluginNoArgs((Class<IConditionOperator>) operatorMap.get("class"));
} catch (final Exception e) {
System.err.println("Exception thrown while building a condition operator without args: " + e.getMessage());
System.exit(1);
return null;
}
}
}
private static Object constructPluginNoArgs(final Class<?> pluginClass) throws Exception {
return pluginClass.getConstructor().newInstance();
}
private static Object constructPluginWithArgs(final Class<?> pluginClass, final Class<?>[] ctrClasses, final Object[] args)