Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

more on validation

  • Loading branch information...
commit fc8446d9c70c8460b4d70c11545e37da834323d5 1 parent eb5bd91
michelegonella authored
6 zen-dataobject/src/main/antlr3/com/nominanuda/dataobject/schema/Jcl.g
View
@@ -8,7 +8,7 @@ output = AST;
}
tokens {
-OBJECT;ARRAY;NAME;ENTRY;PRIMITIVE;VALUECHOICE;VALUESEQ;ENTRYSEQ;TYPEDEF;TYPEREF;
+OBJECT;ARRAY;NAME;ENTRY;PRIMITIVE;VALUECHOICE;VALUESEQ;ENTRYSEQ;TYPEDEF;TYPEREF;EXISTENTIAL;
}
@header {
@@ -100,9 +100,9 @@ entry
;
lval
: LvalTkn
- -> ^(NAME LvalTkn)
+ -> ^(NAME LvalTkn) EXISTENTIAL
| LvalTkn ExistentialTkn
- -> ^(NAME LvalTkn ExistentialTkn)
+ -> ^(NAME LvalTkn) ^(EXISTENTIAL ExistentialTkn)
;
ExistentialTkn
: '!?'|'?!'|'!'|'?'
37 zen-dataobject/src/main/java/com/nominanuda/dataobject/schema/EventConsumer.java
View
@@ -15,60 +15,75 @@
*/
package com.nominanuda.dataobject.schema;
+import java.nio.channels.IllegalSelectorException;
import java.util.Stack;
import com.nominanuda.dataobject.JsonContentHandler;
public abstract class EventConsumer implements JsonContentHandler {
private final Stack<EventConsumer> stack;
+ private ExistentialPredicate predicate;
public EventConsumer(Stack<EventConsumer> stack) {
this.stack = stack;
+ this.predicate = new ExistentialPredicate();
+ }
+
+ public EventConsumer(Stack<EventConsumer> stack, ExistentialPredicate predicate) {
+ this.stack = stack;
+ this.predicate = predicate;
+ }
+
+ public boolean isOptional() {
+ return predicate.isOptional();
+ }
+ public boolean isNullable() {
+ return predicate.isNullable();
}
@Override
public void startJSON() throws RuntimeException {
- throw new UnsupportedOperationException();
+ throw new IllegalStateException();
}
@Override
public void endJSON() throws RuntimeException {
- throw new UnsupportedOperationException();
+ throw new IllegalStateException();
}
@Override
public boolean startObject() throws RuntimeException {
- throw new UnsupportedOperationException();
+ throw new ValidationException("unespected start of object");
}
@Override
public boolean endObject() throws RuntimeException {
- throw new UnsupportedOperationException();
+ throw new ValidationException("unespected end of object");
}
@Override
public boolean startObjectEntry(String key) throws RuntimeException {
- throw new UnsupportedOperationException();
+ throw new ValidationException("unespected start of object entry with key:"+key);
}
@Override
public boolean endObjectEntry() throws RuntimeException {
- throw new UnsupportedOperationException();
+ throw new ValidationException("unespected end of object entry");
}
@Override
public boolean startArray() throws RuntimeException {
- throw new UnsupportedOperationException();
+ throw new ValidationException("unespected start of array");
}
@Override
public boolean endArray() throws RuntimeException {
- throw new UnsupportedOperationException();
+ throw new ValidationException("unespected end of array");
}
@Override
public boolean primitive(Object value) throws RuntimeException {
- throw new UnsupportedOperationException();
+ throw new ValidationException("unespected primitive value "+value);
}
protected void pop() {
@@ -77,4 +92,8 @@ protected void pop() {
protected void push(EventConsumer c) {
stack.push(c);
}
+
+ public void setPredicate(ExistentialPredicate predicate) {
+ this.predicate = predicate;
+ }
}
26 zen-dataobject/src/main/java/com/nominanuda/dataobject/schema/ExistentialPredicate.java
View
@@ -0,0 +1,26 @@
+package com.nominanuda.dataobject.schema;
+
+import com.nominanuda.code.Nullable;
+
+public class ExistentialPredicate {
+ private boolean optional = false;
+ private boolean nullable = true;
+
+ public ExistentialPredicate(@Nullable String p) {
+ if(p != null) {
+ optional = p.contains("?");
+ nullable = !p.contains("!");
+ }
+ }
+
+ public ExistentialPredicate() {
+ }
+
+ public boolean isOptional() {
+ return optional;
+ }
+
+ public boolean isNullable() {
+ return nullable;
+ }
+}
107 zen-dataobject/src/main/java/com/nominanuda/dataobject/schema/JclValidatorFactory.java
View
@@ -16,10 +16,12 @@
package com.nominanuda.dataobject.schema;
import java.io.StringReader;
+import java.nio.channels.IllegalSelectorException;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Stack;
import org.antlr.runtime.ANTLRReaderStream;
@@ -28,6 +30,7 @@
import org.antlr.runtime.tree.CommonTree;
import org.antlr.runtime.tree.Tree;
+import com.nominanuda.code.Nullable;
import com.nominanuda.dataobject.JsonContentHandler;
import com.nominanuda.dataobject.WrappingRecognitionException;
import com.nominanuda.dataobject.transform.BaseJsonTransformer;
@@ -105,7 +108,8 @@ private EventConsumer makeConsumer(Tree node, Stack<EventConsumer> stack) {
for(int i = 0; i < node.getChildCount(); i++) {
Tree entry = node.getChild(i);
if(entry.getType() == ENTRY) {
- EventConsumer c = makeConsumer(entry.getChild(1), stack);
+ EventConsumer c = makeConsumer(entry.getChild(2), stack);
+ c.setPredicate(tokenToPredicate(entry.getChild(1)));
String key = entry.getChild(0).getChild(0).getText();
entryConsumers.put(key, c);
} else if(entry.getType() == ENTRYSEQ) {
@@ -133,6 +137,11 @@ private EventConsumer makeConsumer(Tree node, Stack<EventConsumer> stack) {
}
}
+ private ExistentialPredicate tokenToPredicate(Tree predTnkNode) {
+ String p = predTnkNode.getChildCount() > 0 ? predTnkNode.getChild(0).getText() : null;
+ return new ExistentialPredicate(p);
+ }
+
private Fun1<Object, String> makePrimitiveValidator(Tree node) {
Tree t = node.getChild(0);
String typeDef = t == null
@@ -156,16 +165,48 @@ public boolean startObject() throws RuntimeException {
@Override
public boolean endObject() throws RuntimeException {
- //TODO validate
+ validateExit();
pop();
return true;
}
@Override
+ public boolean primitive(Object value) throws RuntimeException {
+ if(value == null) {
+ if(! isNullable()) {
+ throw new ValidationException("null value of not-nul property");
+ } else {
+ return true;
+ }
+ } else {
+ throw new ValidationException("found "+value.toString()+" where object expected");
+ }
+ }
+ private void validateExit() {
+ for(Entry<String,EventConsumer> e : entryConsumers.entrySet()) {
+ String k = e.getKey();
+ EventConsumer c = e.getValue();
+ if(! isEntrySequence(k)) {
+ if(! c.isOptional()) {
+ throw new ValidationException("missing property:"+k);
+ }
+ }
+ }
+
+ }
+
+ private boolean isEntrySequence(String k) {
+ return "*".equals(k);
+ }
+
+ @Override
public boolean startObjectEntry(String key) throws RuntimeException {
EventConsumer c = entryConsumers.remove(key);
- if(c == null && entryConsumers.size() == 1 && entryConsumers.containsKey("*")) {
- c = entryConsumers.values().iterator().next();
+ if(c == null && entryConsumers.containsKey("*")) {
+ c = entryConsumers.get("*");
+ }
+ if(c == null) {
+ throw new ValidationException("unexpected entry with key "+key);
}
push(Check.notNull(c));
return true;
@@ -189,33 +230,56 @@ public ArrayConsumer(Stack<EventConsumer> stack, List<EventConsumer> elementsCon
@Override
public boolean primitive(Object value) throws RuntimeException {
- nextConsumer().primitive(value);
+ EventConsumer c = pushNextConsumer();
+ c.primitive(value);
return true;
}
+ private EventConsumer pushNextConsumer() {
+ EventConsumer c = nextConsumer();
+ if(c == null) {
+ throw new ValidationException("wrong number of elements in array, at position "+i);
+ }
+ push(c);
+ return c;
+ }
+
@Override
public boolean startObject() throws RuntimeException {
- push(nextConsumer());
+ EventConsumer c = pushNextConsumer();
+ c.startObject();
return true;
}
+ private boolean startArraySeen = false;
@Override
public boolean startArray() throws RuntimeException {
- push(nextConsumer());
+ if(startArraySeen) {
+ EventConsumer c = pushNextConsumer();
+ c.startArray();
+ } else {
+ startArraySeen = true;
+ }
return true;
}
@Override
public boolean endArray() throws RuntimeException {
pop();
- Check.illegalargument.assertEquals(i, elementsConsumers.size());
+ if(i != elementsConsumers.size()) {
+ throw new ValidationException("array expected "+elementsConsumers.size()+" elements, but got "+i);
+ }
return true;
}
- private EventConsumer nextConsumer() {
- EventConsumer c = elementsConsumers.get(i);
- i++;
- return c;
+ private @Nullable EventConsumer nextConsumer() {
+ if(i >= elementsConsumers.size()) {
+ return null;
+ } else {
+ EventConsumer c = elementsConsumers.get(i);
+ i++;
+ return c;
+ }
}
}
@@ -315,13 +379,22 @@ public PrimitiveConsumer(Stack<EventConsumer> stack, Fun1<Object, String> valida
@Override
public boolean primitive(Object value) throws RuntimeException {
- String errorMessage = validator.apply(value);
- if(errorMessage != null) {
- throw new IllegalArgumentException(errorMessage);
+ if(value == null) {
+ if(! isNullable()) {
+ throw new ValidationException("null value of not-nul property");
+ } else {
+ return true;
+ }
+ } else {
+ String errorMessage = validator.apply(value);
+ if(errorMessage != null) {
+ throw new ValidationException(errorMessage);
+ }
+ pop();
+ return true;
}
- pop();
- return true;
}
+
}
public void setPrimitiveValidatorFactory(
10 zen-dataobject/src/main/java/com/nominanuda/dataobject/schema/ValidationException.java
View
@@ -0,0 +1,10 @@
+package com.nominanuda.dataobject.schema;
+
+public class ValidationException extends RuntimeException {
+ public ValidationException(String msg) {
+ super(msg);
+ }
+
+ private static final long serialVersionUID = -451542828562571309L;
+
+}
48 zen-dataobject/src/test/java/com/nominanuda/dataobject/schema/JclValidatorTest.java
View
@@ -19,27 +19,65 @@
import java.io.StringReader;
+import org.junit.Ignore;
import org.junit.Test;
import com.nominanuda.dataobject.DataObjectImpl;
import com.nominanuda.dataobject.DataStruct;
+import com.nominanuda.dataobject.DataStructHelper;
import com.nominanuda.dataobject.transform.JsonPipeline;
import com.nominanuda.dataobject.transform.JsonTransformer;
import com.nominanuda.dataobject.transform.StringValuesJsonTransformer;
+import com.nominanuda.lang.Check;
import com.nominanuda.lang.InstanceFactory;
+//@Ignore
public class JclValidatorTest {
@Test
- public void test1() throws Exception {
- JclValidatorFactory f = new JclValidatorFactory("{a:{r:b,c!:n,e:s}}");
+ public void testValid() throws Exception {
+ valid("{}", "{}");
+ invalid("{}", "{a:null}");
+ valid("{a}", "{a:1}");
+ valid("{a?}", "{}");
+ invalid("{a}", "{}");
+ valid("{c,a?,b}", "{b:1,c:2}");
+ valid("{a!}", "{a:1}");
+ invalid("{a!}", "{a:null}");
+ valid("{a?:{}}", "{a:null}");
+ invalid("{a!:{}}", "{a:null}");
+ valid("{a!:{}}", "{a:{}}");
+ invalid("{a!:{}}", "{a:2}");
+ valid("{a,*}", "{b:{},a:1}");
+ valid("{a,b:{*}}", "{b:{c:{d:1}},a:1}");
+ valid("[]", "[]");
+ valid("[b]", "[true]");
+ invalid("[]", "[true]");
+ invalid("[b,b]", "[true]");
+ invalid("[b,b]", "[true,null,null]");
+ }
+
+ private void invalid(String schema, String instance) throws Exception {
+ try {
+ valid(schema, instance);
+ fail();
+ } catch(RuntimeException e) {
+ if(! (e.getCause() != null
+ && e.getCause() instanceof ValidationException)) {
+ throw e;
+ }
+ }
+ }
+
+ private void valid(String schema, String instance) throws Exception {
+ DataStruct res = DataStructHelper.STRUCT.parse(instance, true);
+ JclValidatorFactory f = new JclValidatorFactory(schema);
JsonPipeline p = new JsonPipeline()
.add(f)
.withLooseParser()
.complete();
- DataStruct res = p.build(new StringReader("{a:{r:false,c:1.1,e:''}}")).apply();
- System.err.println(res.toString());
+ DataStruct res2 = p.build(res).apply();
+ assertEquals(res, res2);
}
-
}
Please sign in to comment.
Something went wrong with that request. Please try again.