diff --git a/pom.xml b/pom.xml index 3d156c5d5..5be0102ae 100644 --- a/pom.xml +++ b/pom.xml @@ -69,6 +69,12 @@ provided + + org.glassfish + javax.json + 1.0.4 + + javax.servlet servlet-api diff --git a/src/main/java/com/avaje/ebean/json/EJson.java b/src/main/java/com/avaje/ebean/json/EJson.java new file mode 100644 index 000000000..0582dd546 --- /dev/null +++ b/src/main/java/com/avaje/ebean/json/EJson.java @@ -0,0 +1,118 @@ +package com.avaje.ebean.json; + +import java.io.Reader; +import java.io.Writer; +import java.util.List; +import java.util.Map; + +import javax.json.stream.JsonGenerator; +import javax.json.stream.JsonParser; + +/** + * Utility that converts between JSON content and java Maps/Lists. + */ +public class EJson { + + /** + * Write the nested Map/List as json. + */ + public static String write(Object object) { + return EJsonWriter.write(object); + } + + /** + * Write the nested Map/List as json to the writer. + */ + public static void write(Object object, Writer writer) { + EJsonWriter.write(object, writer); + } + + /** + * Write the nested Map/List as json to the jsonGenerator. + */ + public static void write(Object object, JsonGenerator jsonGenerator) { + EJsonWriter.write(object, jsonGenerator); + } + + /** + * Parse the json and return as a Map. + */ + public static Map parseObject(String json) { + return EJsonReader.parseObject(json); + } + + /** + * Parse the json and return as a Map taking a reader. + */ + public static Map parseObject(Reader reader) { + return EJsonReader.parseObject(reader); + } + + /** + * Parse the json and return as a Map taking a JsonParser. + */ + public static Map parseObject(JsonParser parser) { + return EJsonReader.parseObject(parser); + } + + /** + * Parse the json and return as a List. + */ + public static List parseList(String json) { + return EJsonReader.parseList(json); + } + + /** + * Parse the json and return as a List taking a Reader. + */ + public static List parseList(Reader reader) { + return EJsonReader.parseList(reader); + } + + /** + * Parse the json and return as a List taking a JsonParser. + */ + public static List parseList(JsonParser parser) { + return EJsonReader.parseList(parser); + } + + + /** + * Parse the json and return as a List or Map. + */ + public static Object parse(String json) { + return EJsonReader.parse(json); + } + + /** + * Parse the json and return as a List or Map. + */ + public static Object parse(Reader reader) { + return EJsonReader.parse(reader, false); + } + + /** + * Parse the json and return as a List or Map. + */ + public static Object parse(JsonParser parser) { + return EJsonReader.parse(parser, false); + } + + /** + * Parse the json and return the next json value, List or Map. + * This will not consume all the reader content and return once the + * next json object, list or value is read. + */ + public static Object parsePartial(Reader reader) { + return EJsonReader.parse(reader, true); + } + + /** + * Parse the json and return the next json value, List or Map. + * This will not consume all the reader content and return once the + * next json object, list or value is read. + */ + public static Object parsePartial(JsonParser parser) { + return EJsonReader.parse(parser, true); + } +} diff --git a/src/main/java/com/avaje/ebean/json/EJsonReader.java b/src/main/java/com/avaje/ebean/json/EJsonReader.java new file mode 100644 index 000000000..f2012afe7 --- /dev/null +++ b/src/main/java/com/avaje/ebean/json/EJsonReader.java @@ -0,0 +1,319 @@ +package com.avaje.ebean.json; + +import java.io.Reader; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; + +import javax.json.Json; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; + +class EJsonReader { + + @SuppressWarnings("unchecked") + static Map parseObject(String json) { + return (Map) parse(json); + } + + @SuppressWarnings("unchecked") + static Map parseObject(Reader reader) { + return (Map) parse(reader, false); + } + + @SuppressWarnings("unchecked") + static Map parseObject(JsonParser parser) { + return (Map) parse(parser, false); + } + + @SuppressWarnings("unchecked") + static List parseList(String json) { + return (List) parse(json); + } + + @SuppressWarnings("unchecked") + static List parseList(Reader reader) { + return (List) parse(reader, false); + } + + @SuppressWarnings("unchecked") + static List parseList(JsonParser parser) { + return (List) parse(parser, false); + } + + static Object parse(String json) { + return parse(new StringReader(json), false); + } + + static Object parse(Reader reader, boolean partial) { + return parse(Json.createParser(reader), partial); + } + + static Object parse(JsonParser parser, boolean partial) { + return new EJsonReader(parser, partial).parseJson(); + } + + private final JsonParser parser; + + private final boolean partial; + + private int depth; + + private Stack stack; + + private Context currentContext; + + + EJsonReader(JsonParser parser, boolean partial) { + this.parser = parser; + this.partial = partial; + } + + private void startArray() { + depth++; + stack.push(currentContext); + currentContext = new ArrayContext(); + } + + private void startObject() { + depth++; + stack.push(currentContext); + currentContext = new ObjectContext(); + } + + private void endArray() { + end(); + } + + private void endObject() { + end(); + } + + private void end() { + depth--; + if (!stack.isEmpty()) { + + //if (currentContext != null) { + // Object value = currentContext.getValue(); + //} + currentContext = stack.pop(currentContext); + } + } + + private void setValue(Object value) { + currentContext.setValue(value); + } + + private void setValueNull() { + currentContext.setValueNull(); + } + + private Object parseJson() { + + if (!parser.hasNext()) { + return null; + } + + Event event = parser.next(); + if (Event.VALUE_NULL == event) { + // it is just a null value + return null; + } + Object simpleValue = getSimpleValue(event); + if (simpleValue != null) { + // it is a simple string, number or boolean + return simpleValue; + } + + stack = new Stack(); + // it is a object or array, process the first event + processEvent(event); + + // process the rest of the object or array + while (parser.hasNext()) { + processEvent(parser.next()); + + if (partial && depth == 0) { + // completed the object/array + return currentContext.getValue(); + } + + } + + return currentContext.getValue(); + } + + /** + * See if the event is a value rather than object or array. + * + * If just a value then return that value else return null. + */ + private Object getSimpleValue(Event event) { + + switch (event) { + case VALUE_STRING: + return parser.getString(); + + case VALUE_NUMBER: + if (parser.isIntegralNumber()) { + return parser.getLong(); + } else { + return parser.getBigDecimal(); + } + + case VALUE_TRUE: + return Boolean.TRUE; + + case VALUE_FALSE: + return Boolean.FALSE; + + default: + return null; + } + } + + /** + * Process the event for objects and arrays. + */ + private void processEvent(Event event) { + switch (event) { + + case START_ARRAY: + startArray(); + break; + + case START_OBJECT: + startObject(); + break; + + case KEY_NAME: + currentContext.setKey(parser.getString()); + break; + + case VALUE_STRING: + setValue(parser.getString()); + break; + + case VALUE_NUMBER: + if (parser.isIntegralNumber()) { + setValue(parser.getLong()); + } else { + setValue(parser.getBigDecimal()); + } + break; + + case VALUE_TRUE: + setValue(Boolean.TRUE); + break; + + case VALUE_FALSE: + setValue(Boolean.FALSE); + break; + + case VALUE_NULL: + setValueNull(); + break; + + case END_OBJECT: + endObject(); + break; + + case END_ARRAY: + endArray(); + break; + + default: + break; + } + } + + private static final class Stack { + + private Context head; + + private void push(Context context) { + if (context != null) { + context.next = head; + head = context; + } + } + + private Context pop(Context endingContext) { + if (head == null) { + throw new NoSuchElementException(); + } + Context temp = head; + head = head.next; + temp.popContext(endingContext); + return temp; + } + + private boolean isEmpty() { + return head == null; + } + } + + private static abstract class Context { + Context next; + abstract void popContext(Context temp); + abstract Object getValue(); + abstract void setKey(String key); + abstract void setValue(Object value); + abstract void setValueNull(); + } + + private static class ObjectContext extends Context { + + private final Map map = new LinkedHashMap(); + + private String key; + + public void popContext(Context temp) { + setValue(temp.getValue()); + } + + Object getValue() { + return map; + } + + void setKey(String key) { + this.key = key; + } + + void setValue(Object value) { + map.put(key, value); + } + + void setValueNull() { + map.put(key, null); + } + } + + private static class ArrayContext extends Context { + + private final List values = new ArrayList(); + + public void popContext(Context temp) { + values.add(temp.getValue()); + } + + Object getValue() { + return values; + } + + void setValue(Object value) { + values.add(value); + } + + void setValueNull() { + // ignore + } + void setKey(String key) { + // not expected + } + } + +} diff --git a/src/main/java/com/avaje/ebean/json/EJsonWriter.java b/src/main/java/com/avaje/ebean/json/EJsonWriter.java new file mode 100644 index 000000000..16a58aed7 --- /dev/null +++ b/src/main/java/com/avaje/ebean/json/EJsonWriter.java @@ -0,0 +1,205 @@ +package com.avaje.ebean.json; + +import java.io.StringWriter; +import java.io.Writer; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Collection; +import java.util.Date; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import javax.json.Json; +import javax.json.stream.JsonGenerator; + +class EJsonWriter { + + static String write(Object object) { + StringWriter writer = new StringWriter(200); + write(object, writer); + return writer.toString(); + } + + static void write(Object object, Writer writer) { + JsonGenerator generator = Json.createGenerator(writer); + write(object, generator); + generator.close(); + } + + static void write(Object object, JsonGenerator jsonGenerator) { + new EJsonWriter(jsonGenerator).writeJson(object); + } + + private final JsonGenerator jsonGenerator; + + private EJsonWriter(JsonGenerator jsonGenerator) { + this.jsonGenerator = jsonGenerator; + } + + private void writeJson(Object object) { + writeJson(null, object); + } + + @SuppressWarnings("unchecked") + private void writeJson(String name, Object object) { + if (object == null) { + writeNull(name); + + } else if (object instanceof Map) { + writeMap(name, (Map) object); + + } else if (object instanceof Collection) { + writeCollection(name, (Collection) object); + + } else if (object instanceof Boolean) { + writeBoolean(name, (Boolean) object); + + } else if (object instanceof Number) { + writeNumber(name, (Number) object); + + } else if (object instanceof Date) { + writeDate(name, (Date) object); + + } else if (object instanceof String) { + writeString(name, (String) object); + + } else if (object instanceof Map.Entry, ?>) { + Map.Entry, ?> entry = (Map.Entry, ?>)object; + writeJson(entry.getKey().toString(), entry.getValue()); + + } else { + writeString(name, object.toString()); + } + + } + + private void writeBoolean(String name, Boolean object) { + if (name == null) { + jsonGenerator.write(object); + } else { + jsonGenerator.write(name, object); + } + } + + private void writeDate(String name, Date object) { + if (name == null) { + jsonGenerator.write(object.getTime()); + } else { + jsonGenerator.write(name, object.getTime()); + } + } + + private void writeNumber(String name, Number object) { + + if (object instanceof Long) { + writeLong(name, object); + + } else if (object instanceof Integer) { + writeInteger(name, object); + + } else if (object instanceof Double) { + writeDouble(name, object); + + } else if (object instanceof BigDecimal) { + writeBigDecimal(name, object); + + } else if (object instanceof BigInteger) { + writeBigInteger(name, object); + + } else { + writeGeneralNumber(name, object); + } + } + + private void writeGeneralNumber(String name, Number object) { + if (name == null) { + jsonGenerator.write(new BigDecimal(object.toString())); + } else { + jsonGenerator.write(name, new BigDecimal(object.toString())); + } + } + + private void writeBigDecimal(String name, Number object) { + if (name == null) { + jsonGenerator.write((BigDecimal) object); + } else { + jsonGenerator.write(name, (BigDecimal) object); + } + } + + private void writeBigInteger(String name, Number object) { + if (name == null) { + jsonGenerator.write((BigInteger) object); + } else { + jsonGenerator.write(name, (BigInteger) object); + } + } + + private void writeDouble(String name, Number object) { + if (name == null) { + jsonGenerator.write((Double) object); + } else { + jsonGenerator.write(name, (Double) object); + } + } + + private void writeLong(String name, Number object) { + if (name == null) { + jsonGenerator.write((Long) object); + } else { + jsonGenerator.write(name, (Long) object); + } + } + + private void writeInteger(String name, Number object) { + if (name == null) { + jsonGenerator.write((Integer) object); + } else { + jsonGenerator.write(name, (Integer) object); + } + } + + private void writeNull(String name) { + if (name == null) { + jsonGenerator.writeNull(); + } else { + jsonGenerator.writeNull(name); + } + } + + private void writeString(String name, String object) { + if (name == null) { + jsonGenerator.write(object); + } else { + jsonGenerator.write(name, object); + } + } + + private void writeCollection(String name, Collection collection) { + if (name == null) { + jsonGenerator.writeStartArray(); + } else { + jsonGenerator.writeStartArray(name); + } + for (Object object : collection) { + writeJson(null, object); + } + jsonGenerator.writeEnd(); + } + + private void writeMap(String name, Map map) { + + if (name == null) { + jsonGenerator.writeStartObject(); + } else { + jsonGenerator.writeStartObject(name); + } + Set> entrySet = map.entrySet(); + for (Entry entry : entrySet) { + writeJson(entry.getKey().toString(), entry.getValue()); + } + jsonGenerator.writeEnd(); + } + +} diff --git a/src/main/java/com/avaje/ebean/text/PathPropertiesParser.java b/src/main/java/com/avaje/ebean/text/PathPropertiesParser.java index d6bef851e..926eeaea1 100644 --- a/src/main/java/com/avaje/ebean/text/PathPropertiesParser.java +++ b/src/main/java/com/avaje/ebean/text/PathPropertiesParser.java @@ -53,6 +53,9 @@ class PathPropertiesParser { case '(': return currentWord(); default: + if (pos == 1) { + return ""; + } } } while (pos < eof); throw new RuntimeException("Hit EOF while reading sectionTitle from " + startPos); @@ -91,6 +94,10 @@ class PathPropertiesParser { } } while (pos < eof); + if (startPos < pos) { + String currentWord = source.substring(startPos, pos); + currentPathProps.addProperty(currentWord); + } } private void addSubpath() { diff --git a/src/main/java/com/avaje/ebean/text/json/JsonContext.java b/src/main/java/com/avaje/ebean/text/json/JsonContext.java index 17dc030ec..a453c9231 100644 --- a/src/main/java/com/avaje/ebean/text/json/JsonContext.java +++ b/src/main/java/com/avaje/ebean/text/json/JsonContext.java @@ -22,49 +22,27 @@ public interface JsonContext { */ public T toBean(Class rootType, Reader json); - /** - * Convert json string input into a Bean of a specific type with options. - */ - public T toBean(Class rootType, String json, JsonReadOptions options); - - /** - * Convert json reader input into a Bean of a specific type with options. - */ - public T toBean(Class rootType, Reader json, JsonReadOptions options); - /** * Convert json string input into a list of beans of a specific type. */ public List toList(Class rootType, String json); - /** - * Convert json string input into a list of beans of a specific type with - * options. - */ - public List toList(Class rootType, String json, JsonReadOptions options); - /** * Convert json reader input into a list of beans of a specific type. */ public List toList(Class rootType, Reader json); /** - * Convert json reader input into a list of beans of a specific type with - * options. + * Use the genericType to determine if this should be converted into a List or + * bean. */ - public List toList(Class rootType, Reader json, JsonReadOptions options); + public Object toObject(Type genericType, Reader json); /** * Use the genericType to determine if this should be converted into a List or * bean. */ - public Object toObject(Type genericType, Reader json, JsonReadOptions options); - - /** - * Use the genericType to determine if this should be converted into a List or - * bean. - */ - public Object toObject(Type genericType, String json, JsonReadOptions options); + public Object toObject(Type genericType, String json); /** * Write the bean or collection in JSON format to the writer with default @@ -77,11 +55,6 @@ public interface JsonContext { */ public void toJsonWriter(Object o, Writer writer); - /** - * With additional pretty output option. - */ - public void toJsonWriter(Object o, Writer writer, boolean pretty); - /** * With additional options to specify JsonValueAdapter and * JsonWriteBeanVisitor's. @@ -93,13 +66,7 @@ public interface JsonContext { * @param options * additional options to control the JSON output */ - public void toJsonWriter(Object o, Writer writer, boolean pretty, JsonWriteOptions options); - - /** - * With additional JSONP callback function. - */ - public void toJsonWriter(Object o, Writer writer, boolean pretty, JsonWriteOptions options, - String callback); + public void toJsonWriter(Object o, Writer writer, JsonWriteOptions options); /** * Convert a bean or collection to json string using default options. @@ -107,20 +74,9 @@ public interface JsonContext { public String toJsonString(Object o); /** - * Convert a bean or collection to json string with pretty format using - * default options. + * Convert a bean or collection to json string. */ - public String toJsonString(Object o, boolean pretty); - - /** - * Convert a bean or collection to json string using options. - */ - public String toJsonString(Object o, boolean pretty, JsonWriteOptions options); - - /** - * Convert a bean or collection to json string using a JSONP callback. - */ - public String toJsonString(Object o, boolean pretty, JsonWriteOptions options, String callback); + public String toJsonString(Object o, JsonWriteOptions options); /** * Return true if the type is known as an Entity or Xml type or a List Set or diff --git a/src/main/java/com/avaje/ebean/text/json/JsonElement.java b/src/main/java/com/avaje/ebean/text/json/JsonElement.java deleted file mode 100644 index 98b76a224..000000000 --- a/src/main/java/com/avaje/ebean/text/json/JsonElement.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.avaje.ebean.text.json; - -/** - * Marker interface for all the Raw JSON types. - * - * You will only use the JsonElements when you register a JsonReadBeanVisitor. - * The JSON elements that are not mapped to a bean property are made available - * to the JsonReadBeanVisitor. - * - * - * @see JsonReadBeanVisitor - * - * @author rbygrave - */ -public interface JsonElement { - - /** - * Return true if this is a JSON primitive type (null, boolean, number or - * string). - */ - public boolean isPrimitive(); - - /** - * Return the string value of this primitive JSON element. - * - * This can not be used for JsonElementObject or JsonElementArray. - * - */ - public String toPrimitiveString(); - - public Object eval(String exp); - - public int evalInt(String exp); - - public String evalString(String exp); - - public boolean evalBoolean(String exp); - -} diff --git a/src/main/java/com/avaje/ebean/text/json/JsonElementArray.java b/src/main/java/com/avaje/ebean/text/json/JsonElementArray.java deleted file mode 100644 index 5a59e28bc..000000000 --- a/src/main/java/com/avaje/ebean/text/json/JsonElementArray.java +++ /dev/null @@ -1,112 +0,0 @@ -package com.avaje.ebean.text.json; - -import java.util.ArrayList; -import java.util.List; - -/** - * JSON Array element. - * - * You will only use the JsonElements when you register a JsonReadBeanVisitor. - * The JSON elements that are not mapped to a bean property are made available - * to the JsonReadBeanVisitor. - * - * - * @see JsonReadBeanVisitor - * - * @author rbygrave - */ -public class JsonElementArray implements JsonElement { - - private final List values = new ArrayList(); - - public List getValues() { - return values; - } - - public void add(JsonElement value) { - values.add(value); - } - - public String toString() { - return values.toString(); - } - - public boolean isPrimitive() { - return false; - } - - public String toPrimitiveString() { - return null; - } - - private String[] split(String exp) { - int pos = exp.indexOf('.'); - if (pos == -1) { - return new String[] { exp, null }; - } - String exp0 = exp.substring(0, pos); - String exp1 = exp.substring(pos + 1); - return new String[] { exp0, exp1 }; - } - - public Object eval(String exp) { - String[] e = split(exp); - return eval(e[0], e[1]); - } - - public int evalInt(String exp) { - String[] e = split(exp); - return evalInt(e[0], e[1]); - } - - public String evalString(String exp) { - String[] e = split(exp); - return evalString(e[0], e[1]); - } - - public boolean evalBoolean(String exp) { - // TODO Auto-generated method stub - return false; - } - - private Object eval(String exp0, String exp1) { - if ("size".equals(exp0)) { - return values.size(); - } - if ("isEmpty".equals(exp0)) { - return values.isEmpty(); - } - int idx = Integer.parseInt(exp0); - JsonElement element = values.get(idx); - return element.eval(exp1); - } - - private int evalInt(String exp0, String exp1) { - if ("size".equals(exp0)) { - return values.size(); - } - if ("isEmpty".equals(exp0)) { - return values.isEmpty() ? 1 : 0; - } - int idx = Integer.parseInt(exp0); - JsonElement element = values.get(idx); - return element.evalInt(exp1); - } - - private String evalString(String exp0, String exp1) { - if ("size".equals(exp0)) { - return String.valueOf(values.size()); - } - if ("isEmpty".equals(exp0)) { - return String.valueOf(values.isEmpty()); - } - int idx = Integer.parseInt(exp0); - JsonElement element = values.get(idx); - return element.evalString(exp1); - } - - public String getString() { - return toString(); - } - -} diff --git a/src/main/java/com/avaje/ebean/text/json/JsonElementBoolean.java b/src/main/java/com/avaje/ebean/text/json/JsonElementBoolean.java deleted file mode 100644 index 69132b8a2..000000000 --- a/src/main/java/com/avaje/ebean/text/json/JsonElementBoolean.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.avaje.ebean.text.json; - -/** - * JSON boolean element. - * - * You will only use the JsonElements when you register a JsonReadBeanVisitor. - * The JSON elements that are not mapped to a bean property are made available - * to the JsonReadBeanVisitor. - * - * - * @see JsonReadBeanVisitor - * - * @author rbygrave - */ - -public class JsonElementBoolean implements JsonElement { - - public static final JsonElementBoolean TRUE = new JsonElementBoolean(true); - - public static final JsonElementBoolean FALSE = new JsonElementBoolean(false); - - private final Boolean value; - - private JsonElementBoolean(Boolean value) { - this.value = value; - } - - public Boolean getValue() { - return value; - } - - public String toString() { - return Boolean.toString(value); - } - - public boolean isPrimitive() { - return true; - } - - public String toPrimitiveString() { - return value.toString(); - } - - public Object eval(String exp) { - if (exp != null) { - throw new IllegalArgumentException("expression [" + exp + "] not allowed on boolean"); - } - return value; - } - - public int evalInt(String exp) { - return value ? 1 : 0; - } - - public String evalString(String exp) { - return toString(); - } - - public boolean evalBoolean(String exp) { - return value; - } - -} diff --git a/src/main/java/com/avaje/ebean/text/json/JsonElementNull.java b/src/main/java/com/avaje/ebean/text/json/JsonElementNull.java deleted file mode 100644 index b38406a57..000000000 --- a/src/main/java/com/avaje/ebean/text/json/JsonElementNull.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.avaje.ebean.text.json; - -/** - * JSON null element. - * - * You will only use the JsonElements when you register a JsonReadBeanVisitor. - * The JSON elements that are not mapped to a bean property are made available - * to the JsonReadBeanVisitor. - * - * - * @see JsonReadBeanVisitor - * - * @author rbygrave - */ -public class JsonElementNull implements JsonElement { - - public static final JsonElementNull NULL = new JsonElementNull(); - - private JsonElementNull() { - } - - public String getValue() { - return "null"; - } - - public String toString() { - return "json null"; - } - - public boolean isPrimitive() { - return true; - } - - public String toPrimitiveString() { - return null; - } - - public Object eval(String exp) { - if (exp != null) { - throw new IllegalArgumentException("expression [" + exp + "] not allowed on null"); - } - return null; - } - - public int evalInt(String exp) { - return 0; - } - - public String evalString(String exp) { - return null; - } - - public boolean evalBoolean(String exp) { - return false; - } - -} diff --git a/src/main/java/com/avaje/ebean/text/json/JsonElementNumber.java b/src/main/java/com/avaje/ebean/text/json/JsonElementNumber.java deleted file mode 100644 index a778315ac..000000000 --- a/src/main/java/com/avaje/ebean/text/json/JsonElementNumber.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.avaje.ebean.text.json; - -/** - * JSON number element. - * - * You will only use the JsonElements when you register a JsonReadBeanVisitor. - * The JSON elements that are not mapped to a bean property are made available - * to the JsonReadBeanVisitor. - * - * - * @see JsonReadBeanVisitor - * - * @author rbygrave - */ -public class JsonElementNumber implements JsonElement { - - private final String value; - - public JsonElementNumber(String value) { - this.value = value; - } - - public String getValue() { - return value; - } - - public String toString() { - return value; - } - - public boolean isPrimitive() { - return true; - } - - public String toPrimitiveString() { - return value; - } - - public Object eval(String exp) { - if (exp != null) { - throw new IllegalArgumentException("expression [" + exp + "] not allowed on number"); - } - return Double.parseDouble(value); - } - - public int evalInt(String exp) { - if (exp != null) { - throw new IllegalArgumentException("expression [" + exp + "] not allowed on number"); - } - return Integer.parseInt(value); - } - - public String evalString(String exp) { - if (exp != null) { - throw new IllegalArgumentException("expression [" + exp + "] not allowed on number"); - } - return value; - } - - public boolean evalBoolean(String exp) { - if (exp != null) { - throw new IllegalArgumentException("expression [" + exp + "] not allowed on number"); - } - return Boolean.parseBoolean(value); - } - -} diff --git a/src/main/java/com/avaje/ebean/text/json/JsonElementObject.java b/src/main/java/com/avaje/ebean/text/json/JsonElementObject.java deleted file mode 100644 index 028158e16..000000000 --- a/src/main/java/com/avaje/ebean/text/json/JsonElementObject.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.avaje.ebean.text.json; - -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; - -/** - * JSON Object element. - * - * You will only use the JsonElements when you register a JsonReadBeanVisitor. - * The JSON elements that are not mapped to a bean property are made available - * to the JsonReadBeanVisitor. - * - * - * @see JsonReadBeanVisitor - * - * @author rbygrave - */ -public class JsonElementObject implements JsonElement { - - private final Map map = new LinkedHashMap(); - - public void put(String key, JsonElement value) { - map.put(key, value); - } - - private String[] split(String exp) { - int pos = exp.indexOf('.'); - if (pos == -1) { - return new String[] { exp, null }; - } - String exp0 = exp.substring(0, pos); - String exp1 = exp.substring(pos + 1); - return new String[] { exp0, exp1 }; - } - - public Object eval(String exp) { - String[] e = split(exp); - return eval(e[0], e[1]); - } - - public int evalInt(String exp) { - String[] e = split(exp); - return evalInt(e[0], e[1]); - } - - public String evalString(String exp) { - if (exp == null) { - return map.toString(); - } - String[] e = split(exp); - return evalString(e[0], e[1]); - } - - public boolean evalBoolean(String exp) { - String[] e = split(exp); - return evalBoolean(e[0], e[1]); - } - - private Object eval(String exp0, String exp1) { - JsonElement e = map.get(exp0); - return e == null ? null : e.eval(exp1); - } - - private int evalInt(String exp0, String exp1) { - JsonElement e = map.get(exp0); - return e == null ? 0 : e.evalInt(exp1); - } - - private String evalString(String exp0, String exp1) { - JsonElement e = map.get(exp0); - return e == null ? "" : e.evalString(exp1); - } - - private boolean evalBoolean(String exp0, String exp1) { - JsonElement e = map.get(exp0); - return e == null ? false : e.evalBoolean(exp1); - } - - public JsonElement get(String key) { - return map.get(key); - } - - public JsonElement getValue(String key) { - return map.get(key); - } - - public Set keySet() { - return map.keySet(); - } - - public Set> entrySet() { - return map.entrySet(); - } - - public String toString() { - return map.toString(); - } - - public boolean isPrimitive() { - return false; - } - - public String toPrimitiveString() { - return null; - } - -} diff --git a/src/main/java/com/avaje/ebean/text/json/JsonElementString.java b/src/main/java/com/avaje/ebean/text/json/JsonElementString.java deleted file mode 100644 index 0b76feafc..000000000 --- a/src/main/java/com/avaje/ebean/text/json/JsonElementString.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.avaje.ebean.text.json; - -/** - * JSON string element. - * - * You will only use the JsonElements when you register a JsonReadBeanVisitor. - * The JSON elements that are not mapped to a bean property are made available - * to the JsonReadBeanVisitor. - * - * - * @see JsonReadBeanVisitor - * - * @author rbygrave - */ -public class JsonElementString implements JsonElement { - - private final String value; - - public JsonElementString(String value) { - this.value = value; - } - - public String getValue() { - return value; - } - - public String toString() { - return value; - } - - public boolean isPrimitive() { - return true; - } - - public String toPrimitiveString() { - return value; - } - - public Object eval(String exp) { - if (exp != null) { - throw new IllegalArgumentException("expression [" + exp + "] not allowed on number"); - } - return value; - } - - public int evalInt(String exp) { - if (exp != null) { - throw new IllegalArgumentException("expression [" + exp + "] not allowed on number"); - } - try { - return Integer.parseInt(value); - } catch (NumberFormatException e) { - return 0; - } - } - - public String evalString(String exp) { - if (exp != null) { - throw new IllegalArgumentException("expression [" + exp + "] not allowed on number"); - } - return value; - } - - public boolean evalBoolean(String exp) { - if (exp != null) { - throw new IllegalArgumentException("expression [" + exp + "] not allowed on number"); - } - return Boolean.parseBoolean(exp); - } - -} diff --git a/src/main/java/com/avaje/ebean/text/json/JsonReadBeanVisitor.java b/src/main/java/com/avaje/ebean/text/json/JsonReadBeanVisitor.java deleted file mode 100644 index 6de15360c..000000000 --- a/src/main/java/com/avaje/ebean/text/json/JsonReadBeanVisitor.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.avaje.ebean.text.json; - -import java.util.Map; - -/** - * Provides for some custom handling of json content as it is read. - * - * This visit method is called after all the known properties of the bean have - * been processed. Any JSON elements that could not be mapped to known bean - * properties are available in the unmapped Map. - * - * - * @author rbygrave - * - * @param - * The type of entity bean - */ -public interface JsonReadBeanVisitor { - - /** - * Visit the bean that has just been processed. - * - * This provides a method of customising the bean and processing any custom - * JSON content. - * - * - * @param bean - * the bean being processed - * @param unmapped - * Map of any JSON elements that didn't map to known bean properties - */ - public void visit(T bean, Map unmapped); - -} diff --git a/src/main/java/com/avaje/ebean/text/json/JsonReadOptions.java b/src/main/java/com/avaje/ebean/text/json/JsonReadOptions.java deleted file mode 100644 index a6832fd21..000000000 --- a/src/main/java/com/avaje/ebean/text/json/JsonReadOptions.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.avaje.ebean.text.json; - -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * Provides the ability to customise the reading of JSON content. - * - * You can optionally provide a custom JsonValueAdapter to handle specific - * formatting for Date and DateTime types. - * - * - * You can optionally register JsonReadBeanVisitors to customise the processing - * of the beans as they are processed and handle any custom JSON elements that - * could not be mapped to bean properties. - * - * - * @author rbygrave - * - */ -public class JsonReadOptions { - - protected JsonValueAdapter valueAdapter; - - protected Map> visitorMap; - - /** - * Default constructor. - */ - public JsonReadOptions() { - this.visitorMap = new LinkedHashMap>(); - } - - /** - * Return the JsonValueAdapter. - */ - public JsonValueAdapter getValueAdapter() { - return valueAdapter; - } - - /** - * Return the map of JsonReadBeanVisitor's. - */ - public Map> getVisitorMap() { - return visitorMap; - } - - /** - * Set a JsonValueAdapter for custom DateTime and Date formatting. - */ - public JsonReadOptions setValueAdapter(JsonValueAdapter valueAdapter) { - this.valueAdapter = valueAdapter; - return this; - } - - /** - * Register a JsonReadBeanVisitor for the root level. - */ - public JsonReadOptions addRootVisitor(JsonReadBeanVisitor> visitor) { - return addVisitor(null, visitor); - } - - /** - * Register a JsonReadBeanVisitor for a given path. - */ - public JsonReadOptions addVisitor(String path, JsonReadBeanVisitor> visitor) { - visitorMap.put(path, visitor); - return this; - } - -} diff --git a/src/main/java/com/avaje/ebean/text/json/JsonValueAdapter.java b/src/main/java/com/avaje/ebean/text/json/JsonValueAdapter.java deleted file mode 100644 index a30dcf38c..000000000 --- a/src/main/java/com/avaje/ebean/text/json/JsonValueAdapter.java +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebean.text.json; - -import java.sql.Timestamp; - -/** - * Allows you to customise the Date and Timestamp formats. - * - * There is not a standard JSON format for Date or Timestamp types. By default - * Ebean uses ISO8601 "yyyy-MM-dd'T'HH:mm:ss.SSSZ" and "yyyy-MM-dd". - * - * - * Note that Ebean will convert Joda types to either of the Date or Timestamp - * types and back for you. - * - * - * @see JsonReadOptions - * - * @author rbygrave - */ -public interface JsonValueAdapter { - - /** - * Convert the Date to json string. - */ - public String jsonFromDate(java.sql.Date date); - - /** - * Convert the DateTime to json string. - */ - public String jsonFromTimestamp(java.sql.Timestamp date); - - /** - * Parse the JSON string into a Date. - */ - public java.sql.Date jsonToDate(String jsonDate); - - /** - * Parse the JSON DateTime into a Timestamp. - */ - public Timestamp jsonToTimestamp(String jsonDateTime); - -} diff --git a/src/main/java/com/avaje/ebean/text/json/JsonWriteBeanVisitor.java b/src/main/java/com/avaje/ebean/text/json/JsonWriteBeanVisitor.java deleted file mode 100644 index eb44cf788..000000000 --- a/src/main/java/com/avaje/ebean/text/json/JsonWriteBeanVisitor.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.avaje.ebean.text.json; - -/** - * Allows for customising the JSON write processing. - * - * You can use this to add raw JSON content via {@link JsonWriter}. - * - * - * You register a JsonWriteBeanVisitor with {@link JsonWriteOptions}. - * - * - * @author rbygrave - * - * @param - * the type of entity bean - * - * @see JsonWriteOptions - */ -public interface JsonWriteBeanVisitor { - - /** - * Visit the bean that has just been writing it's content to JSON. You can - * write your own additional JSON content to the JsonWriter if you wish. - * - * @param bean - * the bean that has been writing it's content - * @param jsonWriter - * the JsonWriter which you can append custom json content to if you - * wish. - */ - public void visit(T bean, JsonWriter jsonWriter); - -} diff --git a/src/main/java/com/avaje/ebean/text/json/JsonWriteOptions.java b/src/main/java/com/avaje/ebean/text/json/JsonWriteOptions.java index 8775f5001..d245725e0 100644 --- a/src/main/java/com/avaje/ebean/text/json/JsonWriteOptions.java +++ b/src/main/java/com/avaje/ebean/text/json/JsonWriteOptions.java @@ -1,8 +1,6 @@ package com.avaje.ebean.text.json; -import java.util.HashMap; import java.util.LinkedHashSet; -import java.util.Map; import java.util.Set; import com.avaje.ebean.text.PathProperties; @@ -69,10 +67,6 @@ public class JsonWriteOptions { protected String callback; - protected JsonValueAdapter valueAdapter; - - protected Map> visitorMap; - protected PathProperties pathProperties; /** @@ -99,11 +93,7 @@ public class JsonWriteOptions { public JsonWriteOptions copy() { JsonWriteOptions copy = new JsonWriteOptions(); copy.callback = callback; - copy.valueAdapter = valueAdapter; copy.pathProperties = pathProperties; - if (visitorMap != null) { - copy.visitorMap = new HashMap>(visitorMap); - } return copy; } @@ -122,39 +112,6 @@ public class JsonWriteOptions { return this; } - /** - * Return the JsonValueAdapter. - */ - public JsonValueAdapter getValueAdapter() { - return valueAdapter; - } - - /** - * Set a JsonValueAdapter for custom DateTime and Date formatting. - */ - public JsonWriteOptions setValueAdapter(JsonValueAdapter valueAdapter) { - this.valueAdapter = valueAdapter; - return this; - } - - /** - * Register a JsonWriteBeanVisitor for the root level. - */ - public JsonWriteOptions setRootPathVisitor(JsonWriteBeanVisitor> visitor) { - return setPathVisitor(null, visitor); - } - - /** - * Register a JsonWriteBeanVisitor for the given path. - */ - public JsonWriteOptions setPathVisitor(String path, JsonWriteBeanVisitor> visitor) { - if (visitorMap == null) { - visitorMap = new HashMap>(); - } - visitorMap.put(path, visitor); - return this; - } - /** * Set the properties to include in the JSON output for the given path. * @@ -213,13 +170,6 @@ public class JsonWriteOptions { return props; } - /** - * Return the Map of registered JsonWriteBeanVisitor's by path. - */ - public Map> getVisitorMap() { - return visitorMap; - } - /** * Set the Map of properties to include by path. */ diff --git a/src/main/java/com/avaje/ebean/text/json/JsonWriter.java b/src/main/java/com/avaje/ebean/text/json/JsonWriter.java deleted file mode 100644 index 58d8dd754..000000000 --- a/src/main/java/com/avaje/ebean/text/json/JsonWriter.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.avaje.ebean.text.json; - -/** - * The JSON Writer made available to JsonWriteBeanVisitor's so that you can - * append your own JSON content into the output. - * - * @see JsonWriteBeanVisitor - * @see JsonWriteOptions#setRootPathVisitor(JsonWriteBeanVisitor) - * @see JsonWriteOptions#setPathVisitor(String, JsonWriteBeanVisitor) - * - * @author rbygrave - */ -public interface JsonWriter { - - /** - * Use this to append some custom content into the JSON output. - * - * @param key - * the json key - * - * @param rawJsonValue - * raw json value - */ - public void appendRawValue(String key, String rawJsonValue); - - public void appendQuoteEscapeValue(String key, String rawJsonValue); - -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java index fedada9b6..a59d768d7 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java @@ -65,7 +65,6 @@ import com.avaje.ebean.meta.MetaBeanInfo; import com.avaje.ebean.meta.MetaInfoManager; import com.avaje.ebean.text.csv.CsvReader; import com.avaje.ebean.text.json.JsonContext; -import com.avaje.ebean.text.json.JsonElement; import com.avaje.ebeaninternal.api.LoadBeanRequest; import com.avaje.ebeaninternal.api.LoadManyRequest; import com.avaje.ebeaninternal.api.ScopeTrans; @@ -1963,10 +1962,6 @@ public final class DefaultServer implements SpiEbeanServer { if (typeInfo == null) { return false; } - Class> beanType = typeInfo.getBeanType(); - if (JsonElement.class.isAssignableFrom(beanType)) { - return true; - } return getBeanDescriptor(typeInfo.getBeanType()) != null; } diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java b/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java index e6f2c404a..79aec6675 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java @@ -11,8 +11,6 @@ import com.avaje.ebean.config.ExternalTransactionManager; import com.avaje.ebean.config.ServerConfig; import com.avaje.ebean.config.dbplatform.DatabasePlatform; import com.avaje.ebean.text.json.JsonContext; -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebeaninternal.api.ClassUtil; import com.avaje.ebeaninternal.api.SpiBackgroundExecutor; import com.avaje.ebeaninternal.api.SpiEbeanServer; import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager; @@ -33,7 +31,6 @@ import com.avaje.ebeaninternal.server.query.DefaultRelationalQueryEngine; import com.avaje.ebeaninternal.server.resource.ResourceManager; import com.avaje.ebeaninternal.server.resource.ResourceManagerFactory; import com.avaje.ebeaninternal.server.text.json.DJsonContext; -import com.avaje.ebeaninternal.server.text.json.DefaultJsonValueAdapter; import com.avaje.ebeaninternal.server.transaction.AutoCommitTransactionManager; import com.avaje.ebeaninternal.server.transaction.DefaultTransactionScopeManager; import com.avaje.ebeaninternal.server.transaction.ExternalTransactionScopeManager; @@ -46,6 +43,8 @@ import com.avaje.ebeaninternal.server.type.TypeManager; /** * Used to extend the ServerConfig with additional objects used to configure and * construct an EbeanServer. + * + * @author rbygrave */ public class InternalConfiguration { @@ -163,16 +162,8 @@ public class InternalConfiguration { public JsonContext createJsonContext(SpiEbeanServer server) { - String s = serverConfig.getProperty("json.pretty", "false"); - boolean dfltPretty = "true".equalsIgnoreCase(s); - - s = serverConfig.getProperty("json.jsonValueAdapter", null); - - JsonValueAdapter va = new DefaultJsonValueAdapter(); - if (s != null) { - va = (JsonValueAdapter) ClassUtil.newInstance(s, this.getClass()); - } - return new DJsonContext(server, va, dfltPretty); + + return new DJsonContext(server); } public XmlConfig getXmlConfig() { diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java.orig b/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java.orig new file mode 100644 index 000000000..9d35dcd0a --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java.orig @@ -0,0 +1,266 @@ +package com.avaje.ebeaninternal.server.core; + +import javax.sql.DataSource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.avaje.ebean.ExpressionFactory; +import com.avaje.ebean.cache.ServerCacheManager; +import com.avaje.ebean.config.ExternalTransactionManager; +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebean.config.dbplatform.DatabasePlatform; +import com.avaje.ebean.text.json.JsonContext; +import com.avaje.ebeaninternal.api.SpiBackgroundExecutor; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager; +import com.avaje.ebeaninternal.server.autofetch.AutoFetchManagerFactory; +import com.avaje.ebeaninternal.server.cluster.ClusterManager; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; +import com.avaje.ebeaninternal.server.deploy.DeployOrmXml; +import com.avaje.ebeaninternal.server.deploy.parse.DeployCreateProperties; +import com.avaje.ebeaninternal.server.deploy.parse.DeployInherit; +import com.avaje.ebeaninternal.server.deploy.parse.DeployUtil; +import com.avaje.ebeaninternal.server.expression.DefaultExpressionFactory; +import com.avaje.ebeaninternal.server.lib.sql.DataSourcePool; +import com.avaje.ebeaninternal.server.persist.Binder; +import com.avaje.ebeaninternal.server.persist.DefaultPersister; +import com.avaje.ebeaninternal.server.query.CQueryEngine; +import com.avaje.ebeaninternal.server.query.DefaultOrmQueryEngine; +import com.avaje.ebeaninternal.server.query.DefaultRelationalQueryEngine; +import com.avaje.ebeaninternal.server.resource.ResourceManager; +import com.avaje.ebeaninternal.server.resource.ResourceManagerFactory; +import com.avaje.ebeaninternal.server.text.json.DJsonContext; +<<<<<<< HEAD +import com.avaje.ebeaninternal.server.text.json.DefaultJsonValueAdapter; +import com.avaje.ebeaninternal.server.transaction.AutoCommitTransactionManager; +======= +>>>>>>> json-refactor +import com.avaje.ebeaninternal.server.transaction.DefaultTransactionScopeManager; +import com.avaje.ebeaninternal.server.transaction.ExternalTransactionScopeManager; +import com.avaje.ebeaninternal.server.transaction.JtaTransactionManager; +import com.avaje.ebeaninternal.server.transaction.TransactionManager; +import com.avaje.ebeaninternal.server.transaction.TransactionScopeManager; +import com.avaje.ebeaninternal.server.type.DefaultTypeManager; +import com.avaje.ebeaninternal.server.type.TypeManager; + +/** + * Used to extend the ServerConfig with additional objects used to configure and + * construct an EbeanServer. + */ +public class InternalConfiguration { + + private static final Logger logger = LoggerFactory.getLogger(InternalConfiguration.class); + + private final ServerConfig serverConfig; + + private final BootupClasses bootupClasses; + + private final DeployInherit deployInherit; + + private final ResourceManager resourceManager; + + private final DeployOrmXml deployOrmXml; + + private final TypeManager typeManager; + + private final Binder binder; + + private final DeployCreateProperties deployCreateProperties; + + private final DeployUtil deployUtil; + + private final BeanDescriptorManager beanDescriptorManager; + + private final TransactionManager transactionManager; + + private final TransactionScopeManager transactionScopeManager; + + private final CQueryEngine cQueryEngine; + + private final ClusterManager clusterManager; + + private final ServerCacheManager cacheManager; + + private final ExpressionFactory expressionFactory; + + private final SpiBackgroundExecutor backgroundExecutor; + + private final PstmtBatch pstmtBatch; + + private final XmlConfig xmlConfig; + + public InternalConfiguration(XmlConfig xmlConfig, ClusterManager clusterManager, + ServerCacheManager cacheManager, SpiBackgroundExecutor backgroundExecutor, + ServerConfig serverConfig, BootupClasses bootupClasses, PstmtBatch pstmtBatch) { + + this.xmlConfig = xmlConfig; + this.pstmtBatch = pstmtBatch; + this.clusterManager = clusterManager; + this.backgroundExecutor = backgroundExecutor; + this.cacheManager = cacheManager; + this.serverConfig = serverConfig; + this.bootupClasses = bootupClasses; + this.expressionFactory = new DefaultExpressionFactory(); + + this.typeManager = new DefaultTypeManager(serverConfig, bootupClasses); + this.binder = new Binder(typeManager); + + this.resourceManager = ResourceManagerFactory.createResourceManager(serverConfig); + this.deployOrmXml = new DeployOrmXml(resourceManager.getResourceSource()); + this.deployInherit = new DeployInherit(bootupClasses); + + this.deployCreateProperties = new DeployCreateProperties(typeManager); + this.deployUtil = new DeployUtil(typeManager, serverConfig); + + this.beanDescriptorManager = new BeanDescriptorManager(this); + beanDescriptorManager.deploy(); + + this.transactionManager = createTransactionManager(); + + this.cQueryEngine = new CQueryEngine(serverConfig.getDatabasePlatform(), binder); + + ExternalTransactionManager externalTransactionManager = serverConfig.getExternalTransactionManager(); + if (externalTransactionManager == null && serverConfig.isUseJtaTransactionManager()) { + externalTransactionManager = new JtaTransactionManager(); + } + if (externalTransactionManager != null) { + externalTransactionManager.setTransactionManager(transactionManager); + this.transactionScopeManager = new ExternalTransactionScopeManager(transactionManager, externalTransactionManager); + logger.info("Using Transaction Manager [" + externalTransactionManager.getClass() + "]"); + } else { + this.transactionScopeManager = new DefaultTransactionScopeManager(transactionManager); + } + + } + + /** + * Create the TransactionManager taking into account autoCommit mode. + */ + private TransactionManager createTransactionManager() { + + if (isAutoCommitMode()) { + return new AutoCommitTransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager, this.getBootupClasses()); + } + + return new TransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager, this.getBootupClasses()); + } + + /** + * Return true if autoCommit mode is on. + */ + private boolean isAutoCommitMode() { + if (serverConfig.isAutoCommitMode()) { + // explicitly set + return true; + } + DataSource dataSource = serverConfig.getDataSource(); + if (dataSource instanceof DataSourcePool && ((DataSourcePool)dataSource).getAutoCommit()) { + // We know the DataSourcePool is using autoCommit + return true; + } + return false; + } + + public JsonContext createJsonContext(SpiEbeanServer server) { + + return new DJsonContext(server); + } + + public XmlConfig getXmlConfig() { + return xmlConfig; + } + + public AutoFetchManager createAutoFetchManager(SpiEbeanServer server) { + return AutoFetchManagerFactory.create(server, serverConfig, resourceManager); + } + + public RelationalQueryEngine createRelationalQueryEngine() { + return new DefaultRelationalQueryEngine(binder, serverConfig.getDatabaseBooleanTrue()); + } + + public OrmQueryEngine createOrmQueryEngine() { + return new DefaultOrmQueryEngine(beanDescriptorManager, cQueryEngine); + } + + public Persister createPersister(SpiEbeanServer server) { + return new DefaultPersister(server, binder, beanDescriptorManager, pstmtBatch); + } + + public PstmtBatch getPstmtBatch() { + return pstmtBatch; + } + + public ServerCacheManager getCacheManager() { + return cacheManager; + } + + public BootupClasses getBootupClasses() { + return bootupClasses; + } + + public DatabasePlatform getDatabasePlatform() { + return serverConfig.getDatabasePlatform(); + } + + public ServerConfig getServerConfig() { + return serverConfig; + } + + public ExpressionFactory getExpressionFactory() { + return expressionFactory; + } + + public TypeManager getTypeManager() { + return typeManager; + } + + public Binder getBinder() { + return binder; + } + + public BeanDescriptorManager getBeanDescriptorManager() { + return beanDescriptorManager; + } + + public DeployInherit getDeployInherit() { + return deployInherit; + } + + public ResourceManager getResourceManager() { + return resourceManager; + } + + public DeployOrmXml getDeployOrmXml() { + return deployOrmXml; + } + + public DeployCreateProperties getDeployCreateProperties() { + return deployCreateProperties; + } + + public DeployUtil getDeployUtil() { + return deployUtil; + } + + public TransactionManager getTransactionManager() { + return transactionManager; + } + + public TransactionScopeManager getTransactionScopeManager() { + return transactionScopeManager; + } + + public CQueryEngine getCQueryEngine() { + return cQueryEngine; + } + + public ClusterManager getClusterManager() { + return clusterManager; + } + + public SpiBackgroundExecutor getBackgroundExecutor() { + return backgroundExecutor; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanCollectionHelp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanCollectionHelp.java index 711d1496c..842e0545f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanCollectionHelp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanCollectionHelp.java @@ -9,7 +9,7 @@ import com.avaje.ebean.bean.BeanCollection; import com.avaje.ebean.bean.BeanCollectionAdd; import com.avaje.ebean.bean.BeanCollectionLoader; import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebeaninternal.server.text.json.WriteJsonContext; +import com.avaje.ebeaninternal.server.text.json.WriteJson; /** * Helper functions for performing tasks on Lists Sets or Maps. @@ -62,6 +62,6 @@ public interface BeanCollectionHelp { /** * Write the collection out as json. */ - public void jsonWrite(WriteJsonContext ctx, String name, Object collection, boolean explicitInclude); + public void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java index 90152549f..67eadf243 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java @@ -13,6 +13,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import javax.json.stream.JsonParser; import javax.persistence.PersistenceException; import org.slf4j.Logger; @@ -34,8 +35,6 @@ import com.avaje.ebean.event.BeanPersistListener; import com.avaje.ebean.event.BeanQueryAdapter; import com.avaje.ebean.meta.MetaBeanInfo; import com.avaje.ebean.meta.MetaQueryPlanStatistic; -import com.avaje.ebean.text.TextException; -import com.avaje.ebean.text.json.JsonWriteBeanVisitor; import com.avaje.ebeaninternal.api.HashQueryPlan; import com.avaje.ebeaninternal.api.SpiEbeanServer; import com.avaje.ebeaninternal.api.SpiQuery; @@ -62,10 +61,7 @@ import com.avaje.ebeaninternal.server.query.CQueryPlan; import com.avaje.ebeaninternal.server.query.CQueryPlanStats.Snapshot; import com.avaje.ebeaninternal.server.query.SplitName; import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail; -import com.avaje.ebeaninternal.server.text.json.ReadJsonContext; -import com.avaje.ebeaninternal.server.text.json.ReadJsonContext.ReadBeanState; -import com.avaje.ebeaninternal.server.text.json.WriteJsonContext; -import com.avaje.ebeaninternal.server.text.json.WriteJsonContext.WriteBeanState; +import com.avaje.ebeaninternal.server.text.json.WriteJson; import com.avaje.ebeaninternal.server.type.DataBind; import com.avaje.ebeaninternal.server.type.TypeManager; import com.avaje.ebeaninternal.util.SortByClause; @@ -195,12 +191,12 @@ public class BeanDescriptor implements MetaBeanInfo { /** * Inheritance information. Server side only. */ - private final InheritInfo inheritInfo; + protected final InheritInfo inheritInfo; /** * Derived list of properties that make up the unique id. */ - private final BeanProperty idProperty; + protected final BeanProperty idProperty; private final int idPropertyIndex; /** @@ -327,7 +323,8 @@ public class BeanDescriptor implements MetaBeanInfo { private final boolean cacheSharableBeans; private final BeanDescriptorCacheHelp cacheHelp; - + private final BeanDescriptorJsonHelp jsonHelp; + private final String defaultSelectClause; private final Set defaultSelectClauseSet; @@ -422,7 +419,7 @@ public class BeanDescriptor implements MetaBeanInfo { this.cacheSharableBeans = noRelationships && deploy.getCacheOptions().isReadOnly(); this.cacheHelp = new BeanDescriptorCacheHelp(this, owner.getCacheManager(), deploy.getCacheOptions(), cacheSharableBeans, propertiesOneImported); - + this.jsonHelp = new BeanDescriptorJsonHelp(this); // Check if there are no cascade save associated beans ( subject to change // in initialiseOther()). Note that if we are in an inheritance hierarchy @@ -2115,167 +2112,23 @@ public class BeanDescriptor implements MetaBeanInfo { return propertiesLocal; } - public void jsonWrite(WriteJsonContext ctx, EntityBean bean) { - - if (bean != null) { - - ctx.appendObjectBegin(); - WriteBeanState prevState = ctx.pushBeanState(bean); - - if (inheritInfo != null) { - InheritInfo localInheritInfo = inheritInfo.readType(bean.getClass()); - String discValue = localInheritInfo.getDiscriminatorStringValue(); - String discColumn = localInheritInfo.getDiscriminatorColumn(); - ctx.appendDiscriminator(discColumn, discValue); - - BeanDescriptor> localDescriptor = localInheritInfo.getBeanDescriptor(); - localDescriptor.jsonWriteProperties(ctx, bean); - - } else { - jsonWriteProperties(ctx, bean); - } - - ctx.pushPreviousState(prevState); - ctx.appendObjectEnd(); - } - } - - @SuppressWarnings("unchecked") - private void jsonWriteProperties(WriteJsonContext ctx, EntityBean bean) { - - JsonWriteBeanVisitor beanVisitor = (JsonWriteBeanVisitor) ctx.getBeanVisitor(); - - Set props = ctx.getIncludeProperties(); - - boolean explicitAllProps; - if (props == null) { - explicitAllProps = false; - } else { - explicitAllProps = props.contains("*"); - if (explicitAllProps || props.isEmpty()) { - props = null; - } - } - - if (idProperty != null) { - Object idValue = idProperty.getValue(bean); - if (idValue != null) { - if (props == null || props.contains(idProperty.getName())) { - idProperty.jsonWrite(ctx, bean); - } - } - } - - if (!explicitAllProps && props == null) { - // just render the loaded properties - props = ((EntityBean)bean)._ebean_getIntercept().getLoadedPropertyNames(); - } - if (props != null) { - // render only the appropriate properties (when not all properties) - for (String prop : props) { - BeanProperty p = getBeanProperty(prop); - if (p != null && !p.isId()) { - p.jsonWrite(ctx, bean); - } - } - } else { - if (explicitAllProps || !isReference(bean._ebean_getIntercept())) { - // render all the properties and invoke lazy loading if required - for (int j = 0; j < propertiesNonTransient.length; j++) { - propertiesNonTransient[j].jsonWrite(ctx, bean); - } - for (int j = 0; j < propertiesTransient.length; j++) { - propertiesTransient[j].jsonWrite(ctx, bean); - } - } - } - - if (beanVisitor != null) { - beanVisitor.visit((T) bean, ctx); - } - } - - @SuppressWarnings("unchecked") - public T jsonReadBean(ReadJsonContext ctx, String path) { - ReadBeanState beanState = jsonRead(ctx, path); - if (beanState == null) { - return null; - } else { - return (T) beanState.getBean(); - } - } - - public ReadBeanState jsonRead(ReadJsonContext ctx, String path) { - if (!ctx.readObjectBegin()) { - // the object is null - return null; - } - - if (inheritInfo == null) { - return jsonReadObject(ctx, path); - - } else { - - // check for the discriminator value to determine the correct sub type - String discColumn = inheritInfo.getRoot().getDiscriminatorColumn(); - - if (!ctx.readKeyNext()) { - String msg = "Error reading inheritance discriminator - expected [" + discColumn + "] but no json key?"; - throw new TextException(msg); - } - - String propName = ctx.getTokenKey(); - String discValue; - if (propName.equalsIgnoreCase(discColumn)) { - discValue = ctx.readScalarValue(); - if (!ctx.readValueNext()) { - // Expected to read a comma to setup for reading the real properties of the bean - String msg = "Error reading inheritance discriminator [" + discColumn + "]. Expected more json name values?"; - throw new TextException(msg); - } - - } else { - // Assume that the we are just reading using this bean type - // Push the token key back so that it is re-read as it is one - // of the real properties of the bean itself - ctx.pushTokenKey(); - discValue = inheritInfo.getDiscriminatorStringValue(); - } - - // determine the sub type for this particular json object - InheritInfo localInheritInfo = inheritInfo.readType(discValue); - BeanDescriptor> localDescriptor = localInheritInfo.getBeanDescriptor(); - return localDescriptor.jsonReadObject(ctx, path); - } - } + public void jsonWrite(WriteJson writeJson, EntityBean bean) { + jsonHelp.jsonWrite(writeJson, bean, null); + } - private ReadBeanState jsonReadObject(ReadJsonContext ctx, String path) { + public void jsonWrite(WriteJson writeJson, EntityBean bean, String key) { + jsonHelp.jsonWrite(writeJson, bean, key); + } - EntityBean bean = createEntityBean(); - ctx.pushBean(bean, path, this); - - do { - if (!ctx.readKeyNext()) { - break; - } else { - // we read a property key ... - String propName = ctx.getTokenKey(); - BeanProperty p = getBeanProperty(propName); - if (p != null) { - p.jsonRead(ctx, bean); - ctx.setProperty(propName); - } else { - // unknown property key ... - ctx.readUnmappedJson(propName); - } - - if (!ctx.readValueNext()) { - break; - } - } - } while (true); - - return ctx.popBeanState(); + protected void jsonWriteProperties(WriteJson writeJson, EntityBean bean) { + jsonHelp.jsonWriteProperties(writeJson, bean); } + public T jsonRead(JsonParser parser, String path) { + return jsonHelp.jsonRead(parser, path); + } + + protected T jsonReadObject(JsonParser parser, String path) { + return jsonHelp.jsonReadObject(parser, path); + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorJsonHelp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorJsonHelp.java new file mode 100644 index 000000000..b6540e7db --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorJsonHelp.java @@ -0,0 +1,145 @@ +package com.avaje.ebeaninternal.server.deploy; + +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.text.TextException; +import com.avaje.ebeaninternal.server.text.json.WriteJson; +import com.avaje.ebeaninternal.server.text.json.WriteJson.WriteBean; + +public class BeanDescriptorJsonHelp { + + private final BeanDescriptor desc; + + private final InheritInfo inheritInfo; + + public BeanDescriptorJsonHelp(BeanDescriptor desc) { + this.desc = desc; + this.inheritInfo = desc.inheritInfo; + } + + public void jsonWrite(WriteJson writeJson, EntityBean bean, String key) { + +// if (writeJson.hasBean()) { + + writeJson.writeStartObject(key); + //WriteBeanState prevState = ctx.pushBeanState(bean); + + if (inheritInfo == null) { + jsonWriteProperties(writeJson, bean); + + } else { + InheritInfo localInheritInfo = inheritInfo.readType(bean.getClass()); + String discValue = localInheritInfo.getDiscriminatorStringValue(); + String discColumn = localInheritInfo.getDiscriminatorColumn(); + writeJson.gen().write(discColumn, discValue); + + BeanDescriptor> localDescriptor = localInheritInfo.getBeanDescriptor(); + localDescriptor.jsonWriteProperties(writeJson, bean); + } + + //ctx.pushPreviousState(prevState); + writeJson.gen().writeEnd(); + } + + protected void jsonWriteProperties(WriteJson writeJson, EntityBean bean) { + + + WriteBean writeBean = writeJson.createWriteBean(desc, bean); + writeBean.write(writeJson); + } + + + @SuppressWarnings("unchecked") + public T jsonRead(JsonParser parser, String path) { + + if (!parser.hasNext()) { + return null; + } + Event event = parser.next(); + if (Event.VALUE_NULL == event || Event.END_ARRAY == event) { + return null; + } + if (Event.START_OBJECT != event) { + throw new RuntimeException("Unexpected token "+event+" - expecting start_object at: "+parser.getLocation()); + } + + if (desc.inheritInfo == null) { + return jsonReadObject(parser, path); + } + + // check for the discriminator value to determine the correct sub type + String discColumn = inheritInfo.getRoot().getDiscriminatorColumn(); + + if (!parser.hasNext() || ((event = parser.next()) != Event.KEY_NAME)) { + String msg = "Error reading inheritance discriminator - expected [" + discColumn + "] but no json key?"; + throw new TextException(msg); + } + + String propName = parser.getString(); + if (!propName.equalsIgnoreCase(discColumn)) { + // just try to assume this is the correct bean type in the inheritance + BeanProperty property = desc.getBeanProperty(propName); + if (property != null) { + EntityBean bean = desc.createEntityBean(); + property.jsonRead(parser, bean); + return jsonReadProperties(parser, bean); + } + String msg = "Error reading inheritance discriminator, expected property ["+discColumn+"] but got [" + propName + "] ?"; + throw new TextException(msg); + } + + if (!parser.hasNext() || ((event = parser.next()) != Event.VALUE_STRING)) { + String msg = "Error reading inheritance discriminator - expected value_string token but got [" + event + "] at ["+parser.getLocation()+"]?"; + throw new TextException(msg); + } + + String discValue = parser.getString(); + + // determine the sub type for this particular json object + InheritInfo localInheritInfo = inheritInfo.readType(discValue); + BeanDescriptor> localDescriptor = localInheritInfo.getBeanDescriptor(); + return (T) localDescriptor.jsonReadObject(parser, path); + } + + protected T jsonReadObject(JsonParser parser, String path) { + + EntityBean bean = desc.createEntityBean(); + //ctx.pushBean(bean, path, this); + + return jsonReadProperties(parser, bean); + } + + @SuppressWarnings("unchecked") + protected T jsonReadProperties(JsonParser parser, EntityBean bean) { + + do { + + if (parser.hasNext()) { + Event event = parser.next(); + if (Event.KEY_NAME == event) { + String key = parser.getString(); + BeanProperty p = desc.getBeanProperty(key); + if (p != null) { + p.jsonRead(parser, bean); + + } else { + //Object rawValue = EJson.parse(parser); + // unknown property key ... + //ctx.readUnmappedJson(propName); + } + + } else if (Event.END_OBJECT == event) { + break; + + } else { + throw new RuntimeException("Unexpected token "+event+" - expecting key or end_object at: "+parser.getLocation()); + } + } + + } while (true); + return (T)bean; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanListHelp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanListHelp.java index 25baefd49..71860bbd6 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanListHelp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanListHelp.java @@ -12,7 +12,7 @@ import com.avaje.ebean.bean.BeanCollectionAdd; import com.avaje.ebean.bean.BeanCollectionLoader; import com.avaje.ebean.bean.EntityBean; import com.avaje.ebean.common.BeanList; -import com.avaje.ebeaninternal.server.text.json.WriteJsonContext; +import com.avaje.ebeaninternal.server.text.json.WriteJson; /** * Helper object for dealing with Lists. @@ -128,7 +128,7 @@ public final class BeanListHelp implements BeanCollectionHelp { } } - public void jsonWrite(WriteJsonContext ctx, String name, Object collection, boolean explicitInclude) { + public void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) { List> list; if (collection instanceof BeanCollection>) { @@ -147,15 +147,11 @@ public final class BeanListHelp implements BeanCollectionHelp { list = (List>) collection; } - ctx.beginAssocMany(name); + ctx.gen().writeStartArray(name); for (int j = 0; j < list.size(); j++) { - if (j > 0) { - ctx.appendComma(); - } - Object detailBean = list.get(j); - targetDescriptor.jsonWrite(ctx, (EntityBean)detailBean); + targetDescriptor.jsonWrite(ctx, (EntityBean)list.get(j)); } - ctx.endAssocMany(); + ctx.gen().writeEnd(); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanMapHelp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanMapHelp.java index 316aa2106..b4c6eb4bb 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanMapHelp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanMapHelp.java @@ -13,7 +13,7 @@ import com.avaje.ebean.bean.BeanCollectionAdd; import com.avaje.ebean.bean.BeanCollectionLoader; import com.avaje.ebean.bean.EntityBean; import com.avaje.ebean.common.BeanMap; -import com.avaje.ebeaninternal.server.text.json.WriteJsonContext; +import com.avaje.ebeaninternal.server.text.json.WriteJson; /** * Helper specifically for dealing with Maps. @@ -156,7 +156,7 @@ public final class BeanMapHelp implements BeanCollectionHelp { } } - public void jsonWrite(WriteJsonContext ctx, String name, Object collection, boolean explicitInclude) { + public void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) { Map,?> map; if (collection instanceof BeanCollection>){ @@ -175,19 +175,14 @@ public final class BeanMapHelp implements BeanCollectionHelp { map = (Map,?>)collection; } - int count = 0; - ctx.beginAssocMany(name); + ctx.gen().writeStartArray(name); Iterator> it = map.entrySet().iterator(); while (it.hasNext()) { Entry, ?> entry = (Entry, ?>)it.next(); - if (count++ > 0){ - ctx.appendComma(); - } //FIXME: json write map key ... - Object detailBean = entry.getValue(); - targetDescriptor.jsonWrite(ctx, (EntityBean)detailBean); + targetDescriptor.jsonWrite(ctx, (EntityBean) entry.getValue()); } - ctx.endAssocMany(); + ctx.gen().writeEnd(); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java index 58f4f5641..85315b389 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java @@ -10,6 +10,8 @@ import java.sql.Types; import java.util.List; import java.util.Map; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; import javax.persistence.PersistenceException; import com.avaje.ebean.bean.EntityBean; @@ -18,7 +20,6 @@ import com.avaje.ebean.config.dbplatform.DbEncryptFunction; import com.avaje.ebean.config.dbplatform.DbType; import com.avaje.ebean.text.StringFormatter; import com.avaje.ebean.text.StringParser; -import com.avaje.ebean.text.TextException; import com.avaje.ebeaninternal.server.core.InternString; import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty; @@ -30,8 +31,7 @@ import com.avaje.ebeaninternal.server.query.SqlBeanLoad; import com.avaje.ebeaninternal.server.query.SqlJoinType; import com.avaje.ebeaninternal.server.reflect.BeanReflectGetter; import com.avaje.ebeaninternal.server.reflect.BeanReflectSetter; -import com.avaje.ebeaninternal.server.text.json.ReadJsonContext; -import com.avaje.ebeaninternal.server.text.json.WriteJsonContext; +import com.avaje.ebeaninternal.server.text.json.WriteJson; import com.avaje.ebeaninternal.server.type.DataBind; import com.avaje.ebeaninternal.server.type.ScalarType; @@ -78,7 +78,7 @@ public class BeanProperty implements ElPropertyValue { * Flag set if this maps to the inheritance discriminator column */ final boolean discriminator; - + /** * Flag to mark the property as embedded. This could be on * BeanPropertyAssocOne rather than here. Put it here for checking Id type @@ -92,7 +92,7 @@ public class BeanProperty implements ElPropertyValue { final boolean version; final boolean naturalKey; - + /** * Set if this property is nullable. */ @@ -136,7 +136,7 @@ public class BeanProperty implements ElPropertyValue { * True if the property is a Clob, Blob LongVarchar or LongVarbinary. */ final boolean lob; - + final boolean fetchEager; final boolean isTransient; @@ -147,7 +147,7 @@ public class BeanProperty implements ElPropertyValue { final String name; final int propertyIndex; - + /** * The reflected field. */ @@ -265,7 +265,6 @@ public class BeanProperty implements ElPropertyValue { final boolean indexed; final String indexName; - public BeanProperty(DeployBeanProperty deploy) { this(null, null, deploy); } @@ -275,10 +274,8 @@ public class BeanProperty implements ElPropertyValue { this.descriptor = descriptor; this.name = InternString.intern(deploy.getName()); this.propertyIndex = deploy.getPropertyIndex(); - this.indexed = deploy.isIndexed(); this.indexName = deploy.getIndexName(); - this.unidirectionalShadow = deploy.isUndirectionalShadow(); this.discriminator = deploy.isDiscriminator(); this.localEncrypted = deploy.isLocalEncrypted(); @@ -333,7 +330,7 @@ public class BeanProperty implements ElPropertyValue { this.lob = isLobType(dbType); this.propertyType = deploy.getPropertyType(); this.field = deploy.getField(); - + EntityType et = descriptor == null ? null : descriptor.getEntityType(); this.elPlaceHolder = tableAliasIntern(descriptor, deploy.getElPlaceHolder(et), false, null); this.elPlaceHolderEncrypted = tableAliasIntern(descriptor, deploy.getElPlaceHolder(et), dbEncrypted, dbColumn); @@ -371,7 +368,6 @@ public class BeanProperty implements ElPropertyValue { this.indexed = source.isIndexed(); this.indexName = source.getIndexName(); - this.dbColumn = InternString.intern(override.getDbColumn()); this.sqlFormulaJoin = InternString.intern(override.getSqlFormulaJoin()); this.sqlFormulaSelect = InternString.intern(override.getSqlFormulaSelect()); @@ -420,7 +416,7 @@ public class BeanProperty implements ElPropertyValue { this.lob = isLobType(dbType); this.propertyType = source.getPropertyType(); this.field = source.getField(); - + this.elPlaceHolder = override.replace(source.elPlaceHolder, source.dbColumn); this.elPlaceHolderEncrypted = override.replace(source.elPlaceHolderEncrypted, source.dbColumn); @@ -487,7 +483,7 @@ public class BeanProperty implements ElPropertyValue { public boolean isDiscriminator() { return discriminator; } - + /** * Return true if the underlying type is mutable. */ @@ -675,6 +671,14 @@ public class BeanProperty implements ElPropertyValue { public BeanProperty getBeanProperty() { return this; } + + public boolean isIndexed() { + return indexed; + } + + public String getIndexName() { + return indexName; + } /** * Return the getter method. @@ -737,12 +741,12 @@ public class BeanProperty implements ElPropertyValue { public Object getCacheDataValue(EntityBean bean) { return getValue(bean); - } + } public void setCacheDataValue(EntityBean bean, Object cacheData) { setValue(bean, cacheData); } - + /** * Return the value of the property method. */ @@ -755,12 +759,12 @@ public class BeanProperty implements ElPropertyValue { throw new RuntimeException(msg, ex); } } - + /** * Explicitly use reflection to get value. */ public Object getValueViaReflection(Object bean) { - try { + try { return readMethod.invoke(bean, NO_ARGS); } catch (Exception ex) { String beanType = bean == null ? "null" : bean.getClass().getName(); @@ -815,7 +819,7 @@ public class BeanProperty implements ElPropertyValue { * Return the position of this property in the enhanced bean. */ public int getPropertyIndex() { - return propertyIndex; + return propertyIndex; } public String getElName() { @@ -829,7 +833,6 @@ public class BeanProperty implements ElPropertyValue { return false; } - @Override public boolean containsFormulaWithJoin() { return formula && sqlFormulaJoin != null; @@ -895,7 +898,7 @@ public class BeanProperty implements ElPropertyValue { public boolean isDirtyValue(Object value) { return scalarType.isDirty(value); } - + /** * Return the scalarType. */ @@ -914,7 +917,7 @@ public class BeanProperty implements ElPropertyValue { public boolean isDateTimeCapable() { return scalarType != null && scalarType.isDateTimeCapable(); } - + public int getJdbcType() { return scalarType == null ? 0 : scalarType.getJdbcType(); } @@ -1020,7 +1023,7 @@ public class BeanProperty implements ElPropertyValue { public boolean isLoadProperty() { return !isTransient || formula; } - + /** * Return true if this is a version column used for concurrency checking. */ @@ -1183,43 +1186,32 @@ public class BeanProperty implements ElPropertyValue { return name; } - @SuppressWarnings("unchecked") - public void jsonWrite(WriteJsonContext ctx, EntityBean bean) { - if (!jsonSerialize) { - return; - } - Object value = getValueIntercept(bean); - if (value == null) { - ctx.appendNull(name); - } else { - ctx.appendNameValue(name, scalarType, value); - } + public void jsonWrite(WriteJson writeJson, EntityBean bean) { + if (!jsonSerialize) { + return; + } + Object value = getValueIntercept(bean); + if (value == null) { + writeJson.gen().writeNull(name); + } else { + scalarType.jsonWrite(writeJson.gen(), name, value); + } + } + + public void jsonRead(JsonParser ctx, EntityBean bean) { + if (!jsonDeserialize) { + return; + } + if (!ctx.hasNext()) { + throw new RuntimeException(ctx.getLocation().toString()); + } + Event event = ctx.next(); + if (Event.VALUE_NULL == event) { + setValue(bean, null); + } else { + Object objValue = scalarType.jsonRead(ctx, event); + setValue(bean, objValue); } - public void jsonRead(ReadJsonContext ctx, EntityBean bean) { - if (!jsonDeserialize) { - return; - } - String jsonValue; - try { - jsonValue = ctx.readScalarValue(); - } catch (TextException e) { - throw new TextException("Error reading property " + getFullBeanName(), e); - } - Object objValue; - if (jsonValue == null) { - objValue = null; - } else { - objValue = scalarType.jsonFromString(jsonValue, ctx.getValueAdapter()); - } - setValue(bean, objValue); - } - - public boolean isIndexed() { - return indexed; - } - - public String getIndexName() { - return indexName; - } + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java.orig b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java.orig new file mode 100644 index 000000000..ef3fa4795 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java.orig @@ -0,0 +1,1257 @@ +package com.avaje.ebeaninternal.server.deploy; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.sql.SQLException; +import java.sql.Types; +import java.util.List; +import java.util.Map; + +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; +import javax.persistence.PersistenceException; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.config.EncryptKey; +import com.avaje.ebean.config.dbplatform.DbEncryptFunction; +import com.avaje.ebean.config.dbplatform.DbType; +import com.avaje.ebean.text.StringFormatter; +import com.avaje.ebean.text.StringParser; +import com.avaje.ebeaninternal.server.core.InternString; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; +import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; +import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder; +import com.avaje.ebeaninternal.server.el.ElPropertyValue; +import com.avaje.ebeaninternal.server.lib.util.StringHelper; +import com.avaje.ebeaninternal.server.query.SqlBeanLoad; +import com.avaje.ebeaninternal.server.query.SqlJoinType; +import com.avaje.ebeaninternal.server.reflect.BeanReflectGetter; +import com.avaje.ebeaninternal.server.reflect.BeanReflectSetter; +import com.avaje.ebeaninternal.server.text.json.WriteJson; +import com.avaje.ebeaninternal.server.type.DataBind; +import com.avaje.ebeaninternal.server.type.ScalarType; + +/** + * Description of a property of a bean. Includes its deployment information such + * as database column mapping information. + */ +public class BeanProperty implements ElPropertyValue { + + /** + * Advanced bean deployment. To exclude this property from update where + * clause. + */ + public static final String EXCLUDE_FROM_UPDATE_WHERE = "EXCLUDE_FROM_UPDATE_WHERE"; + + /** + * Advanced bean deployment. To exclude this property from delete where + * clause. + */ + public static final String EXCLUDE_FROM_DELETE_WHERE = "EXCLUDE_FROM_DELETE_WHERE"; + + /** + * Advanced bean deployment. To exclude this property from insert. + */ + public static final String EXCLUDE_FROM_INSERT = "EXCLUDE_FROM_INSERT"; + + /** + * Advanced bean deployment. To exclude this property from update set + * clause. + */ + public static final String EXCLUDE_FROM_UPDATE = "EXCLUDE_FROM_UPDATE"; + + /** + * Flag to mark this at part of the unique id. + */ + final boolean id; + + /** + * Flag to make this as a dummy property for unidirecitonal relationships. + */ + final boolean unidirectionalShadow; + + /** + * Flag set if this maps to the inheritance discriminator column + */ + final boolean discriminator; + + /** + * Flag to mark the property as embedded. This could be on + * BeanPropertyAssocOne rather than here. Put it here for checking Id type + * (embedded or not). + */ + final boolean embedded; + + /** + * Flag indicating if this the version property. + */ + final boolean version; + + final boolean naturalKey; + + /** + * Set if this property is nullable. + */ + final boolean nullable; + + final boolean unique; + + /** + * Is this property include in database resultSet. + */ + final boolean dbRead; + + /** + * Include in DB insert. + */ + final boolean dbInsertable; + + /** + * Include in DB update. + */ + final boolean dbUpdatable; + + /** + * True if the property is based on a SECONDARY table. + */ + final boolean secondaryTable; + + final TableJoin secondaryTableJoin; + final String secondaryTableJoinPrefix; + + /** + * The property is inherited from a super class. + */ + final boolean inherited; + + final Class> owningType; + + final boolean local; + + /** + * True if the property is a Clob, Blob LongVarchar or LongVarbinary. + */ + final boolean lob; + + final boolean fetchEager; + + final boolean isTransient; + + /** + * The logical bean property name. + */ + final String name; + + final int propertyIndex; + + /** + * The reflected field. + */ + final Field field; + + /** + * The bean type. + */ + final Class> propertyType; + + final String dbBind; + + /** + * The database column. This can include quoted identifiers. + */ + final String dbColumn; + + final String elPlaceHolder; + final String elPlaceHolderEncrypted; + + /** + * Select part of a SQL Formula used to populate this property. + */ + final String sqlFormulaSelect; + + /** + * Join part of a SQL Formula. + */ + final String sqlFormulaJoin; + + final boolean formula; + + /** + * Set to true if stored encrypted. + */ + final boolean dbEncrypted; + + final boolean localEncrypted; + + final int dbEncryptedType; + + /** + * The jdbc data type this maps to. + */ + final int dbType; + + /** + * The default value to insert if null. + */ + final Object defaultValue; + + /** + * Extra deployment parameters. + */ + final Map extraAttributeMap; + + /** + * The method used to read the property. + */ + final Method readMethod; + + /** + * The method used to write the property. + */ + final Method writeMethod; + + /** + * Generator for insert or update timestamp etc. + */ + final GeneratedProperty generatedProperty; + + final BeanReflectGetter getter; + + final BeanReflectSetter setter; + + final BeanDescriptor> descriptor; + + /** + * Used for non-jdbc native types (java.util.Date Enums etc). Converts from + * logical to jdbc types. + */ + @SuppressWarnings("rawtypes") + final ScalarType scalarType; + + boolean cascadeValidate; + + /** + * The length or precision for DB column. + */ + final int dbLength; + + /** + * The scale for DB column (decimal). + */ + final int dbScale; + + /** + * Deployment defined DB column definition. + */ + final String dbColumnDefn; + + /** + * DB Constraint (typically check constraint on enum) + */ + final String dbConstraintExpression; + + final DbEncryptFunction dbEncryptFunction; + + int deployOrder; + + final boolean jsonSerialize; + + final boolean jsonDeserialize; + + final boolean indexed; + + final String indexName; + + public BeanProperty(DeployBeanProperty deploy) { + this(null, null, deploy); + } + + public BeanProperty(BeanDescriptorMap owner, BeanDescriptor> descriptor, DeployBeanProperty deploy) { + + this.descriptor = descriptor; + this.name = InternString.intern(deploy.getName()); + this.propertyIndex = deploy.getPropertyIndex(); + + this.indexed = deploy.isIndexed(); + this.indexName = deploy.getIndexName(); + + this.unidirectionalShadow = deploy.isUndirectionalShadow(); + this.discriminator = deploy.isDiscriminator(); + this.localEncrypted = deploy.isLocalEncrypted(); + this.dbEncrypted = deploy.isDbEncrypted(); + this.dbEncryptedType = deploy.getDbEncryptedType(); + this.dbEncryptFunction = deploy.getDbEncryptFunction(); + this.dbBind = deploy.getDbBind(); + this.dbRead = deploy.isDbRead(); + this.dbInsertable = deploy.isDbInsertable(); + this.dbUpdatable = deploy.isDbUpdateable(); + + this.secondaryTable = deploy.isSecondaryTable(); + if (secondaryTable) { + this.secondaryTableJoin = new TableJoin(deploy.getSecondaryTableJoin(), null); + this.secondaryTableJoinPrefix = deploy.getSecondaryTableJoinPrefix(); + } else { + this.secondaryTableJoin = null; + this.secondaryTableJoinPrefix = null; + } + this.fetchEager = deploy.isFetchEager(); + this.isTransient = deploy.isTransient(); + this.nullable = deploy.isNullable(); + this.unique = deploy.isUnique(); + this.naturalKey = deploy.isNaturalKey(); + this.dbLength = deploy.getDbLength(); + this.dbScale = deploy.getDbScale(); + this.dbColumnDefn = InternString.intern(deploy.getDbColumnDefn()); + this.dbConstraintExpression = InternString.intern(deploy.getDbConstraintExpression()); + + this.inherited = false;// deploy.isInherited(); + this.owningType = deploy.getOwningType(); + this.local = deploy.isLocal(); + + this.version = deploy.isVersionColumn(); + this.embedded = deploy.isEmbedded(); + this.id = deploy.isId(); + this.generatedProperty = deploy.getGeneratedProperty(); + this.readMethod = deploy.getReadMethod(); + this.writeMethod = deploy.getWriteMethod(); + this.getter = deploy.getGetter(); + this.setter = deploy.getSetter(); + + this.dbColumn = tableAliasIntern(descriptor, deploy.getDbColumn(), false, null); + this.sqlFormulaJoin = InternString.intern(deploy.getSqlFormulaJoin()); + this.sqlFormulaSelect = InternString.intern(deploy.getSqlFormulaSelect()); + this.formula = sqlFormulaSelect != null; + + this.extraAttributeMap = deploy.getExtraAttributeMap(); + this.defaultValue = deploy.getDefaultValue(); + this.dbType = deploy.getDbType(); + this.scalarType = deploy.getScalarType(); + this.lob = isLobType(dbType); + this.propertyType = deploy.getPropertyType(); + this.field = deploy.getField(); + + EntityType et = descriptor == null ? null : descriptor.getEntityType(); + this.elPlaceHolder = tableAliasIntern(descriptor, deploy.getElPlaceHolder(et), false, null); + this.elPlaceHolderEncrypted = tableAliasIntern(descriptor, deploy.getElPlaceHolder(et), dbEncrypted, dbColumn); + + this.jsonSerialize = deploy.isExposeSerialize(); + this.jsonDeserialize = deploy.isExposeDeserialize(); + } + + private String tableAliasIntern(BeanDescriptor> descriptor, String s, boolean dbEncrypted, String dbColumn) { + if (descriptor != null) { + s = StringHelper.replaceString(s, "${ta}.", "${}"); + s = StringHelper.replaceString(s, "${ta}", "${}"); + + if (dbEncrypted) { + s = dbEncryptFunction.getDecryptSql(s); + String namedParam = ":encryptkey_" + descriptor.getBaseTable() + "___" + dbColumn; + s = StringHelper.replaceString(s, "?", namedParam); + } + } + return InternString.intern(s); + } + + /** + * Create a Matching BeanProperty with some attributes overridden. + * + * Primarily for supporting Embedded beans with overridden dbColumn + * mappings. + * + */ + public BeanProperty(BeanProperty source, BeanPropertyOverride override) { + + this.descriptor = source.descriptor; + this.name = InternString.intern(source.getName()); + this.propertyIndex = source.propertyIndex; + + this.indexed = source.isIndexed(); + this.indexName = source.getIndexName(); + + this.dbColumn = InternString.intern(override.getDbColumn()); + this.sqlFormulaJoin = InternString.intern(override.getSqlFormulaJoin()); + this.sqlFormulaSelect = InternString.intern(override.getSqlFormulaSelect()); + this.formula = sqlFormulaSelect != null; + + this.fetchEager = source.fetchEager; + this.unidirectionalShadow = source.unidirectionalShadow; + this.discriminator = source.discriminator; + this.localEncrypted = source.isLocalEncrypted(); + this.isTransient = source.isTransient(); + this.secondaryTable = source.isSecondaryTable(); + this.secondaryTableJoin = source.secondaryTableJoin; + this.secondaryTableJoinPrefix = source.secondaryTableJoinPrefix; + + this.dbBind = source.getDbBind(); + this.dbEncrypted = source.isDbEncrypted(); + this.dbEncryptedType = source.getDbEncryptedType(); + this.dbEncryptFunction = source.dbEncryptFunction; + this.dbRead = source.isDbRead(); + this.dbInsertable = source.isDbInsertable(); + this.dbUpdatable = source.isDbUpdatable(); + this.nullable = source.isNullable(); + this.unique = source.isUnique(); + this.naturalKey = source.isNaturalKey(); + this.dbLength = source.getDbLength(); + this.dbScale = source.getDbScale(); + this.dbColumnDefn = InternString.intern(source.getDbColumnDefn()); + this.dbConstraintExpression = InternString.intern(source.getDbConstraintExpression()); + + this.inherited = source.isInherited(); + this.owningType = source.owningType; + this.local = owningType.equals(descriptor.getBeanType()); + + this.version = source.isVersion(); + this.embedded = source.isEmbedded(); + this.id = source.isId(); + this.generatedProperty = source.getGeneratedProperty(); + this.readMethod = source.getReadMethod(); + this.writeMethod = source.getWriteMethod(); + this.getter = source.getter; + this.setter = source.setter; + this.extraAttributeMap = source.extraAttributeMap; + this.defaultValue = source.getDefaultValue(); + this.dbType = source.getDbType(); + this.scalarType = source.scalarType; + this.lob = isLobType(dbType); + this.propertyType = source.getPropertyType(); + this.field = source.getField(); + + this.elPlaceHolder = override.replace(source.elPlaceHolder, source.dbColumn); + this.elPlaceHolderEncrypted = override.replace(source.elPlaceHolderEncrypted, source.dbColumn); + + this.jsonSerialize = source.jsonSerialize; + this.jsonDeserialize = source.jsonDeserialize; + } + + /** + * Initialise the property before returning to client code. Used to + * initialise variables that can't be done in construction due to recursive + * issues. + */ + public void initialise() { + // do nothing for normal BeanProperty + if (!isTransient && scalarType == null) { + String msg = "No ScalarType assigned to " + descriptor.getFullName() + "." + getName(); + throw new RuntimeException(msg); + } + } + + /** + * Return the order this property appears in the bean. + */ + public int getDeployOrder() { + return deployOrder; + } + + /** + * Set the order this property appears in the bean. + */ + public void setDeployOrder(int deployOrder) { + this.deployOrder = deployOrder; + } + + public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, + boolean propertyDeploy) { + throw new PersistenceException("Not valid on scalar bean property " + getFullBeanName()); + } + + /** + * Return the BeanDescriptor that owns this property. + */ + public BeanDescriptor> getBeanDescriptor() { + return descriptor; + } + + /** + * Return true is this is a simple scalar property. + */ + public boolean isScalar() { + return true; + } + + /** + * Return true if this property is based on a formula. + */ + public boolean isFormula() { + return formula; + } + + /** + * Return true if this property maps to the inheritance discriminator column. + */ + public boolean isDiscriminator() { + return discriminator; + } + + /** + * Return true if the underlying type is mutable. + */ + public boolean isMutableScalarType() { + if (scalarType == null) { + return false; + } + return scalarType.isMutable(); + } + + public void copyProperty(EntityBean sourceBean, EntityBean destBean) { + Object value = getValue(sourceBean); + setValue(destBean, value); + } + + /** + * Return the encrypt key for the column matching this property. + */ + public EncryptKey getEncryptKey() { + return descriptor.getEncryptKey(this); + } + + public String getDecryptProperty() { + return dbEncryptFunction.getDecryptSql(this.getName()); + } + + public String getDecryptProperty(String propertyName) { + return dbEncryptFunction.getDecryptSql(propertyName); + } + + public String getDecryptSql() { + return dbEncryptFunction.getDecryptSql(this.getDbColumn()); + } + + public String getDecryptSql(String tableAlias) { + return dbEncryptFunction.getDecryptSql(tableAlias + "." + this.getDbColumn()); + } + + /** + * Add any extra joins required to support this property. Generally a no + * operation except for a OneToOne exported. + */ + public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) { + if (formula && sqlFormulaJoin != null) { + ctx.appendFormulaJoin(sqlFormulaJoin, joinType); + + } else if (secondaryTableJoin != null) { + + String relativePrefix = ctx.getRelativePrefix(secondaryTableJoinPrefix); + secondaryTableJoin.addJoin(joinType, relativePrefix, ctx); + } + } + + /** + * Returns null unless this property is using a secondary table. In that + * case this returns the logical property prefix. + */ + public String getSecondaryTableJoinPrefix() { + return secondaryTableJoinPrefix; + } + + public void appendSelect(DbSqlContext ctx, boolean subQuery) { + if (formula) { + ctx.appendFormulaSelect(sqlFormulaSelect); + + } else if (!isTransient) { + + if (secondaryTableJoin != null) { + String relativePrefix = ctx.getRelativePrefix(secondaryTableJoinPrefix); + ctx.pushTableAlias(relativePrefix); + } + + if (dbEncrypted) { + String decryptSql = getDecryptSql(ctx.peekTableAlias()); + ctx.appendRawColumn(decryptSql); + ctx.addEncryptedProp(this); + + } else { + ctx.appendColumn(dbColumn); + } + + if (secondaryTableJoin != null) { + ctx.popTableAlias(); + } + } + } + + public boolean isAssignableFrom(Class> type) { + return owningType.isAssignableFrom(type); + } + + public Object readSetOwning(DbReadContext ctx, EntityBean bean, Class> type) throws SQLException { + + try { + Object value = scalarType.read(ctx.getDataReader()); + if (value == null || bean == null) { + // not setting the value... + } else { + if (owningType.equals(type)) { + setValue(bean, value); + } + } + return value; + } catch (Exception e) { + String msg = "Error readSet on " + descriptor + "." + name; + throw new PersistenceException(msg, e); + } + } + + public void loadIgnore(DbReadContext ctx) { + scalarType.loadIgnore(ctx.getDataReader()); + } + + public void load(SqlBeanLoad sqlBeanLoad) throws SQLException { + sqlBeanLoad.load(this); + } + + public void buildSelectExpressionChain(String prefix, List selectChain) { + if (prefix == null) { + selectChain.add(name); + } else { + selectChain.add(prefix + "." + name); + } + } + + public Object read(DbReadContext ctx) throws SQLException { + return scalarType.read(ctx.getDataReader()); + } + + public Object readSet(DbReadContext ctx, EntityBean bean, Class> type) throws SQLException { + + try { + Object value = scalarType.read(ctx.getDataReader()); + if (bean == null || (type != null && !owningType.isAssignableFrom(type))) { + // not setting the value... + } else { + setValue(bean, value); + } + return value; + } catch (Exception e) { + String msg = "Error readSet on " + descriptor + "." + name; + throw new PersistenceException(msg, e); + } + } + + /** + * Convert the type to the bean type if required. + * + * Generally only used to ensure id properties are converted for + * Query.setId() use. + * + */ + public Object toBeanType(Object value) { + return scalarType.toBeanType(value); + } + + @SuppressWarnings("unchecked") + public void bind(DataBind b, Object value) throws SQLException { + scalarType.bind(b, value); + } + + public void writeData(DataOutput dataOutput, Object value) throws IOException { + scalarType.writeData(dataOutput, value); + } + + public Object readData(DataInput dataInput) throws IOException { + return scalarType.readData(dataInput); + } + + public boolean isCascadeValidate() { + return cascadeValidate; + } + + /** + * Checks to see if a bean is a reference (will be lazy loaded) or a + * BeanCollection that has not yet been populated. + * + * For base types this returns true. + * + */ + public boolean isValueLoaded(Object value) { + return true; + } + + public BeanProperty getBeanProperty() { + return this; + } + + /** + * Return the getter method. + */ + public Method getReadMethod() { + return readMethod; + } + + /** + * Return the setter method. + */ + public Method getWriteMethod() { + return writeMethod; + } + + /** + * Return true if this object is part of an inheritance hierarchy. + */ + public boolean isInherited() { + return inherited; + } + + /** + * Return true is this type is not from a super type. + */ + public boolean isLocal() { + return local; + } + + /** + * Set the value of the property without interception or + * PropertyChangeSupport. + */ + public void setValue(EntityBean bean, Object value) { + try { + setter.set(bean, value); + } catch (Exception ex) { + String beanType = bean == null ? "null" : bean.getClass().getName(); + String msg = "set " + name + " on [" + descriptor + "] arg[" + value + "] type[" + beanType + + "] threw error"; + throw new RuntimeException(msg, ex); + } + } + + /** + * Set the value of the property. + */ + public void setValueIntercept(EntityBean bean, Object value) { + try { + setter.setIntercept(bean, value); + } catch (Exception ex) { + String beanType = bean == null ? "null" : bean.getClass().getName(); + String msg = "setIntercept " + name + " on [" + descriptor + "] arg[" + value + "] type[" + beanType + + "] threw error"; + throw new RuntimeException(msg, ex); + } + } + + private static Object[] NO_ARGS = new Object[0]; + + public Object getCacheDataValue(EntityBean bean) { + return getValue(bean); + } + + public void setCacheDataValue(EntityBean bean, Object cacheData) { + setValue(bean, cacheData); + } + + /** + * Return the value of the property method. + */ + public Object getValue(EntityBean bean) { + try { + return getter.get(bean); + } catch (Exception ex) { + String beanType = bean == null ? "null" : bean.getClass().getName(); + String msg = "get " + name + " on [" + descriptor + "] type[" + beanType + "] threw error."; + throw new RuntimeException(msg, ex); + } + } + + /** + * Explicitly use reflection to get value. + */ + public Object getValueViaReflection(Object bean) { + try { + return readMethod.invoke(bean, NO_ARGS); + } catch (Exception ex) { + String beanType = bean == null ? "null" : bean.getClass().getName(); + String msg = "get " + name + " on [" + descriptor + "] type[" + beanType + "] threw error."; + throw new RuntimeException(msg, ex); + } + } + + public Object getValueIntercept(EntityBean bean) { + try { + return getter.getIntercept(bean); + } catch (Exception ex) { + String beanType = bean == null ? "null" : bean.getClass().getName(); + String msg = "getIntercept " + name + " on [" + descriptor + "] type[" + beanType + "] threw error."; + throw new RuntimeException(msg, ex); + } + } + + public Object elConvertType(Object value) { + if (value == null) { + return null; + } + return convertToLogicalType(value); + } + + public void elSetValue(EntityBean bean, Object value, boolean populate) { + if (bean != null) { + // Not using setValueIntercept at this stage + setValue(bean, value); + } + } + + public Object elGetValue(EntityBean bean) { + if (bean == null) { + return null; + } + return getValueIntercept(bean); + } + + public Object elGetReference(EntityBean bean) { + throw new RuntimeException("Not expected to call this"); + } + + /** + * Return the name of the property. + */ + public String getName() { + return name; + } + + /** + * Return the position of this property in the enhanced bean. + */ + public int getPropertyIndex() { + return propertyIndex; + } + + public String getElName() { + return name; + } + + /** + * This is a full ElGetValue. + */ + public boolean isDeployOnly() { + return false; + } + + + @Override + public boolean containsFormulaWithJoin() { + return formula && sqlFormulaJoin != null; + } + + public boolean containsManySince(String sinceProperty) { + return containsMany(); + } + + public boolean containsMany() { + return false; + } + + public Object[] getAssocOneIdValues(EntityBean bean) { + // Returns null as not an AssocOne. + return null; + } + + public String getAssocOneIdExpr(String prefix, String operator) { + // Returns null as not an AssocOne. + return null; + } + + public String getAssocIdInExpr(String prefix) { + // Returns null as not an AssocOne. + return null; + } + + public String getAssocIdInValueExpr(int size) { + // Returns null as not an AssocOne. + return null; + } + + public boolean isAssocId() { + // Returns false - override in BeanPropertyAssocOne. + return false; + } + + public boolean isAssocProperty() { + // Returns false - override in BeanPropertyAssocOne. + return false; + } + + public String getElPlaceholder(boolean encrypted) { + return encrypted ? elPlaceHolderEncrypted : elPlaceHolder; + } + + public String getElPrefix() { + return secondaryTableJoinPrefix; + } + + /** + * Return the full name of this property. + */ + public String getFullBeanName() { + return descriptor.getFullName() + "." + name; + } + + /** + * Return true if the mutable value is considered dirty. + * This is only used for 'mutable' scalar types like hstore etc. + */ + public boolean isDirtyValue(Object value) { + return scalarType.isDirty(value); + } + + /** + * Return the scalarType. + */ + public ScalarType> getScalarType() { + return scalarType; + } + + public StringFormatter getStringFormatter() { + return scalarType; + } + + public StringParser getStringParser() { + return scalarType; + } + + public boolean isDateTimeCapable() { + return scalarType != null && scalarType.isDateTimeCapable(); + } + + public int getJdbcType() { + return scalarType == null ? 0 : scalarType.getJdbcType(); + } + + public Object parseDateTime(long systemTimeMillis) { + return scalarType.parseDateTime(systemTimeMillis); + } + + /** + * Return the DB max length (varchar) or precision (decimal). + */ + public int getDbLength() { + return dbLength; + } + + /** + * Return the DB scale for numeric columns. + */ + public int getDbScale() { + return dbScale; + } + + /** + * Return a specific column DDL definition if specified (otherwise null). + */ + public String getDbColumnDefn() { + return dbColumnDefn; + } + + /** + * Return the DB constraint expression (can be null). + * + * For an Enum returns IN expression for the set of Enum values. + * + */ + public String getDbConstraintExpression() { + return dbConstraintExpression; + } + + /** + * Return the DB column type definition. + */ + public String renderDbType(DbType dbType) { + if (dbColumnDefn != null) { + return dbColumnDefn; + } + return dbType.renderType(dbLength, dbScale); + } + + /** + * Return the bean Field associated with this property. + */ + public Field getField() { + return field; + } + + /** + * Return the GeneratedValue. Used to generate update timestamp etc. + */ + public GeneratedProperty getGeneratedProperty() { + return generatedProperty; + } + + /** + * Return true if this is the natural key property. + */ + public boolean isNaturalKey() { + return naturalKey; + } + + /** + * Return true if this property is mandatory. + */ + public boolean isNullable() { + return nullable; + } + + /** + * Return true if DDL Not NULL constraint should be defined for this column + * based on it being a version column or having a generated property. + */ + public boolean isDDLNotNull() { + return isVersion() || (generatedProperty != null && generatedProperty.isDDLNotNullable()); + } + + /** + * Return true if the DB column should be unique. + */ + public boolean isUnique() { + return unique; + } + + /** + * Return true if the property is transient. + */ + public boolean isTransient() { + return isTransient; + } + + /** + * Return true if this property is loadable from a resultSet. + */ + public boolean isLoadProperty() { + return !isTransient || formula; + } + + /** + * Return true if this is a version column used for concurrency checking. + */ + public boolean isVersion() { + return version; + } + + public String getDeployProperty() { + return dbColumn; + } + + /** + * The database column name this is mapped to. + */ + public String getDbColumn() { + return dbColumn; + } + + /** + * Return the database jdbc data type this is mapped to. + */ + public int getDbType() { + return dbType; + } + + /** + * Perform DB to Logical type conversion (if necessary). + */ + public Object convertToLogicalType(Object value) { + if (scalarType != null) { + return scalarType.toBeanType(value); + } + return value; + } + + /** + * Return true if by default this property is set to fetch eager. + * Lob's usually default to fetch lazy. + */ + public boolean isFetchEager() { + return fetchEager; + } + + /** + * Return true if this is mapped to a Clob Blob LongVarchar or + * LongVarbinary. + */ + public boolean isLob() { + return lob; + } + + private boolean isLobType(int type) { + switch (type) { + case Types.CLOB: + return true; + case Types.BLOB: + return true; + case Types.LONGVARBINARY: + return true; + case Types.LONGVARCHAR: + return true; + + default: + return false; + } + } + + /** + * Return the DB bind parameter. Typically is "?" but different for + * encrypted bind. + */ + public String getDbBind() { + return dbBind; + } + + /** + * Returns true if DB encrypted. + */ + public boolean isLocalEncrypted() { + return localEncrypted; + } + + /** + * Return true if this property is stored encrypted. + */ + public boolean isDbEncrypted() { + return dbEncrypted; + } + + public int getDbEncryptedType() { + return dbEncryptedType; + } + + /** + * Return true if this property should be included in an Insert. + */ + public boolean isDbInsertable() { + return dbInsertable; + } + + /** + * Return true if this property should be included in an Update. + */ + public boolean isDbUpdatable() { + return dbUpdatable; + } + + /** + * Return true if this property is included in database queries. + */ + public boolean isDbRead() { + return dbRead; + } + + /** + * Return true if this property is based on a secondary table (not the base + * table). + */ + public boolean isSecondaryTable() { + return secondaryTable; + } + + /** + * Return the property type. + */ + public Class> getPropertyType() { + return propertyType; + } + + /** + * Return true if this is included in the unique id. + */ + public boolean isId() { + return id; + } + + /** + * Return true if this is an Embedded property. In this case it shares the + * table and primary key of its owner object. + */ + public boolean isEmbedded() { + return embedded; + } + + /** + * Return an extra attribute set on this property. + */ + public String getExtraAttribute(String key) { + return extraAttributeMap.get(key); + } + + /** + * Return the default value. + */ + public Object getDefaultValue() { + return defaultValue; + } + + public String toString() { + return name; + } + +<<<<<<< HEAD + @SuppressWarnings("unchecked") + public void jsonWrite(WriteJsonContext ctx, EntityBean bean) { + if (!jsonSerialize) { + return; + } + Object value = getValueIntercept(bean); + if (value == null) { + ctx.appendNull(name); + } else { + ctx.appendNameValue(name, scalarType, value); + } +======= + public void jsonWrite(WriteJson writeJson, EntityBean bean) { + if (!jsonSerialize) { + return; + } + Object value = getValueIntercept(bean); + if (value == null) { + writeJson.gen().writeNull(name); + } else { + scalarType.jsonWrite(writeJson.gen(), name, value); +>>>>>>> json-refactor + } + } + +<<<<<<< HEAD + public void jsonRead(ReadJsonContext ctx, EntityBean bean) { + if (!jsonDeserialize) { + return; + } + String jsonValue; + try { + jsonValue = ctx.readScalarValue(); + } catch (TextException e) { + throw new TextException("Error reading property " + getFullBeanName(), e); + } + Object objValue; + if (jsonValue == null) { + objValue = null; + } else { + objValue = scalarType.jsonFromString(jsonValue, ctx.getValueAdapter()); + } + setValue(bean, objValue); + } + + public boolean isIndexed() { + return indexed; + } + + public String getIndexName() { + return indexName; + } +======= + public void jsonRead(JsonParser ctx, EntityBean bean) { + if (!jsonDeserialize) { + return; + } + if (!ctx.hasNext()) { + throw new RuntimeException(ctx.getLocation().toString()); + } + Event event = ctx.next(); + if (Event.VALUE_NULL == event) { + setValue(bean, null); + } else { + Object objValue = scalarType.jsonRead(ctx, event); + setValue(bean, objValue); + } + + } +>>>>>>> json-refactor +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java index 172800de5..b31fccab6 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import javax.json.stream.JsonParser; import javax.persistence.PersistenceException; import org.slf4j.Logger; @@ -28,9 +29,7 @@ import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder; import com.avaje.ebeaninternal.server.el.ElPropertyValue; import com.avaje.ebeaninternal.server.lib.util.StringHelper; import com.avaje.ebeaninternal.server.query.SqlBeanLoad; -import com.avaje.ebeaninternal.server.text.json.ReadJsonContext; -import com.avaje.ebeaninternal.server.text.json.ReadJsonContext.ReadBeanState; -import com.avaje.ebeaninternal.server.text.json.WriteJsonContext; +import com.avaje.ebeaninternal.server.text.json.WriteJson; /** * Property mapped to a List Set or Map. @@ -39,6 +38,8 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc { private static final Logger logger = LoggerFactory.getLogger(BeanPropertyAssocMany.class); + private final BeanPropertyAssocManyJsonHelp jsonHelp; + /** * Join for manyToMany intersection table. */ @@ -90,7 +91,7 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc { /** * Property on the 'child' bean that links back to the 'master'. */ - private BeanPropertyAssocOne> childMasterProperty; + protected BeanPropertyAssocOne> childMasterProperty; private boolean embeddedExportedProperties; @@ -115,6 +116,7 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc { this.intersectionJoin = deploy.createIntersectionTableJoin(); this.inverseJoin = deploy.createInverseTableJoin(); this.modifyListenMode = deploy.getModifyListenMode(); + this.jsonHelp = new BeanPropertyAssocManyJsonHelp(this); } public void initialise() { @@ -875,7 +877,7 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc { return null != targetDescriptor.getId(otherBean); } - public void jsonWrite(WriteJsonContext ctx, EntityBean bean) { + public void jsonWrite(WriteJson ctx, EntityBean bean) { if(!this.jsonSerialize){ return; } @@ -896,37 +898,7 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc { } } - public void jsonRead(ReadJsonContext ctx, EntityBean bean){ - if(!this.jsonDeserialize){ - return; - } - if (!ctx.readArrayBegin()) { - // the array is null - return; - } - - Object collection = help.createEmpty(false); - BeanCollectionAdd add = getBeanCollectionAdd(collection, null); - do { - ReadBeanState detailBeanState = targetDescriptor.jsonRead(ctx, name); - if (detailBeanState == null){ - // probably empty array - break; - } - EntityBean detailBean = (EntityBean)detailBeanState.getBean(); - add.addBean(detailBean); - - if (bean != null && childMasterProperty != null){ - // bind detail bean back to master via mappedBy property - childMasterProperty.setValue(detailBean, bean); - detailBeanState.setLoaded(childMasterProperty.getName()); - } - - if (!ctx.readArrayNext()){ - break; - } - } while(true); - - setValue(bean, collection); + public void jsonRead(JsonParser parser, EntityBean parentBean) { + jsonHelp.jsonRead(parser, parentBean); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocManyJsonHelp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocManyJsonHelp.java new file mode 100644 index 000000000..9c923f52d --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocManyJsonHelp.java @@ -0,0 +1,49 @@ +package com.avaje.ebeaninternal.server.deploy; + +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; + +import com.avaje.ebean.bean.BeanCollectionAdd; +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.text.TextException; + +public class BeanPropertyAssocManyJsonHelp { + + private final BeanPropertyAssocMany> many; + + public BeanPropertyAssocManyJsonHelp(BeanPropertyAssocMany> many) { + this.many = many; + } + + public void jsonRead(JsonParser parser, EntityBean parentBean) { + + if (!this.many.jsonDeserialize || !parser.hasNext()) { + return; + } + Event event = parser.next(); + if (Event.VALUE_NULL == event) { + return; + } + if (Event.START_ARRAY != event) { + throw new TextException("Unexpected token "+event+" - expecting start_array at: "+parser.getLocation()); + } + + Object collection = many.createEmpty(false); + BeanCollectionAdd add = many.getBeanCollectionAdd(collection, null); + do { + EntityBean detailBean = (EntityBean)many.targetDescriptor.jsonRead(parser, many.name); + if (detailBean == null) { + // read the entire array + break; + } + add.addBean(detailBean); + + if (parentBean != null && many.childMasterProperty != null) { + // bind detail bean back to master via mappedBy property + many.childMasterProperty.setValue(detailBean, parentBean); + } + } while (true); + + many.setValue(parentBean, collection); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java index 7d9b620bc..d0a7c964d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import javax.json.stream.JsonParser; import javax.persistence.PersistenceException; import com.avaje.ebean.EbeanServer; @@ -24,8 +25,7 @@ import com.avaje.ebeaninternal.server.el.ElPropertyValue; import com.avaje.ebeaninternal.server.query.SplitName; import com.avaje.ebeaninternal.server.query.SqlBeanLoad; import com.avaje.ebeaninternal.server.query.SqlJoinType; -import com.avaje.ebeaninternal.server.text.json.ReadJsonContext; -import com.avaje.ebeaninternal.server.text.json.WriteJsonContext; +import com.avaje.ebeaninternal.server.text.json.WriteJson; /** * Property mapped to a joined bean. @@ -831,36 +831,34 @@ public class BeanPropertyAssocOne extends BeanPropertyAssoc { } @Override - public void jsonWrite(WriteJsonContext ctx, EntityBean bean) { + public void jsonWrite(WriteJson writeJson, EntityBean bean) { Object value = getValueIntercept(bean); if (value == null){ - ctx.beginAssocOneIsNull(name); + writeJson.gen().writeNull(name); } else { - if (ctx.isParentBean(value)){ + if (writeJson.isParentBean(value)){ // bi-directional and already rendered parent } else { // Hmmm, not writing complex non-entity bean if (value instanceof EntityBean) { - ctx.pushParentBean(bean); - ctx.beginAssocOne(name); + writeJson.beginAssocOne(name, bean); BeanDescriptor> refDesc = descriptor.getBeanDescriptor(value.getClass()); - refDesc.jsonWrite(ctx, (EntityBean)value); - ctx.endAssocOne(); - ctx.popParentBean(); + refDesc.jsonWrite(writeJson, (EntityBean)value, name); + writeJson.endAssocOne(); } } } } - + @Override - public void jsonRead(ReadJsonContext ctx, EntityBean bean){ - if (targetDescriptor != null) { - T assocBean = targetDescriptor.jsonReadBean(ctx, name); - setValue(bean, assocBean); - } + public void jsonRead(JsonParser parser, EntityBean bean) { + if (targetDescriptor != null) { + T assocBean = targetDescriptor.jsonRead(parser, name); + setValue(bean, assocBean); + } } public boolean isReference(Object detailBean) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompound.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompound.java index 00132e1d3..ea8c31606 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompound.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompound.java @@ -3,15 +3,18 @@ package com.avaje.ebeaninternal.server.deploy; import java.sql.SQLException; import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; + +import javax.json.stream.JsonParser; import com.avaje.ebean.bean.EntityBean; import com.avaje.ebean.config.ScalarTypeConverter; +import com.avaje.ebean.json.EJson; import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound; import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder; import com.avaje.ebeaninternal.server.el.ElPropertyValue; import com.avaje.ebeaninternal.server.query.SqlBeanLoad; -import com.avaje.ebeaninternal.server.text.json.ReadJsonContext; -import com.avaje.ebeaninternal.server.text.json.WriteJsonContext; +import com.avaje.ebeaninternal.server.text.json.WriteJson; import com.avaje.ebeaninternal.server.type.CtCompoundProperty; import com.avaje.ebeaninternal.server.type.CtCompoundPropertyElAdapter; import com.avaje.ebeaninternal.server.type.CtCompoundType; @@ -177,15 +180,32 @@ public class BeanPropertyCompound extends BeanProperty { return bean; } - public void jsonWrite(WriteJsonContext ctx, EntityBean bean) { - - Object valueObject = getValueIntercept(bean); - compoundType.jsonWrite(ctx, valueObject, name); + public void jsonWrite(WriteJson ctx, EntityBean bean) { + if (!jsonSerialize) { + return; + } + Object value = getValueIntercept(bean); + if (value == null) { + ctx.gen().writeNull(name); + } else { + compoundType.jsonWrite(ctx, value, name); + } } - public void jsonRead(ReadJsonContext ctx, EntityBean bean){ - - Object objValue = compoundType.jsonRead(ctx); + public void jsonRead(JsonParser ctx, EntityBean bean) { + + if (!jsonDeserialize) { + return; + } + + Object value = EJson.parsePartial(ctx); + if (value == null) { + setValue(bean, null); + } else { + @SuppressWarnings("unchecked") + Map map = (Map)value; + Object objValue = compoundType.jsonConvert(map); setValue(bean, objValue); + } } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanSetHelp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanSetHelp.java index 57ff5575c..bc2f09931 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanSetHelp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanSetHelp.java @@ -12,7 +12,7 @@ import com.avaje.ebean.bean.BeanCollectionAdd; import com.avaje.ebean.bean.BeanCollectionLoader; import com.avaje.ebean.bean.EntityBean; import com.avaje.ebean.common.BeanSet; -import com.avaje.ebeaninternal.server.text.json.WriteJsonContext; +import com.avaje.ebeaninternal.server.text.json.WriteJson; /** * Helper specifically for dealing with Sets. @@ -129,7 +129,7 @@ public final class BeanSetHelp implements BeanCollectionHelp { } } - public void jsonWrite(WriteJsonContext ctx, String name, Object collection, boolean explicitInclude) { + public void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) { Set> set; if (collection instanceof BeanCollection>){ @@ -148,16 +148,11 @@ public final class BeanSetHelp implements BeanCollectionHelp { set = (Set>)collection; } - int count = 0; - ctx.beginAssocMany(name); + ctx.gen().writeStartArray(name); Iterator> it = set.iterator(); while (it.hasNext()) { - Object detailBean = it.next(); - if (count++ > 0){ - ctx.appendComma(); - } - targetDescriptor.jsonWrite(ctx, (EntityBean)detailBean); + targetDescriptor.jsonWrite(ctx, (EntityBean)it.next()); } - ctx.endAssocMany(); + ctx.gen().writeEnd(); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/EJsonReader.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/EJsonReader.java new file mode 100644 index 000000000..71f4e790e --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/EJsonReader.java @@ -0,0 +1,215 @@ +package com.avaje.ebeaninternal.server.deploy; + +import java.io.Reader; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; + +import javax.json.Json; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; + +public class EJsonReader { + + @SuppressWarnings("unchecked") + public static Map parseObject(String json) { + return (Map) parse(json); + } + + @SuppressWarnings("unchecked") + public static List parseList(String json) { + return (List) parse(json); + } + + public static Object parse(String json) { + return parse(new StringReader(json)); + } + + public static Object parse(Reader reader) { + return parse(Json.createParser(reader)); + } + + public static Object parse(JsonParser parser) { + return new EJsonReader(parser).parseJson(); + } + + private final JsonParser parser; + + private final Stack stack = new Stack(); + + private Context currentContext; + + + EJsonReader(JsonParser parser) { + this.parser = parser; + } + + private void startArray() { + stack.push(currentContext); + currentContext = new ArrayContext(); + } + + private void startObject() { + stack.push(currentContext); + currentContext = new ObjectContext(); + } + + private void endArray() { + end(); + } + + private void endObject() { + end(); + } + + private void end() { + + if (!stack.isEmpty()) { + currentContext = stack.pop(); + } + } + + private void setValue(Object value) { + currentContext.setValue(value); + } + + private void setValueNull() { + currentContext.setValueNull(); + } + + private Object parseJson() { + + while (parser.hasNext()) { + Event event = parser.next(); + switch (event) { + + case START_ARRAY: + startArray(); + break; + + case START_OBJECT: + startObject(); + break; + + case KEY_NAME: + currentContext.setKey(parser.getString()); + break; + + case VALUE_STRING: + setValue(parser.getString()); + break; + + case VALUE_NUMBER: + if (parser.isIntegralNumber()) { + setValue(parser.getLong()); + } else { + setValue(parser.getBigDecimal()); + } + break; + + case VALUE_TRUE: + setValue(Boolean.TRUE); + break; + + case VALUE_FALSE: + setValue(Boolean.FALSE); + break; + + case VALUE_NULL: + setValueNull(); + break; + + case END_OBJECT: + endObject(); + break; + + case END_ARRAY: + endArray(); + break; + + default: + break; + } + } + + return currentContext.getValue(); + } + + private static final class Stack { + + private Context head; + + private void push(Context context) { + if (context != null) { + context.next = head; + head = context; + } + } + + private Context pop() { + if (head == null) { + throw new NoSuchElementException(); + } + Context temp = head; + head = head.next; + return temp; + } + + private boolean isEmpty() { + return head == null; + } + } + + private static abstract class Context { + Context next; + abstract Object getValue(); + abstract void setKey(String key); + abstract void setValue(Object value); + abstract void setValueNull(); + } + + private static class ObjectContext extends Context { + + private String key; + + Map map = new LinkedHashMap(); + + Object getValue() { + return map; + } + + public void setKey(String key) { + this.key = key; + } + + void setValue(Object value) { + map.put(key, value); + } + + void setValueNull() { + map.put(key, null); + } + } + + private static class ArrayContext extends Context { + + List values = new ArrayList(); + + Object getValue() { + return values; + } + + void setValue(Object value) { + values.add(value); + } + + void setValueNull() { + } + void setKey(String key) { + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/ReadJson.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/ReadJson.java new file mode 100644 index 000000000..871517f40 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/ReadJson.java @@ -0,0 +1,9 @@ +package com.avaje.ebeaninternal.server.deploy; + +import javax.json.stream.JsonParser; + +public class ReadJson { + + JsonParser parser; + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/DJsonContext.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/DJsonContext.java index a176731bb..8a43ccbef 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/DJsonContext.java +++ b/src/main/java/com/avaje/ebeaninternal/server/text/json/DJsonContext.java @@ -1,6 +1,8 @@ package com.avaje.ebeaninternal.server.text.json; import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; import java.io.Writer; import java.lang.reflect.Type; import java.util.ArrayList; @@ -11,16 +13,19 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import javax.json.Json; +import javax.json.stream.JsonGenerator; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; + import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.json.EJson; +import com.avaje.ebean.text.PathProperties; import com.avaje.ebean.text.TextException; import com.avaje.ebean.text.json.JsonContext; -import com.avaje.ebean.text.json.JsonElement; -import com.avaje.ebean.text.json.JsonReadOptions; -import com.avaje.ebean.text.json.JsonValueAdapter; import com.avaje.ebean.text.json.JsonWriteOptions; import com.avaje.ebeaninternal.api.SpiEbeanServer; import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.type.EscapeJson; import com.avaje.ebeaninternal.util.ParamTypeHelper; import com.avaje.ebeaninternal.util.ParamTypeHelper.ManyType; import com.avaje.ebeaninternal.util.ParamTypeHelper.TypeInfo; @@ -32,275 +37,215 @@ import com.avaje.ebeaninternal.util.ParamTypeHelper.TypeInfo; */ public class DJsonContext implements JsonContext { - private final SpiEbeanServer server; - - private final JsonValueAdapter dfltValueAdapter; - - private final boolean dfltPretty; - - public DJsonContext(SpiEbeanServer server, JsonValueAdapter dfltValueAdapter, boolean dfltPretty){ - this.server = server; - this.dfltValueAdapter = dfltValueAdapter; - this.dfltPretty = dfltPretty; - } + private final SpiEbeanServer server; - public boolean isSupportedType(Type genericType) { - return server.isSupportedType(genericType); - } + public DJsonContext(SpiEbeanServer server) { + this.server = server; + } - private ReadJsonSource createReader(Reader jsonReader) { - return new ReadJsonSourceReader(jsonReader, 256, 512); - } - - public T toBean(Class cls, String json){ - return toBean(cls, new ReadJsonSourceString(json), null); - } - - public T toBean(Class cls, Reader jsonReader) { - return toBean(cls, createReader(jsonReader), null); - } - - public T toBean(Class cls, String json, JsonReadOptions options){ - return toBean(cls, new ReadJsonSourceString(json), options); - } + public boolean isSupportedType(Type genericType) { + return server.isSupportedType(genericType); + } - public T toBean(Class cls, Reader jsonReader, JsonReadOptions options) { - return toBean(cls, createReader(jsonReader), options); - } + private JsonParser createReader(Reader jsonReader) { + return Json.createParser(jsonReader); + } - private T toBean(Class cls, ReadJsonSource src, JsonReadOptions options){ + public T toBean(Class cls, String json) { + return toBean(cls, new StringReader(json)); + } - BeanDescriptor d = getDecriptor(cls); - ReadJsonContext ctx = new ReadJsonContext(src, dfltValueAdapter, options); - return d.jsonReadBean(ctx, null); - } + public T toBean(Class cls, Reader jsonReader) { + return toBean(cls, createReader(jsonReader)); + } - public List toList(Class cls, String json){ - return toList(cls, new ReadJsonSourceString(json), null); - } + private T toBean(Class cls, JsonParser parser) { - public List toList(Class cls, String json, JsonReadOptions options){ - return toList(cls, new ReadJsonSourceString(json), options); - } - - public List toList(Class cls, Reader jsonReader){ - return toList(cls, createReader(jsonReader), null); - } + BeanDescriptor d = getDecriptor(cls); + return d.jsonRead(parser, null); + } - public List toList(Class cls, Reader jsonReader, JsonReadOptions options){ - return toList(cls, createReader(jsonReader), options); - } - - private List toList(Class cls, ReadJsonSource src, JsonReadOptions options){ - - try { - BeanDescriptor d = getDecriptor(cls); - - List list = new ArrayList(); - - ReadJsonContext ctx = new ReadJsonContext(src, dfltValueAdapter, options); - ctx.readArrayBegin(); - do { - T bean = d.jsonReadBean(ctx, null); - if (bean != null){ - list.add(bean); - } - if (!ctx.readArrayNext()){ - break; - } - } while(true); - - return list; - } catch (RuntimeException e){ - throw new TextException("Error parsing "+src, e); - } - } - - - public Object toObject(Type genericType, String json, JsonReadOptions options) { - - TypeInfo info = ParamTypeHelper.getTypeInfo(genericType); - Class> beanType = info.getBeanType(); - if (JsonElement.class.isAssignableFrom(beanType)){ - return InternalJsonParser.parse(json); - } - - ManyType manyType = info.getManyType(); - switch (manyType) { - case NONE: - return toBean(info.getBeanType(), json, options); - - case LIST: - return toList(info.getBeanType(), json, options); - - default: - String msg = "ManyType "+manyType+" not supported yet"; - throw new TextException(msg); - } - } - - public Object toObject(Type genericType, Reader json, JsonReadOptions options) { - - TypeInfo info = ParamTypeHelper.getTypeInfo(genericType); - Class> beanType = info.getBeanType(); - if (JsonElement.class.isAssignableFrom(beanType)){ - return InternalJsonParser.parse(json); - } - - ManyType manyType = info.getManyType(); - switch (manyType) { - case NONE: - return toBean(info.getBeanType(), json, options); - - case LIST: - return toList(info.getBeanType(), json, options); - - default: - String msg = "ManyType "+manyType+" not supported yet"; - throw new TextException(msg); - } - } + public List toList(Class cls, String json) { + return toList(cls, new StringReader(json)); + } - public void toJsonWriter(Object o, Writer writer) { - toJsonWriter(o, writer, dfltPretty, null, null); - } + public List toList(Class cls, Reader jsonReader) { + return toList(cls, createReader(jsonReader)); + } - public void toJsonWriter(Object o, Writer writer, boolean pretty) { - toJsonWriter(o, writer, pretty, null, null); - } + private List toList(Class cls, JsonParser src) { - public void toJsonWriter(Object o, Writer writer, boolean pretty, JsonWriteOptions options){ - toJsonWriter(o, writer, pretty, null, null); - } - - public void toJsonWriter(Object o, Writer writer, boolean pretty, JsonWriteOptions options, String callback) { - toJsonInternal(o, new WriteJsonBufferWriter(writer), pretty, options, callback); - } + try { + BeanDescriptor d = getDecriptor(cls); - public String toJsonString(Object o){ - return toJsonString(o, dfltPretty, null); - } + List list = new ArrayList(); - public String toJsonString(Object o, boolean pretty){ - return toJsonString(o, pretty, null); - } + if (!src.hasNext()) { + return list; + } + Event event = src.next(); + if (event != Event.START_ARRAY) { + throw new TextException("Expecting start_array event but got [" + event + "] at [" + src.getLocation() + "]"); + } - public String toJsonString(Object o, boolean pretty, JsonWriteOptions options){ - return toJsonString(o, pretty, options, null); - } - - public String toJsonString(Object o, boolean pretty, JsonWriteOptions options, String callback){ - WriteJsonBufferString b = new WriteJsonBufferString(); - toJsonInternal(o, b, pretty, options, callback); - return b.getBufferOutput(); - } - - @SuppressWarnings("unchecked") - private void toJsonInternal(Object o, WriteJsonBuffer buffer, boolean pretty, JsonWriteOptions options, String requestCallback){ - - if (o == null){ - buffer.append("null"); - } else if (o instanceof Number) { - buffer.append(o.toString()); - } else if (o instanceof Boolean) { - buffer.append(o.toString()); - } else if (o instanceof String) { - EscapeJson.escapeQuote(o.toString(), buffer); - } else if (o instanceof JsonElement) { - - } else if (o instanceof Map,?>){ - toJsonFromMap((Map)o, buffer, pretty, options, requestCallback); - - } else if (o instanceof Collection>){ - toJsonFromCollection((Collection>)o, buffer, pretty, options, requestCallback); - + do { + T bean = d.jsonRead(src, null); + if (bean == null) { + break; } else { - BeanDescriptor> d = getDecriptor(o.getClass()); - WriteJsonContext ctx = new WriteJsonContext(buffer, pretty, dfltValueAdapter, options, requestCallback, server); - d.jsonWrite(ctx, (EntityBean)o); - ctx.end(); + list.add(bean); } + } while (true); + + return list; + + } catch (RuntimeException e) { + throw new TextException("Error parsing " + src, e); } - + } - private void toJsonFromCollection(Collection c, WriteJsonBuffer buffer, boolean pretty, JsonWriteOptions options, String requestCallback){ - - Iterator it = c.iterator(); - if (!it.hasNext()){ - buffer.append("[]"); - return; - } - - WriteJsonContext ctx = new WriteJsonContext(buffer, pretty, dfltValueAdapter, options, requestCallback, server); + public Object toObject(Type genericType, String json) { - Object o = it.next(); - BeanDescriptor> d = getDecriptor(o.getClass()); + TypeInfo info = ParamTypeHelper.getTypeInfo(genericType); + ManyType manyType = info.getManyType(); + switch (manyType) { + case NONE: + return toBean(info.getBeanType(), json); - ctx.appendArrayBegin(); - d.jsonWrite(ctx, (EntityBean)o); - while (it.hasNext()) { - ctx.appendComma(); - T t = it.next(); - d.jsonWrite(ctx, (EntityBean)t); - } - ctx.appendArrayEnd(); - ctx.end(); + case LIST: + return toList(info.getBeanType(), json); + + default: + throw new TextException("Type " + manyType + " not supported"); + } + } + + public Object toObject(Type genericType, Reader json) { + + TypeInfo info = ParamTypeHelper.getTypeInfo(genericType); + ManyType manyType = info.getManyType(); + switch (manyType) { + case NONE: + return toBean(info.getBeanType(), json); + + case LIST: + return toList(info.getBeanType(), json); + + default: + throw new TextException("Type " + manyType + " not supported"); + } + } + + public void toJsonWriter(Object o, Writer writer) { + toJsonWriter(o, writer, null); + } + + + public void toJsonWriter(Object o, Writer writer, JsonWriteOptions options) { + JsonGenerator generator = Json.createGenerator(writer); + toJsonInternal(o, generator, options); + generator.close(); + } + + public String toJsonString(Object o) { + return toJsonString(o, null); + } + + public String toJsonString(Object o, JsonWriteOptions options) { + StringWriter writer = new StringWriter(500); + JsonGenerator gen = Json.createGenerator(writer); + toJsonInternal(o, gen, options); + gen.close(); + return writer.toString(); + } + + @SuppressWarnings("unchecked") + private void toJsonInternal(Object o, JsonGenerator gen, JsonWriteOptions options) { + + if (o == null) { + gen.writeNull(); + } else if (o instanceof Number) { + gen.write(((Number) o).doubleValue()); + } else if (o instanceof Boolean) { + gen.write(((Boolean) o).booleanValue()); + } else if (o instanceof String) { + gen.write((String) o); + + // } else if (o instanceof JsonElement) { + + } else if (o instanceof Map, ?>) { + toJsonFromMap((Map) o, gen, options); + + } else if (o instanceof Collection>) { + toJsonFromCollection((Collection>) o, null, gen, options); + + } else if (o instanceof EntityBean) { + BeanDescriptor> d = getDecriptor(o.getClass()); + WriteJson writeJson = createWriteJson(gen, options); + d.jsonWrite(writeJson, (EntityBean)o, null); + } + } + + private WriteJson createWriteJson(JsonGenerator gen, JsonWriteOptions options) { + PathProperties pathProps = (options == null) ? null : options.getPathProperties(); + return new WriteJson(server, gen, pathProps); + } + + private void toJsonFromCollection(Collection c, String key, JsonGenerator gen, JsonWriteOptions options) { + + if (key == null) { + gen.writeStartArray(); + } else { + gen.writeStartArray(key); } - private void toJsonFromMap(Map map, WriteJsonBuffer buffer, boolean pretty, JsonWriteOptions options, String requestCallback){ - - if (map.isEmpty()){ - buffer.append("{}"); - return; - } - - WriteJsonContext ctx = new WriteJsonContext(buffer, pretty, dfltValueAdapter, options, requestCallback, server); + WriteJson writeJson = createWriteJson(gen, options); - Set> entrySet = map.entrySet(); - Iterator> it = entrySet.iterator(); - - Entry entry = it.next(); - - ctx.appendObjectBegin(); - toJsonMapKey(buffer, false, entry.getKey()); - toJsonMapValue(buffer, pretty, options, requestCallback, entry.getValue()); - - while (it.hasNext()) { - entry = it.next(); - ctx.appendComma(); - toJsonMapKey(buffer, pretty, entry.getKey()); - toJsonMapValue(buffer, pretty, options, requestCallback, entry.getValue()); - } - ctx.appendObjectEnd(); - ctx.end(); + Iterator it = c.iterator(); + while (it.hasNext()) { + T t = it.next(); + BeanDescriptor> d = getDecriptor(t.getClass()); + d.jsonWrite(writeJson, (EntityBean)t, null); } + gen.writeEnd(); + } - private void toJsonMapKey(WriteJsonBuffer buffer, boolean pretty, Object key) { - if (pretty){ - buffer.append("\n"); - } - buffer.append("\""); - buffer.append(key.toString()); - buffer.append("\":"); - } - - private void toJsonMapValue(WriteJsonBuffer buffer, boolean pretty, JsonWriteOptions options, String requestCallback, - Object value) { - - if (value == null){ - buffer.append("null"); - } else { - toJsonInternal(value, buffer, pretty, options, requestCallback); - } - } + private void toJsonFromMap(Map map, JsonGenerator gen, JsonWriteOptions options) { + + Set> entrySet = map.entrySet(); + Iterator> it = entrySet.iterator(); + + WriteJson writeJson = createWriteJson(gen, options); + gen.writeStartObject(); - private BeanDescriptor getDecriptor(Class cls) { - BeanDescriptor d = server.getBeanDescriptor(cls); - if (d == null){ - String msg = "No BeanDescriptor found for "+cls; - throw new RuntimeException(msg); + while (it.hasNext()) { + Entry entry = it.next(); + String key = entry.getKey().toString(); + Object value = entry.getValue(); + if (value == null) { + gen.writeNull(key); + } else { + if (value instanceof Collection>) { + toJsonFromCollection((Collection>) value, key, gen, options); + + } else if (value instanceof EntityBean) { + BeanDescriptor> d = getDecriptor(value.getClass()); + d.jsonWrite(writeJson,(EntityBean) value, key); + + } else { + EJson.write(entry, gen); } - return d; + } } + gen.writeEnd(); + } + + private BeanDescriptor getDecriptor(Class cls) { + BeanDescriptor d = server.getBeanDescriptor(cls); + if (d == null) { + throw new RuntimeException("No BeanDescriptor found for " + cls); + } + return d; + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/DefaultJsonValueAdapter.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/DefaultJsonValueAdapter.java index e2b3dd35f..85fc55c78 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/DefaultJsonValueAdapter.java +++ b/src/main/java/com/avaje/ebeaninternal/server/text/json/DefaultJsonValueAdapter.java @@ -5,9 +5,7 @@ import java.sql.Timestamp; import java.text.SimpleDateFormat; import java.util.TimeZone; -import com.avaje.ebean.text.json.JsonValueAdapter; - -public class DefaultJsonValueAdapter implements JsonValueAdapter { +public class DefaultJsonValueAdapter {//implements JsonValueAdapter { private final SimpleDateFormat dateTimeProto; diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/InternalJsonParser.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/InternalJsonParser.java deleted file mode 100644 index f767fac17..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/InternalJsonParser.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.avaje.ebeaninternal.server.text.json; - -import java.io.Reader; - -import com.avaje.ebean.text.json.JsonElement; - -public class InternalJsonParser { - - public static JsonElement parse(String s) { - - ReadJsonSourceString src = new ReadJsonSourceString(s); - ReadBasicJsonContext b = new ReadBasicJsonContext(src); - return ReadJsonRawReader.readJsonElement(b); - } - - public static JsonElement parse(Reader s) { - - ReadJsonSourceReader src = new ReadJsonSourceReader(s, 512, 256); - ReadBasicJsonContext b = new ReadBasicJsonContext(src); - return ReadJsonRawReader.readJsonElement(b); - } - -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/PathStack.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/PathStack.java index 42825abeb..b62bd1d97 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/PathStack.java +++ b/src/main/java/com/avaje/ebeaninternal/server/text/json/PathStack.java @@ -4,23 +4,23 @@ import com.avaje.ebeaninternal.server.util.ArrayStack; public class PathStack extends ArrayStack { - public String peekFullPath(String key){ - - String prefix = peekWithNull(); - if (prefix != null){ - return prefix+"."+key; - } else { - return key; - } - } - - public void pushPathKey(String key) { + public String peekFullPath(String key) { - String prefix = peekWithNull(); - if (prefix != null){ - key = prefix+"."+key; - } - push(key); + String prefix = peekWithNull(); + if (prefix != null) { + return prefix + "." + key; + } else { + return key; } + } + + public void pushPathKey(String key) { + + String prefix = peekWithNull(); + if (prefix != null) { + key = prefix + "." + key; + } + push(key); + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadBasicJsonContext.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadBasicJsonContext.java deleted file mode 100644 index bcc9c3bfd..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadBasicJsonContext.java +++ /dev/null @@ -1,273 +0,0 @@ -package com.avaje.ebeaninternal.server.text.json; - -import com.avaje.ebean.text.TextException; - -public class ReadBasicJsonContext implements ReadJsonInterface { - - private final ReadJsonSource src; - - private char tokenStart; - private String tokenKey; - private boolean pushedTokenKey; - - public ReadBasicJsonContext(ReadJsonSource src) { - this.src = src; - } - - /** - * Push the current token key back onto the 'stack'. - */ - public void pushTokenKey() { - pushedTokenKey = true; - } - - public char getToken() { - return tokenStart; - } - - public String getTokenKey() { - return tokenKey; - } - - public boolean isTokenKey() { - return '\"' == tokenStart; - } - - public boolean isTokenObjectEnd() { - return '}' == tokenStart; - } - - public boolean readObjectBegin() { - readNextToken(); - if ('{' == tokenStart){ - return true; - } else if ('n' == tokenStart) { - return false; - } else if (']' == tokenStart) { - // an empty array - return false; - } - throw new RuntimeException("Expected object begin at "+src.getErrorHelp()); - } - - public boolean readKeyNext() { - readNextToken(); - if ('\"' == tokenStart){ - return true; - } else if ('}' == tokenStart) { - return false; - } - throw new RuntimeException("Expected '\"' or '}' at "+src.getErrorHelp()); - } - - public boolean readValueNext() { - readNextToken(); - if (',' == tokenStart){ - return true; - } else if ('}' == tokenStart) { - return false; - } - throw new RuntimeException("Expected ',' or '}' at "+src.getErrorHelp()+" but got "+tokenStart); - } - - public boolean readArrayBegin() { - readNextToken(); - if ('[' == tokenStart){ - return true; - } else if ('n' == tokenStart) { - return false; - } - throw new RuntimeException("Expected array begin at "+src.getErrorHelp()); - } - - public boolean readArrayNext() { - readNextToken(); - if (',' == tokenStart){ - return true; - } - if (']' == tokenStart){ - return false; - } - throw new RuntimeException("Expected ',' or ']' at "+src.getErrorHelp()); - } - - public void readNextToken() { - - if (pushedTokenKey) { - // Do nothing - pushedTokenKey = false; - return; - } - - ignoreWhiteSpace(); - - tokenStart = src.nextChar("EOF finding next token"); - switch (tokenStart) { - case '"': - internalReadKey(); - break; - case '{': break; - case '}': break; - case '[': break; // not expected - case ']': break; // not expected - case ',': break; // not expected - case ':': break; // not expected - case 'n': - internalReadNull(); - break; // not expected - - default: - throw new RuntimeException("Unexpected tokenStart["+tokenStart+"] "+src.getErrorHelp()); - } - - } - - public String readQuotedValue() { - - boolean escape = false; - StringBuilder sb = new StringBuilder(); - - do { - char ch = src.nextChar("EOF reading quoted value"); - if (escape) { - // in escape mode so just append the character - escape = false; - switch (ch) { - case 'n': - sb.append('\n'); - break; - case 'r': - sb.append('\r'); - break; - case 't': - sb.append('\t'); - break; - case 'f': - sb.append('\f'); - break; - case 'b': - sb.append('\b'); - break; - case '"': - sb.append('"'); - break; - case 'u': - String msg = "EOF reading unicode value"; - char c1 = src.nextChar(msg); - char c2 = src.nextChar(msg); - char c3 = src.nextChar(msg); - char c4 = src.nextChar(msg); - char u = (char) Integer.parseInt(""+c1+c2+c3+c4, 16); - sb.append(u); - break; - - default: - sb.append('\\'); - sb.append(ch); - break; - } - - } else { - switch (ch) { - case '\\': - // put into 'escape' mode for next character - escape = true; - break; - case '"': - return sb.toString(); - - default: - sb.append(ch); - } - } - } while (true); - } - - public String readUnquotedValue(char c) { - String v = readUnquotedValueRaw(c); - if ("null".equals(v)){ - return null; - } else { - return v; - } - } - - private String readUnquotedValueRaw(char c) { - - StringBuilder sb = new StringBuilder(); - sb.append(c); - - do { - tokenStart = src.nextChar("EOF reading unquoted value"); - switch (tokenStart) { - case ',': - src.back(); - return sb.toString(); - - case '}': - src.back(); - return sb.toString(); - - case ' ': - return sb.toString(); - - case '\t': - return sb.toString(); - - case '\r': - return sb.toString(); - - case '\n': - return sb.toString(); - - default: - sb.append(tokenStart); - } - - } while (true); - - } - - private void internalReadNull() { - - StringBuilder sb = new StringBuilder(4); - sb.append(tokenStart); - for (int i = 0; i < 3; i++) { - char c = src.nextChar("EOF reading null "); - sb.append(c); - } - if (!"null".equals(sb.toString())){ - throw new TextException("Expected 'null' but got "+sb.toString()+" "+src.getErrorHelp()); - } - } - - private void internalReadKey() { - StringBuilder sb = new StringBuilder(); - do { - char c = src.nextChar("EOF reading key"); - if ('\"' == c){ - tokenKey = sb.toString(); - break; - } else { - sb.append(c); - } - } while (true); - - ignoreWhiteSpace(); - - char c = src.nextChar("EOF reading ':'"); - if (':' != c){ - throw new TextException("Expected to find colon after key at "+(src.pos()-1)+" but found ["+c+"]"+src.getErrorHelp()); - } - } - - public void ignoreWhiteSpace() { - src.ignoreWhiteSpace(); - } - - public char nextChar() { - tokenStart = src.nextChar("EOF getting nextChar for raw json"); - return tokenStart; - } - -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonContext.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonContext.java deleted file mode 100644 index 6b7f2d29c..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonContext.java +++ /dev/null @@ -1,177 +0,0 @@ -package com.avaje.ebeaninternal.server.text.json; - -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; - -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.bean.EntityBeanIntercept; -import com.avaje.ebean.text.json.JsonElement; -import com.avaje.ebean.text.json.JsonReadBeanVisitor; -import com.avaje.ebean.text.json.JsonReadOptions; -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.util.ArrayStack; - -public class ReadJsonContext extends ReadBasicJsonContext { - - private final Map> visitorMap; - - private final JsonValueAdapter valueAdapter; - - private final PathStack pathStack; - - private final ArrayStack beanState; - private ReadBeanState currentState; - - public ReadJsonContext(ReadJsonSource src, JsonValueAdapter dfltValueAdapter, JsonReadOptions options) { - super(src); - this.beanState = new ArrayStack(); - if (options == null){ - this.valueAdapter = dfltValueAdapter; - this.visitorMap = null; - this.pathStack = null; - } else { - this.valueAdapter = getValueAdapter(dfltValueAdapter, options.getValueAdapter()); - this.visitorMap = options.getVisitorMap(); - this.pathStack = (visitorMap == null || visitorMap.isEmpty()) ? null : new PathStack(); - } - } - - private JsonValueAdapter getValueAdapter(JsonValueAdapter dfltValueAdapter, JsonValueAdapter valueAdapter) { - return valueAdapter == null ? dfltValueAdapter : valueAdapter; - } - - public JsonValueAdapter getValueAdapter() { - return valueAdapter; - } - - public String readScalarValue() { - - ignoreWhiteSpace(); - - char prevChar = nextChar();//"EOF reading scalarValue?"); - if ('"' == prevChar){ - return readQuotedValue(); - } else { - return readUnquotedValue(prevChar); - } - } - - public void pushBean(Object bean, String path, BeanDescriptor> beanDescriptor){ - currentState = new ReadBeanState(bean, beanDescriptor); - beanState.push(currentState); - if (pathStack != null){ - pathStack.pushPathKey(path); - } - } - - public ReadBeanState popBeanState() { - if (pathStack != null){ - String path = pathStack.peekWithNull(); - JsonReadBeanVisitor> beanVisitor = visitorMap.get(path); - if (beanVisitor != null){ - currentState.visit(beanVisitor); - } - pathStack.pop(); - } - - // return the current ReadBeanState as we can't call setLoadedState() - // yet. We might bind master/detail beans together via mappedBy property - // so wait until after that before calling ReadBeanStatesetLoadedState(); - ReadBeanState s = currentState; - - beanState.pop(); - currentState = beanState.peekWithNull(); - return s; - } - - public void setProperty(String propertyName){ - currentState.setLoaded(propertyName); - } - - /** - * Got a key that doesn't map to a known property so read the json value - * which could be json primitive, object or array. - * - * Provide these values to a JsonReadBeanVisitor if registered. - * - */ - public JsonElement readUnmappedJson(String key) { - - JsonElement rawJsonValue = ReadJsonRawReader.readJsonElement(this); - if (visitorMap != null){ - currentState.addUnmappedJson(key, rawJsonValue); - } - return rawJsonValue; - } - - public static class ReadBeanState implements PropertyChangeListener { - - private final Object bean; - private final BeanDescriptor> beanDescriptor; - private final EntityBeanIntercept ebi; - private final Set loadedProps; - private Map unmapped; - - private ReadBeanState(Object bean, BeanDescriptor> beanDescriptor) { - this.bean = bean; - this.beanDescriptor = beanDescriptor; - if (bean instanceof EntityBean){ - this.ebi = ((EntityBean)bean)._ebean_getIntercept(); - this.loadedProps = new HashSet(); - } else { - this.ebi = null; - this.loadedProps = null; - } - } - public String toString(){ - return bean.getClass().getSimpleName()+" loaded:"+loadedProps; - } - - /** - * Add a loaded/set property to the set of loadedProps. - */ - public void setLoaded(String propertyName){ - if (ebi != null){ - loadedProps.add(propertyName); - } - } - - private void addUnmappedJson(String key, JsonElement value){ - if (unmapped == null){ - unmapped = new LinkedHashMap(); - } - unmapped.put(key, value); - } - - @SuppressWarnings("unchecked") - private void visit(JsonReadBeanVisitor beanVisitor) { - // listen for property change events so that - // we can update the loadedProps if necessary - if (ebi != null){ - ebi.addPropertyChangeListener(this); - } - beanVisitor.visit((T)bean, unmapped); - if (ebi != null){ - ebi.removePropertyChangeListener(this); - } - } - - public void propertyChange(PropertyChangeEvent evt) { - String propName = evt.getPropertyName(); - loadedProps.add(propName); - } - - public Object getBean() { - return bean; - } - - } - - - -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonInterface.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonInterface.java deleted file mode 100644 index 150ca475d..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonInterface.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.avaje.ebeaninternal.server.text.json; - -public interface ReadJsonInterface { - - public void ignoreWhiteSpace(); - - public char nextChar(); - - public String getTokenKey(); - - public boolean readKeyNext(); - - public boolean readValueNext(); - - public boolean readArrayNext(); - - public String readQuotedValue(); - - public String readUnquotedValue(char c); - - -} \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonRawReader.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonRawReader.java deleted file mode 100644 index 1a91f9463..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonRawReader.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.avaje.ebeaninternal.server.text.json; - -import com.avaje.ebean.text.json.JsonElement; -import com.avaje.ebean.text.json.JsonElementArray; -import com.avaje.ebean.text.json.JsonElementBoolean; -import com.avaje.ebean.text.json.JsonElementNull; -import com.avaje.ebean.text.json.JsonElementNumber; -import com.avaje.ebean.text.json.JsonElementObject; -import com.avaje.ebean.text.json.JsonElementString; - - - -public class ReadJsonRawReader { - - public static JsonElement readJsonElement(ReadJsonInterface ctx) { - return new ReadJsonRawReader(ctx).readJsonElement(); - } - - private final ReadJsonInterface ctx; - - private ReadJsonRawReader(ReadJsonInterface ctx){ - this.ctx = ctx; - } - - private JsonElement readJsonElement() { - return readValue(); - } - - private JsonElement readValue() { - - ctx.ignoreWhiteSpace(); - - char c = ctx.nextChar(); - - switch (c) { - case '{': - return readObject(); - - case '[': - return readArray(); - - case '"': - return readString(); - - default: - return readUnquoted(c); - } - } - - private JsonElement readArray() { - - JsonElementArray a = new JsonElementArray(); - - do { - JsonElement value = readValue(); - a.add(value); - if (!ctx.readArrayNext()){ - break; - } - } while(true); - - return a; - } - - private JsonElement readObject() { - - JsonElementObject o = new JsonElementObject(); - - do { - if (!ctx.readKeyNext()){ - break; - } else { - // we read a property key ... - String key = ctx.getTokenKey(); - JsonElement value = readValue(); - - o.put(key, value); - - if (!ctx.readValueNext()){ - break; - } - } - } while(true); - - return o; - } - - private JsonElement readString() { - String s = ctx.readQuotedValue(); - return new JsonElementString(s); - } - - private JsonElement readUnquoted(char c) { - String s = ctx.readUnquotedValue(c); - if ("null".equals(s)){ - return JsonElementNull.NULL; - - } else if ("true".equals(s)){ - return JsonElementBoolean.TRUE; - - } else if ("false".equals(s)) { - return JsonElementBoolean.FALSE; - - } - return new JsonElementNumber(s); - } -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonSource.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonSource.java deleted file mode 100644 index 0d1e12486..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonSource.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.avaje.ebeaninternal.server.text.json; - -public interface ReadJsonSource { - - public char nextChar(String eofMsg); - - public void ignoreWhiteSpace(); - - public void back(); - - public int pos(); - - public String getErrorHelp(); - -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonSourceReader.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonSourceReader.java deleted file mode 100644 index fe8b259a4..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonSourceReader.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.avaje.ebeaninternal.server.text.json; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.Reader; - -import com.avaje.ebean.text.TextException; - -public class ReadJsonSourceReader implements ReadJsonSource { - - private final Reader reader; - - private char[] localBuffer; - - private int totalPos; - private int localPos; - private int localPosEnd; - - public ReadJsonSourceReader(Reader reader, int localBufferSize, int bufferSize) { - this.reader = new BufferedReader(reader,bufferSize); - this.localBuffer = new char[localBufferSize]; - } - - public String toString() { - return String.valueOf(localBuffer); - } - - - - public String getErrorHelp() { - int prev = localPos - 30; - if (prev < 0){ - prev = 0; - } - String c = new String(localBuffer, prev, (localPos-prev)); - return "pos:"+pos()+" preceding:"+c; - } - - public int pos() { - return totalPos+localPos; - } - - - public void ignoreWhiteSpace() { - do { - char c = nextChar("EOF ignoring whitespace"); - if (!Character.isWhitespace(c)){ - --localPos; - break; - } - } while(true); - } - - public void back() { - localPos--; - } - - public char nextChar(String eofMsg) { - if (localPos >= localPosEnd){ - if (!loadLocalBuffer()) { - throw new TextException(eofMsg+" at pos:"+(totalPos+localPos)); - } - } - return localBuffer[localPos++]; - } - - private boolean loadLocalBuffer() { - try { - localPosEnd = reader.read(localBuffer); - if (localPosEnd > 0){ - totalPos += localPos; - localPos = 0; - return true; - } else { - this.localBuffer = null; - return false; - } - - } catch (IOException e){ - throw new TextException(e); - } - } -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonSourceString.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonSourceString.java deleted file mode 100644 index bcbda0961..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonSourceString.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.avaje.ebeaninternal.server.text.json; - -import com.avaje.ebean.text.TextException; - -public class ReadJsonSourceString implements ReadJsonSource { - - private final String source; - private final int sourceLength; - private int pos; - - public ReadJsonSourceString(String source){ - this.source = source; - this.sourceLength = source.length(); - } - - public String getErrorHelp() { - int prev = pos - 50; - if (prev < 0){ - prev = 0; - } - String c = source.substring(prev, pos); - return "pos:"+pos+" precedingcontent:"+c; - } - - public String toString() { - return source; - } - - public int pos() { - return pos; - } - - public void back() { - pos--; - } - - public char nextChar(String eofMsg) { - if (pos >= sourceLength){ - throw new TextException(eofMsg+" at pos:"+pos); - } - return source.charAt(pos++); - } - - public void ignoreWhiteSpace() { - do { - char c = source.charAt(pos); - if (Character.isWhitespace(c)){ - ++pos; - } else { - break; - } - } while(true); - } -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJson.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJson.java new file mode 100644 index 000000000..7fb263f09 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJson.java @@ -0,0 +1,202 @@ +package com.avaje.ebeaninternal.server.text.json; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; + +import javax.json.stream.JsonGenerator; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.text.PathProperties; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.util.ArrayStack; + +public class WriteJson { + + private final SpiEbeanServer server; + + private final JsonGenerator generator; + + private final PathProperties pathProperties; + + private final PathStack pathStack = new PathStack(); + + private final ArrayStack parentBeans = new ArrayStack(); + + public WriteJson(SpiEbeanServer server, JsonGenerator generator, PathProperties pathProperties){ + this.server = server; + this.generator = generator; + this.pathProperties = pathProperties; + } + + public JsonGenerator gen() { + return generator; + } + + public boolean isParentBean(Object bean) { + if (parentBeans.isEmpty()) { + return false; + } else { + return parentBeans.contains(bean); + } + } + + public void pushParentBeanMany(Object parentBean) { + parentBeans.push(parentBean); + } + + public void popParentBeanMany() { + parentBeans.pop(); + } + + public void beginAssocOne(String key, Object bean) { + parentBeans.push(bean); + pathStack.pushPathKey(key); + } + + public void endAssocOne() { + parentBeans.pop(); + pathStack.pop(); + } + + public Set getIncludeProperties() { + + if (pathProperties == null) { + return null; + } else { + return pathProperties.get(pathStack.peekWithNull()); + } + } + + public WriteBean createWriteBean(BeanDescriptor> desc, EntityBean bean) { + + if (pathProperties == null) { + return new WriteBean(desc, bean); + } + + boolean explicitAllProps = false; + Set currentIncludeProps = pathProperties.get(pathStack.peekWithNull()); + if (currentIncludeProps != null) { + explicitAllProps = currentIncludeProps.contains("*"); + if (explicitAllProps || currentIncludeProps.isEmpty()) { + currentIncludeProps = null; + } + } + return new WriteBean(desc, explicitAllProps, currentIncludeProps, bean); + } + + public class WriteBean { + + final boolean explicitAllProps; + final Set currentIncludeProps; + final BeanDescriptor> desc; + final EntityBean currentBean; + + WriteBean(BeanDescriptor> desc, EntityBean currentBean){ + this(desc, false, null, currentBean); + } + + WriteBean(BeanDescriptor> desc, boolean explicitAllProps, Set currentIncludeProps, EntityBean currentBean) { + super(); + this.desc = desc; + this.currentBean = currentBean; + this.explicitAllProps = explicitAllProps; + this.currentIncludeProps = currentIncludeProps; + } + + private boolean isReferenceOnly() { + return !explicitAllProps && currentIncludeProps == null && currentBean._ebean_getIntercept().isReference(); + } + + private boolean isIncludeProperty(BeanProperty prop) { + if (explicitAllProps) + return true; + if (currentIncludeProps != null) { + // explicitly controlled by pathProperties + return currentIncludeProps.contains(prop.getName()); + } else { + // include only loaded properties + return currentBean._ebean_getIntercept().isLoadedProperty(prop.getPropertyIndex()); + } + } + + public void write(WriteJson writeJson) { + //EntityBean bean = writeJson.getBean(); + BeanProperty beanProp = desc.getIdProperty(); + if (beanProp != null) { + if (isIncludeProperty(beanProp)) { + beanProp.jsonWrite(writeJson, currentBean); + } + } + + if (!isReferenceOnly()) { + // render all the properties and invoke lazy loading if required + BeanProperty[] props = desc.propertiesNonTransient(); + for (int j = 0; j < props.length; j++) { + System.out.println("bean "+ currentBean+" prop:"+props[j]); + if (isIncludeProperty(props[j])) { + props[j].jsonWrite(writeJson, currentBean); + } + } + props = desc.propertiesTransient(); + for (int j = 0; j < props.length; j++) { + if (isIncludeProperty(props[j])) { + props[j].jsonWrite(writeJson, currentBean); + } + } + } + } + } + + + public Boolean includeMany(String key) { + if (pathProperties != null) { + String fullPath = pathStack.peekFullPath(key); + return pathProperties.hasPath(fullPath); + } + return null; + } + + public void toJson(String name, Collection> c) { + + beginAssocMany(name); + + Iterator> it = c.iterator(); + while (it.hasNext()) { + EntityBean o = (EntityBean) it.next(); + BeanDescriptor> d = getDecriptor(o.getClass()); + d.jsonWrite(this, o, null); + } + endAssocMany(); + } + + private BeanDescriptor getDecriptor(Class cls) { + BeanDescriptor d = server.getBeanDescriptor(cls); + if (d == null) { + String msg = "No BeanDescriptor found for " + cls; + throw new RuntimeException(msg); + } + return d; + } + + public void beginAssocMany(String key) { + pathStack.pushPathKey(key); + generator.writeStartArray(key); + } + + public void endAssocMany() { + pathStack.pop(); + generator.writeEnd(); + } + + public void writeStartObject(String key) { + if (key == null) { + generator.writeStartObject(); + } else { + generator.writeStartObject(key); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonBuffer.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonBuffer.java deleted file mode 100644 index a16eb47f2..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonBuffer.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.avaje.ebeaninternal.server.text.json; - - -public interface WriteJsonBuffer extends Appendable { - - public WriteJsonBuffer append(String content); - -} \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonBufferString.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonBufferString.java deleted file mode 100644 index 213f8d70f..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonBufferString.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.avaje.ebeaninternal.server.text.json; - -import java.io.IOException; - -public class WriteJsonBufferString implements WriteJsonBuffer { - - private final StringBuilder buffer; - - public WriteJsonBufferString(){ - this.buffer = new StringBuilder(256); - } - - public WriteJsonBufferString append(CharSequence csq) throws IOException { - buffer.append(csq); - return this; - } - - public WriteJsonBufferString append(CharSequence csq, int start, int end) throws IOException { - buffer.append(csq, start, end); - return this; - } - - public WriteJsonBufferString append(char c) throws IOException { - buffer.append(c); - return this; - } - - public WriteJsonBufferString append(String content){ - buffer.append(content); - return this; - } - - public String getBufferOutput() { - return buffer.toString(); - } - - public String toString() { - return buffer.toString(); - } -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonBufferWriter.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonBufferWriter.java deleted file mode 100644 index 422364529..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonBufferWriter.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.avaje.ebeaninternal.server.text.json; - -import java.io.IOException; -import java.io.Writer; - -import com.avaje.ebean.text.TextException; - -public class WriteJsonBufferWriter implements WriteJsonBuffer { - - private final Writer buffer; - - public WriteJsonBufferWriter(Writer buffer){ - this.buffer = buffer; - } - - public WriteJsonBufferWriter append(String content){ - try { - buffer.write(content); - return this; - } catch (IOException e) { - throw new TextException(e); - } - } - - public WriteJsonBufferWriter append(CharSequence csq) throws IOException { - return append(csq, 0, csq.length()); - } - - public WriteJsonBufferWriter append(CharSequence csq, int start, int end) throws IOException { - for (int i = start; i < end; i++) { - buffer.append(csq.charAt(i)); - } - return this; - } - - public WriteJsonBufferWriter append(char c) throws IOException { - try { - buffer.write(c); - return this; - } catch (IOException e) { - throw new TextException(e); - } - } - -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonContext.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonContext.java deleted file mode 100644 index 7112f8a4e..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonContext.java +++ /dev/null @@ -1,376 +0,0 @@ -package com.avaje.ebeaninternal.server.text.json; - -import java.util.Collection; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; - -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.bean.EntityBeanIntercept; -import com.avaje.ebean.text.PathProperties; -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebean.text.json.JsonWriteBeanVisitor; -import com.avaje.ebean.text.json.JsonWriteOptions; -import com.avaje.ebean.text.json.JsonWriter; -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.type.EscapeJson; -import com.avaje.ebeaninternal.server.type.ScalarType; -import com.avaje.ebeaninternal.server.util.ArrayStack; - - -public class WriteJsonContext implements JsonWriter { - - private final SpiEbeanServer server; - - private final WriteJsonBuffer buffer; - - private final boolean pretty; - - private final JsonValueAdapter valueAdapter; - - private final ArrayStack parentBeans = new ArrayStack(); - - private final PathProperties pathProperties; - - private final Map> visitorMap; - - private final String callback; - - private final PathStack pathStack; - - private WriteBeanState beanState; - - private int depthOffset; - - boolean assocOne; - - public WriteJsonContext(WriteJsonBuffer buffer, boolean pretty, JsonValueAdapter dfltValueAdapter, - JsonWriteOptions options, String requestCallback, SpiEbeanServer server){ - - this.server = server; - this.buffer = buffer; - this.pretty = pretty; - this.pathStack = new PathStack(); - this.callback = getCallback(requestCallback, options); - if (options == null){ - this.valueAdapter = dfltValueAdapter; - this.visitorMap = null; - this.pathProperties = null; - - } else { - this.valueAdapter = getValueAdapter(dfltValueAdapter, options.getValueAdapter()); - this.visitorMap = emptyToNull(options.getVisitorMap()); - this.pathProperties = emptyToNull(options.getPathProperties()); - } - - if (callback != null){ - buffer.append(requestCallback).append("("); - } - } - - public void toJson(String name, Collection> c) { - - beginAssocMany(name); - - Iterator> it = c.iterator(); - if (!it.hasNext()){ - endAssocMany(); - return; - } - - EntityBean o = (EntityBean)it.next(); - BeanDescriptor> d = getDecriptor(o.getClass()); - - d.jsonWrite(this, o); - while (it.hasNext()) { - appendComma(); - EntityBean t = (EntityBean)it.next(); - d.jsonWrite(this, t); - } - endAssocMany(); - } - - private BeanDescriptor getDecriptor(Class cls) { - BeanDescriptor d = server.getBeanDescriptor(cls); - if (d == null){ - String msg = "No BeanDescriptor found for "+cls; - throw new RuntimeException(msg); - } - return d; - } - - public void appendRawValue(String key, String rawJsonValue) { - appendKeyWithComma(key, true); - buffer.append(rawJsonValue); - } - - public void appendQuoteEscapeValue(String key, String valueToEscape) { - appendKeyWithComma(key, true); - EscapeJson.escapeQuote(valueToEscape, buffer); - } - - public void end() { - if (callback != null){ - buffer.append(")"); - } - } - - private Map emptyToNull(Map m){ - if ( m == null || m.isEmpty()) { - return null; - } else { - return m; - } - } - - private PathProperties emptyToNull(PathProperties m){ - if ( m == null || m.isEmpty()) { - return null; - } else { - return m; - } - } - - private String getCallback(String requestCallback, JsonWriteOptions options) { - if (requestCallback != null){ - return requestCallback; - } - if (options != null){ - return options.getCallback(); - } - return null; - } - - private JsonValueAdapter getValueAdapter(JsonValueAdapter dfltValueAdapter, JsonValueAdapter valueAdapter) { - return valueAdapter == null ? dfltValueAdapter : valueAdapter; - } - - /** - * Return the set of properties to write to JSON. If null is returned then - * the default will output the properties loaded for this bean. - */ - public Set getIncludeProperties() { - if (pathProperties != null){ - String path = pathStack.peekWithNull(); - return pathProperties.get(path); - } - return null; - } - - public JsonWriteBeanVisitor> getBeanVisitor() { - if (visitorMap != null){ - String path = pathStack.peekWithNull(); - return visitorMap.get(path); - } - return null; - } - - public String getJson() { - return buffer.toString(); - } - - private void appendIndent(){ - - buffer.append("\n"); - int depth = depthOffset + parentBeans.size(); - for (int i = 0; i < depth; i++) { - buffer.append(" "); - } - } - - public void appendObjectBegin(){ - if (pretty && !assocOne){ - appendIndent(); - } - buffer.append("{"); - } - public void appendObjectEnd(){ - buffer.append("}"); - } - - public void appendArrayBegin(){ - if (pretty){ - appendIndent(); - } - buffer.append("["); - depthOffset++; - } - - public void appendArrayEnd(){ - depthOffset--; - if (pretty){ - appendIndent(); - } - buffer.append("]"); - } - - public void appendComma(){ - buffer.append(","); - } - - public void addDepthOffset(int offset){ - depthOffset += offset; - } - - public void beginAssocOneIsNull(String key) { - depthOffset++; - internalAppendKeyBegin(key); - appendNull(); - depthOffset--; - } - - public void beginAssocOne(String key) { - pathStack.pushPathKey(key); - - internalAppendKeyBegin(key); - assocOne = true; - } - - public void endAssocOne() { - - pathStack.pop(); - assocOne = false; - } - - public Boolean includeMany(String key) { - if (pathProperties != null){ - String fullPath = pathStack.peekFullPath(key); - return pathProperties.hasPath(fullPath); - } - return null; - } - - public void beginAssocMany(String key) { - - pathStack.pushPathKey(key); - - depthOffset--; - internalAppendKeyBegin(key); - depthOffset++; - buffer.append("["); - } - - public void endAssocMany(){ - - pathStack.pop(); - - if (pretty){ - depthOffset--; - appendIndent(); - depthOffset++; - } - buffer.append("]"); - } - - private void internalAppendKeyBegin(String key) { - if (!beanState.isFirstKey()){ - buffer.append(","); - } - if (pretty){ - appendIndent(); - } - appendKeyWithComma(key, false); - } - - public void appendNameValue(String key, ScalarType scalarType, T value) { - appendKeyWithComma(key, true); - scalarType.jsonWrite(buffer, value, getValueAdapter()); - } - - public void appendDiscriminator(String key, String discValue) { - appendKeyWithComma(key, true); - buffer.append("\""); - buffer.append(discValue); - buffer.append("\""); - } - - private void appendKeyWithComma(String key, boolean withComma) { - if (withComma){ - if (!beanState.isFirstKey()){ - buffer.append(","); - } - } - buffer.append("\""); - if(key == null) { - buffer.append("null"); - } else { - buffer.append(key); - } - buffer.append("\":"); - } - - public void appendNull(String key) { - appendKeyWithComma(key, true); - buffer.append("null"); - } - - public void appendNull() { - buffer.append("null"); - } - - public JsonValueAdapter getValueAdapter() { - return valueAdapter; - } - - public String toString() { - return buffer.toString(); - } - - public void popParentBean(){ - parentBeans.pop(); - } - - public void pushParentBean(Object parentBean){ - parentBeans.push(parentBean); - } - - public void popParentBeanMany(){ - parentBeans.pop(); - depthOffset--; - } - - public void pushParentBeanMany(Object parentBean){ - parentBeans.push(parentBean); - depthOffset++; - } - - public boolean isParentBean(Object bean){ - if (parentBeans.isEmpty()){ - return false; - } else { - return parentBeans.contains(bean); - } - } - - public WriteBeanState pushBeanState(Object bean) { - WriteBeanState newState = new WriteBeanState();//bean); - WriteBeanState prevState = beanState; - beanState = newState; - return prevState; - } - - public void pushPreviousState(WriteBeanState previousState) { - this.beanState = previousState; - } - - - public static class WriteBeanState { - - private boolean firstKeyOut; - - public WriteBeanState() { - - } - - public boolean isFirstKey() { - if (!firstKeyOut){ - firstKeyOut = true; - return true; - } else { - return false; - } - } - - } -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/CtCompoundType.java b/src/main/java/com/avaje/ebeaninternal/server/type/CtCompoundType.java index a929f2e35..da6ab5d71 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/CtCompoundType.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/CtCompoundType.java @@ -6,17 +6,10 @@ import java.util.Map; import com.avaje.ebean.config.CompoundType; import com.avaje.ebean.config.CompoundTypeProperty; -import com.avaje.ebean.text.json.JsonElement; -import com.avaje.ebean.text.json.JsonElementObject; -import com.avaje.ebeaninternal.server.text.json.ReadJsonContext; -import com.avaje.ebeaninternal.server.text.json.WriteJsonContext; -import com.avaje.ebeaninternal.server.text.json.WriteJsonContext.WriteBeanState; +import com.avaje.ebeaninternal.server.text.json.WriteJson; /** - * The internal representation of a Compound Type (Immutable Compound Value - * Object). - * - * @author rbygrave + * The internal representation of a Compound Type (Immutable Compound Value Object). * * @param * The Type of the "Immutable Compound Value Object". @@ -168,47 +161,26 @@ public final class CtCompoundType implements ScalarDataReader { return parent + "." + propName; } } - - public Object jsonRead(ReadJsonContext ctx) { - - if (!ctx.readObjectBegin()) { - // the object is null - return null; - } - - JsonElementObject jsonObject = new JsonElementObject(); - do { - if (!ctx.readKeyNext()){ - break; - } else { - // we read a property key ... - String propName = ctx.getTokenKey(); - JsonElement unmappedJson = ctx.readUnmappedJson(propName); - jsonObject.put(propName, unmappedJson); - - if (!ctx.readValueNext()){ - break; - } - } - } while(true); - - return readJsonElementObject(ctx, jsonObject); + + public Object jsonConvert(Map map) { + return readJsonElementObject(map); } - private Object readJsonElementObject(ReadJsonContext ctx, JsonElementObject jsonObject){ + @SuppressWarnings("unchecked") + private Object readJsonElementObject(Map jsonObject){ boolean nullValue = false; Object[] values = new Object[propReaders.length]; for (int i = 0; i < propReaders.length; i++) { String propName = properties[i].getName(); - JsonElement jsonElement = jsonObject.get(propName); + Object jsonElement = jsonObject.get(propName); if (propReaders[i] instanceof CtCompoundType>) { - values[i] = ((CtCompoundType>)propReaders[i]).readJsonElementObject(ctx, (JsonElementObject)jsonElement); - + values[i] = ((CtCompoundType>)propReaders[i]).readJsonElementObject((Map)jsonElement); } else { - values[i] = ((ScalarType>)propReaders[i]).jsonFromString(jsonElement.toPrimitiveString(), ctx.getValueAdapter()); + //((ScalarType>)propReaders[i]).jsonFromString(jsonElement.toPrimitiveString(), ctx.getValueAdapter()); + values[i] = ((ScalarType>)propReaders[i]).parse(jsonElement.toString());; } if (values[i] == null){ nullValue = true; @@ -223,40 +195,35 @@ public final class CtCompoundType implements ScalarDataReader { } - public void jsonWrite(WriteJsonContext ctx, Object valueObject, String propertyName) { - - if (valueObject == null){ - ctx.beginAssocOneIsNull(propertyName); - - } else { - ctx.pushParentBean(valueObject); - ctx.beginAssocOne(propertyName); - jsonWriteProps(ctx, valueObject, propertyName); - ctx.endAssocOne(); - ctx.popParentBean(); - } + public void jsonWrite(WriteJson ctx, Object valueObject, String propertyName) { + + ctx.beginAssocOne(propertyName, valueObject); + jsonWriteProps(ctx, valueObject, propertyName); + ctx.endAssocOne(); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private void jsonWriteProps(WriteJson ctx, Object valueObject, String propertyName) { + + if (propertyName != null) { + ctx.gen().writeStartObject(propertyName); + } else { + ctx.gen().writeStartObject(); } + for (int i = 0; i < properties.length; i++) { + String propName = properties[i].getName(); + Object value = properties[i].getValue((V) valueObject); + if (propReaders[i] instanceof CtCompoundType>) { + ((CtCompoundType) propReaders[i]).jsonWrite(ctx, value, propName); - @SuppressWarnings({ "unchecked", "rawtypes" }) - private void jsonWriteProps(WriteJsonContext ctx, Object valueObject, String propertyName) { - - ctx.appendObjectBegin(); - WriteBeanState prevState = ctx.pushBeanState(valueObject); - - for (int i = 0; i < properties.length; i++) { - String propName = properties[i].getName(); - Object value = properties[i].getValue((V)valueObject); - if (propReaders[i] instanceof CtCompoundType>) { - ((CtCompoundType)propReaders[i]).jsonWrite(ctx, value, propName); - - } else { - ctx.appendNameValue(propName, (ScalarType)propReaders[i], value); - } - } - - ctx.pushPreviousState(prevState); - ctx.appendObjectEnd(); + } else { + ((ScalarType) propReaders[i]).jsonWrite(ctx.gen(), propName, value); + //ctx.appendNameValue(propName, (ScalarType) propReaders[i], value); + } } + ctx.gen().writeEnd(); + } + } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/EscapeJson.java b/src/main/java/com/avaje/ebeaninternal/server/type/EscapeJson.java index 1267aa31a..c173e606a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/EscapeJson.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/EscapeJson.java @@ -4,7 +4,6 @@ import java.io.IOException; import com.avaje.ebean.text.TextException; import com.avaje.ebean.util.StringHelper; -import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; public class EscapeJson { @@ -45,24 +44,6 @@ public class EscapeJson { } - public static void escape(String value, WriteJsonBuffer sb) { - if (value == null) { - sb.append("null"); - } else { - escapeAppend(value, sb); - } - } - - public static void escapeQuote(String value, WriteJsonBuffer sb) { - if (value == null) { - sb.append("null"); - } else { - sb.append("\""); - escapeAppend(value, sb); - sb.append("\""); - } - } - /** * Escape quotes, \, /, \r, \n, \b, \f, \t and characters (U+0000 through * U+001F). diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarType.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarType.java index 693a46a3d..8495fc3f8 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarType.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarType.java @@ -5,10 +5,12 @@ import java.io.DataOutput; import java.io.IOException; import java.sql.SQLException; +import javax.json.stream.JsonGenerator; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; + import com.avaje.ebean.text.StringFormatter; import com.avaje.ebean.text.StringParser; -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; /** * Describes a scalar type. @@ -183,14 +185,12 @@ public interface ScalarType extends StringParser, StringFormatter, ScalarData */ public boolean isDateTimeCapable(); - public void jsonWrite(WriteJsonBuffer buffer, T value, JsonValueAdapter ctx); - - public String jsonToString(T value, JsonValueAdapter ctx); + public Object readData(DataInput dataInput) throws IOException; - public T jsonFromString(String value, JsonValueAdapter ctx); + public void writeData(DataOutput dataOutput, Object v) throws IOException; - public Object readData(DataInput dataInput) throws IOException; + public Object jsonRead(JsonParser ctx, Event event); - public void writeData(DataOutput dataOutput, Object v) throws IOException; + public void jsonWrite(JsonGenerator ctx, String name, Object value); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBase.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBase.java index 842433c1b..c553dcad0 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBase.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBase.java @@ -1,7 +1,5 @@ package com.avaje.ebeaninternal.server.type; -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; @@ -74,25 +72,12 @@ public abstract class ScalarTypeBase implements ScalarType { return value; } - public void loadIgnore(DataReader dataReader) { - dataReader.incrementPos(1); - } - - public void accumulateScalarTypes(String propName, CtCompoundTypeScalarList list) { - list.addScalarType(propName, this); - } + public void loadIgnore(DataReader dataReader) { + dataReader.incrementPos(1); + } - public void jsonWrite(WriteJsonBuffer buffer, T value, JsonValueAdapter ctx) { - String v = jsonToString(value, ctx); - buffer.append(v); - } - - public String jsonToString(T value, JsonValueAdapter ctx) { - return formatValue(value); - } - - public T jsonFromString(String value, JsonValueAdapter ctx) { - return parse(value); - } + public void accumulateScalarTypes(String propName, CtCompoundTypeScalarList list) { + list.addScalarType(propName, this); + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseDate.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseDate.java index b11c01c2c..d48b040c2 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseDate.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseDate.java @@ -7,8 +7,9 @@ import java.sql.Date; import java.sql.SQLException; import java.sql.Types; -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; +import javax.json.stream.JsonGenerator; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; /** * Base class for Date types. @@ -62,22 +63,22 @@ public abstract class ScalarTypeBaseDate extends ScalarTypeBase { } @Override - public String jsonToString(T value, JsonValueAdapter ctx) { - Date date = convertToDate(value); - return ctx.jsonFromDate(date); + public Object jsonRead(JsonParser ctx, Event event) { + if (ctx.isIntegralNumber()) { + return parseDateTime(ctx.getLong()); + } else { + String string = ctx.getString(); + throw new RuntimeException("convert "+string); + } } - @Override - public void jsonWrite(WriteJsonBuffer buffer, T value, JsonValueAdapter ctx) { - String s = jsonToString(value, ctx); - buffer.append(s); + public void jsonWrite(JsonGenerator ctx, String name, Object value) { + long millis = convertToMillis(value); + ctx.write(name, millis); } + + public abstract long convertToMillis(Object value); - @Override - public T jsonFromString(String value, JsonValueAdapter ctx) { - Date ts = ctx.jsonToDate(value); - return convertFromDate(ts); - } public Object readData(DataInput dataInput) throws IOException { if (!dataInput.readBoolean()) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseDateTime.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseDateTime.java index 9c6d3f0e2..287de52b9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseDateTime.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseDateTime.java @@ -7,8 +7,9 @@ import java.sql.SQLException; import java.sql.Timestamp; import java.sql.Types; -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; +import javax.json.stream.JsonGenerator; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; /** * Base type for DateTime types. @@ -19,6 +20,8 @@ public abstract class ScalarTypeBaseDateTime extends ScalarTypeBase { super(type, jdbcNative, jdbcType); } + public abstract long convertToMillis(Object value); + public abstract Timestamp convertToTimestamp(T t); public abstract T convertFromTimestamp(Timestamp ts); @@ -42,6 +45,23 @@ public abstract class ScalarTypeBaseDateTime extends ScalarTypeBase { } } + @Override + public Object jsonRead(JsonParser ctx, Event event) { + if (ctx.isIntegralNumber()) { + long millis = ctx.getLong(); + return parseDateTime(millis); + } else { + String string = ctx.getString(); + throw new RuntimeException("convert "+string); + } + } + + @Override + public void jsonWrite(JsonGenerator ctx, String name, Object value) { + long millis = convertToMillis(value); + ctx.write(name, millis); + } + public String formatValue(T t) { Timestamp ts = convertToTimestamp(t); return ts.toString(); @@ -60,24 +80,6 @@ public abstract class ScalarTypeBaseDateTime extends ScalarTypeBase { public boolean isDateTimeCapable() { return true; } - - @Override - public void jsonWrite(WriteJsonBuffer buffer, T value, JsonValueAdapter ctx) { - String v = jsonToString(value, ctx); - buffer.append(v); - } - - @Override - public String jsonToString(T value, JsonValueAdapter ctx) { - Timestamp ts = convertToTimestamp(value); - return ctx.jsonFromTimestamp(ts); - } - - @Override - public T jsonFromString(String value, JsonValueAdapter ctx) { - Timestamp ts = ctx.jsonToTimestamp(value); - return convertFromTimestamp(ts); - } public Object readData(DataInput dataInput) throws IOException { if (!dataInput.readBoolean()) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseVarchar.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseVarchar.java index e54a325ce..38950f0d9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseVarchar.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseVarchar.java @@ -6,9 +6,11 @@ import java.io.IOException; import java.sql.SQLException; import java.sql.Types; +import javax.json.stream.JsonGenerator; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; + import com.avaje.ebean.text.TextException; -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; /** * Base ScalarType for types which converts to and from a VARCHAR database @@ -81,19 +83,6 @@ public abstract class ScalarTypeBaseVarchar extends ScalarTypeBase { return formatValue((T) v); } - public T jsonFromString(String value, JsonValueAdapter ctx) { - return parse(EscapeJson.unescapeSlash(value)); - } - - public String toJsonString(Object value, JsonValueAdapter ctx) { - return EscapeJson.escapeQuote(format(value)); - } - - @Override - public void jsonWrite(WriteJsonBuffer buffer, T value, JsonValueAdapter ctx) { - EscapeJson.escapeQuote(format(value), buffer); - } - public Object readData(DataInput dataInput) throws IOException { if (!dataInput.readBoolean()) { return null; @@ -115,4 +104,13 @@ public abstract class ScalarTypeBaseVarchar extends ScalarTypeBase { dataOutput.writeUTF(s); } } + + @Override + public Object jsonRead(JsonParser ctx, Event event) { + return parse(ctx.getString()); + } + + public void jsonWrite(JsonGenerator ctx, String name, Object value) { + ctx.write(name, format(value)); + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBigDecimal.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBigDecimal.java index bc3e1e25a..91f363fe5 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBigDecimal.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBigDecimal.java @@ -4,9 +4,14 @@ import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.math.BigDecimal; +import java.math.BigInteger; import java.sql.SQLException; import java.sql.Types; +import javax.json.stream.JsonGenerator; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; + import com.avaje.ebeaninternal.server.core.BasicTypeConverter; /** @@ -74,5 +79,15 @@ public class ScalarTypeBigDecimal extends ScalarTypeBase { public boolean isDateTimeCapable() { return true; } + + @Override + public Object jsonRead(JsonParser ctx, Event event) { + return ctx.getBigDecimal(); + } + + public void jsonWrite(JsonGenerator ctx, String name, Object value) { + ctx.write(name, (BigDecimal)value); + } + } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBoolean.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBoolean.java index f26d43e21..44608da90 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBoolean.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBoolean.java @@ -3,9 +3,14 @@ package com.avaje.ebeaninternal.server.type; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; +import java.math.BigDecimal; import java.sql.SQLException; import java.sql.Types; +import javax.json.stream.JsonGenerator; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; + import com.avaje.ebean.text.TextException; import com.avaje.ebeaninternal.server.core.BasicTypeConverter; @@ -286,6 +291,15 @@ public class ScalarTypeBoolean { dataOutput.writeBoolean(val.booleanValue()); } } + + @Override + public Object jsonRead(JsonParser ctx, Event event) { + return Event.VALUE_TRUE == event ? Boolean.TRUE : Boolean.FALSE; + } + + public void jsonWrite(JsonGenerator ctx, String name, Object value) { + ctx.write(name, (Boolean)value); + } } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeByte.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeByte.java index 502e37b10..003b78e2a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeByte.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeByte.java @@ -6,6 +6,10 @@ import java.io.IOException; import java.sql.SQLException; import java.sql.Types; +import javax.json.stream.JsonGenerator; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; + import com.avaje.ebean.text.TextException; import com.avaje.ebeaninternal.server.core.BasicTypeConverter; @@ -37,9 +41,18 @@ public class ScalarTypeByte extends ScalarTypeBase { public Byte toBeanType(Object value) { return BasicTypeConverter.toByte(value); } - - public String formatValue(Byte t) { + @Override + public void jsonWrite(JsonGenerator ctx, String name, Object value) { + throw new TextException("Not supported"); + } + + @Override + public Object jsonRead(JsonParser ctx, Event event) { + throw new TextException("Not supported"); + } + + public String formatValue(Byte t) { return t.toString(); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesBase.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesBase.java index 34992b06c..a2c11e603 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesBase.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesBase.java @@ -5,6 +5,10 @@ import java.io.DataOutput; import java.io.IOException; import java.sql.SQLException; +import javax.json.stream.JsonGenerator; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; + import com.avaje.ebean.text.TextException; /** @@ -41,7 +45,17 @@ public abstract class ScalarTypeBytesBase extends ScalarTypeBase { } - public String formatValue(byte[] t) { + @Override + public void jsonWrite(JsonGenerator ctx, String name, Object value) { + throw new TextException("Not supported"); + } + + @Override + public Object jsonRead(JsonParser ctx, Event event) { + throw new TextException("Not supported"); + } + + public String formatValue(byte[] t) { throw new TextException("Not supported"); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesEncrypted.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesEncrypted.java index 843977369..032002882 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesEncrypted.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesEncrypted.java @@ -5,8 +5,11 @@ import java.io.DataOutput; import java.io.IOException; import java.sql.SQLException; -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; +import javax.json.stream.JsonGenerator; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; + +import com.avaje.ebean.text.TextException; /** * Encrypted ScalarType that wraps a byte[] types. @@ -65,6 +68,16 @@ public class ScalarTypeBytesEncrypted implements ScalarType { baseType.loadIgnore(dataReader); } + @Override + public void jsonWrite(JsonGenerator ctx, String name, Object value) { + throw new TextException("Not supported"); + } + + @Override + public Object jsonRead(JsonParser ctx, Event event) { + throw new TextException("Not supported"); + } + public String format(Object v) { throw new RuntimeException("Not used"); } @@ -100,18 +113,6 @@ public class ScalarTypeBytesEncrypted implements ScalarType { baseType.accumulateScalarTypes(propName, list); } - public void jsonWrite(WriteJsonBuffer buffer, byte[] value, JsonValueAdapter ctx) { - baseType.jsonWrite(buffer, value, ctx); - } - - public String jsonToString(byte[] value, JsonValueAdapter ctx) { - return baseType.jsonToString(value, ctx); - } - - public byte[] jsonFromString(String value, JsonValueAdapter ctx) { - return baseType.jsonFromString(value, ctx); - } - public Object readData(DataInput dataInput) throws IOException { int len = dataInput.readInt(); byte[] value = new byte[len]; diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCalendar.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCalendar.java index 4655f4d93..c21e95600 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCalendar.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCalendar.java @@ -39,6 +39,11 @@ public class ScalarTypeCalendar extends ScalarTypeBaseDateTime { return calendar; } + @Override + public long convertToMillis(Object value) { + return ((Calendar) value).getTimeInMillis(); + } + @Override public Timestamp convertToTimestamp(Calendar t) { return new Timestamp(t.getTimeInMillis()); diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeChar.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeChar.java index c69474b47..8dc695e7a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeChar.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeChar.java @@ -3,7 +3,9 @@ package com.avaje.ebeaninternal.server.type; import java.sql.SQLException; import java.sql.Types; -import com.avaje.ebean.text.json.JsonValueAdapter; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; + import com.avaje.ebeaninternal.server.core.BasicTypeConverter; /** @@ -59,15 +61,11 @@ public class ScalarTypeChar extends ScalarTypeBaseVarchar { public Character parse(String value) { return value.charAt(0); } - + @Override - public Character jsonFromString(String value, JsonValueAdapter ctx) { - return value.charAt(0); + public Object jsonRead(JsonParser ctx, Event event) { + return ctx.getString(); } - @Override - public String jsonToString(Character value, JsonValueAdapter ctx) { - return EscapeJson.escapeQuote(value.toString()); - } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCharArray.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCharArray.java index 83cea2c8c..72adf5552 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCharArray.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCharArray.java @@ -3,7 +3,10 @@ package com.avaje.ebeaninternal.server.type; import java.sql.SQLException; import java.sql.Types; -import com.avaje.ebean.text.json.JsonValueAdapter; +import javax.json.stream.JsonGenerator; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; + import com.avaje.ebeaninternal.server.core.BasicTypeConverter; /** @@ -59,15 +62,13 @@ public class ScalarTypeCharArray extends ScalarTypeBaseVarchar
+ * If just a value then return that value else return null. + */ + private Object getSimpleValue(Event event) { + + switch (event) { + case VALUE_STRING: + return parser.getString(); + + case VALUE_NUMBER: + if (parser.isIntegralNumber()) { + return parser.getLong(); + } else { + return parser.getBigDecimal(); + } + + case VALUE_TRUE: + return Boolean.TRUE; + + case VALUE_FALSE: + return Boolean.FALSE; + + default: + return null; + } + } + + /** + * Process the event for objects and arrays. + */ + private void processEvent(Event event) { + switch (event) { + + case START_ARRAY: + startArray(); + break; + + case START_OBJECT: + startObject(); + break; + + case KEY_NAME: + currentContext.setKey(parser.getString()); + break; + + case VALUE_STRING: + setValue(parser.getString()); + break; + + case VALUE_NUMBER: + if (parser.isIntegralNumber()) { + setValue(parser.getLong()); + } else { + setValue(parser.getBigDecimal()); + } + break; + + case VALUE_TRUE: + setValue(Boolean.TRUE); + break; + + case VALUE_FALSE: + setValue(Boolean.FALSE); + break; + + case VALUE_NULL: + setValueNull(); + break; + + case END_OBJECT: + endObject(); + break; + + case END_ARRAY: + endArray(); + break; + + default: + break; + } + } + + private static final class Stack { + + private Context head; + + private void push(Context context) { + if (context != null) { + context.next = head; + head = context; + } + } + + private Context pop(Context endingContext) { + if (head == null) { + throw new NoSuchElementException(); + } + Context temp = head; + head = head.next; + temp.popContext(endingContext); + return temp; + } + + private boolean isEmpty() { + return head == null; + } + } + + private static abstract class Context { + Context next; + abstract void popContext(Context temp); + abstract Object getValue(); + abstract void setKey(String key); + abstract void setValue(Object value); + abstract void setValueNull(); + } + + private static class ObjectContext extends Context { + + private final Map map = new LinkedHashMap(); + + private String key; + + public void popContext(Context temp) { + setValue(temp.getValue()); + } + + Object getValue() { + return map; + } + + void setKey(String key) { + this.key = key; + } + + void setValue(Object value) { + map.put(key, value); + } + + void setValueNull() { + map.put(key, null); + } + } + + private static class ArrayContext extends Context { + + private final List values = new ArrayList(); + + public void popContext(Context temp) { + values.add(temp.getValue()); + } + + Object getValue() { + return values; + } + + void setValue(Object value) { + values.add(value); + } + + void setValueNull() { + // ignore + } + void setKey(String key) { + // not expected + } + } + +} diff --git a/src/main/java/com/avaje/ebean/json/EJsonWriter.java b/src/main/java/com/avaje/ebean/json/EJsonWriter.java new file mode 100644 index 000000000..16a58aed7 --- /dev/null +++ b/src/main/java/com/avaje/ebean/json/EJsonWriter.java @@ -0,0 +1,205 @@ +package com.avaje.ebean.json; + +import java.io.StringWriter; +import java.io.Writer; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Collection; +import java.util.Date; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import javax.json.Json; +import javax.json.stream.JsonGenerator; + +class EJsonWriter { + + static String write(Object object) { + StringWriter writer = new StringWriter(200); + write(object, writer); + return writer.toString(); + } + + static void write(Object object, Writer writer) { + JsonGenerator generator = Json.createGenerator(writer); + write(object, generator); + generator.close(); + } + + static void write(Object object, JsonGenerator jsonGenerator) { + new EJsonWriter(jsonGenerator).writeJson(object); + } + + private final JsonGenerator jsonGenerator; + + private EJsonWriter(JsonGenerator jsonGenerator) { + this.jsonGenerator = jsonGenerator; + } + + private void writeJson(Object object) { + writeJson(null, object); + } + + @SuppressWarnings("unchecked") + private void writeJson(String name, Object object) { + if (object == null) { + writeNull(name); + + } else if (object instanceof Map) { + writeMap(name, (Map) object); + + } else if (object instanceof Collection) { + writeCollection(name, (Collection) object); + + } else if (object instanceof Boolean) { + writeBoolean(name, (Boolean) object); + + } else if (object instanceof Number) { + writeNumber(name, (Number) object); + + } else if (object instanceof Date) { + writeDate(name, (Date) object); + + } else if (object instanceof String) { + writeString(name, (String) object); + + } else if (object instanceof Map.Entry, ?>) { + Map.Entry, ?> entry = (Map.Entry, ?>)object; + writeJson(entry.getKey().toString(), entry.getValue()); + + } else { + writeString(name, object.toString()); + } + + } + + private void writeBoolean(String name, Boolean object) { + if (name == null) { + jsonGenerator.write(object); + } else { + jsonGenerator.write(name, object); + } + } + + private void writeDate(String name, Date object) { + if (name == null) { + jsonGenerator.write(object.getTime()); + } else { + jsonGenerator.write(name, object.getTime()); + } + } + + private void writeNumber(String name, Number object) { + + if (object instanceof Long) { + writeLong(name, object); + + } else if (object instanceof Integer) { + writeInteger(name, object); + + } else if (object instanceof Double) { + writeDouble(name, object); + + } else if (object instanceof BigDecimal) { + writeBigDecimal(name, object); + + } else if (object instanceof BigInteger) { + writeBigInteger(name, object); + + } else { + writeGeneralNumber(name, object); + } + } + + private void writeGeneralNumber(String name, Number object) { + if (name == null) { + jsonGenerator.write(new BigDecimal(object.toString())); + } else { + jsonGenerator.write(name, new BigDecimal(object.toString())); + } + } + + private void writeBigDecimal(String name, Number object) { + if (name == null) { + jsonGenerator.write((BigDecimal) object); + } else { + jsonGenerator.write(name, (BigDecimal) object); + } + } + + private void writeBigInteger(String name, Number object) { + if (name == null) { + jsonGenerator.write((BigInteger) object); + } else { + jsonGenerator.write(name, (BigInteger) object); + } + } + + private void writeDouble(String name, Number object) { + if (name == null) { + jsonGenerator.write((Double) object); + } else { + jsonGenerator.write(name, (Double) object); + } + } + + private void writeLong(String name, Number object) { + if (name == null) { + jsonGenerator.write((Long) object); + } else { + jsonGenerator.write(name, (Long) object); + } + } + + private void writeInteger(String name, Number object) { + if (name == null) { + jsonGenerator.write((Integer) object); + } else { + jsonGenerator.write(name, (Integer) object); + } + } + + private void writeNull(String name) { + if (name == null) { + jsonGenerator.writeNull(); + } else { + jsonGenerator.writeNull(name); + } + } + + private void writeString(String name, String object) { + if (name == null) { + jsonGenerator.write(object); + } else { + jsonGenerator.write(name, object); + } + } + + private void writeCollection(String name, Collection collection) { + if (name == null) { + jsonGenerator.writeStartArray(); + } else { + jsonGenerator.writeStartArray(name); + } + for (Object object : collection) { + writeJson(null, object); + } + jsonGenerator.writeEnd(); + } + + private void writeMap(String name, Map map) { + + if (name == null) { + jsonGenerator.writeStartObject(); + } else { + jsonGenerator.writeStartObject(name); + } + Set> entrySet = map.entrySet(); + for (Entry entry : entrySet) { + writeJson(entry.getKey().toString(), entry.getValue()); + } + jsonGenerator.writeEnd(); + } + +} diff --git a/src/main/java/com/avaje/ebean/text/PathPropertiesParser.java b/src/main/java/com/avaje/ebean/text/PathPropertiesParser.java index d6bef851e..926eeaea1 100644 --- a/src/main/java/com/avaje/ebean/text/PathPropertiesParser.java +++ b/src/main/java/com/avaje/ebean/text/PathPropertiesParser.java @@ -53,6 +53,9 @@ class PathPropertiesParser { case '(': return currentWord(); default: + if (pos == 1) { + return ""; + } } } while (pos < eof); throw new RuntimeException("Hit EOF while reading sectionTitle from " + startPos); @@ -91,6 +94,10 @@ class PathPropertiesParser { } } while (pos < eof); + if (startPos < pos) { + String currentWord = source.substring(startPos, pos); + currentPathProps.addProperty(currentWord); + } } private void addSubpath() { diff --git a/src/main/java/com/avaje/ebean/text/json/JsonContext.java b/src/main/java/com/avaje/ebean/text/json/JsonContext.java index 17dc030ec..a453c9231 100644 --- a/src/main/java/com/avaje/ebean/text/json/JsonContext.java +++ b/src/main/java/com/avaje/ebean/text/json/JsonContext.java @@ -22,49 +22,27 @@ public interface JsonContext { */ public T toBean(Class rootType, Reader json); - /** - * Convert json string input into a Bean of a specific type with options. - */ - public T toBean(Class rootType, String json, JsonReadOptions options); - - /** - * Convert json reader input into a Bean of a specific type with options. - */ - public T toBean(Class rootType, Reader json, JsonReadOptions options); - /** * Convert json string input into a list of beans of a specific type. */ public List toList(Class rootType, String json); - /** - * Convert json string input into a list of beans of a specific type with - * options. - */ - public List toList(Class rootType, String json, JsonReadOptions options); - /** * Convert json reader input into a list of beans of a specific type. */ public List toList(Class rootType, Reader json); /** - * Convert json reader input into a list of beans of a specific type with - * options. + * Use the genericType to determine if this should be converted into a List or + * bean. */ - public List toList(Class rootType, Reader json, JsonReadOptions options); + public Object toObject(Type genericType, Reader json); /** * Use the genericType to determine if this should be converted into a List or * bean. */ - public Object toObject(Type genericType, Reader json, JsonReadOptions options); - - /** - * Use the genericType to determine if this should be converted into a List or - * bean. - */ - public Object toObject(Type genericType, String json, JsonReadOptions options); + public Object toObject(Type genericType, String json); /** * Write the bean or collection in JSON format to the writer with default @@ -77,11 +55,6 @@ public interface JsonContext { */ public void toJsonWriter(Object o, Writer writer); - /** - * With additional pretty output option. - */ - public void toJsonWriter(Object o, Writer writer, boolean pretty); - /** * With additional options to specify JsonValueAdapter and * JsonWriteBeanVisitor's. @@ -93,13 +66,7 @@ public interface JsonContext { * @param options * additional options to control the JSON output */ - public void toJsonWriter(Object o, Writer writer, boolean pretty, JsonWriteOptions options); - - /** - * With additional JSONP callback function. - */ - public void toJsonWriter(Object o, Writer writer, boolean pretty, JsonWriteOptions options, - String callback); + public void toJsonWriter(Object o, Writer writer, JsonWriteOptions options); /** * Convert a bean or collection to json string using default options. @@ -107,20 +74,9 @@ public interface JsonContext { public String toJsonString(Object o); /** - * Convert a bean or collection to json string with pretty format using - * default options. + * Convert a bean or collection to json string. */ - public String toJsonString(Object o, boolean pretty); - - /** - * Convert a bean or collection to json string using options. - */ - public String toJsonString(Object o, boolean pretty, JsonWriteOptions options); - - /** - * Convert a bean or collection to json string using a JSONP callback. - */ - public String toJsonString(Object o, boolean pretty, JsonWriteOptions options, String callback); + public String toJsonString(Object o, JsonWriteOptions options); /** * Return true if the type is known as an Entity or Xml type or a List Set or diff --git a/src/main/java/com/avaje/ebean/text/json/JsonElement.java b/src/main/java/com/avaje/ebean/text/json/JsonElement.java deleted file mode 100644 index 98b76a224..000000000 --- a/src/main/java/com/avaje/ebean/text/json/JsonElement.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.avaje.ebean.text.json; - -/** - * Marker interface for all the Raw JSON types. - * - * You will only use the JsonElements when you register a JsonReadBeanVisitor. - * The JSON elements that are not mapped to a bean property are made available - * to the JsonReadBeanVisitor. - * - * - * @see JsonReadBeanVisitor - * - * @author rbygrave - */ -public interface JsonElement { - - /** - * Return true if this is a JSON primitive type (null, boolean, number or - * string). - */ - public boolean isPrimitive(); - - /** - * Return the string value of this primitive JSON element. - * - * This can not be used for JsonElementObject or JsonElementArray. - * - */ - public String toPrimitiveString(); - - public Object eval(String exp); - - public int evalInt(String exp); - - public String evalString(String exp); - - public boolean evalBoolean(String exp); - -} diff --git a/src/main/java/com/avaje/ebean/text/json/JsonElementArray.java b/src/main/java/com/avaje/ebean/text/json/JsonElementArray.java deleted file mode 100644 index 5a59e28bc..000000000 --- a/src/main/java/com/avaje/ebean/text/json/JsonElementArray.java +++ /dev/null @@ -1,112 +0,0 @@ -package com.avaje.ebean.text.json; - -import java.util.ArrayList; -import java.util.List; - -/** - * JSON Array element. - * - * You will only use the JsonElements when you register a JsonReadBeanVisitor. - * The JSON elements that are not mapped to a bean property are made available - * to the JsonReadBeanVisitor. - * - * - * @see JsonReadBeanVisitor - * - * @author rbygrave - */ -public class JsonElementArray implements JsonElement { - - private final List values = new ArrayList(); - - public List getValues() { - return values; - } - - public void add(JsonElement value) { - values.add(value); - } - - public String toString() { - return values.toString(); - } - - public boolean isPrimitive() { - return false; - } - - public String toPrimitiveString() { - return null; - } - - private String[] split(String exp) { - int pos = exp.indexOf('.'); - if (pos == -1) { - return new String[] { exp, null }; - } - String exp0 = exp.substring(0, pos); - String exp1 = exp.substring(pos + 1); - return new String[] { exp0, exp1 }; - } - - public Object eval(String exp) { - String[] e = split(exp); - return eval(e[0], e[1]); - } - - public int evalInt(String exp) { - String[] e = split(exp); - return evalInt(e[0], e[1]); - } - - public String evalString(String exp) { - String[] e = split(exp); - return evalString(e[0], e[1]); - } - - public boolean evalBoolean(String exp) { - // TODO Auto-generated method stub - return false; - } - - private Object eval(String exp0, String exp1) { - if ("size".equals(exp0)) { - return values.size(); - } - if ("isEmpty".equals(exp0)) { - return values.isEmpty(); - } - int idx = Integer.parseInt(exp0); - JsonElement element = values.get(idx); - return element.eval(exp1); - } - - private int evalInt(String exp0, String exp1) { - if ("size".equals(exp0)) { - return values.size(); - } - if ("isEmpty".equals(exp0)) { - return values.isEmpty() ? 1 : 0; - } - int idx = Integer.parseInt(exp0); - JsonElement element = values.get(idx); - return element.evalInt(exp1); - } - - private String evalString(String exp0, String exp1) { - if ("size".equals(exp0)) { - return String.valueOf(values.size()); - } - if ("isEmpty".equals(exp0)) { - return String.valueOf(values.isEmpty()); - } - int idx = Integer.parseInt(exp0); - JsonElement element = values.get(idx); - return element.evalString(exp1); - } - - public String getString() { - return toString(); - } - -} diff --git a/src/main/java/com/avaje/ebean/text/json/JsonElementBoolean.java b/src/main/java/com/avaje/ebean/text/json/JsonElementBoolean.java deleted file mode 100644 index 69132b8a2..000000000 --- a/src/main/java/com/avaje/ebean/text/json/JsonElementBoolean.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.avaje.ebean.text.json; - -/** - * JSON boolean element. - * - * You will only use the JsonElements when you register a JsonReadBeanVisitor. - * The JSON elements that are not mapped to a bean property are made available - * to the JsonReadBeanVisitor. - * - * - * @see JsonReadBeanVisitor - * - * @author rbygrave - */ - -public class JsonElementBoolean implements JsonElement { - - public static final JsonElementBoolean TRUE = new JsonElementBoolean(true); - - public static final JsonElementBoolean FALSE = new JsonElementBoolean(false); - - private final Boolean value; - - private JsonElementBoolean(Boolean value) { - this.value = value; - } - - public Boolean getValue() { - return value; - } - - public String toString() { - return Boolean.toString(value); - } - - public boolean isPrimitive() { - return true; - } - - public String toPrimitiveString() { - return value.toString(); - } - - public Object eval(String exp) { - if (exp != null) { - throw new IllegalArgumentException("expression [" + exp + "] not allowed on boolean"); - } - return value; - } - - public int evalInt(String exp) { - return value ? 1 : 0; - } - - public String evalString(String exp) { - return toString(); - } - - public boolean evalBoolean(String exp) { - return value; - } - -} diff --git a/src/main/java/com/avaje/ebean/text/json/JsonElementNull.java b/src/main/java/com/avaje/ebean/text/json/JsonElementNull.java deleted file mode 100644 index b38406a57..000000000 --- a/src/main/java/com/avaje/ebean/text/json/JsonElementNull.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.avaje.ebean.text.json; - -/** - * JSON null element. - * - * You will only use the JsonElements when you register a JsonReadBeanVisitor. - * The JSON elements that are not mapped to a bean property are made available - * to the JsonReadBeanVisitor. - * - * - * @see JsonReadBeanVisitor - * - * @author rbygrave - */ -public class JsonElementNull implements JsonElement { - - public static final JsonElementNull NULL = new JsonElementNull(); - - private JsonElementNull() { - } - - public String getValue() { - return "null"; - } - - public String toString() { - return "json null"; - } - - public boolean isPrimitive() { - return true; - } - - public String toPrimitiveString() { - return null; - } - - public Object eval(String exp) { - if (exp != null) { - throw new IllegalArgumentException("expression [" + exp + "] not allowed on null"); - } - return null; - } - - public int evalInt(String exp) { - return 0; - } - - public String evalString(String exp) { - return null; - } - - public boolean evalBoolean(String exp) { - return false; - } - -} diff --git a/src/main/java/com/avaje/ebean/text/json/JsonElementNumber.java b/src/main/java/com/avaje/ebean/text/json/JsonElementNumber.java deleted file mode 100644 index a778315ac..000000000 --- a/src/main/java/com/avaje/ebean/text/json/JsonElementNumber.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.avaje.ebean.text.json; - -/** - * JSON number element. - * - * You will only use the JsonElements when you register a JsonReadBeanVisitor. - * The JSON elements that are not mapped to a bean property are made available - * to the JsonReadBeanVisitor. - * - * - * @see JsonReadBeanVisitor - * - * @author rbygrave - */ -public class JsonElementNumber implements JsonElement { - - private final String value; - - public JsonElementNumber(String value) { - this.value = value; - } - - public String getValue() { - return value; - } - - public String toString() { - return value; - } - - public boolean isPrimitive() { - return true; - } - - public String toPrimitiveString() { - return value; - } - - public Object eval(String exp) { - if (exp != null) { - throw new IllegalArgumentException("expression [" + exp + "] not allowed on number"); - } - return Double.parseDouble(value); - } - - public int evalInt(String exp) { - if (exp != null) { - throw new IllegalArgumentException("expression [" + exp + "] not allowed on number"); - } - return Integer.parseInt(value); - } - - public String evalString(String exp) { - if (exp != null) { - throw new IllegalArgumentException("expression [" + exp + "] not allowed on number"); - } - return value; - } - - public boolean evalBoolean(String exp) { - if (exp != null) { - throw new IllegalArgumentException("expression [" + exp + "] not allowed on number"); - } - return Boolean.parseBoolean(value); - } - -} diff --git a/src/main/java/com/avaje/ebean/text/json/JsonElementObject.java b/src/main/java/com/avaje/ebean/text/json/JsonElementObject.java deleted file mode 100644 index 028158e16..000000000 --- a/src/main/java/com/avaje/ebean/text/json/JsonElementObject.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.avaje.ebean.text.json; - -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; - -/** - * JSON Object element. - * - * You will only use the JsonElements when you register a JsonReadBeanVisitor. - * The JSON elements that are not mapped to a bean property are made available - * to the JsonReadBeanVisitor. - * - * - * @see JsonReadBeanVisitor - * - * @author rbygrave - */ -public class JsonElementObject implements JsonElement { - - private final Map map = new LinkedHashMap(); - - public void put(String key, JsonElement value) { - map.put(key, value); - } - - private String[] split(String exp) { - int pos = exp.indexOf('.'); - if (pos == -1) { - return new String[] { exp, null }; - } - String exp0 = exp.substring(0, pos); - String exp1 = exp.substring(pos + 1); - return new String[] { exp0, exp1 }; - } - - public Object eval(String exp) { - String[] e = split(exp); - return eval(e[0], e[1]); - } - - public int evalInt(String exp) { - String[] e = split(exp); - return evalInt(e[0], e[1]); - } - - public String evalString(String exp) { - if (exp == null) { - return map.toString(); - } - String[] e = split(exp); - return evalString(e[0], e[1]); - } - - public boolean evalBoolean(String exp) { - String[] e = split(exp); - return evalBoolean(e[0], e[1]); - } - - private Object eval(String exp0, String exp1) { - JsonElement e = map.get(exp0); - return e == null ? null : e.eval(exp1); - } - - private int evalInt(String exp0, String exp1) { - JsonElement e = map.get(exp0); - return e == null ? 0 : e.evalInt(exp1); - } - - private String evalString(String exp0, String exp1) { - JsonElement e = map.get(exp0); - return e == null ? "" : e.evalString(exp1); - } - - private boolean evalBoolean(String exp0, String exp1) { - JsonElement e = map.get(exp0); - return e == null ? false : e.evalBoolean(exp1); - } - - public JsonElement get(String key) { - return map.get(key); - } - - public JsonElement getValue(String key) { - return map.get(key); - } - - public Set keySet() { - return map.keySet(); - } - - public Set> entrySet() { - return map.entrySet(); - } - - public String toString() { - return map.toString(); - } - - public boolean isPrimitive() { - return false; - } - - public String toPrimitiveString() { - return null; - } - -} diff --git a/src/main/java/com/avaje/ebean/text/json/JsonElementString.java b/src/main/java/com/avaje/ebean/text/json/JsonElementString.java deleted file mode 100644 index 0b76feafc..000000000 --- a/src/main/java/com/avaje/ebean/text/json/JsonElementString.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.avaje.ebean.text.json; - -/** - * JSON string element. - * - * You will only use the JsonElements when you register a JsonReadBeanVisitor. - * The JSON elements that are not mapped to a bean property are made available - * to the JsonReadBeanVisitor. - * - * - * @see JsonReadBeanVisitor - * - * @author rbygrave - */ -public class JsonElementString implements JsonElement { - - private final String value; - - public JsonElementString(String value) { - this.value = value; - } - - public String getValue() { - return value; - } - - public String toString() { - return value; - } - - public boolean isPrimitive() { - return true; - } - - public String toPrimitiveString() { - return value; - } - - public Object eval(String exp) { - if (exp != null) { - throw new IllegalArgumentException("expression [" + exp + "] not allowed on number"); - } - return value; - } - - public int evalInt(String exp) { - if (exp != null) { - throw new IllegalArgumentException("expression [" + exp + "] not allowed on number"); - } - try { - return Integer.parseInt(value); - } catch (NumberFormatException e) { - return 0; - } - } - - public String evalString(String exp) { - if (exp != null) { - throw new IllegalArgumentException("expression [" + exp + "] not allowed on number"); - } - return value; - } - - public boolean evalBoolean(String exp) { - if (exp != null) { - throw new IllegalArgumentException("expression [" + exp + "] not allowed on number"); - } - return Boolean.parseBoolean(exp); - } - -} diff --git a/src/main/java/com/avaje/ebean/text/json/JsonReadBeanVisitor.java b/src/main/java/com/avaje/ebean/text/json/JsonReadBeanVisitor.java deleted file mode 100644 index 6de15360c..000000000 --- a/src/main/java/com/avaje/ebean/text/json/JsonReadBeanVisitor.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.avaje.ebean.text.json; - -import java.util.Map; - -/** - * Provides for some custom handling of json content as it is read. - * - * This visit method is called after all the known properties of the bean have - * been processed. Any JSON elements that could not be mapped to known bean - * properties are available in the unmapped Map. - * - * - * @author rbygrave - * - * @param - * The type of entity bean - */ -public interface JsonReadBeanVisitor { - - /** - * Visit the bean that has just been processed. - * - * This provides a method of customising the bean and processing any custom - * JSON content. - * - * - * @param bean - * the bean being processed - * @param unmapped - * Map of any JSON elements that didn't map to known bean properties - */ - public void visit(T bean, Map unmapped); - -} diff --git a/src/main/java/com/avaje/ebean/text/json/JsonReadOptions.java b/src/main/java/com/avaje/ebean/text/json/JsonReadOptions.java deleted file mode 100644 index a6832fd21..000000000 --- a/src/main/java/com/avaje/ebean/text/json/JsonReadOptions.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.avaje.ebean.text.json; - -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * Provides the ability to customise the reading of JSON content. - * - * You can optionally provide a custom JsonValueAdapter to handle specific - * formatting for Date and DateTime types. - * - * - * You can optionally register JsonReadBeanVisitors to customise the processing - * of the beans as they are processed and handle any custom JSON elements that - * could not be mapped to bean properties. - * - * - * @author rbygrave - * - */ -public class JsonReadOptions { - - protected JsonValueAdapter valueAdapter; - - protected Map> visitorMap; - - /** - * Default constructor. - */ - public JsonReadOptions() { - this.visitorMap = new LinkedHashMap>(); - } - - /** - * Return the JsonValueAdapter. - */ - public JsonValueAdapter getValueAdapter() { - return valueAdapter; - } - - /** - * Return the map of JsonReadBeanVisitor's. - */ - public Map> getVisitorMap() { - return visitorMap; - } - - /** - * Set a JsonValueAdapter for custom DateTime and Date formatting. - */ - public JsonReadOptions setValueAdapter(JsonValueAdapter valueAdapter) { - this.valueAdapter = valueAdapter; - return this; - } - - /** - * Register a JsonReadBeanVisitor for the root level. - */ - public JsonReadOptions addRootVisitor(JsonReadBeanVisitor> visitor) { - return addVisitor(null, visitor); - } - - /** - * Register a JsonReadBeanVisitor for a given path. - */ - public JsonReadOptions addVisitor(String path, JsonReadBeanVisitor> visitor) { - visitorMap.put(path, visitor); - return this; - } - -} diff --git a/src/main/java/com/avaje/ebean/text/json/JsonValueAdapter.java b/src/main/java/com/avaje/ebean/text/json/JsonValueAdapter.java deleted file mode 100644 index a30dcf38c..000000000 --- a/src/main/java/com/avaje/ebean/text/json/JsonValueAdapter.java +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebean.text.json; - -import java.sql.Timestamp; - -/** - * Allows you to customise the Date and Timestamp formats. - * - * There is not a standard JSON format for Date or Timestamp types. By default - * Ebean uses ISO8601 "yyyy-MM-dd'T'HH:mm:ss.SSSZ" and "yyyy-MM-dd". - * - * - * Note that Ebean will convert Joda types to either of the Date or Timestamp - * types and back for you. - * - * - * @see JsonReadOptions - * - * @author rbygrave - */ -public interface JsonValueAdapter { - - /** - * Convert the Date to json string. - */ - public String jsonFromDate(java.sql.Date date); - - /** - * Convert the DateTime to json string. - */ - public String jsonFromTimestamp(java.sql.Timestamp date); - - /** - * Parse the JSON string into a Date. - */ - public java.sql.Date jsonToDate(String jsonDate); - - /** - * Parse the JSON DateTime into a Timestamp. - */ - public Timestamp jsonToTimestamp(String jsonDateTime); - -} diff --git a/src/main/java/com/avaje/ebean/text/json/JsonWriteBeanVisitor.java b/src/main/java/com/avaje/ebean/text/json/JsonWriteBeanVisitor.java deleted file mode 100644 index eb44cf788..000000000 --- a/src/main/java/com/avaje/ebean/text/json/JsonWriteBeanVisitor.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.avaje.ebean.text.json; - -/** - * Allows for customising the JSON write processing. - * - * You can use this to add raw JSON content via {@link JsonWriter}. - * - * - * You register a JsonWriteBeanVisitor with {@link JsonWriteOptions}. - * - * - * @author rbygrave - * - * @param - * the type of entity bean - * - * @see JsonWriteOptions - */ -public interface JsonWriteBeanVisitor { - - /** - * Visit the bean that has just been writing it's content to JSON. You can - * write your own additional JSON content to the JsonWriter if you wish. - * - * @param bean - * the bean that has been writing it's content - * @param jsonWriter - * the JsonWriter which you can append custom json content to if you - * wish. - */ - public void visit(T bean, JsonWriter jsonWriter); - -} diff --git a/src/main/java/com/avaje/ebean/text/json/JsonWriteOptions.java b/src/main/java/com/avaje/ebean/text/json/JsonWriteOptions.java index 8775f5001..d245725e0 100644 --- a/src/main/java/com/avaje/ebean/text/json/JsonWriteOptions.java +++ b/src/main/java/com/avaje/ebean/text/json/JsonWriteOptions.java @@ -1,8 +1,6 @@ package com.avaje.ebean.text.json; -import java.util.HashMap; import java.util.LinkedHashSet; -import java.util.Map; import java.util.Set; import com.avaje.ebean.text.PathProperties; @@ -69,10 +67,6 @@ public class JsonWriteOptions { protected String callback; - protected JsonValueAdapter valueAdapter; - - protected Map> visitorMap; - protected PathProperties pathProperties; /** @@ -99,11 +93,7 @@ public class JsonWriteOptions { public JsonWriteOptions copy() { JsonWriteOptions copy = new JsonWriteOptions(); copy.callback = callback; - copy.valueAdapter = valueAdapter; copy.pathProperties = pathProperties; - if (visitorMap != null) { - copy.visitorMap = new HashMap>(visitorMap); - } return copy; } @@ -122,39 +112,6 @@ public class JsonWriteOptions { return this; } - /** - * Return the JsonValueAdapter. - */ - public JsonValueAdapter getValueAdapter() { - return valueAdapter; - } - - /** - * Set a JsonValueAdapter for custom DateTime and Date formatting. - */ - public JsonWriteOptions setValueAdapter(JsonValueAdapter valueAdapter) { - this.valueAdapter = valueAdapter; - return this; - } - - /** - * Register a JsonWriteBeanVisitor for the root level. - */ - public JsonWriteOptions setRootPathVisitor(JsonWriteBeanVisitor> visitor) { - return setPathVisitor(null, visitor); - } - - /** - * Register a JsonWriteBeanVisitor for the given path. - */ - public JsonWriteOptions setPathVisitor(String path, JsonWriteBeanVisitor> visitor) { - if (visitorMap == null) { - visitorMap = new HashMap>(); - } - visitorMap.put(path, visitor); - return this; - } - /** * Set the properties to include in the JSON output for the given path. * @@ -213,13 +170,6 @@ public class JsonWriteOptions { return props; } - /** - * Return the Map of registered JsonWriteBeanVisitor's by path. - */ - public Map> getVisitorMap() { - return visitorMap; - } - /** * Set the Map of properties to include by path. */ diff --git a/src/main/java/com/avaje/ebean/text/json/JsonWriter.java b/src/main/java/com/avaje/ebean/text/json/JsonWriter.java deleted file mode 100644 index 58d8dd754..000000000 --- a/src/main/java/com/avaje/ebean/text/json/JsonWriter.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.avaje.ebean.text.json; - -/** - * The JSON Writer made available to JsonWriteBeanVisitor's so that you can - * append your own JSON content into the output. - * - * @see JsonWriteBeanVisitor - * @see JsonWriteOptions#setRootPathVisitor(JsonWriteBeanVisitor) - * @see JsonWriteOptions#setPathVisitor(String, JsonWriteBeanVisitor) - * - * @author rbygrave - */ -public interface JsonWriter { - - /** - * Use this to append some custom content into the JSON output. - * - * @param key - * the json key - * - * @param rawJsonValue - * raw json value - */ - public void appendRawValue(String key, String rawJsonValue); - - public void appendQuoteEscapeValue(String key, String rawJsonValue); - -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java index fedada9b6..a59d768d7 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java @@ -65,7 +65,6 @@ import com.avaje.ebean.meta.MetaBeanInfo; import com.avaje.ebean.meta.MetaInfoManager; import com.avaje.ebean.text.csv.CsvReader; import com.avaje.ebean.text.json.JsonContext; -import com.avaje.ebean.text.json.JsonElement; import com.avaje.ebeaninternal.api.LoadBeanRequest; import com.avaje.ebeaninternal.api.LoadManyRequest; import com.avaje.ebeaninternal.api.ScopeTrans; @@ -1963,10 +1962,6 @@ public final class DefaultServer implements SpiEbeanServer { if (typeInfo == null) { return false; } - Class> beanType = typeInfo.getBeanType(); - if (JsonElement.class.isAssignableFrom(beanType)) { - return true; - } return getBeanDescriptor(typeInfo.getBeanType()) != null; } diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java b/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java index e6f2c404a..79aec6675 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java @@ -11,8 +11,6 @@ import com.avaje.ebean.config.ExternalTransactionManager; import com.avaje.ebean.config.ServerConfig; import com.avaje.ebean.config.dbplatform.DatabasePlatform; import com.avaje.ebean.text.json.JsonContext; -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebeaninternal.api.ClassUtil; import com.avaje.ebeaninternal.api.SpiBackgroundExecutor; import com.avaje.ebeaninternal.api.SpiEbeanServer; import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager; @@ -33,7 +31,6 @@ import com.avaje.ebeaninternal.server.query.DefaultRelationalQueryEngine; import com.avaje.ebeaninternal.server.resource.ResourceManager; import com.avaje.ebeaninternal.server.resource.ResourceManagerFactory; import com.avaje.ebeaninternal.server.text.json.DJsonContext; -import com.avaje.ebeaninternal.server.text.json.DefaultJsonValueAdapter; import com.avaje.ebeaninternal.server.transaction.AutoCommitTransactionManager; import com.avaje.ebeaninternal.server.transaction.DefaultTransactionScopeManager; import com.avaje.ebeaninternal.server.transaction.ExternalTransactionScopeManager; @@ -46,6 +43,8 @@ import com.avaje.ebeaninternal.server.type.TypeManager; /** * Used to extend the ServerConfig with additional objects used to configure and * construct an EbeanServer. + * + * @author rbygrave */ public class InternalConfiguration { @@ -163,16 +162,8 @@ public class InternalConfiguration { public JsonContext createJsonContext(SpiEbeanServer server) { - String s = serverConfig.getProperty("json.pretty", "false"); - boolean dfltPretty = "true".equalsIgnoreCase(s); - - s = serverConfig.getProperty("json.jsonValueAdapter", null); - - JsonValueAdapter va = new DefaultJsonValueAdapter(); - if (s != null) { - va = (JsonValueAdapter) ClassUtil.newInstance(s, this.getClass()); - } - return new DJsonContext(server, va, dfltPretty); + + return new DJsonContext(server); } public XmlConfig getXmlConfig() { diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java.orig b/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java.orig new file mode 100644 index 000000000..9d35dcd0a --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java.orig @@ -0,0 +1,266 @@ +package com.avaje.ebeaninternal.server.core; + +import javax.sql.DataSource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.avaje.ebean.ExpressionFactory; +import com.avaje.ebean.cache.ServerCacheManager; +import com.avaje.ebean.config.ExternalTransactionManager; +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebean.config.dbplatform.DatabasePlatform; +import com.avaje.ebean.text.json.JsonContext; +import com.avaje.ebeaninternal.api.SpiBackgroundExecutor; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager; +import com.avaje.ebeaninternal.server.autofetch.AutoFetchManagerFactory; +import com.avaje.ebeaninternal.server.cluster.ClusterManager; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; +import com.avaje.ebeaninternal.server.deploy.DeployOrmXml; +import com.avaje.ebeaninternal.server.deploy.parse.DeployCreateProperties; +import com.avaje.ebeaninternal.server.deploy.parse.DeployInherit; +import com.avaje.ebeaninternal.server.deploy.parse.DeployUtil; +import com.avaje.ebeaninternal.server.expression.DefaultExpressionFactory; +import com.avaje.ebeaninternal.server.lib.sql.DataSourcePool; +import com.avaje.ebeaninternal.server.persist.Binder; +import com.avaje.ebeaninternal.server.persist.DefaultPersister; +import com.avaje.ebeaninternal.server.query.CQueryEngine; +import com.avaje.ebeaninternal.server.query.DefaultOrmQueryEngine; +import com.avaje.ebeaninternal.server.query.DefaultRelationalQueryEngine; +import com.avaje.ebeaninternal.server.resource.ResourceManager; +import com.avaje.ebeaninternal.server.resource.ResourceManagerFactory; +import com.avaje.ebeaninternal.server.text.json.DJsonContext; +<<<<<<< HEAD +import com.avaje.ebeaninternal.server.text.json.DefaultJsonValueAdapter; +import com.avaje.ebeaninternal.server.transaction.AutoCommitTransactionManager; +======= +>>>>>>> json-refactor +import com.avaje.ebeaninternal.server.transaction.DefaultTransactionScopeManager; +import com.avaje.ebeaninternal.server.transaction.ExternalTransactionScopeManager; +import com.avaje.ebeaninternal.server.transaction.JtaTransactionManager; +import com.avaje.ebeaninternal.server.transaction.TransactionManager; +import com.avaje.ebeaninternal.server.transaction.TransactionScopeManager; +import com.avaje.ebeaninternal.server.type.DefaultTypeManager; +import com.avaje.ebeaninternal.server.type.TypeManager; + +/** + * Used to extend the ServerConfig with additional objects used to configure and + * construct an EbeanServer. + */ +public class InternalConfiguration { + + private static final Logger logger = LoggerFactory.getLogger(InternalConfiguration.class); + + private final ServerConfig serverConfig; + + private final BootupClasses bootupClasses; + + private final DeployInherit deployInherit; + + private final ResourceManager resourceManager; + + private final DeployOrmXml deployOrmXml; + + private final TypeManager typeManager; + + private final Binder binder; + + private final DeployCreateProperties deployCreateProperties; + + private final DeployUtil deployUtil; + + private final BeanDescriptorManager beanDescriptorManager; + + private final TransactionManager transactionManager; + + private final TransactionScopeManager transactionScopeManager; + + private final CQueryEngine cQueryEngine; + + private final ClusterManager clusterManager; + + private final ServerCacheManager cacheManager; + + private final ExpressionFactory expressionFactory; + + private final SpiBackgroundExecutor backgroundExecutor; + + private final PstmtBatch pstmtBatch; + + private final XmlConfig xmlConfig; + + public InternalConfiguration(XmlConfig xmlConfig, ClusterManager clusterManager, + ServerCacheManager cacheManager, SpiBackgroundExecutor backgroundExecutor, + ServerConfig serverConfig, BootupClasses bootupClasses, PstmtBatch pstmtBatch) { + + this.xmlConfig = xmlConfig; + this.pstmtBatch = pstmtBatch; + this.clusterManager = clusterManager; + this.backgroundExecutor = backgroundExecutor; + this.cacheManager = cacheManager; + this.serverConfig = serverConfig; + this.bootupClasses = bootupClasses; + this.expressionFactory = new DefaultExpressionFactory(); + + this.typeManager = new DefaultTypeManager(serverConfig, bootupClasses); + this.binder = new Binder(typeManager); + + this.resourceManager = ResourceManagerFactory.createResourceManager(serverConfig); + this.deployOrmXml = new DeployOrmXml(resourceManager.getResourceSource()); + this.deployInherit = new DeployInherit(bootupClasses); + + this.deployCreateProperties = new DeployCreateProperties(typeManager); + this.deployUtil = new DeployUtil(typeManager, serverConfig); + + this.beanDescriptorManager = new BeanDescriptorManager(this); + beanDescriptorManager.deploy(); + + this.transactionManager = createTransactionManager(); + + this.cQueryEngine = new CQueryEngine(serverConfig.getDatabasePlatform(), binder); + + ExternalTransactionManager externalTransactionManager = serverConfig.getExternalTransactionManager(); + if (externalTransactionManager == null && serverConfig.isUseJtaTransactionManager()) { + externalTransactionManager = new JtaTransactionManager(); + } + if (externalTransactionManager != null) { + externalTransactionManager.setTransactionManager(transactionManager); + this.transactionScopeManager = new ExternalTransactionScopeManager(transactionManager, externalTransactionManager); + logger.info("Using Transaction Manager [" + externalTransactionManager.getClass() + "]"); + } else { + this.transactionScopeManager = new DefaultTransactionScopeManager(transactionManager); + } + + } + + /** + * Create the TransactionManager taking into account autoCommit mode. + */ + private TransactionManager createTransactionManager() { + + if (isAutoCommitMode()) { + return new AutoCommitTransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager, this.getBootupClasses()); + } + + return new TransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager, this.getBootupClasses()); + } + + /** + * Return true if autoCommit mode is on. + */ + private boolean isAutoCommitMode() { + if (serverConfig.isAutoCommitMode()) { + // explicitly set + return true; + } + DataSource dataSource = serverConfig.getDataSource(); + if (dataSource instanceof DataSourcePool && ((DataSourcePool)dataSource).getAutoCommit()) { + // We know the DataSourcePool is using autoCommit + return true; + } + return false; + } + + public JsonContext createJsonContext(SpiEbeanServer server) { + + return new DJsonContext(server); + } + + public XmlConfig getXmlConfig() { + return xmlConfig; + } + + public AutoFetchManager createAutoFetchManager(SpiEbeanServer server) { + return AutoFetchManagerFactory.create(server, serverConfig, resourceManager); + } + + public RelationalQueryEngine createRelationalQueryEngine() { + return new DefaultRelationalQueryEngine(binder, serverConfig.getDatabaseBooleanTrue()); + } + + public OrmQueryEngine createOrmQueryEngine() { + return new DefaultOrmQueryEngine(beanDescriptorManager, cQueryEngine); + } + + public Persister createPersister(SpiEbeanServer server) { + return new DefaultPersister(server, binder, beanDescriptorManager, pstmtBatch); + } + + public PstmtBatch getPstmtBatch() { + return pstmtBatch; + } + + public ServerCacheManager getCacheManager() { + return cacheManager; + } + + public BootupClasses getBootupClasses() { + return bootupClasses; + } + + public DatabasePlatform getDatabasePlatform() { + return serverConfig.getDatabasePlatform(); + } + + public ServerConfig getServerConfig() { + return serverConfig; + } + + public ExpressionFactory getExpressionFactory() { + return expressionFactory; + } + + public TypeManager getTypeManager() { + return typeManager; + } + + public Binder getBinder() { + return binder; + } + + public BeanDescriptorManager getBeanDescriptorManager() { + return beanDescriptorManager; + } + + public DeployInherit getDeployInherit() { + return deployInherit; + } + + public ResourceManager getResourceManager() { + return resourceManager; + } + + public DeployOrmXml getDeployOrmXml() { + return deployOrmXml; + } + + public DeployCreateProperties getDeployCreateProperties() { + return deployCreateProperties; + } + + public DeployUtil getDeployUtil() { + return deployUtil; + } + + public TransactionManager getTransactionManager() { + return transactionManager; + } + + public TransactionScopeManager getTransactionScopeManager() { + return transactionScopeManager; + } + + public CQueryEngine getCQueryEngine() { + return cQueryEngine; + } + + public ClusterManager getClusterManager() { + return clusterManager; + } + + public SpiBackgroundExecutor getBackgroundExecutor() { + return backgroundExecutor; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanCollectionHelp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanCollectionHelp.java index 711d1496c..842e0545f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanCollectionHelp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanCollectionHelp.java @@ -9,7 +9,7 @@ import com.avaje.ebean.bean.BeanCollection; import com.avaje.ebean.bean.BeanCollectionAdd; import com.avaje.ebean.bean.BeanCollectionLoader; import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebeaninternal.server.text.json.WriteJsonContext; +import com.avaje.ebeaninternal.server.text.json.WriteJson; /** * Helper functions for performing tasks on Lists Sets or Maps. @@ -62,6 +62,6 @@ public interface BeanCollectionHelp { /** * Write the collection out as json. */ - public void jsonWrite(WriteJsonContext ctx, String name, Object collection, boolean explicitInclude); + public void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java index 90152549f..67eadf243 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java @@ -13,6 +13,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import javax.json.stream.JsonParser; import javax.persistence.PersistenceException; import org.slf4j.Logger; @@ -34,8 +35,6 @@ import com.avaje.ebean.event.BeanPersistListener; import com.avaje.ebean.event.BeanQueryAdapter; import com.avaje.ebean.meta.MetaBeanInfo; import com.avaje.ebean.meta.MetaQueryPlanStatistic; -import com.avaje.ebean.text.TextException; -import com.avaje.ebean.text.json.JsonWriteBeanVisitor; import com.avaje.ebeaninternal.api.HashQueryPlan; import com.avaje.ebeaninternal.api.SpiEbeanServer; import com.avaje.ebeaninternal.api.SpiQuery; @@ -62,10 +61,7 @@ import com.avaje.ebeaninternal.server.query.CQueryPlan; import com.avaje.ebeaninternal.server.query.CQueryPlanStats.Snapshot; import com.avaje.ebeaninternal.server.query.SplitName; import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail; -import com.avaje.ebeaninternal.server.text.json.ReadJsonContext; -import com.avaje.ebeaninternal.server.text.json.ReadJsonContext.ReadBeanState; -import com.avaje.ebeaninternal.server.text.json.WriteJsonContext; -import com.avaje.ebeaninternal.server.text.json.WriteJsonContext.WriteBeanState; +import com.avaje.ebeaninternal.server.text.json.WriteJson; import com.avaje.ebeaninternal.server.type.DataBind; import com.avaje.ebeaninternal.server.type.TypeManager; import com.avaje.ebeaninternal.util.SortByClause; @@ -195,12 +191,12 @@ public class BeanDescriptor implements MetaBeanInfo { /** * Inheritance information. Server side only. */ - private final InheritInfo inheritInfo; + protected final InheritInfo inheritInfo; /** * Derived list of properties that make up the unique id. */ - private final BeanProperty idProperty; + protected final BeanProperty idProperty; private final int idPropertyIndex; /** @@ -327,7 +323,8 @@ public class BeanDescriptor implements MetaBeanInfo { private final boolean cacheSharableBeans; private final BeanDescriptorCacheHelp cacheHelp; - + private final BeanDescriptorJsonHelp jsonHelp; + private final String defaultSelectClause; private final Set defaultSelectClauseSet; @@ -422,7 +419,7 @@ public class BeanDescriptor implements MetaBeanInfo { this.cacheSharableBeans = noRelationships && deploy.getCacheOptions().isReadOnly(); this.cacheHelp = new BeanDescriptorCacheHelp(this, owner.getCacheManager(), deploy.getCacheOptions(), cacheSharableBeans, propertiesOneImported); - + this.jsonHelp = new BeanDescriptorJsonHelp(this); // Check if there are no cascade save associated beans ( subject to change // in initialiseOther()). Note that if we are in an inheritance hierarchy @@ -2115,167 +2112,23 @@ public class BeanDescriptor implements MetaBeanInfo { return propertiesLocal; } - public void jsonWrite(WriteJsonContext ctx, EntityBean bean) { - - if (bean != null) { - - ctx.appendObjectBegin(); - WriteBeanState prevState = ctx.pushBeanState(bean); - - if (inheritInfo != null) { - InheritInfo localInheritInfo = inheritInfo.readType(bean.getClass()); - String discValue = localInheritInfo.getDiscriminatorStringValue(); - String discColumn = localInheritInfo.getDiscriminatorColumn(); - ctx.appendDiscriminator(discColumn, discValue); - - BeanDescriptor> localDescriptor = localInheritInfo.getBeanDescriptor(); - localDescriptor.jsonWriteProperties(ctx, bean); - - } else { - jsonWriteProperties(ctx, bean); - } - - ctx.pushPreviousState(prevState); - ctx.appendObjectEnd(); - } - } - - @SuppressWarnings("unchecked") - private void jsonWriteProperties(WriteJsonContext ctx, EntityBean bean) { - - JsonWriteBeanVisitor beanVisitor = (JsonWriteBeanVisitor) ctx.getBeanVisitor(); - - Set props = ctx.getIncludeProperties(); - - boolean explicitAllProps; - if (props == null) { - explicitAllProps = false; - } else { - explicitAllProps = props.contains("*"); - if (explicitAllProps || props.isEmpty()) { - props = null; - } - } - - if (idProperty != null) { - Object idValue = idProperty.getValue(bean); - if (idValue != null) { - if (props == null || props.contains(idProperty.getName())) { - idProperty.jsonWrite(ctx, bean); - } - } - } - - if (!explicitAllProps && props == null) { - // just render the loaded properties - props = ((EntityBean)bean)._ebean_getIntercept().getLoadedPropertyNames(); - } - if (props != null) { - // render only the appropriate properties (when not all properties) - for (String prop : props) { - BeanProperty p = getBeanProperty(prop); - if (p != null && !p.isId()) { - p.jsonWrite(ctx, bean); - } - } - } else { - if (explicitAllProps || !isReference(bean._ebean_getIntercept())) { - // render all the properties and invoke lazy loading if required - for (int j = 0; j < propertiesNonTransient.length; j++) { - propertiesNonTransient[j].jsonWrite(ctx, bean); - } - for (int j = 0; j < propertiesTransient.length; j++) { - propertiesTransient[j].jsonWrite(ctx, bean); - } - } - } - - if (beanVisitor != null) { - beanVisitor.visit((T) bean, ctx); - } - } - - @SuppressWarnings("unchecked") - public T jsonReadBean(ReadJsonContext ctx, String path) { - ReadBeanState beanState = jsonRead(ctx, path); - if (beanState == null) { - return null; - } else { - return (T) beanState.getBean(); - } - } - - public ReadBeanState jsonRead(ReadJsonContext ctx, String path) { - if (!ctx.readObjectBegin()) { - // the object is null - return null; - } - - if (inheritInfo == null) { - return jsonReadObject(ctx, path); - - } else { - - // check for the discriminator value to determine the correct sub type - String discColumn = inheritInfo.getRoot().getDiscriminatorColumn(); - - if (!ctx.readKeyNext()) { - String msg = "Error reading inheritance discriminator - expected [" + discColumn + "] but no json key?"; - throw new TextException(msg); - } - - String propName = ctx.getTokenKey(); - String discValue; - if (propName.equalsIgnoreCase(discColumn)) { - discValue = ctx.readScalarValue(); - if (!ctx.readValueNext()) { - // Expected to read a comma to setup for reading the real properties of the bean - String msg = "Error reading inheritance discriminator [" + discColumn + "]. Expected more json name values?"; - throw new TextException(msg); - } - - } else { - // Assume that the we are just reading using this bean type - // Push the token key back so that it is re-read as it is one - // of the real properties of the bean itself - ctx.pushTokenKey(); - discValue = inheritInfo.getDiscriminatorStringValue(); - } - - // determine the sub type for this particular json object - InheritInfo localInheritInfo = inheritInfo.readType(discValue); - BeanDescriptor> localDescriptor = localInheritInfo.getBeanDescriptor(); - return localDescriptor.jsonReadObject(ctx, path); - } - } + public void jsonWrite(WriteJson writeJson, EntityBean bean) { + jsonHelp.jsonWrite(writeJson, bean, null); + } - private ReadBeanState jsonReadObject(ReadJsonContext ctx, String path) { + public void jsonWrite(WriteJson writeJson, EntityBean bean, String key) { + jsonHelp.jsonWrite(writeJson, bean, key); + } - EntityBean bean = createEntityBean(); - ctx.pushBean(bean, path, this); - - do { - if (!ctx.readKeyNext()) { - break; - } else { - // we read a property key ... - String propName = ctx.getTokenKey(); - BeanProperty p = getBeanProperty(propName); - if (p != null) { - p.jsonRead(ctx, bean); - ctx.setProperty(propName); - } else { - // unknown property key ... - ctx.readUnmappedJson(propName); - } - - if (!ctx.readValueNext()) { - break; - } - } - } while (true); - - return ctx.popBeanState(); + protected void jsonWriteProperties(WriteJson writeJson, EntityBean bean) { + jsonHelp.jsonWriteProperties(writeJson, bean); } + public T jsonRead(JsonParser parser, String path) { + return jsonHelp.jsonRead(parser, path); + } + + protected T jsonReadObject(JsonParser parser, String path) { + return jsonHelp.jsonReadObject(parser, path); + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorJsonHelp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorJsonHelp.java new file mode 100644 index 000000000..b6540e7db --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorJsonHelp.java @@ -0,0 +1,145 @@ +package com.avaje.ebeaninternal.server.deploy; + +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.text.TextException; +import com.avaje.ebeaninternal.server.text.json.WriteJson; +import com.avaje.ebeaninternal.server.text.json.WriteJson.WriteBean; + +public class BeanDescriptorJsonHelp { + + private final BeanDescriptor desc; + + private final InheritInfo inheritInfo; + + public BeanDescriptorJsonHelp(BeanDescriptor desc) { + this.desc = desc; + this.inheritInfo = desc.inheritInfo; + } + + public void jsonWrite(WriteJson writeJson, EntityBean bean, String key) { + +// if (writeJson.hasBean()) { + + writeJson.writeStartObject(key); + //WriteBeanState prevState = ctx.pushBeanState(bean); + + if (inheritInfo == null) { + jsonWriteProperties(writeJson, bean); + + } else { + InheritInfo localInheritInfo = inheritInfo.readType(bean.getClass()); + String discValue = localInheritInfo.getDiscriminatorStringValue(); + String discColumn = localInheritInfo.getDiscriminatorColumn(); + writeJson.gen().write(discColumn, discValue); + + BeanDescriptor> localDescriptor = localInheritInfo.getBeanDescriptor(); + localDescriptor.jsonWriteProperties(writeJson, bean); + } + + //ctx.pushPreviousState(prevState); + writeJson.gen().writeEnd(); + } + + protected void jsonWriteProperties(WriteJson writeJson, EntityBean bean) { + + + WriteBean writeBean = writeJson.createWriteBean(desc, bean); + writeBean.write(writeJson); + } + + + @SuppressWarnings("unchecked") + public T jsonRead(JsonParser parser, String path) { + + if (!parser.hasNext()) { + return null; + } + Event event = parser.next(); + if (Event.VALUE_NULL == event || Event.END_ARRAY == event) { + return null; + } + if (Event.START_OBJECT != event) { + throw new RuntimeException("Unexpected token "+event+" - expecting start_object at: "+parser.getLocation()); + } + + if (desc.inheritInfo == null) { + return jsonReadObject(parser, path); + } + + // check for the discriminator value to determine the correct sub type + String discColumn = inheritInfo.getRoot().getDiscriminatorColumn(); + + if (!parser.hasNext() || ((event = parser.next()) != Event.KEY_NAME)) { + String msg = "Error reading inheritance discriminator - expected [" + discColumn + "] but no json key?"; + throw new TextException(msg); + } + + String propName = parser.getString(); + if (!propName.equalsIgnoreCase(discColumn)) { + // just try to assume this is the correct bean type in the inheritance + BeanProperty property = desc.getBeanProperty(propName); + if (property != null) { + EntityBean bean = desc.createEntityBean(); + property.jsonRead(parser, bean); + return jsonReadProperties(parser, bean); + } + String msg = "Error reading inheritance discriminator, expected property ["+discColumn+"] but got [" + propName + "] ?"; + throw new TextException(msg); + } + + if (!parser.hasNext() || ((event = parser.next()) != Event.VALUE_STRING)) { + String msg = "Error reading inheritance discriminator - expected value_string token but got [" + event + "] at ["+parser.getLocation()+"]?"; + throw new TextException(msg); + } + + String discValue = parser.getString(); + + // determine the sub type for this particular json object + InheritInfo localInheritInfo = inheritInfo.readType(discValue); + BeanDescriptor> localDescriptor = localInheritInfo.getBeanDescriptor(); + return (T) localDescriptor.jsonReadObject(parser, path); + } + + protected T jsonReadObject(JsonParser parser, String path) { + + EntityBean bean = desc.createEntityBean(); + //ctx.pushBean(bean, path, this); + + return jsonReadProperties(parser, bean); + } + + @SuppressWarnings("unchecked") + protected T jsonReadProperties(JsonParser parser, EntityBean bean) { + + do { + + if (parser.hasNext()) { + Event event = parser.next(); + if (Event.KEY_NAME == event) { + String key = parser.getString(); + BeanProperty p = desc.getBeanProperty(key); + if (p != null) { + p.jsonRead(parser, bean); + + } else { + //Object rawValue = EJson.parse(parser); + // unknown property key ... + //ctx.readUnmappedJson(propName); + } + + } else if (Event.END_OBJECT == event) { + break; + + } else { + throw new RuntimeException("Unexpected token "+event+" - expecting key or end_object at: "+parser.getLocation()); + } + } + + } while (true); + return (T)bean; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanListHelp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanListHelp.java index 25baefd49..71860bbd6 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanListHelp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanListHelp.java @@ -12,7 +12,7 @@ import com.avaje.ebean.bean.BeanCollectionAdd; import com.avaje.ebean.bean.BeanCollectionLoader; import com.avaje.ebean.bean.EntityBean; import com.avaje.ebean.common.BeanList; -import com.avaje.ebeaninternal.server.text.json.WriteJsonContext; +import com.avaje.ebeaninternal.server.text.json.WriteJson; /** * Helper object for dealing with Lists. @@ -128,7 +128,7 @@ public final class BeanListHelp implements BeanCollectionHelp { } } - public void jsonWrite(WriteJsonContext ctx, String name, Object collection, boolean explicitInclude) { + public void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) { List> list; if (collection instanceof BeanCollection>) { @@ -147,15 +147,11 @@ public final class BeanListHelp implements BeanCollectionHelp { list = (List>) collection; } - ctx.beginAssocMany(name); + ctx.gen().writeStartArray(name); for (int j = 0; j < list.size(); j++) { - if (j > 0) { - ctx.appendComma(); - } - Object detailBean = list.get(j); - targetDescriptor.jsonWrite(ctx, (EntityBean)detailBean); + targetDescriptor.jsonWrite(ctx, (EntityBean)list.get(j)); } - ctx.endAssocMany(); + ctx.gen().writeEnd(); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanMapHelp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanMapHelp.java index 316aa2106..b4c6eb4bb 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanMapHelp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanMapHelp.java @@ -13,7 +13,7 @@ import com.avaje.ebean.bean.BeanCollectionAdd; import com.avaje.ebean.bean.BeanCollectionLoader; import com.avaje.ebean.bean.EntityBean; import com.avaje.ebean.common.BeanMap; -import com.avaje.ebeaninternal.server.text.json.WriteJsonContext; +import com.avaje.ebeaninternal.server.text.json.WriteJson; /** * Helper specifically for dealing with Maps. @@ -156,7 +156,7 @@ public final class BeanMapHelp implements BeanCollectionHelp { } } - public void jsonWrite(WriteJsonContext ctx, String name, Object collection, boolean explicitInclude) { + public void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) { Map,?> map; if (collection instanceof BeanCollection>){ @@ -175,19 +175,14 @@ public final class BeanMapHelp implements BeanCollectionHelp { map = (Map,?>)collection; } - int count = 0; - ctx.beginAssocMany(name); + ctx.gen().writeStartArray(name); Iterator> it = map.entrySet().iterator(); while (it.hasNext()) { Entry, ?> entry = (Entry, ?>)it.next(); - if (count++ > 0){ - ctx.appendComma(); - } //FIXME: json write map key ... - Object detailBean = entry.getValue(); - targetDescriptor.jsonWrite(ctx, (EntityBean)detailBean); + targetDescriptor.jsonWrite(ctx, (EntityBean) entry.getValue()); } - ctx.endAssocMany(); + ctx.gen().writeEnd(); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java index 58f4f5641..85315b389 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java @@ -10,6 +10,8 @@ import java.sql.Types; import java.util.List; import java.util.Map; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; import javax.persistence.PersistenceException; import com.avaje.ebean.bean.EntityBean; @@ -18,7 +20,6 @@ import com.avaje.ebean.config.dbplatform.DbEncryptFunction; import com.avaje.ebean.config.dbplatform.DbType; import com.avaje.ebean.text.StringFormatter; import com.avaje.ebean.text.StringParser; -import com.avaje.ebean.text.TextException; import com.avaje.ebeaninternal.server.core.InternString; import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty; @@ -30,8 +31,7 @@ import com.avaje.ebeaninternal.server.query.SqlBeanLoad; import com.avaje.ebeaninternal.server.query.SqlJoinType; import com.avaje.ebeaninternal.server.reflect.BeanReflectGetter; import com.avaje.ebeaninternal.server.reflect.BeanReflectSetter; -import com.avaje.ebeaninternal.server.text.json.ReadJsonContext; -import com.avaje.ebeaninternal.server.text.json.WriteJsonContext; +import com.avaje.ebeaninternal.server.text.json.WriteJson; import com.avaje.ebeaninternal.server.type.DataBind; import com.avaje.ebeaninternal.server.type.ScalarType; @@ -78,7 +78,7 @@ public class BeanProperty implements ElPropertyValue { * Flag set if this maps to the inheritance discriminator column */ final boolean discriminator; - + /** * Flag to mark the property as embedded. This could be on * BeanPropertyAssocOne rather than here. Put it here for checking Id type @@ -92,7 +92,7 @@ public class BeanProperty implements ElPropertyValue { final boolean version; final boolean naturalKey; - + /** * Set if this property is nullable. */ @@ -136,7 +136,7 @@ public class BeanProperty implements ElPropertyValue { * True if the property is a Clob, Blob LongVarchar or LongVarbinary. */ final boolean lob; - + final boolean fetchEager; final boolean isTransient; @@ -147,7 +147,7 @@ public class BeanProperty implements ElPropertyValue { final String name; final int propertyIndex; - + /** * The reflected field. */ @@ -265,7 +265,6 @@ public class BeanProperty implements ElPropertyValue { final boolean indexed; final String indexName; - public BeanProperty(DeployBeanProperty deploy) { this(null, null, deploy); } @@ -275,10 +274,8 @@ public class BeanProperty implements ElPropertyValue { this.descriptor = descriptor; this.name = InternString.intern(deploy.getName()); this.propertyIndex = deploy.getPropertyIndex(); - this.indexed = deploy.isIndexed(); this.indexName = deploy.getIndexName(); - this.unidirectionalShadow = deploy.isUndirectionalShadow(); this.discriminator = deploy.isDiscriminator(); this.localEncrypted = deploy.isLocalEncrypted(); @@ -333,7 +330,7 @@ public class BeanProperty implements ElPropertyValue { this.lob = isLobType(dbType); this.propertyType = deploy.getPropertyType(); this.field = deploy.getField(); - + EntityType et = descriptor == null ? null : descriptor.getEntityType(); this.elPlaceHolder = tableAliasIntern(descriptor, deploy.getElPlaceHolder(et), false, null); this.elPlaceHolderEncrypted = tableAliasIntern(descriptor, deploy.getElPlaceHolder(et), dbEncrypted, dbColumn); @@ -371,7 +368,6 @@ public class BeanProperty implements ElPropertyValue { this.indexed = source.isIndexed(); this.indexName = source.getIndexName(); - this.dbColumn = InternString.intern(override.getDbColumn()); this.sqlFormulaJoin = InternString.intern(override.getSqlFormulaJoin()); this.sqlFormulaSelect = InternString.intern(override.getSqlFormulaSelect()); @@ -420,7 +416,7 @@ public class BeanProperty implements ElPropertyValue { this.lob = isLobType(dbType); this.propertyType = source.getPropertyType(); this.field = source.getField(); - + this.elPlaceHolder = override.replace(source.elPlaceHolder, source.dbColumn); this.elPlaceHolderEncrypted = override.replace(source.elPlaceHolderEncrypted, source.dbColumn); @@ -487,7 +483,7 @@ public class BeanProperty implements ElPropertyValue { public boolean isDiscriminator() { return discriminator; } - + /** * Return true if the underlying type is mutable. */ @@ -675,6 +671,14 @@ public class BeanProperty implements ElPropertyValue { public BeanProperty getBeanProperty() { return this; } + + public boolean isIndexed() { + return indexed; + } + + public String getIndexName() { + return indexName; + } /** * Return the getter method. @@ -737,12 +741,12 @@ public class BeanProperty implements ElPropertyValue { public Object getCacheDataValue(EntityBean bean) { return getValue(bean); - } + } public void setCacheDataValue(EntityBean bean, Object cacheData) { setValue(bean, cacheData); } - + /** * Return the value of the property method. */ @@ -755,12 +759,12 @@ public class BeanProperty implements ElPropertyValue { throw new RuntimeException(msg, ex); } } - + /** * Explicitly use reflection to get value. */ public Object getValueViaReflection(Object bean) { - try { + try { return readMethod.invoke(bean, NO_ARGS); } catch (Exception ex) { String beanType = bean == null ? "null" : bean.getClass().getName(); @@ -815,7 +819,7 @@ public class BeanProperty implements ElPropertyValue { * Return the position of this property in the enhanced bean. */ public int getPropertyIndex() { - return propertyIndex; + return propertyIndex; } public String getElName() { @@ -829,7 +833,6 @@ public class BeanProperty implements ElPropertyValue { return false; } - @Override public boolean containsFormulaWithJoin() { return formula && sqlFormulaJoin != null; @@ -895,7 +898,7 @@ public class BeanProperty implements ElPropertyValue { public boolean isDirtyValue(Object value) { return scalarType.isDirty(value); } - + /** * Return the scalarType. */ @@ -914,7 +917,7 @@ public class BeanProperty implements ElPropertyValue { public boolean isDateTimeCapable() { return scalarType != null && scalarType.isDateTimeCapable(); } - + public int getJdbcType() { return scalarType == null ? 0 : scalarType.getJdbcType(); } @@ -1020,7 +1023,7 @@ public class BeanProperty implements ElPropertyValue { public boolean isLoadProperty() { return !isTransient || formula; } - + /** * Return true if this is a version column used for concurrency checking. */ @@ -1183,43 +1186,32 @@ public class BeanProperty implements ElPropertyValue { return name; } - @SuppressWarnings("unchecked") - public void jsonWrite(WriteJsonContext ctx, EntityBean bean) { - if (!jsonSerialize) { - return; - } - Object value = getValueIntercept(bean); - if (value == null) { - ctx.appendNull(name); - } else { - ctx.appendNameValue(name, scalarType, value); - } + public void jsonWrite(WriteJson writeJson, EntityBean bean) { + if (!jsonSerialize) { + return; + } + Object value = getValueIntercept(bean); + if (value == null) { + writeJson.gen().writeNull(name); + } else { + scalarType.jsonWrite(writeJson.gen(), name, value); + } + } + + public void jsonRead(JsonParser ctx, EntityBean bean) { + if (!jsonDeserialize) { + return; + } + if (!ctx.hasNext()) { + throw new RuntimeException(ctx.getLocation().toString()); + } + Event event = ctx.next(); + if (Event.VALUE_NULL == event) { + setValue(bean, null); + } else { + Object objValue = scalarType.jsonRead(ctx, event); + setValue(bean, objValue); } - public void jsonRead(ReadJsonContext ctx, EntityBean bean) { - if (!jsonDeserialize) { - return; - } - String jsonValue; - try { - jsonValue = ctx.readScalarValue(); - } catch (TextException e) { - throw new TextException("Error reading property " + getFullBeanName(), e); - } - Object objValue; - if (jsonValue == null) { - objValue = null; - } else { - objValue = scalarType.jsonFromString(jsonValue, ctx.getValueAdapter()); - } - setValue(bean, objValue); - } - - public boolean isIndexed() { - return indexed; - } - - public String getIndexName() { - return indexName; - } + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java.orig b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java.orig new file mode 100644 index 000000000..ef3fa4795 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java.orig @@ -0,0 +1,1257 @@ +package com.avaje.ebeaninternal.server.deploy; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.sql.SQLException; +import java.sql.Types; +import java.util.List; +import java.util.Map; + +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; +import javax.persistence.PersistenceException; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.config.EncryptKey; +import com.avaje.ebean.config.dbplatform.DbEncryptFunction; +import com.avaje.ebean.config.dbplatform.DbType; +import com.avaje.ebean.text.StringFormatter; +import com.avaje.ebean.text.StringParser; +import com.avaje.ebeaninternal.server.core.InternString; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; +import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; +import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder; +import com.avaje.ebeaninternal.server.el.ElPropertyValue; +import com.avaje.ebeaninternal.server.lib.util.StringHelper; +import com.avaje.ebeaninternal.server.query.SqlBeanLoad; +import com.avaje.ebeaninternal.server.query.SqlJoinType; +import com.avaje.ebeaninternal.server.reflect.BeanReflectGetter; +import com.avaje.ebeaninternal.server.reflect.BeanReflectSetter; +import com.avaje.ebeaninternal.server.text.json.WriteJson; +import com.avaje.ebeaninternal.server.type.DataBind; +import com.avaje.ebeaninternal.server.type.ScalarType; + +/** + * Description of a property of a bean. Includes its deployment information such + * as database column mapping information. + */ +public class BeanProperty implements ElPropertyValue { + + /** + * Advanced bean deployment. To exclude this property from update where + * clause. + */ + public static final String EXCLUDE_FROM_UPDATE_WHERE = "EXCLUDE_FROM_UPDATE_WHERE"; + + /** + * Advanced bean deployment. To exclude this property from delete where + * clause. + */ + public static final String EXCLUDE_FROM_DELETE_WHERE = "EXCLUDE_FROM_DELETE_WHERE"; + + /** + * Advanced bean deployment. To exclude this property from insert. + */ + public static final String EXCLUDE_FROM_INSERT = "EXCLUDE_FROM_INSERT"; + + /** + * Advanced bean deployment. To exclude this property from update set + * clause. + */ + public static final String EXCLUDE_FROM_UPDATE = "EXCLUDE_FROM_UPDATE"; + + /** + * Flag to mark this at part of the unique id. + */ + final boolean id; + + /** + * Flag to make this as a dummy property for unidirecitonal relationships. + */ + final boolean unidirectionalShadow; + + /** + * Flag set if this maps to the inheritance discriminator column + */ + final boolean discriminator; + + /** + * Flag to mark the property as embedded. This could be on + * BeanPropertyAssocOne rather than here. Put it here for checking Id type + * (embedded or not). + */ + final boolean embedded; + + /** + * Flag indicating if this the version property. + */ + final boolean version; + + final boolean naturalKey; + + /** + * Set if this property is nullable. + */ + final boolean nullable; + + final boolean unique; + + /** + * Is this property include in database resultSet. + */ + final boolean dbRead; + + /** + * Include in DB insert. + */ + final boolean dbInsertable; + + /** + * Include in DB update. + */ + final boolean dbUpdatable; + + /** + * True if the property is based on a SECONDARY table. + */ + final boolean secondaryTable; + + final TableJoin secondaryTableJoin; + final String secondaryTableJoinPrefix; + + /** + * The property is inherited from a super class. + */ + final boolean inherited; + + final Class> owningType; + + final boolean local; + + /** + * True if the property is a Clob, Blob LongVarchar or LongVarbinary. + */ + final boolean lob; + + final boolean fetchEager; + + final boolean isTransient; + + /** + * The logical bean property name. + */ + final String name; + + final int propertyIndex; + + /** + * The reflected field. + */ + final Field field; + + /** + * The bean type. + */ + final Class> propertyType; + + final String dbBind; + + /** + * The database column. This can include quoted identifiers. + */ + final String dbColumn; + + final String elPlaceHolder; + final String elPlaceHolderEncrypted; + + /** + * Select part of a SQL Formula used to populate this property. + */ + final String sqlFormulaSelect; + + /** + * Join part of a SQL Formula. + */ + final String sqlFormulaJoin; + + final boolean formula; + + /** + * Set to true if stored encrypted. + */ + final boolean dbEncrypted; + + final boolean localEncrypted; + + final int dbEncryptedType; + + /** + * The jdbc data type this maps to. + */ + final int dbType; + + /** + * The default value to insert if null. + */ + final Object defaultValue; + + /** + * Extra deployment parameters. + */ + final Map extraAttributeMap; + + /** + * The method used to read the property. + */ + final Method readMethod; + + /** + * The method used to write the property. + */ + final Method writeMethod; + + /** + * Generator for insert or update timestamp etc. + */ + final GeneratedProperty generatedProperty; + + final BeanReflectGetter getter; + + final BeanReflectSetter setter; + + final BeanDescriptor> descriptor; + + /** + * Used for non-jdbc native types (java.util.Date Enums etc). Converts from + * logical to jdbc types. + */ + @SuppressWarnings("rawtypes") + final ScalarType scalarType; + + boolean cascadeValidate; + + /** + * The length or precision for DB column. + */ + final int dbLength; + + /** + * The scale for DB column (decimal). + */ + final int dbScale; + + /** + * Deployment defined DB column definition. + */ + final String dbColumnDefn; + + /** + * DB Constraint (typically check constraint on enum) + */ + final String dbConstraintExpression; + + final DbEncryptFunction dbEncryptFunction; + + int deployOrder; + + final boolean jsonSerialize; + + final boolean jsonDeserialize; + + final boolean indexed; + + final String indexName; + + public BeanProperty(DeployBeanProperty deploy) { + this(null, null, deploy); + } + + public BeanProperty(BeanDescriptorMap owner, BeanDescriptor> descriptor, DeployBeanProperty deploy) { + + this.descriptor = descriptor; + this.name = InternString.intern(deploy.getName()); + this.propertyIndex = deploy.getPropertyIndex(); + + this.indexed = deploy.isIndexed(); + this.indexName = deploy.getIndexName(); + + this.unidirectionalShadow = deploy.isUndirectionalShadow(); + this.discriminator = deploy.isDiscriminator(); + this.localEncrypted = deploy.isLocalEncrypted(); + this.dbEncrypted = deploy.isDbEncrypted(); + this.dbEncryptedType = deploy.getDbEncryptedType(); + this.dbEncryptFunction = deploy.getDbEncryptFunction(); + this.dbBind = deploy.getDbBind(); + this.dbRead = deploy.isDbRead(); + this.dbInsertable = deploy.isDbInsertable(); + this.dbUpdatable = deploy.isDbUpdateable(); + + this.secondaryTable = deploy.isSecondaryTable(); + if (secondaryTable) { + this.secondaryTableJoin = new TableJoin(deploy.getSecondaryTableJoin(), null); + this.secondaryTableJoinPrefix = deploy.getSecondaryTableJoinPrefix(); + } else { + this.secondaryTableJoin = null; + this.secondaryTableJoinPrefix = null; + } + this.fetchEager = deploy.isFetchEager(); + this.isTransient = deploy.isTransient(); + this.nullable = deploy.isNullable(); + this.unique = deploy.isUnique(); + this.naturalKey = deploy.isNaturalKey(); + this.dbLength = deploy.getDbLength(); + this.dbScale = deploy.getDbScale(); + this.dbColumnDefn = InternString.intern(deploy.getDbColumnDefn()); + this.dbConstraintExpression = InternString.intern(deploy.getDbConstraintExpression()); + + this.inherited = false;// deploy.isInherited(); + this.owningType = deploy.getOwningType(); + this.local = deploy.isLocal(); + + this.version = deploy.isVersionColumn(); + this.embedded = deploy.isEmbedded(); + this.id = deploy.isId(); + this.generatedProperty = deploy.getGeneratedProperty(); + this.readMethod = deploy.getReadMethod(); + this.writeMethod = deploy.getWriteMethod(); + this.getter = deploy.getGetter(); + this.setter = deploy.getSetter(); + + this.dbColumn = tableAliasIntern(descriptor, deploy.getDbColumn(), false, null); + this.sqlFormulaJoin = InternString.intern(deploy.getSqlFormulaJoin()); + this.sqlFormulaSelect = InternString.intern(deploy.getSqlFormulaSelect()); + this.formula = sqlFormulaSelect != null; + + this.extraAttributeMap = deploy.getExtraAttributeMap(); + this.defaultValue = deploy.getDefaultValue(); + this.dbType = deploy.getDbType(); + this.scalarType = deploy.getScalarType(); + this.lob = isLobType(dbType); + this.propertyType = deploy.getPropertyType(); + this.field = deploy.getField(); + + EntityType et = descriptor == null ? null : descriptor.getEntityType(); + this.elPlaceHolder = tableAliasIntern(descriptor, deploy.getElPlaceHolder(et), false, null); + this.elPlaceHolderEncrypted = tableAliasIntern(descriptor, deploy.getElPlaceHolder(et), dbEncrypted, dbColumn); + + this.jsonSerialize = deploy.isExposeSerialize(); + this.jsonDeserialize = deploy.isExposeDeserialize(); + } + + private String tableAliasIntern(BeanDescriptor> descriptor, String s, boolean dbEncrypted, String dbColumn) { + if (descriptor != null) { + s = StringHelper.replaceString(s, "${ta}.", "${}"); + s = StringHelper.replaceString(s, "${ta}", "${}"); + + if (dbEncrypted) { + s = dbEncryptFunction.getDecryptSql(s); + String namedParam = ":encryptkey_" + descriptor.getBaseTable() + "___" + dbColumn; + s = StringHelper.replaceString(s, "?", namedParam); + } + } + return InternString.intern(s); + } + + /** + * Create a Matching BeanProperty with some attributes overridden. + * + * Primarily for supporting Embedded beans with overridden dbColumn + * mappings. + * + */ + public BeanProperty(BeanProperty source, BeanPropertyOverride override) { + + this.descriptor = source.descriptor; + this.name = InternString.intern(source.getName()); + this.propertyIndex = source.propertyIndex; + + this.indexed = source.isIndexed(); + this.indexName = source.getIndexName(); + + this.dbColumn = InternString.intern(override.getDbColumn()); + this.sqlFormulaJoin = InternString.intern(override.getSqlFormulaJoin()); + this.sqlFormulaSelect = InternString.intern(override.getSqlFormulaSelect()); + this.formula = sqlFormulaSelect != null; + + this.fetchEager = source.fetchEager; + this.unidirectionalShadow = source.unidirectionalShadow; + this.discriminator = source.discriminator; + this.localEncrypted = source.isLocalEncrypted(); + this.isTransient = source.isTransient(); + this.secondaryTable = source.isSecondaryTable(); + this.secondaryTableJoin = source.secondaryTableJoin; + this.secondaryTableJoinPrefix = source.secondaryTableJoinPrefix; + + this.dbBind = source.getDbBind(); + this.dbEncrypted = source.isDbEncrypted(); + this.dbEncryptedType = source.getDbEncryptedType(); + this.dbEncryptFunction = source.dbEncryptFunction; + this.dbRead = source.isDbRead(); + this.dbInsertable = source.isDbInsertable(); + this.dbUpdatable = source.isDbUpdatable(); + this.nullable = source.isNullable(); + this.unique = source.isUnique(); + this.naturalKey = source.isNaturalKey(); + this.dbLength = source.getDbLength(); + this.dbScale = source.getDbScale(); + this.dbColumnDefn = InternString.intern(source.getDbColumnDefn()); + this.dbConstraintExpression = InternString.intern(source.getDbConstraintExpression()); + + this.inherited = source.isInherited(); + this.owningType = source.owningType; + this.local = owningType.equals(descriptor.getBeanType()); + + this.version = source.isVersion(); + this.embedded = source.isEmbedded(); + this.id = source.isId(); + this.generatedProperty = source.getGeneratedProperty(); + this.readMethod = source.getReadMethod(); + this.writeMethod = source.getWriteMethod(); + this.getter = source.getter; + this.setter = source.setter; + this.extraAttributeMap = source.extraAttributeMap; + this.defaultValue = source.getDefaultValue(); + this.dbType = source.getDbType(); + this.scalarType = source.scalarType; + this.lob = isLobType(dbType); + this.propertyType = source.getPropertyType(); + this.field = source.getField(); + + this.elPlaceHolder = override.replace(source.elPlaceHolder, source.dbColumn); + this.elPlaceHolderEncrypted = override.replace(source.elPlaceHolderEncrypted, source.dbColumn); + + this.jsonSerialize = source.jsonSerialize; + this.jsonDeserialize = source.jsonDeserialize; + } + + /** + * Initialise the property before returning to client code. Used to + * initialise variables that can't be done in construction due to recursive + * issues. + */ + public void initialise() { + // do nothing for normal BeanProperty + if (!isTransient && scalarType == null) { + String msg = "No ScalarType assigned to " + descriptor.getFullName() + "." + getName(); + throw new RuntimeException(msg); + } + } + + /** + * Return the order this property appears in the bean. + */ + public int getDeployOrder() { + return deployOrder; + } + + /** + * Set the order this property appears in the bean. + */ + public void setDeployOrder(int deployOrder) { + this.deployOrder = deployOrder; + } + + public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, + boolean propertyDeploy) { + throw new PersistenceException("Not valid on scalar bean property " + getFullBeanName()); + } + + /** + * Return the BeanDescriptor that owns this property. + */ + public BeanDescriptor> getBeanDescriptor() { + return descriptor; + } + + /** + * Return true is this is a simple scalar property. + */ + public boolean isScalar() { + return true; + } + + /** + * Return true if this property is based on a formula. + */ + public boolean isFormula() { + return formula; + } + + /** + * Return true if this property maps to the inheritance discriminator column. + */ + public boolean isDiscriminator() { + return discriminator; + } + + /** + * Return true if the underlying type is mutable. + */ + public boolean isMutableScalarType() { + if (scalarType == null) { + return false; + } + return scalarType.isMutable(); + } + + public void copyProperty(EntityBean sourceBean, EntityBean destBean) { + Object value = getValue(sourceBean); + setValue(destBean, value); + } + + /** + * Return the encrypt key for the column matching this property. + */ + public EncryptKey getEncryptKey() { + return descriptor.getEncryptKey(this); + } + + public String getDecryptProperty() { + return dbEncryptFunction.getDecryptSql(this.getName()); + } + + public String getDecryptProperty(String propertyName) { + return dbEncryptFunction.getDecryptSql(propertyName); + } + + public String getDecryptSql() { + return dbEncryptFunction.getDecryptSql(this.getDbColumn()); + } + + public String getDecryptSql(String tableAlias) { + return dbEncryptFunction.getDecryptSql(tableAlias + "." + this.getDbColumn()); + } + + /** + * Add any extra joins required to support this property. Generally a no + * operation except for a OneToOne exported. + */ + public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) { + if (formula && sqlFormulaJoin != null) { + ctx.appendFormulaJoin(sqlFormulaJoin, joinType); + + } else if (secondaryTableJoin != null) { + + String relativePrefix = ctx.getRelativePrefix(secondaryTableJoinPrefix); + secondaryTableJoin.addJoin(joinType, relativePrefix, ctx); + } + } + + /** + * Returns null unless this property is using a secondary table. In that + * case this returns the logical property prefix. + */ + public String getSecondaryTableJoinPrefix() { + return secondaryTableJoinPrefix; + } + + public void appendSelect(DbSqlContext ctx, boolean subQuery) { + if (formula) { + ctx.appendFormulaSelect(sqlFormulaSelect); + + } else if (!isTransient) { + + if (secondaryTableJoin != null) { + String relativePrefix = ctx.getRelativePrefix(secondaryTableJoinPrefix); + ctx.pushTableAlias(relativePrefix); + } + + if (dbEncrypted) { + String decryptSql = getDecryptSql(ctx.peekTableAlias()); + ctx.appendRawColumn(decryptSql); + ctx.addEncryptedProp(this); + + } else { + ctx.appendColumn(dbColumn); + } + + if (secondaryTableJoin != null) { + ctx.popTableAlias(); + } + } + } + + public boolean isAssignableFrom(Class> type) { + return owningType.isAssignableFrom(type); + } + + public Object readSetOwning(DbReadContext ctx, EntityBean bean, Class> type) throws SQLException { + + try { + Object value = scalarType.read(ctx.getDataReader()); + if (value == null || bean == null) { + // not setting the value... + } else { + if (owningType.equals(type)) { + setValue(bean, value); + } + } + return value; + } catch (Exception e) { + String msg = "Error readSet on " + descriptor + "." + name; + throw new PersistenceException(msg, e); + } + } + + public void loadIgnore(DbReadContext ctx) { + scalarType.loadIgnore(ctx.getDataReader()); + } + + public void load(SqlBeanLoad sqlBeanLoad) throws SQLException { + sqlBeanLoad.load(this); + } + + public void buildSelectExpressionChain(String prefix, List selectChain) { + if (prefix == null) { + selectChain.add(name); + } else { + selectChain.add(prefix + "." + name); + } + } + + public Object read(DbReadContext ctx) throws SQLException { + return scalarType.read(ctx.getDataReader()); + } + + public Object readSet(DbReadContext ctx, EntityBean bean, Class> type) throws SQLException { + + try { + Object value = scalarType.read(ctx.getDataReader()); + if (bean == null || (type != null && !owningType.isAssignableFrom(type))) { + // not setting the value... + } else { + setValue(bean, value); + } + return value; + } catch (Exception e) { + String msg = "Error readSet on " + descriptor + "." + name; + throw new PersistenceException(msg, e); + } + } + + /** + * Convert the type to the bean type if required. + * + * Generally only used to ensure id properties are converted for + * Query.setId() use. + * + */ + public Object toBeanType(Object value) { + return scalarType.toBeanType(value); + } + + @SuppressWarnings("unchecked") + public void bind(DataBind b, Object value) throws SQLException { + scalarType.bind(b, value); + } + + public void writeData(DataOutput dataOutput, Object value) throws IOException { + scalarType.writeData(dataOutput, value); + } + + public Object readData(DataInput dataInput) throws IOException { + return scalarType.readData(dataInput); + } + + public boolean isCascadeValidate() { + return cascadeValidate; + } + + /** + * Checks to see if a bean is a reference (will be lazy loaded) or a + * BeanCollection that has not yet been populated. + * + * For base types this returns true. + * + */ + public boolean isValueLoaded(Object value) { + return true; + } + + public BeanProperty getBeanProperty() { + return this; + } + + /** + * Return the getter method. + */ + public Method getReadMethod() { + return readMethod; + } + + /** + * Return the setter method. + */ + public Method getWriteMethod() { + return writeMethod; + } + + /** + * Return true if this object is part of an inheritance hierarchy. + */ + public boolean isInherited() { + return inherited; + } + + /** + * Return true is this type is not from a super type. + */ + public boolean isLocal() { + return local; + } + + /** + * Set the value of the property without interception or + * PropertyChangeSupport. + */ + public void setValue(EntityBean bean, Object value) { + try { + setter.set(bean, value); + } catch (Exception ex) { + String beanType = bean == null ? "null" : bean.getClass().getName(); + String msg = "set " + name + " on [" + descriptor + "] arg[" + value + "] type[" + beanType + + "] threw error"; + throw new RuntimeException(msg, ex); + } + } + + /** + * Set the value of the property. + */ + public void setValueIntercept(EntityBean bean, Object value) { + try { + setter.setIntercept(bean, value); + } catch (Exception ex) { + String beanType = bean == null ? "null" : bean.getClass().getName(); + String msg = "setIntercept " + name + " on [" + descriptor + "] arg[" + value + "] type[" + beanType + + "] threw error"; + throw new RuntimeException(msg, ex); + } + } + + private static Object[] NO_ARGS = new Object[0]; + + public Object getCacheDataValue(EntityBean bean) { + return getValue(bean); + } + + public void setCacheDataValue(EntityBean bean, Object cacheData) { + setValue(bean, cacheData); + } + + /** + * Return the value of the property method. + */ + public Object getValue(EntityBean bean) { + try { + return getter.get(bean); + } catch (Exception ex) { + String beanType = bean == null ? "null" : bean.getClass().getName(); + String msg = "get " + name + " on [" + descriptor + "] type[" + beanType + "] threw error."; + throw new RuntimeException(msg, ex); + } + } + + /** + * Explicitly use reflection to get value. + */ + public Object getValueViaReflection(Object bean) { + try { + return readMethod.invoke(bean, NO_ARGS); + } catch (Exception ex) { + String beanType = bean == null ? "null" : bean.getClass().getName(); + String msg = "get " + name + " on [" + descriptor + "] type[" + beanType + "] threw error."; + throw new RuntimeException(msg, ex); + } + } + + public Object getValueIntercept(EntityBean bean) { + try { + return getter.getIntercept(bean); + } catch (Exception ex) { + String beanType = bean == null ? "null" : bean.getClass().getName(); + String msg = "getIntercept " + name + " on [" + descriptor + "] type[" + beanType + "] threw error."; + throw new RuntimeException(msg, ex); + } + } + + public Object elConvertType(Object value) { + if (value == null) { + return null; + } + return convertToLogicalType(value); + } + + public void elSetValue(EntityBean bean, Object value, boolean populate) { + if (bean != null) { + // Not using setValueIntercept at this stage + setValue(bean, value); + } + } + + public Object elGetValue(EntityBean bean) { + if (bean == null) { + return null; + } + return getValueIntercept(bean); + } + + public Object elGetReference(EntityBean bean) { + throw new RuntimeException("Not expected to call this"); + } + + /** + * Return the name of the property. + */ + public String getName() { + return name; + } + + /** + * Return the position of this property in the enhanced bean. + */ + public int getPropertyIndex() { + return propertyIndex; + } + + public String getElName() { + return name; + } + + /** + * This is a full ElGetValue. + */ + public boolean isDeployOnly() { + return false; + } + + + @Override + public boolean containsFormulaWithJoin() { + return formula && sqlFormulaJoin != null; + } + + public boolean containsManySince(String sinceProperty) { + return containsMany(); + } + + public boolean containsMany() { + return false; + } + + public Object[] getAssocOneIdValues(EntityBean bean) { + // Returns null as not an AssocOne. + return null; + } + + public String getAssocOneIdExpr(String prefix, String operator) { + // Returns null as not an AssocOne. + return null; + } + + public String getAssocIdInExpr(String prefix) { + // Returns null as not an AssocOne. + return null; + } + + public String getAssocIdInValueExpr(int size) { + // Returns null as not an AssocOne. + return null; + } + + public boolean isAssocId() { + // Returns false - override in BeanPropertyAssocOne. + return false; + } + + public boolean isAssocProperty() { + // Returns false - override in BeanPropertyAssocOne. + return false; + } + + public String getElPlaceholder(boolean encrypted) { + return encrypted ? elPlaceHolderEncrypted : elPlaceHolder; + } + + public String getElPrefix() { + return secondaryTableJoinPrefix; + } + + /** + * Return the full name of this property. + */ + public String getFullBeanName() { + return descriptor.getFullName() + "." + name; + } + + /** + * Return true if the mutable value is considered dirty. + * This is only used for 'mutable' scalar types like hstore etc. + */ + public boolean isDirtyValue(Object value) { + return scalarType.isDirty(value); + } + + /** + * Return the scalarType. + */ + public ScalarType> getScalarType() { + return scalarType; + } + + public StringFormatter getStringFormatter() { + return scalarType; + } + + public StringParser getStringParser() { + return scalarType; + } + + public boolean isDateTimeCapable() { + return scalarType != null && scalarType.isDateTimeCapable(); + } + + public int getJdbcType() { + return scalarType == null ? 0 : scalarType.getJdbcType(); + } + + public Object parseDateTime(long systemTimeMillis) { + return scalarType.parseDateTime(systemTimeMillis); + } + + /** + * Return the DB max length (varchar) or precision (decimal). + */ + public int getDbLength() { + return dbLength; + } + + /** + * Return the DB scale for numeric columns. + */ + public int getDbScale() { + return dbScale; + } + + /** + * Return a specific column DDL definition if specified (otherwise null). + */ + public String getDbColumnDefn() { + return dbColumnDefn; + } + + /** + * Return the DB constraint expression (can be null). + * + * For an Enum returns IN expression for the set of Enum values. + * + */ + public String getDbConstraintExpression() { + return dbConstraintExpression; + } + + /** + * Return the DB column type definition. + */ + public String renderDbType(DbType dbType) { + if (dbColumnDefn != null) { + return dbColumnDefn; + } + return dbType.renderType(dbLength, dbScale); + } + + /** + * Return the bean Field associated with this property. + */ + public Field getField() { + return field; + } + + /** + * Return the GeneratedValue. Used to generate update timestamp etc. + */ + public GeneratedProperty getGeneratedProperty() { + return generatedProperty; + } + + /** + * Return true if this is the natural key property. + */ + public boolean isNaturalKey() { + return naturalKey; + } + + /** + * Return true if this property is mandatory. + */ + public boolean isNullable() { + return nullable; + } + + /** + * Return true if DDL Not NULL constraint should be defined for this column + * based on it being a version column or having a generated property. + */ + public boolean isDDLNotNull() { + return isVersion() || (generatedProperty != null && generatedProperty.isDDLNotNullable()); + } + + /** + * Return true if the DB column should be unique. + */ + public boolean isUnique() { + return unique; + } + + /** + * Return true if the property is transient. + */ + public boolean isTransient() { + return isTransient; + } + + /** + * Return true if this property is loadable from a resultSet. + */ + public boolean isLoadProperty() { + return !isTransient || formula; + } + + /** + * Return true if this is a version column used for concurrency checking. + */ + public boolean isVersion() { + return version; + } + + public String getDeployProperty() { + return dbColumn; + } + + /** + * The database column name this is mapped to. + */ + public String getDbColumn() { + return dbColumn; + } + + /** + * Return the database jdbc data type this is mapped to. + */ + public int getDbType() { + return dbType; + } + + /** + * Perform DB to Logical type conversion (if necessary). + */ + public Object convertToLogicalType(Object value) { + if (scalarType != null) { + return scalarType.toBeanType(value); + } + return value; + } + + /** + * Return true if by default this property is set to fetch eager. + * Lob's usually default to fetch lazy. + */ + public boolean isFetchEager() { + return fetchEager; + } + + /** + * Return true if this is mapped to a Clob Blob LongVarchar or + * LongVarbinary. + */ + public boolean isLob() { + return lob; + } + + private boolean isLobType(int type) { + switch (type) { + case Types.CLOB: + return true; + case Types.BLOB: + return true; + case Types.LONGVARBINARY: + return true; + case Types.LONGVARCHAR: + return true; + + default: + return false; + } + } + + /** + * Return the DB bind parameter. Typically is "?" but different for + * encrypted bind. + */ + public String getDbBind() { + return dbBind; + } + + /** + * Returns true if DB encrypted. + */ + public boolean isLocalEncrypted() { + return localEncrypted; + } + + /** + * Return true if this property is stored encrypted. + */ + public boolean isDbEncrypted() { + return dbEncrypted; + } + + public int getDbEncryptedType() { + return dbEncryptedType; + } + + /** + * Return true if this property should be included in an Insert. + */ + public boolean isDbInsertable() { + return dbInsertable; + } + + /** + * Return true if this property should be included in an Update. + */ + public boolean isDbUpdatable() { + return dbUpdatable; + } + + /** + * Return true if this property is included in database queries. + */ + public boolean isDbRead() { + return dbRead; + } + + /** + * Return true if this property is based on a secondary table (not the base + * table). + */ + public boolean isSecondaryTable() { + return secondaryTable; + } + + /** + * Return the property type. + */ + public Class> getPropertyType() { + return propertyType; + } + + /** + * Return true if this is included in the unique id. + */ + public boolean isId() { + return id; + } + + /** + * Return true if this is an Embedded property. In this case it shares the + * table and primary key of its owner object. + */ + public boolean isEmbedded() { + return embedded; + } + + /** + * Return an extra attribute set on this property. + */ + public String getExtraAttribute(String key) { + return extraAttributeMap.get(key); + } + + /** + * Return the default value. + */ + public Object getDefaultValue() { + return defaultValue; + } + + public String toString() { + return name; + } + +<<<<<<< HEAD + @SuppressWarnings("unchecked") + public void jsonWrite(WriteJsonContext ctx, EntityBean bean) { + if (!jsonSerialize) { + return; + } + Object value = getValueIntercept(bean); + if (value == null) { + ctx.appendNull(name); + } else { + ctx.appendNameValue(name, scalarType, value); + } +======= + public void jsonWrite(WriteJson writeJson, EntityBean bean) { + if (!jsonSerialize) { + return; + } + Object value = getValueIntercept(bean); + if (value == null) { + writeJson.gen().writeNull(name); + } else { + scalarType.jsonWrite(writeJson.gen(), name, value); +>>>>>>> json-refactor + } + } + +<<<<<<< HEAD + public void jsonRead(ReadJsonContext ctx, EntityBean bean) { + if (!jsonDeserialize) { + return; + } + String jsonValue; + try { + jsonValue = ctx.readScalarValue(); + } catch (TextException e) { + throw new TextException("Error reading property " + getFullBeanName(), e); + } + Object objValue; + if (jsonValue == null) { + objValue = null; + } else { + objValue = scalarType.jsonFromString(jsonValue, ctx.getValueAdapter()); + } + setValue(bean, objValue); + } + + public boolean isIndexed() { + return indexed; + } + + public String getIndexName() { + return indexName; + } +======= + public void jsonRead(JsonParser ctx, EntityBean bean) { + if (!jsonDeserialize) { + return; + } + if (!ctx.hasNext()) { + throw new RuntimeException(ctx.getLocation().toString()); + } + Event event = ctx.next(); + if (Event.VALUE_NULL == event) { + setValue(bean, null); + } else { + Object objValue = scalarType.jsonRead(ctx, event); + setValue(bean, objValue); + } + + } +>>>>>>> json-refactor +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java index 172800de5..b31fccab6 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import javax.json.stream.JsonParser; import javax.persistence.PersistenceException; import org.slf4j.Logger; @@ -28,9 +29,7 @@ import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder; import com.avaje.ebeaninternal.server.el.ElPropertyValue; import com.avaje.ebeaninternal.server.lib.util.StringHelper; import com.avaje.ebeaninternal.server.query.SqlBeanLoad; -import com.avaje.ebeaninternal.server.text.json.ReadJsonContext; -import com.avaje.ebeaninternal.server.text.json.ReadJsonContext.ReadBeanState; -import com.avaje.ebeaninternal.server.text.json.WriteJsonContext; +import com.avaje.ebeaninternal.server.text.json.WriteJson; /** * Property mapped to a List Set or Map. @@ -39,6 +38,8 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc { private static final Logger logger = LoggerFactory.getLogger(BeanPropertyAssocMany.class); + private final BeanPropertyAssocManyJsonHelp jsonHelp; + /** * Join for manyToMany intersection table. */ @@ -90,7 +91,7 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc { /** * Property on the 'child' bean that links back to the 'master'. */ - private BeanPropertyAssocOne> childMasterProperty; + protected BeanPropertyAssocOne> childMasterProperty; private boolean embeddedExportedProperties; @@ -115,6 +116,7 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc { this.intersectionJoin = deploy.createIntersectionTableJoin(); this.inverseJoin = deploy.createInverseTableJoin(); this.modifyListenMode = deploy.getModifyListenMode(); + this.jsonHelp = new BeanPropertyAssocManyJsonHelp(this); } public void initialise() { @@ -875,7 +877,7 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc { return null != targetDescriptor.getId(otherBean); } - public void jsonWrite(WriteJsonContext ctx, EntityBean bean) { + public void jsonWrite(WriteJson ctx, EntityBean bean) { if(!this.jsonSerialize){ return; } @@ -896,37 +898,7 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc { } } - public void jsonRead(ReadJsonContext ctx, EntityBean bean){ - if(!this.jsonDeserialize){ - return; - } - if (!ctx.readArrayBegin()) { - // the array is null - return; - } - - Object collection = help.createEmpty(false); - BeanCollectionAdd add = getBeanCollectionAdd(collection, null); - do { - ReadBeanState detailBeanState = targetDescriptor.jsonRead(ctx, name); - if (detailBeanState == null){ - // probably empty array - break; - } - EntityBean detailBean = (EntityBean)detailBeanState.getBean(); - add.addBean(detailBean); - - if (bean != null && childMasterProperty != null){ - // bind detail bean back to master via mappedBy property - childMasterProperty.setValue(detailBean, bean); - detailBeanState.setLoaded(childMasterProperty.getName()); - } - - if (!ctx.readArrayNext()){ - break; - } - } while(true); - - setValue(bean, collection); + public void jsonRead(JsonParser parser, EntityBean parentBean) { + jsonHelp.jsonRead(parser, parentBean); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocManyJsonHelp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocManyJsonHelp.java new file mode 100644 index 000000000..9c923f52d --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocManyJsonHelp.java @@ -0,0 +1,49 @@ +package com.avaje.ebeaninternal.server.deploy; + +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; + +import com.avaje.ebean.bean.BeanCollectionAdd; +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.text.TextException; + +public class BeanPropertyAssocManyJsonHelp { + + private final BeanPropertyAssocMany> many; + + public BeanPropertyAssocManyJsonHelp(BeanPropertyAssocMany> many) { + this.many = many; + } + + public void jsonRead(JsonParser parser, EntityBean parentBean) { + + if (!this.many.jsonDeserialize || !parser.hasNext()) { + return; + } + Event event = parser.next(); + if (Event.VALUE_NULL == event) { + return; + } + if (Event.START_ARRAY != event) { + throw new TextException("Unexpected token "+event+" - expecting start_array at: "+parser.getLocation()); + } + + Object collection = many.createEmpty(false); + BeanCollectionAdd add = many.getBeanCollectionAdd(collection, null); + do { + EntityBean detailBean = (EntityBean)many.targetDescriptor.jsonRead(parser, many.name); + if (detailBean == null) { + // read the entire array + break; + } + add.addBean(detailBean); + + if (parentBean != null && many.childMasterProperty != null) { + // bind detail bean back to master via mappedBy property + many.childMasterProperty.setValue(detailBean, parentBean); + } + } while (true); + + many.setValue(parentBean, collection); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java index 7d9b620bc..d0a7c964d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import javax.json.stream.JsonParser; import javax.persistence.PersistenceException; import com.avaje.ebean.EbeanServer; @@ -24,8 +25,7 @@ import com.avaje.ebeaninternal.server.el.ElPropertyValue; import com.avaje.ebeaninternal.server.query.SplitName; import com.avaje.ebeaninternal.server.query.SqlBeanLoad; import com.avaje.ebeaninternal.server.query.SqlJoinType; -import com.avaje.ebeaninternal.server.text.json.ReadJsonContext; -import com.avaje.ebeaninternal.server.text.json.WriteJsonContext; +import com.avaje.ebeaninternal.server.text.json.WriteJson; /** * Property mapped to a joined bean. @@ -831,36 +831,34 @@ public class BeanPropertyAssocOne extends BeanPropertyAssoc { } @Override - public void jsonWrite(WriteJsonContext ctx, EntityBean bean) { + public void jsonWrite(WriteJson writeJson, EntityBean bean) { Object value = getValueIntercept(bean); if (value == null){ - ctx.beginAssocOneIsNull(name); + writeJson.gen().writeNull(name); } else { - if (ctx.isParentBean(value)){ + if (writeJson.isParentBean(value)){ // bi-directional and already rendered parent } else { // Hmmm, not writing complex non-entity bean if (value instanceof EntityBean) { - ctx.pushParentBean(bean); - ctx.beginAssocOne(name); + writeJson.beginAssocOne(name, bean); BeanDescriptor> refDesc = descriptor.getBeanDescriptor(value.getClass()); - refDesc.jsonWrite(ctx, (EntityBean)value); - ctx.endAssocOne(); - ctx.popParentBean(); + refDesc.jsonWrite(writeJson, (EntityBean)value, name); + writeJson.endAssocOne(); } } } } - + @Override - public void jsonRead(ReadJsonContext ctx, EntityBean bean){ - if (targetDescriptor != null) { - T assocBean = targetDescriptor.jsonReadBean(ctx, name); - setValue(bean, assocBean); - } + public void jsonRead(JsonParser parser, EntityBean bean) { + if (targetDescriptor != null) { + T assocBean = targetDescriptor.jsonRead(parser, name); + setValue(bean, assocBean); + } } public boolean isReference(Object detailBean) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompound.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompound.java index 00132e1d3..ea8c31606 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompound.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompound.java @@ -3,15 +3,18 @@ package com.avaje.ebeaninternal.server.deploy; import java.sql.SQLException; import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; + +import javax.json.stream.JsonParser; import com.avaje.ebean.bean.EntityBean; import com.avaje.ebean.config.ScalarTypeConverter; +import com.avaje.ebean.json.EJson; import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound; import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder; import com.avaje.ebeaninternal.server.el.ElPropertyValue; import com.avaje.ebeaninternal.server.query.SqlBeanLoad; -import com.avaje.ebeaninternal.server.text.json.ReadJsonContext; -import com.avaje.ebeaninternal.server.text.json.WriteJsonContext; +import com.avaje.ebeaninternal.server.text.json.WriteJson; import com.avaje.ebeaninternal.server.type.CtCompoundProperty; import com.avaje.ebeaninternal.server.type.CtCompoundPropertyElAdapter; import com.avaje.ebeaninternal.server.type.CtCompoundType; @@ -177,15 +180,32 @@ public class BeanPropertyCompound extends BeanProperty { return bean; } - public void jsonWrite(WriteJsonContext ctx, EntityBean bean) { - - Object valueObject = getValueIntercept(bean); - compoundType.jsonWrite(ctx, valueObject, name); + public void jsonWrite(WriteJson ctx, EntityBean bean) { + if (!jsonSerialize) { + return; + } + Object value = getValueIntercept(bean); + if (value == null) { + ctx.gen().writeNull(name); + } else { + compoundType.jsonWrite(ctx, value, name); + } } - public void jsonRead(ReadJsonContext ctx, EntityBean bean){ - - Object objValue = compoundType.jsonRead(ctx); + public void jsonRead(JsonParser ctx, EntityBean bean) { + + if (!jsonDeserialize) { + return; + } + + Object value = EJson.parsePartial(ctx); + if (value == null) { + setValue(bean, null); + } else { + @SuppressWarnings("unchecked") + Map map = (Map)value; + Object objValue = compoundType.jsonConvert(map); setValue(bean, objValue); + } } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanSetHelp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanSetHelp.java index 57ff5575c..bc2f09931 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanSetHelp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanSetHelp.java @@ -12,7 +12,7 @@ import com.avaje.ebean.bean.BeanCollectionAdd; import com.avaje.ebean.bean.BeanCollectionLoader; import com.avaje.ebean.bean.EntityBean; import com.avaje.ebean.common.BeanSet; -import com.avaje.ebeaninternal.server.text.json.WriteJsonContext; +import com.avaje.ebeaninternal.server.text.json.WriteJson; /** * Helper specifically for dealing with Sets. @@ -129,7 +129,7 @@ public final class BeanSetHelp implements BeanCollectionHelp { } } - public void jsonWrite(WriteJsonContext ctx, String name, Object collection, boolean explicitInclude) { + public void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) { Set> set; if (collection instanceof BeanCollection>){ @@ -148,16 +148,11 @@ public final class BeanSetHelp implements BeanCollectionHelp { set = (Set>)collection; } - int count = 0; - ctx.beginAssocMany(name); + ctx.gen().writeStartArray(name); Iterator> it = set.iterator(); while (it.hasNext()) { - Object detailBean = it.next(); - if (count++ > 0){ - ctx.appendComma(); - } - targetDescriptor.jsonWrite(ctx, (EntityBean)detailBean); + targetDescriptor.jsonWrite(ctx, (EntityBean)it.next()); } - ctx.endAssocMany(); + ctx.gen().writeEnd(); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/EJsonReader.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/EJsonReader.java new file mode 100644 index 000000000..71f4e790e --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/EJsonReader.java @@ -0,0 +1,215 @@ +package com.avaje.ebeaninternal.server.deploy; + +import java.io.Reader; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; + +import javax.json.Json; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; + +public class EJsonReader { + + @SuppressWarnings("unchecked") + public static Map parseObject(String json) { + return (Map) parse(json); + } + + @SuppressWarnings("unchecked") + public static List parseList(String json) { + return (List) parse(json); + } + + public static Object parse(String json) { + return parse(new StringReader(json)); + } + + public static Object parse(Reader reader) { + return parse(Json.createParser(reader)); + } + + public static Object parse(JsonParser parser) { + return new EJsonReader(parser).parseJson(); + } + + private final JsonParser parser; + + private final Stack stack = new Stack(); + + private Context currentContext; + + + EJsonReader(JsonParser parser) { + this.parser = parser; + } + + private void startArray() { + stack.push(currentContext); + currentContext = new ArrayContext(); + } + + private void startObject() { + stack.push(currentContext); + currentContext = new ObjectContext(); + } + + private void endArray() { + end(); + } + + private void endObject() { + end(); + } + + private void end() { + + if (!stack.isEmpty()) { + currentContext = stack.pop(); + } + } + + private void setValue(Object value) { + currentContext.setValue(value); + } + + private void setValueNull() { + currentContext.setValueNull(); + } + + private Object parseJson() { + + while (parser.hasNext()) { + Event event = parser.next(); + switch (event) { + + case START_ARRAY: + startArray(); + break; + + case START_OBJECT: + startObject(); + break; + + case KEY_NAME: + currentContext.setKey(parser.getString()); + break; + + case VALUE_STRING: + setValue(parser.getString()); + break; + + case VALUE_NUMBER: + if (parser.isIntegralNumber()) { + setValue(parser.getLong()); + } else { + setValue(parser.getBigDecimal()); + } + break; + + case VALUE_TRUE: + setValue(Boolean.TRUE); + break; + + case VALUE_FALSE: + setValue(Boolean.FALSE); + break; + + case VALUE_NULL: + setValueNull(); + break; + + case END_OBJECT: + endObject(); + break; + + case END_ARRAY: + endArray(); + break; + + default: + break; + } + } + + return currentContext.getValue(); + } + + private static final class Stack { + + private Context head; + + private void push(Context context) { + if (context != null) { + context.next = head; + head = context; + } + } + + private Context pop() { + if (head == null) { + throw new NoSuchElementException(); + } + Context temp = head; + head = head.next; + return temp; + } + + private boolean isEmpty() { + return head == null; + } + } + + private static abstract class Context { + Context next; + abstract Object getValue(); + abstract void setKey(String key); + abstract void setValue(Object value); + abstract void setValueNull(); + } + + private static class ObjectContext extends Context { + + private String key; + + Map map = new LinkedHashMap(); + + Object getValue() { + return map; + } + + public void setKey(String key) { + this.key = key; + } + + void setValue(Object value) { + map.put(key, value); + } + + void setValueNull() { + map.put(key, null); + } + } + + private static class ArrayContext extends Context { + + List values = new ArrayList(); + + Object getValue() { + return values; + } + + void setValue(Object value) { + values.add(value); + } + + void setValueNull() { + } + void setKey(String key) { + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/ReadJson.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/ReadJson.java new file mode 100644 index 000000000..871517f40 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/ReadJson.java @@ -0,0 +1,9 @@ +package com.avaje.ebeaninternal.server.deploy; + +import javax.json.stream.JsonParser; + +public class ReadJson { + + JsonParser parser; + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/DJsonContext.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/DJsonContext.java index a176731bb..8a43ccbef 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/DJsonContext.java +++ b/src/main/java/com/avaje/ebeaninternal/server/text/json/DJsonContext.java @@ -1,6 +1,8 @@ package com.avaje.ebeaninternal.server.text.json; import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; import java.io.Writer; import java.lang.reflect.Type; import java.util.ArrayList; @@ -11,16 +13,19 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import javax.json.Json; +import javax.json.stream.JsonGenerator; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; + import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.json.EJson; +import com.avaje.ebean.text.PathProperties; import com.avaje.ebean.text.TextException; import com.avaje.ebean.text.json.JsonContext; -import com.avaje.ebean.text.json.JsonElement; -import com.avaje.ebean.text.json.JsonReadOptions; -import com.avaje.ebean.text.json.JsonValueAdapter; import com.avaje.ebean.text.json.JsonWriteOptions; import com.avaje.ebeaninternal.api.SpiEbeanServer; import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.type.EscapeJson; import com.avaje.ebeaninternal.util.ParamTypeHelper; import com.avaje.ebeaninternal.util.ParamTypeHelper.ManyType; import com.avaje.ebeaninternal.util.ParamTypeHelper.TypeInfo; @@ -32,275 +37,215 @@ import com.avaje.ebeaninternal.util.ParamTypeHelper.TypeInfo; */ public class DJsonContext implements JsonContext { - private final SpiEbeanServer server; - - private final JsonValueAdapter dfltValueAdapter; - - private final boolean dfltPretty; - - public DJsonContext(SpiEbeanServer server, JsonValueAdapter dfltValueAdapter, boolean dfltPretty){ - this.server = server; - this.dfltValueAdapter = dfltValueAdapter; - this.dfltPretty = dfltPretty; - } + private final SpiEbeanServer server; - public boolean isSupportedType(Type genericType) { - return server.isSupportedType(genericType); - } + public DJsonContext(SpiEbeanServer server) { + this.server = server; + } - private ReadJsonSource createReader(Reader jsonReader) { - return new ReadJsonSourceReader(jsonReader, 256, 512); - } - - public T toBean(Class cls, String json){ - return toBean(cls, new ReadJsonSourceString(json), null); - } - - public T toBean(Class cls, Reader jsonReader) { - return toBean(cls, createReader(jsonReader), null); - } - - public T toBean(Class cls, String json, JsonReadOptions options){ - return toBean(cls, new ReadJsonSourceString(json), options); - } + public boolean isSupportedType(Type genericType) { + return server.isSupportedType(genericType); + } - public T toBean(Class cls, Reader jsonReader, JsonReadOptions options) { - return toBean(cls, createReader(jsonReader), options); - } + private JsonParser createReader(Reader jsonReader) { + return Json.createParser(jsonReader); + } - private T toBean(Class cls, ReadJsonSource src, JsonReadOptions options){ + public T toBean(Class cls, String json) { + return toBean(cls, new StringReader(json)); + } - BeanDescriptor d = getDecriptor(cls); - ReadJsonContext ctx = new ReadJsonContext(src, dfltValueAdapter, options); - return d.jsonReadBean(ctx, null); - } + public T toBean(Class cls, Reader jsonReader) { + return toBean(cls, createReader(jsonReader)); + } - public List toList(Class cls, String json){ - return toList(cls, new ReadJsonSourceString(json), null); - } + private T toBean(Class cls, JsonParser parser) { - public List toList(Class cls, String json, JsonReadOptions options){ - return toList(cls, new ReadJsonSourceString(json), options); - } - - public List toList(Class cls, Reader jsonReader){ - return toList(cls, createReader(jsonReader), null); - } + BeanDescriptor d = getDecriptor(cls); + return d.jsonRead(parser, null); + } - public List toList(Class cls, Reader jsonReader, JsonReadOptions options){ - return toList(cls, createReader(jsonReader), options); - } - - private List toList(Class cls, ReadJsonSource src, JsonReadOptions options){ - - try { - BeanDescriptor d = getDecriptor(cls); - - List list = new ArrayList(); - - ReadJsonContext ctx = new ReadJsonContext(src, dfltValueAdapter, options); - ctx.readArrayBegin(); - do { - T bean = d.jsonReadBean(ctx, null); - if (bean != null){ - list.add(bean); - } - if (!ctx.readArrayNext()){ - break; - } - } while(true); - - return list; - } catch (RuntimeException e){ - throw new TextException("Error parsing "+src, e); - } - } - - - public Object toObject(Type genericType, String json, JsonReadOptions options) { - - TypeInfo info = ParamTypeHelper.getTypeInfo(genericType); - Class> beanType = info.getBeanType(); - if (JsonElement.class.isAssignableFrom(beanType)){ - return InternalJsonParser.parse(json); - } - - ManyType manyType = info.getManyType(); - switch (manyType) { - case NONE: - return toBean(info.getBeanType(), json, options); - - case LIST: - return toList(info.getBeanType(), json, options); - - default: - String msg = "ManyType "+manyType+" not supported yet"; - throw new TextException(msg); - } - } - - public Object toObject(Type genericType, Reader json, JsonReadOptions options) { - - TypeInfo info = ParamTypeHelper.getTypeInfo(genericType); - Class> beanType = info.getBeanType(); - if (JsonElement.class.isAssignableFrom(beanType)){ - return InternalJsonParser.parse(json); - } - - ManyType manyType = info.getManyType(); - switch (manyType) { - case NONE: - return toBean(info.getBeanType(), json, options); - - case LIST: - return toList(info.getBeanType(), json, options); - - default: - String msg = "ManyType "+manyType+" not supported yet"; - throw new TextException(msg); - } - } + public List toList(Class cls, String json) { + return toList(cls, new StringReader(json)); + } - public void toJsonWriter(Object o, Writer writer) { - toJsonWriter(o, writer, dfltPretty, null, null); - } + public List toList(Class cls, Reader jsonReader) { + return toList(cls, createReader(jsonReader)); + } - public void toJsonWriter(Object o, Writer writer, boolean pretty) { - toJsonWriter(o, writer, pretty, null, null); - } + private List toList(Class cls, JsonParser src) { - public void toJsonWriter(Object o, Writer writer, boolean pretty, JsonWriteOptions options){ - toJsonWriter(o, writer, pretty, null, null); - } - - public void toJsonWriter(Object o, Writer writer, boolean pretty, JsonWriteOptions options, String callback) { - toJsonInternal(o, new WriteJsonBufferWriter(writer), pretty, options, callback); - } + try { + BeanDescriptor d = getDecriptor(cls); - public String toJsonString(Object o){ - return toJsonString(o, dfltPretty, null); - } + List list = new ArrayList(); - public String toJsonString(Object o, boolean pretty){ - return toJsonString(o, pretty, null); - } + if (!src.hasNext()) { + return list; + } + Event event = src.next(); + if (event != Event.START_ARRAY) { + throw new TextException("Expecting start_array event but got [" + event + "] at [" + src.getLocation() + "]"); + } - public String toJsonString(Object o, boolean pretty, JsonWriteOptions options){ - return toJsonString(o, pretty, options, null); - } - - public String toJsonString(Object o, boolean pretty, JsonWriteOptions options, String callback){ - WriteJsonBufferString b = new WriteJsonBufferString(); - toJsonInternal(o, b, pretty, options, callback); - return b.getBufferOutput(); - } - - @SuppressWarnings("unchecked") - private void toJsonInternal(Object o, WriteJsonBuffer buffer, boolean pretty, JsonWriteOptions options, String requestCallback){ - - if (o == null){ - buffer.append("null"); - } else if (o instanceof Number) { - buffer.append(o.toString()); - } else if (o instanceof Boolean) { - buffer.append(o.toString()); - } else if (o instanceof String) { - EscapeJson.escapeQuote(o.toString(), buffer); - } else if (o instanceof JsonElement) { - - } else if (o instanceof Map,?>){ - toJsonFromMap((Map)o, buffer, pretty, options, requestCallback); - - } else if (o instanceof Collection>){ - toJsonFromCollection((Collection>)o, buffer, pretty, options, requestCallback); - + do { + T bean = d.jsonRead(src, null); + if (bean == null) { + break; } else { - BeanDescriptor> d = getDecriptor(o.getClass()); - WriteJsonContext ctx = new WriteJsonContext(buffer, pretty, dfltValueAdapter, options, requestCallback, server); - d.jsonWrite(ctx, (EntityBean)o); - ctx.end(); + list.add(bean); } + } while (true); + + return list; + + } catch (RuntimeException e) { + throw new TextException("Error parsing " + src, e); } - + } - private void toJsonFromCollection(Collection c, WriteJsonBuffer buffer, boolean pretty, JsonWriteOptions options, String requestCallback){ - - Iterator it = c.iterator(); - if (!it.hasNext()){ - buffer.append("[]"); - return; - } - - WriteJsonContext ctx = new WriteJsonContext(buffer, pretty, dfltValueAdapter, options, requestCallback, server); + public Object toObject(Type genericType, String json) { - Object o = it.next(); - BeanDescriptor> d = getDecriptor(o.getClass()); + TypeInfo info = ParamTypeHelper.getTypeInfo(genericType); + ManyType manyType = info.getManyType(); + switch (manyType) { + case NONE: + return toBean(info.getBeanType(), json); - ctx.appendArrayBegin(); - d.jsonWrite(ctx, (EntityBean)o); - while (it.hasNext()) { - ctx.appendComma(); - T t = it.next(); - d.jsonWrite(ctx, (EntityBean)t); - } - ctx.appendArrayEnd(); - ctx.end(); + case LIST: + return toList(info.getBeanType(), json); + + default: + throw new TextException("Type " + manyType + " not supported"); + } + } + + public Object toObject(Type genericType, Reader json) { + + TypeInfo info = ParamTypeHelper.getTypeInfo(genericType); + ManyType manyType = info.getManyType(); + switch (manyType) { + case NONE: + return toBean(info.getBeanType(), json); + + case LIST: + return toList(info.getBeanType(), json); + + default: + throw new TextException("Type " + manyType + " not supported"); + } + } + + public void toJsonWriter(Object o, Writer writer) { + toJsonWriter(o, writer, null); + } + + + public void toJsonWriter(Object o, Writer writer, JsonWriteOptions options) { + JsonGenerator generator = Json.createGenerator(writer); + toJsonInternal(o, generator, options); + generator.close(); + } + + public String toJsonString(Object o) { + return toJsonString(o, null); + } + + public String toJsonString(Object o, JsonWriteOptions options) { + StringWriter writer = new StringWriter(500); + JsonGenerator gen = Json.createGenerator(writer); + toJsonInternal(o, gen, options); + gen.close(); + return writer.toString(); + } + + @SuppressWarnings("unchecked") + private void toJsonInternal(Object o, JsonGenerator gen, JsonWriteOptions options) { + + if (o == null) { + gen.writeNull(); + } else if (o instanceof Number) { + gen.write(((Number) o).doubleValue()); + } else if (o instanceof Boolean) { + gen.write(((Boolean) o).booleanValue()); + } else if (o instanceof String) { + gen.write((String) o); + + // } else if (o instanceof JsonElement) { + + } else if (o instanceof Map, ?>) { + toJsonFromMap((Map) o, gen, options); + + } else if (o instanceof Collection>) { + toJsonFromCollection((Collection>) o, null, gen, options); + + } else if (o instanceof EntityBean) { + BeanDescriptor> d = getDecriptor(o.getClass()); + WriteJson writeJson = createWriteJson(gen, options); + d.jsonWrite(writeJson, (EntityBean)o, null); + } + } + + private WriteJson createWriteJson(JsonGenerator gen, JsonWriteOptions options) { + PathProperties pathProps = (options == null) ? null : options.getPathProperties(); + return new WriteJson(server, gen, pathProps); + } + + private void toJsonFromCollection(Collection c, String key, JsonGenerator gen, JsonWriteOptions options) { + + if (key == null) { + gen.writeStartArray(); + } else { + gen.writeStartArray(key); } - private void toJsonFromMap(Map map, WriteJsonBuffer buffer, boolean pretty, JsonWriteOptions options, String requestCallback){ - - if (map.isEmpty()){ - buffer.append("{}"); - return; - } - - WriteJsonContext ctx = new WriteJsonContext(buffer, pretty, dfltValueAdapter, options, requestCallback, server); + WriteJson writeJson = createWriteJson(gen, options); - Set> entrySet = map.entrySet(); - Iterator> it = entrySet.iterator(); - - Entry entry = it.next(); - - ctx.appendObjectBegin(); - toJsonMapKey(buffer, false, entry.getKey()); - toJsonMapValue(buffer, pretty, options, requestCallback, entry.getValue()); - - while (it.hasNext()) { - entry = it.next(); - ctx.appendComma(); - toJsonMapKey(buffer, pretty, entry.getKey()); - toJsonMapValue(buffer, pretty, options, requestCallback, entry.getValue()); - } - ctx.appendObjectEnd(); - ctx.end(); + Iterator it = c.iterator(); + while (it.hasNext()) { + T t = it.next(); + BeanDescriptor> d = getDecriptor(t.getClass()); + d.jsonWrite(writeJson, (EntityBean)t, null); } + gen.writeEnd(); + } - private void toJsonMapKey(WriteJsonBuffer buffer, boolean pretty, Object key) { - if (pretty){ - buffer.append("\n"); - } - buffer.append("\""); - buffer.append(key.toString()); - buffer.append("\":"); - } - - private void toJsonMapValue(WriteJsonBuffer buffer, boolean pretty, JsonWriteOptions options, String requestCallback, - Object value) { - - if (value == null){ - buffer.append("null"); - } else { - toJsonInternal(value, buffer, pretty, options, requestCallback); - } - } + private void toJsonFromMap(Map map, JsonGenerator gen, JsonWriteOptions options) { + + Set> entrySet = map.entrySet(); + Iterator> it = entrySet.iterator(); + + WriteJson writeJson = createWriteJson(gen, options); + gen.writeStartObject(); - private BeanDescriptor getDecriptor(Class cls) { - BeanDescriptor d = server.getBeanDescriptor(cls); - if (d == null){ - String msg = "No BeanDescriptor found for "+cls; - throw new RuntimeException(msg); + while (it.hasNext()) { + Entry entry = it.next(); + String key = entry.getKey().toString(); + Object value = entry.getValue(); + if (value == null) { + gen.writeNull(key); + } else { + if (value instanceof Collection>) { + toJsonFromCollection((Collection>) value, key, gen, options); + + } else if (value instanceof EntityBean) { + BeanDescriptor> d = getDecriptor(value.getClass()); + d.jsonWrite(writeJson,(EntityBean) value, key); + + } else { + EJson.write(entry, gen); } - return d; + } } + gen.writeEnd(); + } + + private BeanDescriptor getDecriptor(Class cls) { + BeanDescriptor d = server.getBeanDescriptor(cls); + if (d == null) { + throw new RuntimeException("No BeanDescriptor found for " + cls); + } + return d; + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/DefaultJsonValueAdapter.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/DefaultJsonValueAdapter.java index e2b3dd35f..85fc55c78 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/DefaultJsonValueAdapter.java +++ b/src/main/java/com/avaje/ebeaninternal/server/text/json/DefaultJsonValueAdapter.java @@ -5,9 +5,7 @@ import java.sql.Timestamp; import java.text.SimpleDateFormat; import java.util.TimeZone; -import com.avaje.ebean.text.json.JsonValueAdapter; - -public class DefaultJsonValueAdapter implements JsonValueAdapter { +public class DefaultJsonValueAdapter {//implements JsonValueAdapter { private final SimpleDateFormat dateTimeProto; diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/InternalJsonParser.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/InternalJsonParser.java deleted file mode 100644 index f767fac17..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/InternalJsonParser.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.avaje.ebeaninternal.server.text.json; - -import java.io.Reader; - -import com.avaje.ebean.text.json.JsonElement; - -public class InternalJsonParser { - - public static JsonElement parse(String s) { - - ReadJsonSourceString src = new ReadJsonSourceString(s); - ReadBasicJsonContext b = new ReadBasicJsonContext(src); - return ReadJsonRawReader.readJsonElement(b); - } - - public static JsonElement parse(Reader s) { - - ReadJsonSourceReader src = new ReadJsonSourceReader(s, 512, 256); - ReadBasicJsonContext b = new ReadBasicJsonContext(src); - return ReadJsonRawReader.readJsonElement(b); - } - -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/PathStack.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/PathStack.java index 42825abeb..b62bd1d97 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/PathStack.java +++ b/src/main/java/com/avaje/ebeaninternal/server/text/json/PathStack.java @@ -4,23 +4,23 @@ import com.avaje.ebeaninternal.server.util.ArrayStack; public class PathStack extends ArrayStack { - public String peekFullPath(String key){ - - String prefix = peekWithNull(); - if (prefix != null){ - return prefix+"."+key; - } else { - return key; - } - } - - public void pushPathKey(String key) { + public String peekFullPath(String key) { - String prefix = peekWithNull(); - if (prefix != null){ - key = prefix+"."+key; - } - push(key); + String prefix = peekWithNull(); + if (prefix != null) { + return prefix + "." + key; + } else { + return key; } + } + + public void pushPathKey(String key) { + + String prefix = peekWithNull(); + if (prefix != null) { + key = prefix + "." + key; + } + push(key); + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadBasicJsonContext.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadBasicJsonContext.java deleted file mode 100644 index bcc9c3bfd..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadBasicJsonContext.java +++ /dev/null @@ -1,273 +0,0 @@ -package com.avaje.ebeaninternal.server.text.json; - -import com.avaje.ebean.text.TextException; - -public class ReadBasicJsonContext implements ReadJsonInterface { - - private final ReadJsonSource src; - - private char tokenStart; - private String tokenKey; - private boolean pushedTokenKey; - - public ReadBasicJsonContext(ReadJsonSource src) { - this.src = src; - } - - /** - * Push the current token key back onto the 'stack'. - */ - public void pushTokenKey() { - pushedTokenKey = true; - } - - public char getToken() { - return tokenStart; - } - - public String getTokenKey() { - return tokenKey; - } - - public boolean isTokenKey() { - return '\"' == tokenStart; - } - - public boolean isTokenObjectEnd() { - return '}' == tokenStart; - } - - public boolean readObjectBegin() { - readNextToken(); - if ('{' == tokenStart){ - return true; - } else if ('n' == tokenStart) { - return false; - } else if (']' == tokenStart) { - // an empty array - return false; - } - throw new RuntimeException("Expected object begin at "+src.getErrorHelp()); - } - - public boolean readKeyNext() { - readNextToken(); - if ('\"' == tokenStart){ - return true; - } else if ('}' == tokenStart) { - return false; - } - throw new RuntimeException("Expected '\"' or '}' at "+src.getErrorHelp()); - } - - public boolean readValueNext() { - readNextToken(); - if (',' == tokenStart){ - return true; - } else if ('}' == tokenStart) { - return false; - } - throw new RuntimeException("Expected ',' or '}' at "+src.getErrorHelp()+" but got "+tokenStart); - } - - public boolean readArrayBegin() { - readNextToken(); - if ('[' == tokenStart){ - return true; - } else if ('n' == tokenStart) { - return false; - } - throw new RuntimeException("Expected array begin at "+src.getErrorHelp()); - } - - public boolean readArrayNext() { - readNextToken(); - if (',' == tokenStart){ - return true; - } - if (']' == tokenStart){ - return false; - } - throw new RuntimeException("Expected ',' or ']' at "+src.getErrorHelp()); - } - - public void readNextToken() { - - if (pushedTokenKey) { - // Do nothing - pushedTokenKey = false; - return; - } - - ignoreWhiteSpace(); - - tokenStart = src.nextChar("EOF finding next token"); - switch (tokenStart) { - case '"': - internalReadKey(); - break; - case '{': break; - case '}': break; - case '[': break; // not expected - case ']': break; // not expected - case ',': break; // not expected - case ':': break; // not expected - case 'n': - internalReadNull(); - break; // not expected - - default: - throw new RuntimeException("Unexpected tokenStart["+tokenStart+"] "+src.getErrorHelp()); - } - - } - - public String readQuotedValue() { - - boolean escape = false; - StringBuilder sb = new StringBuilder(); - - do { - char ch = src.nextChar("EOF reading quoted value"); - if (escape) { - // in escape mode so just append the character - escape = false; - switch (ch) { - case 'n': - sb.append('\n'); - break; - case 'r': - sb.append('\r'); - break; - case 't': - sb.append('\t'); - break; - case 'f': - sb.append('\f'); - break; - case 'b': - sb.append('\b'); - break; - case '"': - sb.append('"'); - break; - case 'u': - String msg = "EOF reading unicode value"; - char c1 = src.nextChar(msg); - char c2 = src.nextChar(msg); - char c3 = src.nextChar(msg); - char c4 = src.nextChar(msg); - char u = (char) Integer.parseInt(""+c1+c2+c3+c4, 16); - sb.append(u); - break; - - default: - sb.append('\\'); - sb.append(ch); - break; - } - - } else { - switch (ch) { - case '\\': - // put into 'escape' mode for next character - escape = true; - break; - case '"': - return sb.toString(); - - default: - sb.append(ch); - } - } - } while (true); - } - - public String readUnquotedValue(char c) { - String v = readUnquotedValueRaw(c); - if ("null".equals(v)){ - return null; - } else { - return v; - } - } - - private String readUnquotedValueRaw(char c) { - - StringBuilder sb = new StringBuilder(); - sb.append(c); - - do { - tokenStart = src.nextChar("EOF reading unquoted value"); - switch (tokenStart) { - case ',': - src.back(); - return sb.toString(); - - case '}': - src.back(); - return sb.toString(); - - case ' ': - return sb.toString(); - - case '\t': - return sb.toString(); - - case '\r': - return sb.toString(); - - case '\n': - return sb.toString(); - - default: - sb.append(tokenStart); - } - - } while (true); - - } - - private void internalReadNull() { - - StringBuilder sb = new StringBuilder(4); - sb.append(tokenStart); - for (int i = 0; i < 3; i++) { - char c = src.nextChar("EOF reading null "); - sb.append(c); - } - if (!"null".equals(sb.toString())){ - throw new TextException("Expected 'null' but got "+sb.toString()+" "+src.getErrorHelp()); - } - } - - private void internalReadKey() { - StringBuilder sb = new StringBuilder(); - do { - char c = src.nextChar("EOF reading key"); - if ('\"' == c){ - tokenKey = sb.toString(); - break; - } else { - sb.append(c); - } - } while (true); - - ignoreWhiteSpace(); - - char c = src.nextChar("EOF reading ':'"); - if (':' != c){ - throw new TextException("Expected to find colon after key at "+(src.pos()-1)+" but found ["+c+"]"+src.getErrorHelp()); - } - } - - public void ignoreWhiteSpace() { - src.ignoreWhiteSpace(); - } - - public char nextChar() { - tokenStart = src.nextChar("EOF getting nextChar for raw json"); - return tokenStart; - } - -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonContext.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonContext.java deleted file mode 100644 index 6b7f2d29c..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonContext.java +++ /dev/null @@ -1,177 +0,0 @@ -package com.avaje.ebeaninternal.server.text.json; - -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; - -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.bean.EntityBeanIntercept; -import com.avaje.ebean.text.json.JsonElement; -import com.avaje.ebean.text.json.JsonReadBeanVisitor; -import com.avaje.ebean.text.json.JsonReadOptions; -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.util.ArrayStack; - -public class ReadJsonContext extends ReadBasicJsonContext { - - private final Map> visitorMap; - - private final JsonValueAdapter valueAdapter; - - private final PathStack pathStack; - - private final ArrayStack beanState; - private ReadBeanState currentState; - - public ReadJsonContext(ReadJsonSource src, JsonValueAdapter dfltValueAdapter, JsonReadOptions options) { - super(src); - this.beanState = new ArrayStack(); - if (options == null){ - this.valueAdapter = dfltValueAdapter; - this.visitorMap = null; - this.pathStack = null; - } else { - this.valueAdapter = getValueAdapter(dfltValueAdapter, options.getValueAdapter()); - this.visitorMap = options.getVisitorMap(); - this.pathStack = (visitorMap == null || visitorMap.isEmpty()) ? null : new PathStack(); - } - } - - private JsonValueAdapter getValueAdapter(JsonValueAdapter dfltValueAdapter, JsonValueAdapter valueAdapter) { - return valueAdapter == null ? dfltValueAdapter : valueAdapter; - } - - public JsonValueAdapter getValueAdapter() { - return valueAdapter; - } - - public String readScalarValue() { - - ignoreWhiteSpace(); - - char prevChar = nextChar();//"EOF reading scalarValue?"); - if ('"' == prevChar){ - return readQuotedValue(); - } else { - return readUnquotedValue(prevChar); - } - } - - public void pushBean(Object bean, String path, BeanDescriptor> beanDescriptor){ - currentState = new ReadBeanState(bean, beanDescriptor); - beanState.push(currentState); - if (pathStack != null){ - pathStack.pushPathKey(path); - } - } - - public ReadBeanState popBeanState() { - if (pathStack != null){ - String path = pathStack.peekWithNull(); - JsonReadBeanVisitor> beanVisitor = visitorMap.get(path); - if (beanVisitor != null){ - currentState.visit(beanVisitor); - } - pathStack.pop(); - } - - // return the current ReadBeanState as we can't call setLoadedState() - // yet. We might bind master/detail beans together via mappedBy property - // so wait until after that before calling ReadBeanStatesetLoadedState(); - ReadBeanState s = currentState; - - beanState.pop(); - currentState = beanState.peekWithNull(); - return s; - } - - public void setProperty(String propertyName){ - currentState.setLoaded(propertyName); - } - - /** - * Got a key that doesn't map to a known property so read the json value - * which could be json primitive, object or array. - * - * Provide these values to a JsonReadBeanVisitor if registered. - * - */ - public JsonElement readUnmappedJson(String key) { - - JsonElement rawJsonValue = ReadJsonRawReader.readJsonElement(this); - if (visitorMap != null){ - currentState.addUnmappedJson(key, rawJsonValue); - } - return rawJsonValue; - } - - public static class ReadBeanState implements PropertyChangeListener { - - private final Object bean; - private final BeanDescriptor> beanDescriptor; - private final EntityBeanIntercept ebi; - private final Set loadedProps; - private Map unmapped; - - private ReadBeanState(Object bean, BeanDescriptor> beanDescriptor) { - this.bean = bean; - this.beanDescriptor = beanDescriptor; - if (bean instanceof EntityBean){ - this.ebi = ((EntityBean)bean)._ebean_getIntercept(); - this.loadedProps = new HashSet(); - } else { - this.ebi = null; - this.loadedProps = null; - } - } - public String toString(){ - return bean.getClass().getSimpleName()+" loaded:"+loadedProps; - } - - /** - * Add a loaded/set property to the set of loadedProps. - */ - public void setLoaded(String propertyName){ - if (ebi != null){ - loadedProps.add(propertyName); - } - } - - private void addUnmappedJson(String key, JsonElement value){ - if (unmapped == null){ - unmapped = new LinkedHashMap(); - } - unmapped.put(key, value); - } - - @SuppressWarnings("unchecked") - private void visit(JsonReadBeanVisitor beanVisitor) { - // listen for property change events so that - // we can update the loadedProps if necessary - if (ebi != null){ - ebi.addPropertyChangeListener(this); - } - beanVisitor.visit((T)bean, unmapped); - if (ebi != null){ - ebi.removePropertyChangeListener(this); - } - } - - public void propertyChange(PropertyChangeEvent evt) { - String propName = evt.getPropertyName(); - loadedProps.add(propName); - } - - public Object getBean() { - return bean; - } - - } - - - -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonInterface.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonInterface.java deleted file mode 100644 index 150ca475d..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonInterface.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.avaje.ebeaninternal.server.text.json; - -public interface ReadJsonInterface { - - public void ignoreWhiteSpace(); - - public char nextChar(); - - public String getTokenKey(); - - public boolean readKeyNext(); - - public boolean readValueNext(); - - public boolean readArrayNext(); - - public String readQuotedValue(); - - public String readUnquotedValue(char c); - - -} \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonRawReader.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonRawReader.java deleted file mode 100644 index 1a91f9463..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonRawReader.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.avaje.ebeaninternal.server.text.json; - -import com.avaje.ebean.text.json.JsonElement; -import com.avaje.ebean.text.json.JsonElementArray; -import com.avaje.ebean.text.json.JsonElementBoolean; -import com.avaje.ebean.text.json.JsonElementNull; -import com.avaje.ebean.text.json.JsonElementNumber; -import com.avaje.ebean.text.json.JsonElementObject; -import com.avaje.ebean.text.json.JsonElementString; - - - -public class ReadJsonRawReader { - - public static JsonElement readJsonElement(ReadJsonInterface ctx) { - return new ReadJsonRawReader(ctx).readJsonElement(); - } - - private final ReadJsonInterface ctx; - - private ReadJsonRawReader(ReadJsonInterface ctx){ - this.ctx = ctx; - } - - private JsonElement readJsonElement() { - return readValue(); - } - - private JsonElement readValue() { - - ctx.ignoreWhiteSpace(); - - char c = ctx.nextChar(); - - switch (c) { - case '{': - return readObject(); - - case '[': - return readArray(); - - case '"': - return readString(); - - default: - return readUnquoted(c); - } - } - - private JsonElement readArray() { - - JsonElementArray a = new JsonElementArray(); - - do { - JsonElement value = readValue(); - a.add(value); - if (!ctx.readArrayNext()){ - break; - } - } while(true); - - return a; - } - - private JsonElement readObject() { - - JsonElementObject o = new JsonElementObject(); - - do { - if (!ctx.readKeyNext()){ - break; - } else { - // we read a property key ... - String key = ctx.getTokenKey(); - JsonElement value = readValue(); - - o.put(key, value); - - if (!ctx.readValueNext()){ - break; - } - } - } while(true); - - return o; - } - - private JsonElement readString() { - String s = ctx.readQuotedValue(); - return new JsonElementString(s); - } - - private JsonElement readUnquoted(char c) { - String s = ctx.readUnquotedValue(c); - if ("null".equals(s)){ - return JsonElementNull.NULL; - - } else if ("true".equals(s)){ - return JsonElementBoolean.TRUE; - - } else if ("false".equals(s)) { - return JsonElementBoolean.FALSE; - - } - return new JsonElementNumber(s); - } -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonSource.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonSource.java deleted file mode 100644 index 0d1e12486..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonSource.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.avaje.ebeaninternal.server.text.json; - -public interface ReadJsonSource { - - public char nextChar(String eofMsg); - - public void ignoreWhiteSpace(); - - public void back(); - - public int pos(); - - public String getErrorHelp(); - -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonSourceReader.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonSourceReader.java deleted file mode 100644 index fe8b259a4..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonSourceReader.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.avaje.ebeaninternal.server.text.json; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.Reader; - -import com.avaje.ebean.text.TextException; - -public class ReadJsonSourceReader implements ReadJsonSource { - - private final Reader reader; - - private char[] localBuffer; - - private int totalPos; - private int localPos; - private int localPosEnd; - - public ReadJsonSourceReader(Reader reader, int localBufferSize, int bufferSize) { - this.reader = new BufferedReader(reader,bufferSize); - this.localBuffer = new char[localBufferSize]; - } - - public String toString() { - return String.valueOf(localBuffer); - } - - - - public String getErrorHelp() { - int prev = localPos - 30; - if (prev < 0){ - prev = 0; - } - String c = new String(localBuffer, prev, (localPos-prev)); - return "pos:"+pos()+" preceding:"+c; - } - - public int pos() { - return totalPos+localPos; - } - - - public void ignoreWhiteSpace() { - do { - char c = nextChar("EOF ignoring whitespace"); - if (!Character.isWhitespace(c)){ - --localPos; - break; - } - } while(true); - } - - public void back() { - localPos--; - } - - public char nextChar(String eofMsg) { - if (localPos >= localPosEnd){ - if (!loadLocalBuffer()) { - throw new TextException(eofMsg+" at pos:"+(totalPos+localPos)); - } - } - return localBuffer[localPos++]; - } - - private boolean loadLocalBuffer() { - try { - localPosEnd = reader.read(localBuffer); - if (localPosEnd > 0){ - totalPos += localPos; - localPos = 0; - return true; - } else { - this.localBuffer = null; - return false; - } - - } catch (IOException e){ - throw new TextException(e); - } - } -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonSourceString.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonSourceString.java deleted file mode 100644 index bcbda0961..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonSourceString.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.avaje.ebeaninternal.server.text.json; - -import com.avaje.ebean.text.TextException; - -public class ReadJsonSourceString implements ReadJsonSource { - - private final String source; - private final int sourceLength; - private int pos; - - public ReadJsonSourceString(String source){ - this.source = source; - this.sourceLength = source.length(); - } - - public String getErrorHelp() { - int prev = pos - 50; - if (prev < 0){ - prev = 0; - } - String c = source.substring(prev, pos); - return "pos:"+pos+" precedingcontent:"+c; - } - - public String toString() { - return source; - } - - public int pos() { - return pos; - } - - public void back() { - pos--; - } - - public char nextChar(String eofMsg) { - if (pos >= sourceLength){ - throw new TextException(eofMsg+" at pos:"+pos); - } - return source.charAt(pos++); - } - - public void ignoreWhiteSpace() { - do { - char c = source.charAt(pos); - if (Character.isWhitespace(c)){ - ++pos; - } else { - break; - } - } while(true); - } -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJson.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJson.java new file mode 100644 index 000000000..7fb263f09 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJson.java @@ -0,0 +1,202 @@ +package com.avaje.ebeaninternal.server.text.json; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; + +import javax.json.stream.JsonGenerator; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.text.PathProperties; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.util.ArrayStack; + +public class WriteJson { + + private final SpiEbeanServer server; + + private final JsonGenerator generator; + + private final PathProperties pathProperties; + + private final PathStack pathStack = new PathStack(); + + private final ArrayStack parentBeans = new ArrayStack(); + + public WriteJson(SpiEbeanServer server, JsonGenerator generator, PathProperties pathProperties){ + this.server = server; + this.generator = generator; + this.pathProperties = pathProperties; + } + + public JsonGenerator gen() { + return generator; + } + + public boolean isParentBean(Object bean) { + if (parentBeans.isEmpty()) { + return false; + } else { + return parentBeans.contains(bean); + } + } + + public void pushParentBeanMany(Object parentBean) { + parentBeans.push(parentBean); + } + + public void popParentBeanMany() { + parentBeans.pop(); + } + + public void beginAssocOne(String key, Object bean) { + parentBeans.push(bean); + pathStack.pushPathKey(key); + } + + public void endAssocOne() { + parentBeans.pop(); + pathStack.pop(); + } + + public Set getIncludeProperties() { + + if (pathProperties == null) { + return null; + } else { + return pathProperties.get(pathStack.peekWithNull()); + } + } + + public WriteBean createWriteBean(BeanDescriptor> desc, EntityBean bean) { + + if (pathProperties == null) { + return new WriteBean(desc, bean); + } + + boolean explicitAllProps = false; + Set currentIncludeProps = pathProperties.get(pathStack.peekWithNull()); + if (currentIncludeProps != null) { + explicitAllProps = currentIncludeProps.contains("*"); + if (explicitAllProps || currentIncludeProps.isEmpty()) { + currentIncludeProps = null; + } + } + return new WriteBean(desc, explicitAllProps, currentIncludeProps, bean); + } + + public class WriteBean { + + final boolean explicitAllProps; + final Set currentIncludeProps; + final BeanDescriptor> desc; + final EntityBean currentBean; + + WriteBean(BeanDescriptor> desc, EntityBean currentBean){ + this(desc, false, null, currentBean); + } + + WriteBean(BeanDescriptor> desc, boolean explicitAllProps, Set currentIncludeProps, EntityBean currentBean) { + super(); + this.desc = desc; + this.currentBean = currentBean; + this.explicitAllProps = explicitAllProps; + this.currentIncludeProps = currentIncludeProps; + } + + private boolean isReferenceOnly() { + return !explicitAllProps && currentIncludeProps == null && currentBean._ebean_getIntercept().isReference(); + } + + private boolean isIncludeProperty(BeanProperty prop) { + if (explicitAllProps) + return true; + if (currentIncludeProps != null) { + // explicitly controlled by pathProperties + return currentIncludeProps.contains(prop.getName()); + } else { + // include only loaded properties + return currentBean._ebean_getIntercept().isLoadedProperty(prop.getPropertyIndex()); + } + } + + public void write(WriteJson writeJson) { + //EntityBean bean = writeJson.getBean(); + BeanProperty beanProp = desc.getIdProperty(); + if (beanProp != null) { + if (isIncludeProperty(beanProp)) { + beanProp.jsonWrite(writeJson, currentBean); + } + } + + if (!isReferenceOnly()) { + // render all the properties and invoke lazy loading if required + BeanProperty[] props = desc.propertiesNonTransient(); + for (int j = 0; j < props.length; j++) { + System.out.println("bean "+ currentBean+" prop:"+props[j]); + if (isIncludeProperty(props[j])) { + props[j].jsonWrite(writeJson, currentBean); + } + } + props = desc.propertiesTransient(); + for (int j = 0; j < props.length; j++) { + if (isIncludeProperty(props[j])) { + props[j].jsonWrite(writeJson, currentBean); + } + } + } + } + } + + + public Boolean includeMany(String key) { + if (pathProperties != null) { + String fullPath = pathStack.peekFullPath(key); + return pathProperties.hasPath(fullPath); + } + return null; + } + + public void toJson(String name, Collection> c) { + + beginAssocMany(name); + + Iterator> it = c.iterator(); + while (it.hasNext()) { + EntityBean o = (EntityBean) it.next(); + BeanDescriptor> d = getDecriptor(o.getClass()); + d.jsonWrite(this, o, null); + } + endAssocMany(); + } + + private BeanDescriptor getDecriptor(Class cls) { + BeanDescriptor d = server.getBeanDescriptor(cls); + if (d == null) { + String msg = "No BeanDescriptor found for " + cls; + throw new RuntimeException(msg); + } + return d; + } + + public void beginAssocMany(String key) { + pathStack.pushPathKey(key); + generator.writeStartArray(key); + } + + public void endAssocMany() { + pathStack.pop(); + generator.writeEnd(); + } + + public void writeStartObject(String key) { + if (key == null) { + generator.writeStartObject(); + } else { + generator.writeStartObject(key); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonBuffer.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonBuffer.java deleted file mode 100644 index a16eb47f2..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonBuffer.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.avaje.ebeaninternal.server.text.json; - - -public interface WriteJsonBuffer extends Appendable { - - public WriteJsonBuffer append(String content); - -} \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonBufferString.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonBufferString.java deleted file mode 100644 index 213f8d70f..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonBufferString.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.avaje.ebeaninternal.server.text.json; - -import java.io.IOException; - -public class WriteJsonBufferString implements WriteJsonBuffer { - - private final StringBuilder buffer; - - public WriteJsonBufferString(){ - this.buffer = new StringBuilder(256); - } - - public WriteJsonBufferString append(CharSequence csq) throws IOException { - buffer.append(csq); - return this; - } - - public WriteJsonBufferString append(CharSequence csq, int start, int end) throws IOException { - buffer.append(csq, start, end); - return this; - } - - public WriteJsonBufferString append(char c) throws IOException { - buffer.append(c); - return this; - } - - public WriteJsonBufferString append(String content){ - buffer.append(content); - return this; - } - - public String getBufferOutput() { - return buffer.toString(); - } - - public String toString() { - return buffer.toString(); - } -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonBufferWriter.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonBufferWriter.java deleted file mode 100644 index 422364529..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonBufferWriter.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.avaje.ebeaninternal.server.text.json; - -import java.io.IOException; -import java.io.Writer; - -import com.avaje.ebean.text.TextException; - -public class WriteJsonBufferWriter implements WriteJsonBuffer { - - private final Writer buffer; - - public WriteJsonBufferWriter(Writer buffer){ - this.buffer = buffer; - } - - public WriteJsonBufferWriter append(String content){ - try { - buffer.write(content); - return this; - } catch (IOException e) { - throw new TextException(e); - } - } - - public WriteJsonBufferWriter append(CharSequence csq) throws IOException { - return append(csq, 0, csq.length()); - } - - public WriteJsonBufferWriter append(CharSequence csq, int start, int end) throws IOException { - for (int i = start; i < end; i++) { - buffer.append(csq.charAt(i)); - } - return this; - } - - public WriteJsonBufferWriter append(char c) throws IOException { - try { - buffer.write(c); - return this; - } catch (IOException e) { - throw new TextException(e); - } - } - -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonContext.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonContext.java deleted file mode 100644 index 7112f8a4e..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonContext.java +++ /dev/null @@ -1,376 +0,0 @@ -package com.avaje.ebeaninternal.server.text.json; - -import java.util.Collection; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; - -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.bean.EntityBeanIntercept; -import com.avaje.ebean.text.PathProperties; -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebean.text.json.JsonWriteBeanVisitor; -import com.avaje.ebean.text.json.JsonWriteOptions; -import com.avaje.ebean.text.json.JsonWriter; -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.type.EscapeJson; -import com.avaje.ebeaninternal.server.type.ScalarType; -import com.avaje.ebeaninternal.server.util.ArrayStack; - - -public class WriteJsonContext implements JsonWriter { - - private final SpiEbeanServer server; - - private final WriteJsonBuffer buffer; - - private final boolean pretty; - - private final JsonValueAdapter valueAdapter; - - private final ArrayStack parentBeans = new ArrayStack(); - - private final PathProperties pathProperties; - - private final Map> visitorMap; - - private final String callback; - - private final PathStack pathStack; - - private WriteBeanState beanState; - - private int depthOffset; - - boolean assocOne; - - public WriteJsonContext(WriteJsonBuffer buffer, boolean pretty, JsonValueAdapter dfltValueAdapter, - JsonWriteOptions options, String requestCallback, SpiEbeanServer server){ - - this.server = server; - this.buffer = buffer; - this.pretty = pretty; - this.pathStack = new PathStack(); - this.callback = getCallback(requestCallback, options); - if (options == null){ - this.valueAdapter = dfltValueAdapter; - this.visitorMap = null; - this.pathProperties = null; - - } else { - this.valueAdapter = getValueAdapter(dfltValueAdapter, options.getValueAdapter()); - this.visitorMap = emptyToNull(options.getVisitorMap()); - this.pathProperties = emptyToNull(options.getPathProperties()); - } - - if (callback != null){ - buffer.append(requestCallback).append("("); - } - } - - public void toJson(String name, Collection> c) { - - beginAssocMany(name); - - Iterator> it = c.iterator(); - if (!it.hasNext()){ - endAssocMany(); - return; - } - - EntityBean o = (EntityBean)it.next(); - BeanDescriptor> d = getDecriptor(o.getClass()); - - d.jsonWrite(this, o); - while (it.hasNext()) { - appendComma(); - EntityBean t = (EntityBean)it.next(); - d.jsonWrite(this, t); - } - endAssocMany(); - } - - private BeanDescriptor getDecriptor(Class cls) { - BeanDescriptor d = server.getBeanDescriptor(cls); - if (d == null){ - String msg = "No BeanDescriptor found for "+cls; - throw new RuntimeException(msg); - } - return d; - } - - public void appendRawValue(String key, String rawJsonValue) { - appendKeyWithComma(key, true); - buffer.append(rawJsonValue); - } - - public void appendQuoteEscapeValue(String key, String valueToEscape) { - appendKeyWithComma(key, true); - EscapeJson.escapeQuote(valueToEscape, buffer); - } - - public void end() { - if (callback != null){ - buffer.append(")"); - } - } - - private Map emptyToNull(Map m){ - if ( m == null || m.isEmpty()) { - return null; - } else { - return m; - } - } - - private PathProperties emptyToNull(PathProperties m){ - if ( m == null || m.isEmpty()) { - return null; - } else { - return m; - } - } - - private String getCallback(String requestCallback, JsonWriteOptions options) { - if (requestCallback != null){ - return requestCallback; - } - if (options != null){ - return options.getCallback(); - } - return null; - } - - private JsonValueAdapter getValueAdapter(JsonValueAdapter dfltValueAdapter, JsonValueAdapter valueAdapter) { - return valueAdapter == null ? dfltValueAdapter : valueAdapter; - } - - /** - * Return the set of properties to write to JSON. If null is returned then - * the default will output the properties loaded for this bean. - */ - public Set getIncludeProperties() { - if (pathProperties != null){ - String path = pathStack.peekWithNull(); - return pathProperties.get(path); - } - return null; - } - - public JsonWriteBeanVisitor> getBeanVisitor() { - if (visitorMap != null){ - String path = pathStack.peekWithNull(); - return visitorMap.get(path); - } - return null; - } - - public String getJson() { - return buffer.toString(); - } - - private void appendIndent(){ - - buffer.append("\n"); - int depth = depthOffset + parentBeans.size(); - for (int i = 0; i < depth; i++) { - buffer.append(" "); - } - } - - public void appendObjectBegin(){ - if (pretty && !assocOne){ - appendIndent(); - } - buffer.append("{"); - } - public void appendObjectEnd(){ - buffer.append("}"); - } - - public void appendArrayBegin(){ - if (pretty){ - appendIndent(); - } - buffer.append("["); - depthOffset++; - } - - public void appendArrayEnd(){ - depthOffset--; - if (pretty){ - appendIndent(); - } - buffer.append("]"); - } - - public void appendComma(){ - buffer.append(","); - } - - public void addDepthOffset(int offset){ - depthOffset += offset; - } - - public void beginAssocOneIsNull(String key) { - depthOffset++; - internalAppendKeyBegin(key); - appendNull(); - depthOffset--; - } - - public void beginAssocOne(String key) { - pathStack.pushPathKey(key); - - internalAppendKeyBegin(key); - assocOne = true; - } - - public void endAssocOne() { - - pathStack.pop(); - assocOne = false; - } - - public Boolean includeMany(String key) { - if (pathProperties != null){ - String fullPath = pathStack.peekFullPath(key); - return pathProperties.hasPath(fullPath); - } - return null; - } - - public void beginAssocMany(String key) { - - pathStack.pushPathKey(key); - - depthOffset--; - internalAppendKeyBegin(key); - depthOffset++; - buffer.append("["); - } - - public void endAssocMany(){ - - pathStack.pop(); - - if (pretty){ - depthOffset--; - appendIndent(); - depthOffset++; - } - buffer.append("]"); - } - - private void internalAppendKeyBegin(String key) { - if (!beanState.isFirstKey()){ - buffer.append(","); - } - if (pretty){ - appendIndent(); - } - appendKeyWithComma(key, false); - } - - public void appendNameValue(String key, ScalarType scalarType, T value) { - appendKeyWithComma(key, true); - scalarType.jsonWrite(buffer, value, getValueAdapter()); - } - - public void appendDiscriminator(String key, String discValue) { - appendKeyWithComma(key, true); - buffer.append("\""); - buffer.append(discValue); - buffer.append("\""); - } - - private void appendKeyWithComma(String key, boolean withComma) { - if (withComma){ - if (!beanState.isFirstKey()){ - buffer.append(","); - } - } - buffer.append("\""); - if(key == null) { - buffer.append("null"); - } else { - buffer.append(key); - } - buffer.append("\":"); - } - - public void appendNull(String key) { - appendKeyWithComma(key, true); - buffer.append("null"); - } - - public void appendNull() { - buffer.append("null"); - } - - public JsonValueAdapter getValueAdapter() { - return valueAdapter; - } - - public String toString() { - return buffer.toString(); - } - - public void popParentBean(){ - parentBeans.pop(); - } - - public void pushParentBean(Object parentBean){ - parentBeans.push(parentBean); - } - - public void popParentBeanMany(){ - parentBeans.pop(); - depthOffset--; - } - - public void pushParentBeanMany(Object parentBean){ - parentBeans.push(parentBean); - depthOffset++; - } - - public boolean isParentBean(Object bean){ - if (parentBeans.isEmpty()){ - return false; - } else { - return parentBeans.contains(bean); - } - } - - public WriteBeanState pushBeanState(Object bean) { - WriteBeanState newState = new WriteBeanState();//bean); - WriteBeanState prevState = beanState; - beanState = newState; - return prevState; - } - - public void pushPreviousState(WriteBeanState previousState) { - this.beanState = previousState; - } - - - public static class WriteBeanState { - - private boolean firstKeyOut; - - public WriteBeanState() { - - } - - public boolean isFirstKey() { - if (!firstKeyOut){ - firstKeyOut = true; - return true; - } else { - return false; - } - } - - } -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/CtCompoundType.java b/src/main/java/com/avaje/ebeaninternal/server/type/CtCompoundType.java index a929f2e35..da6ab5d71 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/CtCompoundType.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/CtCompoundType.java @@ -6,17 +6,10 @@ import java.util.Map; import com.avaje.ebean.config.CompoundType; import com.avaje.ebean.config.CompoundTypeProperty; -import com.avaje.ebean.text.json.JsonElement; -import com.avaje.ebean.text.json.JsonElementObject; -import com.avaje.ebeaninternal.server.text.json.ReadJsonContext; -import com.avaje.ebeaninternal.server.text.json.WriteJsonContext; -import com.avaje.ebeaninternal.server.text.json.WriteJsonContext.WriteBeanState; +import com.avaje.ebeaninternal.server.text.json.WriteJson; /** - * The internal representation of a Compound Type (Immutable Compound Value - * Object). - * - * @author rbygrave + * The internal representation of a Compound Type (Immutable Compound Value Object). * * @param * The Type of the "Immutable Compound Value Object". @@ -168,47 +161,26 @@ public final class CtCompoundType implements ScalarDataReader { return parent + "." + propName; } } - - public Object jsonRead(ReadJsonContext ctx) { - - if (!ctx.readObjectBegin()) { - // the object is null - return null; - } - - JsonElementObject jsonObject = new JsonElementObject(); - do { - if (!ctx.readKeyNext()){ - break; - } else { - // we read a property key ... - String propName = ctx.getTokenKey(); - JsonElement unmappedJson = ctx.readUnmappedJson(propName); - jsonObject.put(propName, unmappedJson); - - if (!ctx.readValueNext()){ - break; - } - } - } while(true); - - return readJsonElementObject(ctx, jsonObject); + + public Object jsonConvert(Map map) { + return readJsonElementObject(map); } - private Object readJsonElementObject(ReadJsonContext ctx, JsonElementObject jsonObject){ + @SuppressWarnings("unchecked") + private Object readJsonElementObject(Map jsonObject){ boolean nullValue = false; Object[] values = new Object[propReaders.length]; for (int i = 0; i < propReaders.length; i++) { String propName = properties[i].getName(); - JsonElement jsonElement = jsonObject.get(propName); + Object jsonElement = jsonObject.get(propName); if (propReaders[i] instanceof CtCompoundType>) { - values[i] = ((CtCompoundType>)propReaders[i]).readJsonElementObject(ctx, (JsonElementObject)jsonElement); - + values[i] = ((CtCompoundType>)propReaders[i]).readJsonElementObject((Map)jsonElement); } else { - values[i] = ((ScalarType>)propReaders[i]).jsonFromString(jsonElement.toPrimitiveString(), ctx.getValueAdapter()); + //((ScalarType>)propReaders[i]).jsonFromString(jsonElement.toPrimitiveString(), ctx.getValueAdapter()); + values[i] = ((ScalarType>)propReaders[i]).parse(jsonElement.toString());; } if (values[i] == null){ nullValue = true; @@ -223,40 +195,35 @@ public final class CtCompoundType implements ScalarDataReader { } - public void jsonWrite(WriteJsonContext ctx, Object valueObject, String propertyName) { - - if (valueObject == null){ - ctx.beginAssocOneIsNull(propertyName); - - } else { - ctx.pushParentBean(valueObject); - ctx.beginAssocOne(propertyName); - jsonWriteProps(ctx, valueObject, propertyName); - ctx.endAssocOne(); - ctx.popParentBean(); - } + public void jsonWrite(WriteJson ctx, Object valueObject, String propertyName) { + + ctx.beginAssocOne(propertyName, valueObject); + jsonWriteProps(ctx, valueObject, propertyName); + ctx.endAssocOne(); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private void jsonWriteProps(WriteJson ctx, Object valueObject, String propertyName) { + + if (propertyName != null) { + ctx.gen().writeStartObject(propertyName); + } else { + ctx.gen().writeStartObject(); } + for (int i = 0; i < properties.length; i++) { + String propName = properties[i].getName(); + Object value = properties[i].getValue((V) valueObject); + if (propReaders[i] instanceof CtCompoundType>) { + ((CtCompoundType) propReaders[i]).jsonWrite(ctx, value, propName); - @SuppressWarnings({ "unchecked", "rawtypes" }) - private void jsonWriteProps(WriteJsonContext ctx, Object valueObject, String propertyName) { - - ctx.appendObjectBegin(); - WriteBeanState prevState = ctx.pushBeanState(valueObject); - - for (int i = 0; i < properties.length; i++) { - String propName = properties[i].getName(); - Object value = properties[i].getValue((V)valueObject); - if (propReaders[i] instanceof CtCompoundType>) { - ((CtCompoundType)propReaders[i]).jsonWrite(ctx, value, propName); - - } else { - ctx.appendNameValue(propName, (ScalarType)propReaders[i], value); - } - } - - ctx.pushPreviousState(prevState); - ctx.appendObjectEnd(); + } else { + ((ScalarType) propReaders[i]).jsonWrite(ctx.gen(), propName, value); + //ctx.appendNameValue(propName, (ScalarType) propReaders[i], value); + } } + ctx.gen().writeEnd(); + } + } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/EscapeJson.java b/src/main/java/com/avaje/ebeaninternal/server/type/EscapeJson.java index 1267aa31a..c173e606a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/EscapeJson.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/EscapeJson.java @@ -4,7 +4,6 @@ import java.io.IOException; import com.avaje.ebean.text.TextException; import com.avaje.ebean.util.StringHelper; -import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; public class EscapeJson { @@ -45,24 +44,6 @@ public class EscapeJson { } - public static void escape(String value, WriteJsonBuffer sb) { - if (value == null) { - sb.append("null"); - } else { - escapeAppend(value, sb); - } - } - - public static void escapeQuote(String value, WriteJsonBuffer sb) { - if (value == null) { - sb.append("null"); - } else { - sb.append("\""); - escapeAppend(value, sb); - sb.append("\""); - } - } - /** * Escape quotes, \, /, \r, \n, \b, \f, \t and characters (U+0000 through * U+001F). diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarType.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarType.java index 693a46a3d..8495fc3f8 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarType.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarType.java @@ -5,10 +5,12 @@ import java.io.DataOutput; import java.io.IOException; import java.sql.SQLException; +import javax.json.stream.JsonGenerator; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; + import com.avaje.ebean.text.StringFormatter; import com.avaje.ebean.text.StringParser; -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; /** * Describes a scalar type. @@ -183,14 +185,12 @@ public interface ScalarType extends StringParser, StringFormatter, ScalarData */ public boolean isDateTimeCapable(); - public void jsonWrite(WriteJsonBuffer buffer, T value, JsonValueAdapter ctx); - - public String jsonToString(T value, JsonValueAdapter ctx); + public Object readData(DataInput dataInput) throws IOException; - public T jsonFromString(String value, JsonValueAdapter ctx); + public void writeData(DataOutput dataOutput, Object v) throws IOException; - public Object readData(DataInput dataInput) throws IOException; + public Object jsonRead(JsonParser ctx, Event event); - public void writeData(DataOutput dataOutput, Object v) throws IOException; + public void jsonWrite(JsonGenerator ctx, String name, Object value); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBase.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBase.java index 842433c1b..c553dcad0 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBase.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBase.java @@ -1,7 +1,5 @@ package com.avaje.ebeaninternal.server.type; -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; @@ -74,25 +72,12 @@ public abstract class ScalarTypeBase implements ScalarType { return value; } - public void loadIgnore(DataReader dataReader) { - dataReader.incrementPos(1); - } - - public void accumulateScalarTypes(String propName, CtCompoundTypeScalarList list) { - list.addScalarType(propName, this); - } + public void loadIgnore(DataReader dataReader) { + dataReader.incrementPos(1); + } - public void jsonWrite(WriteJsonBuffer buffer, T value, JsonValueAdapter ctx) { - String v = jsonToString(value, ctx); - buffer.append(v); - } - - public String jsonToString(T value, JsonValueAdapter ctx) { - return formatValue(value); - } - - public T jsonFromString(String value, JsonValueAdapter ctx) { - return parse(value); - } + public void accumulateScalarTypes(String propName, CtCompoundTypeScalarList list) { + list.addScalarType(propName, this); + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseDate.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseDate.java index b11c01c2c..d48b040c2 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseDate.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseDate.java @@ -7,8 +7,9 @@ import java.sql.Date; import java.sql.SQLException; import java.sql.Types; -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; +import javax.json.stream.JsonGenerator; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; /** * Base class for Date types. @@ -62,22 +63,22 @@ public abstract class ScalarTypeBaseDate extends ScalarTypeBase { } @Override - public String jsonToString(T value, JsonValueAdapter ctx) { - Date date = convertToDate(value); - return ctx.jsonFromDate(date); + public Object jsonRead(JsonParser ctx, Event event) { + if (ctx.isIntegralNumber()) { + return parseDateTime(ctx.getLong()); + } else { + String string = ctx.getString(); + throw new RuntimeException("convert "+string); + } } - @Override - public void jsonWrite(WriteJsonBuffer buffer, T value, JsonValueAdapter ctx) { - String s = jsonToString(value, ctx); - buffer.append(s); + public void jsonWrite(JsonGenerator ctx, String name, Object value) { + long millis = convertToMillis(value); + ctx.write(name, millis); } + + public abstract long convertToMillis(Object value); - @Override - public T jsonFromString(String value, JsonValueAdapter ctx) { - Date ts = ctx.jsonToDate(value); - return convertFromDate(ts); - } public Object readData(DataInput dataInput) throws IOException { if (!dataInput.readBoolean()) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseDateTime.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseDateTime.java index 9c6d3f0e2..287de52b9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseDateTime.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseDateTime.java @@ -7,8 +7,9 @@ import java.sql.SQLException; import java.sql.Timestamp; import java.sql.Types; -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; +import javax.json.stream.JsonGenerator; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; /** * Base type for DateTime types. @@ -19,6 +20,8 @@ public abstract class ScalarTypeBaseDateTime extends ScalarTypeBase { super(type, jdbcNative, jdbcType); } + public abstract long convertToMillis(Object value); + public abstract Timestamp convertToTimestamp(T t); public abstract T convertFromTimestamp(Timestamp ts); @@ -42,6 +45,23 @@ public abstract class ScalarTypeBaseDateTime extends ScalarTypeBase { } } + @Override + public Object jsonRead(JsonParser ctx, Event event) { + if (ctx.isIntegralNumber()) { + long millis = ctx.getLong(); + return parseDateTime(millis); + } else { + String string = ctx.getString(); + throw new RuntimeException("convert "+string); + } + } + + @Override + public void jsonWrite(JsonGenerator ctx, String name, Object value) { + long millis = convertToMillis(value); + ctx.write(name, millis); + } + public String formatValue(T t) { Timestamp ts = convertToTimestamp(t); return ts.toString(); @@ -60,24 +80,6 @@ public abstract class ScalarTypeBaseDateTime extends ScalarTypeBase { public boolean isDateTimeCapable() { return true; } - - @Override - public void jsonWrite(WriteJsonBuffer buffer, T value, JsonValueAdapter ctx) { - String v = jsonToString(value, ctx); - buffer.append(v); - } - - @Override - public String jsonToString(T value, JsonValueAdapter ctx) { - Timestamp ts = convertToTimestamp(value); - return ctx.jsonFromTimestamp(ts); - } - - @Override - public T jsonFromString(String value, JsonValueAdapter ctx) { - Timestamp ts = ctx.jsonToTimestamp(value); - return convertFromTimestamp(ts); - } public Object readData(DataInput dataInput) throws IOException { if (!dataInput.readBoolean()) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseVarchar.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseVarchar.java index e54a325ce..38950f0d9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseVarchar.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseVarchar.java @@ -6,9 +6,11 @@ import java.io.IOException; import java.sql.SQLException; import java.sql.Types; +import javax.json.stream.JsonGenerator; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; + import com.avaje.ebean.text.TextException; -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; /** * Base ScalarType for types which converts to and from a VARCHAR database @@ -81,19 +83,6 @@ public abstract class ScalarTypeBaseVarchar extends ScalarTypeBase { return formatValue((T) v); } - public T jsonFromString(String value, JsonValueAdapter ctx) { - return parse(EscapeJson.unescapeSlash(value)); - } - - public String toJsonString(Object value, JsonValueAdapter ctx) { - return EscapeJson.escapeQuote(format(value)); - } - - @Override - public void jsonWrite(WriteJsonBuffer buffer, T value, JsonValueAdapter ctx) { - EscapeJson.escapeQuote(format(value), buffer); - } - public Object readData(DataInput dataInput) throws IOException { if (!dataInput.readBoolean()) { return null; @@ -115,4 +104,13 @@ public abstract class ScalarTypeBaseVarchar extends ScalarTypeBase { dataOutput.writeUTF(s); } } + + @Override + public Object jsonRead(JsonParser ctx, Event event) { + return parse(ctx.getString()); + } + + public void jsonWrite(JsonGenerator ctx, String name, Object value) { + ctx.write(name, format(value)); + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBigDecimal.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBigDecimal.java index bc3e1e25a..91f363fe5 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBigDecimal.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBigDecimal.java @@ -4,9 +4,14 @@ import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.math.BigDecimal; +import java.math.BigInteger; import java.sql.SQLException; import java.sql.Types; +import javax.json.stream.JsonGenerator; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; + import com.avaje.ebeaninternal.server.core.BasicTypeConverter; /** @@ -74,5 +79,15 @@ public class ScalarTypeBigDecimal extends ScalarTypeBase { public boolean isDateTimeCapable() { return true; } + + @Override + public Object jsonRead(JsonParser ctx, Event event) { + return ctx.getBigDecimal(); + } + + public void jsonWrite(JsonGenerator ctx, String name, Object value) { + ctx.write(name, (BigDecimal)value); + } + } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBoolean.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBoolean.java index f26d43e21..44608da90 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBoolean.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBoolean.java @@ -3,9 +3,14 @@ package com.avaje.ebeaninternal.server.type; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; +import java.math.BigDecimal; import java.sql.SQLException; import java.sql.Types; +import javax.json.stream.JsonGenerator; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; + import com.avaje.ebean.text.TextException; import com.avaje.ebeaninternal.server.core.BasicTypeConverter; @@ -286,6 +291,15 @@ public class ScalarTypeBoolean { dataOutput.writeBoolean(val.booleanValue()); } } + + @Override + public Object jsonRead(JsonParser ctx, Event event) { + return Event.VALUE_TRUE == event ? Boolean.TRUE : Boolean.FALSE; + } + + public void jsonWrite(JsonGenerator ctx, String name, Object value) { + ctx.write(name, (Boolean)value); + } } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeByte.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeByte.java index 502e37b10..003b78e2a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeByte.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeByte.java @@ -6,6 +6,10 @@ import java.io.IOException; import java.sql.SQLException; import java.sql.Types; +import javax.json.stream.JsonGenerator; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; + import com.avaje.ebean.text.TextException; import com.avaje.ebeaninternal.server.core.BasicTypeConverter; @@ -37,9 +41,18 @@ public class ScalarTypeByte extends ScalarTypeBase { public Byte toBeanType(Object value) { return BasicTypeConverter.toByte(value); } - - public String formatValue(Byte t) { + @Override + public void jsonWrite(JsonGenerator ctx, String name, Object value) { + throw new TextException("Not supported"); + } + + @Override + public Object jsonRead(JsonParser ctx, Event event) { + throw new TextException("Not supported"); + } + + public String formatValue(Byte t) { return t.toString(); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesBase.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesBase.java index 34992b06c..a2c11e603 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesBase.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesBase.java @@ -5,6 +5,10 @@ import java.io.DataOutput; import java.io.IOException; import java.sql.SQLException; +import javax.json.stream.JsonGenerator; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; + import com.avaje.ebean.text.TextException; /** @@ -41,7 +45,17 @@ public abstract class ScalarTypeBytesBase extends ScalarTypeBase { } - public String formatValue(byte[] t) { + @Override + public void jsonWrite(JsonGenerator ctx, String name, Object value) { + throw new TextException("Not supported"); + } + + @Override + public Object jsonRead(JsonParser ctx, Event event) { + throw new TextException("Not supported"); + } + + public String formatValue(byte[] t) { throw new TextException("Not supported"); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesEncrypted.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesEncrypted.java index 843977369..032002882 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesEncrypted.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesEncrypted.java @@ -5,8 +5,11 @@ import java.io.DataOutput; import java.io.IOException; import java.sql.SQLException; -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; +import javax.json.stream.JsonGenerator; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; + +import com.avaje.ebean.text.TextException; /** * Encrypted ScalarType that wraps a byte[] types. @@ -65,6 +68,16 @@ public class ScalarTypeBytesEncrypted implements ScalarType { baseType.loadIgnore(dataReader); } + @Override + public void jsonWrite(JsonGenerator ctx, String name, Object value) { + throw new TextException("Not supported"); + } + + @Override + public Object jsonRead(JsonParser ctx, Event event) { + throw new TextException("Not supported"); + } + public String format(Object v) { throw new RuntimeException("Not used"); } @@ -100,18 +113,6 @@ public class ScalarTypeBytesEncrypted implements ScalarType { baseType.accumulateScalarTypes(propName, list); } - public void jsonWrite(WriteJsonBuffer buffer, byte[] value, JsonValueAdapter ctx) { - baseType.jsonWrite(buffer, value, ctx); - } - - public String jsonToString(byte[] value, JsonValueAdapter ctx) { - return baseType.jsonToString(value, ctx); - } - - public byte[] jsonFromString(String value, JsonValueAdapter ctx) { - return baseType.jsonFromString(value, ctx); - } - public Object readData(DataInput dataInput) throws IOException { int len = dataInput.readInt(); byte[] value = new byte[len]; diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCalendar.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCalendar.java index 4655f4d93..c21e95600 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCalendar.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCalendar.java @@ -39,6 +39,11 @@ public class ScalarTypeCalendar extends ScalarTypeBaseDateTime { return calendar; } + @Override + public long convertToMillis(Object value) { + return ((Calendar) value).getTimeInMillis(); + } + @Override public Timestamp convertToTimestamp(Calendar t) { return new Timestamp(t.getTimeInMillis()); diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeChar.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeChar.java index c69474b47..8dc695e7a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeChar.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeChar.java @@ -3,7 +3,9 @@ package com.avaje.ebeaninternal.server.type; import java.sql.SQLException; import java.sql.Types; -import com.avaje.ebean.text.json.JsonValueAdapter; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; + import com.avaje.ebeaninternal.server.core.BasicTypeConverter; /** @@ -59,15 +61,11 @@ public class ScalarTypeChar extends ScalarTypeBaseVarchar { public Character parse(String value) { return value.charAt(0); } - + @Override - public Character jsonFromString(String value, JsonValueAdapter ctx) { - return value.charAt(0); + public Object jsonRead(JsonParser ctx, Event event) { + return ctx.getString(); } - @Override - public String jsonToString(Character value, JsonValueAdapter ctx) { - return EscapeJson.escapeQuote(value.toString()); - } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCharArray.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCharArray.java index 83cea2c8c..72adf5552 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCharArray.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCharArray.java @@ -3,7 +3,10 @@ package com.avaje.ebeaninternal.server.type; import java.sql.SQLException; import java.sql.Types; -import com.avaje.ebean.text.json.JsonValueAdapter; +import javax.json.stream.JsonGenerator; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; + import com.avaje.ebeaninternal.server.core.BasicTypeConverter; /** @@ -59,15 +62,13 @@ public class ScalarTypeCharArray extends ScalarTypeBaseVarchar
- * You will only use the JsonElements when you register a JsonReadBeanVisitor. - * The JSON elements that are not mapped to a bean property are made available - * to the JsonReadBeanVisitor. - *
- * This can not be used for JsonElementObject or JsonElementArray. - *
- * This visit method is called after all the known properties of the bean have - * been processed. Any JSON elements that could not be mapped to known bean - * properties are available in the unmapped Map. - *
- * This provides a method of customising the bean and processing any custom - * JSON content. - *
- * You can optionally provide a custom JsonValueAdapter to handle specific - * formatting for Date and DateTime types. - *
- * You can optionally register JsonReadBeanVisitors to customise the processing - * of the beans as they are processed and handle any custom JSON elements that - * could not be mapped to bean properties. - *
- * There is not a standard JSON format for Date or Timestamp types. By default - * Ebean uses ISO8601 "yyyy-MM-dd'T'HH:mm:ss.SSSZ" and "yyyy-MM-dd". - *
- * Note that Ebean will convert Joda types to either of the Date or Timestamp - * types and back for you. - *
- * You can use this to add raw JSON content via {@link JsonWriter}. - *
- * You register a JsonWriteBeanVisitor with {@link JsonWriteOptions}. - *
+ * Primarily for supporting Embedded beans with overridden dbColumn + * mappings. + *
+ * Generally only used to ensure id properties are converted for + * Query.setId() use. + *
+ * For base types this returns true. + *
+ * For an Enum returns IN expression for the set of Enum values. + *
- * Provide these values to a JsonReadBeanVisitor if registered. - *