You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@johnzon.apache.org by st...@apache.org on 2016/04/03 19:01:23 UTC
[04/10] incubator-johnzon git commit: JOHNZON-71 introduce
MapperConfig and improve ObjectConverter interface
JOHNZON-71 introduce MapperConfig and improve ObjectConverter interface
Project: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/commit/e41b829b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/tree/e41b829b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/diff/e41b829b
Branch: refs/heads/master
Commit: e41b829b34e785e28b24d9771e27976a0fee2ce6
Parents: 45cfcf2
Author: Mark Struberg <st...@apache.org>
Authored: Sun Mar 27 19:04:09 2016 +0200
Committer: Mark Struberg <st...@apache.org>
Committed: Sun Mar 27 19:04:09 2016 +0200
----------------------------------------------------------------------
.../johnzon/core/JsonParserFactoryImpl.java | 2 +-
.../apache/johnzon/mapper/JsonbGenerator.java | 61 --
.../org/apache/johnzon/mapper/JsonbParser.java | 40 -
.../java/org/apache/johnzon/mapper/Mapper.java | 65 +-
.../apache/johnzon/mapper/MapperBuilder.java | 76 +-
.../org/apache/johnzon/mapper/MapperConfig.java | 131 ++++
.../apache/johnzon/mapper/MappingGenerator.java | 61 ++
.../apache/johnzon/mapper/MappingParser.java | 49 ++
.../org/apache/johnzon/mapper/Mappings.java | 765 ++++++++++++++++++
.../apache/johnzon/mapper/ObjectConverter.java | 7 +-
.../johnzon/mapper/reflection/Mappings.java | 770 -------------------
.../org/apache/johnzon/mapper/AdapterTest.java | 6 +-
.../apache/johnzon/mapper/ObjectTypeTest.java | 41 +-
13 files changed, 1107 insertions(+), 967 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/e41b829b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonParserFactoryImpl.java
----------------------------------------------------------------------
diff --git a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonParserFactoryImpl.java b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonParserFactoryImpl.java
index 6480ac8..c57f144 100644
--- a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonParserFactoryImpl.java
+++ b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonParserFactoryImpl.java
@@ -32,7 +32,7 @@ import javax.json.JsonObject;
import javax.json.stream.JsonParser;
import javax.json.stream.JsonParserFactory;
-class JsonParserFactoryImpl extends AbstractJsonFactory implements JsonParserFactory {
+public class JsonParserFactoryImpl extends AbstractJsonFactory implements JsonParserFactory {
public static final String MAX_STRING_LENGTH = "org.apache.johnzon.max-string-length";
public static final int DEFAULT_MAX_STRING_LENGTH = Integer.getInteger(MAX_STRING_LENGTH, 10 * 1024 * 1024); //10m
http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/e41b829b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JsonbGenerator.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JsonbGenerator.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JsonbGenerator.java
deleted file mode 100644
index 9edfa01..0000000
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JsonbGenerator.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.johnzon.mapper;
-
-import javax.json.stream.JsonGenerator;
-
-/**
- * Handles writing Json for Objects.
- * Internally it uses a {@link JsonGenerator} to write JSON
- *
- * To write JSON-P structure elements you can use the {@link #getJsonGenerator()} method.
- *
- */
-public interface JsonbGenerator {
-
- /**
- * @return the {@link JsonGenerator} used internally to write the JSON output.
- */
- JsonGenerator getJsonGenerator();
-
- /**
- * Write the given Object o into the current JSON layer.
- * This will <em>not</em> open a new json layer ('{', '}')
- * but really just write the attributes of o to the currently opened layer.
- *
- * Consider you have a class
- * <pre>
- * public class Customer {
- * private String firstName;
- * private String lastName;
- * private Address address;
- * ...
- * }
- * </pre>
- * then the resulting JSON String will e.g. look like
- * <pre>
- * "firstName":"Karl", "lastName":"SomeName", "address":{"street":"mystreet"}
- * </pre>
- * @param o the object to write
- * @return itself, for easier chaining of commands
- */
- JsonbGenerator writeObject(Object o);
-
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/e41b829b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JsonbParser.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JsonbParser.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JsonbParser.java
deleted file mode 100644
index ef128f3..0000000
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JsonbParser.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.johnzon.mapper;
-
-import javax.json.stream.JsonGenerator;
-import javax.json.stream.JsonParser;
-
-/**
- * Handles reading Json for Objects.
- * Internally it uses a {@link JsonParser} to write JSON
- *
- * To write JSON-P structure elements you can use the {@link #getJsonParser()} ()} method.
- *
- */
-public interface JsonbParser {
-
- /**
- * @return the {@link JsonGenerator} used internally to write the JSON output.
- */
- JsonParser getJsonParser();
-
-
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/e41b829b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
index 063a6e3..53e0904 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
@@ -25,7 +25,6 @@ import org.apache.johnzon.mapper.internal.AdapterKey;
import org.apache.johnzon.mapper.internal.ConverterAdapter;
import org.apache.johnzon.mapper.reflection.JohnzonCollectionType;
import org.apache.johnzon.mapper.reflection.JohnzonParameterizedType;
-import org.apache.johnzon.mapper.reflection.Mappings;
import javax.json.JsonArray;
import javax.json.JsonNumber;
@@ -54,7 +53,6 @@ import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
-import java.nio.charset.Charset;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
@@ -87,43 +85,28 @@ public class Mapper implements Closeable {
private static final Adapter<Object, String> FALLBACK_CONVERTER = new ConverterAdapter<Object>(new FallbackConverter());
private static final JohnzonParameterizedType ANY_LIST = new JohnzonParameterizedType(List.class, Object.class);
+ protected final MapperConfig config;
protected final Mappings mappings;
protected final JsonReaderFactory readerFactory;
protected final JsonGeneratorFactory generatorFactory;
- protected final boolean close;
protected final ConcurrentMap<AdapterKey, Adapter<?, ?>> adapters;
protected final ConcurrentMap<Adapter<?, ?>, AdapterKey> reverseAdaptersRegistry = new ConcurrentHashMap<Adapter<?, ?>, AdapterKey>();
protected final int version;
- protected final boolean skipNull;
- protected final boolean skipEmptyArray;
- protected final boolean treatByteArrayAsBase64;
- protected final boolean treatByteArrayAsBase64URL;
- protected final boolean readAttributeBeforeWrite;
- protected final Charset encoding;
protected final ReaderHandler readerHandler;
protected final Collection<Closeable> closeables;
- // CHECKSTYLE:OFF
- public Mapper(final JsonReaderFactory readerFactory, final JsonGeneratorFactory generatorFactory,
- final boolean doClose, final Map<AdapterKey, Adapter<?, ?>> adapters,
- final int version, final Comparator<String> attributeOrder, final boolean skipNull, final boolean skipEmptyArray,
- final AccessMode accessMode, final boolean treatByteArrayAsBase64, final boolean treatByteArrayAsBase64URL, final Charset encoding,
- final Collection<Closeable> closeables, final boolean readAttributeBeforeWrite) {
- // CHECKSTYLE:ON
+ Mapper(final JsonReaderFactory readerFactory, final JsonGeneratorFactory generatorFactory, MapperConfig config,
+ final Map<AdapterKey, Adapter<?, ?>> adapters,
+ final int version, final Comparator<String> attributeOrder,
+ final Collection<Closeable> closeables) {
this.readerFactory = readerFactory;
this.generatorFactory = generatorFactory;
- this.close = doClose;
+ this.config = config;
this.adapters = new ConcurrentHashMap<AdapterKey, Adapter<?, ?>>(adapters);
this.version = version;
- this.mappings = new Mappings(attributeOrder, accessMode, version, this.adapters);
- this.skipNull = skipNull;
- this.skipEmptyArray = skipEmptyArray;
- this.treatByteArrayAsBase64 = treatByteArrayAsBase64;
- this.treatByteArrayAsBase64URL = treatByteArrayAsBase64URL;
- this.encoding = encoding;
+ this.mappings = new Mappings(attributeOrder, config.getAccessMode(), version, this.adapters);
this.readerHandler = ReaderHandler.create(readerFactory);
this.closeables = closeables;
- this.readAttributeBeforeWrite = readAttributeBeforeWrite;
}
private static JsonGenerator writePrimitives(final JsonGenerator generator, final Object value) {
@@ -245,7 +228,7 @@ public class Mapper implements Closeable {
}
public <T> void writeArray(final Collection<T> object, final OutputStream stream) {
- writeArray(object, new OutputStreamWriter(stream, encoding));
+ writeArray(object, new OutputStreamWriter(stream, config.getEncoding()));
}
public <T> void writeArray(final Collection<T> object, final Writer stream) {
@@ -276,7 +259,7 @@ public class Mapper implements Closeable {
}
private void doCloseOrFlush(final JsonGenerator generator) {
- if (close) {
+ if (config.isClose()) {
generator.close();
} else {
generator.flush();
@@ -284,7 +267,7 @@ public class Mapper implements Closeable {
}
public <T> void writeIterable(final Iterable<T> object, final OutputStream stream) {
- writeIterable(object, new OutputStreamWriter(stream, encoding));
+ writeIterable(object, new OutputStreamWriter(stream, config.getEncoding()));
}
public <T> void writeIterable(final Iterable<T> object, final Writer stream) {
@@ -311,7 +294,7 @@ public class Mapper implements Closeable {
} catch (final IOException e) {
throw new MapperException(e);
} finally {
- if (close) {
+ if (config.isClose()) {
try {
stream.close();
} catch (final IOException e) {
@@ -332,7 +315,7 @@ public class Mapper implements Closeable {
}
public void writeObject(final Object object, final OutputStream stream) {
- final JsonGenerator generator = generatorFactory.createGenerator(stream, encoding);
+ final JsonGenerator generator = generatorFactory.createGenerator(stream, config.getEncoding());
doWriteHandlingNullObject(object, generator);
}
@@ -435,7 +418,7 @@ public class Mapper implements Closeable {
}
if (value == null) {
- if (skipNull && !getter.reader.isNillable()) {
+ if (config.isSkipNull() && !getter.reader.isNillable()) {
continue;
} else {
gen.writeNull(getterEntry.getKey());
@@ -462,7 +445,7 @@ public class Mapper implements Closeable {
final Object key = entry.getKey();
if (value == null) {
- if (skipNull) {
+ if (config.isSkipNull()) {
continue;
} else {
gen.writeNull(key == null ? "null" : key.toString());
@@ -490,16 +473,16 @@ public class Mapper implements Closeable {
final String key, final Object value) throws InvocationTargetException, IllegalAccessException {
if (array) {
final int length = Array.getLength(value);
- if (length == 0 && skipEmptyArray) {
+ if (length == 0 && config.isSkipEmptyArray()) {
return generator;
}
- if(treatByteArrayAsBase64 && (type == byte[].class /*|| type == Byte[].class*/)) {
+ if(config.isTreatByteArrayAsBase64() && (type == byte[].class /*|| type == Byte[].class*/)) {
String base64EncodedByteArray = DatatypeConverter.printBase64Binary((byte[]) value);
generator.write(key, base64EncodedByteArray);
return generator;
}
- if(treatByteArrayAsBase64URL && (type == byte[].class /*|| type == Byte[].class*/)) {
+ if(config.isTreatByteArrayAsBase64URL() && (type == byte[].class /*|| type == Byte[].class*/)) {
return generator.write(key, String.valueOf(Adapter.class.cast(adapters.get(new AdapterKey(byte[].class, String.class))).to(value)));
}
@@ -542,7 +525,7 @@ public class Mapper implements Closeable {
newGen = doWriteArray(Collection.class.cast(o), generator);
} else if (o != null && o.getClass().isArray()) {
final int length = Array.getLength(o);
- if (length > 0 || !skipEmptyArray) {
+ if (length > 0 || !config.isSkipEmptyArray()) {
newGen = generator.writeStartArray();
for (int i = 0; i < length; i++) {
newGen = writeItem(newGen, Array.get(o, i));
@@ -613,7 +596,7 @@ public class Mapper implements Closeable {
} catch (final Exception e) {
throw new MapperException(e);
} finally {
- if (close) {
+ if (config.isClose()) {
reader.close();
}
}
@@ -630,7 +613,7 @@ public class Mapper implements Closeable {
} catch (final Exception e) {
throw new MapperException(e);
} finally {
- if (close) {
+ if (config.isClose()) {
reader.close();
}
}
@@ -655,7 +638,7 @@ public class Mapper implements Closeable {
} catch (final Exception e) {
throw new MapperException(e);
} finally {
- if (close) {
+ if (config.isClose()) {
reader.close();
}
}
@@ -687,7 +670,7 @@ public class Mapper implements Closeable {
} catch (final Exception e) {
throw new MapperException(e);
} finally {
- if (close) {
+ if (config.isClose()) {
reader.close();
}
}
@@ -790,7 +773,7 @@ public class Mapper implements Closeable {
setterMethod.write(t, null);
} else {
Object existingInstance = null;
- if (readAttributeBeforeWrite) {
+ if (config.isReadAttributeBeforeWrite()) {
final Mappings.Getter getter = classMapping.getters.get(setter.getKey());
if (getter != null) {
try {
@@ -869,7 +852,7 @@ public class Mapper implements Closeable {
throw new MapperException("Unable to parse " + jsonValue + " to boolean");
}
- if(treatByteArrayAsBase64 && jsonValue.getValueType() == ValueType.STRING && (type == byte[].class /*|| type == Byte[].class*/)) {
+ if(config.isTreatByteArrayAsBase64() && jsonValue.getValueType() == ValueType.STRING && (type == byte[].class /*|| type == Byte[].class*/)) {
return DatatypeConverter.parseBase64Binary(((JsonString)jsonValue).getString());
}
http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/e41b829b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java
index e78f2f2..eb23074 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java
@@ -43,6 +43,8 @@ import org.apache.johnzon.mapper.converter.URLConverter;
import org.apache.johnzon.mapper.internal.AdapterKey;
import org.apache.johnzon.mapper.internal.ConverterAdapter;
+import org.apache.johnzon.core.JsonParserFactoryImpl;
+
import javax.json.JsonReaderFactory;
import javax.json.spi.JsonProvider;
import javax.json.stream.JsonGenerator;
@@ -94,27 +96,19 @@ public class MapperBuilder {
DEFAULT_CONVERTERS.put(new AdapterKey(Locale.class, String.class), new LocaleConverter());
}
+ private MapperConfig builderConfig = new MapperConfig();
+
private JsonReaderFactory readerFactory;
private JsonGeneratorFactory generatorFactory;
- private boolean doCloseOnStreams = false;
private boolean supportHiddenAccess = true;
private int version = -1;
private int maxSize = -1;
private int bufferSize = -1;
private String bufferStrategy;
private Comparator<String> attributeOrder = null;
- private boolean skipNull = true;
- private boolean skipEmptyArray = false;
- private boolean supportsComments = false;
- protected boolean pretty;
- private AccessMode accessMode;
- private boolean treatByteArrayAsBase64;
- private boolean treatByteArrayAsBase64URL;
private final Map<AdapterKey, Adapter<?, ?>> adapters = new HashMap<AdapterKey, Adapter<?, ?>>(DEFAULT_CONVERTERS);
private boolean supportConstructors;
- private Charset encoding = Charset.forName(System.getProperty("johnzon.mapper.encoding", "UTF-8"));
private boolean useGetterForCollections;
- private boolean readAttributeBeforeWrite;
private String accessModeName;
private final Collection<Closeable> closeables = new ArrayList<Closeable>();
@@ -123,9 +117,9 @@ public class MapperBuilder {
final JsonProvider provider = JsonProvider.provider();
final Map<String, Object> config = new HashMap<String, Object>();
if (bufferStrategy != null) {
- config.put("org.apache.johnzon.buffer-strategy", bufferStrategy);
+ config.put(JsonParserFactoryImpl.BUFFER_STRATEGY, bufferStrategy);
}
- if (pretty) {
+ if (builderConfig.isPrettyPrint()) {
config.put(JsonGenerator.PRETTY_PRINTING, true);
}
@@ -134,46 +128,44 @@ public class MapperBuilder {
}
config.remove(JsonGenerator.PRETTY_PRINTING); // doesnt mean anything anymore for reader
- if (supportsComments) {
- config.put("org.apache.johnzon.supports-comments", "true");
+ if (builderConfig.isSupportsComments()) {
+ config.put(JsonParserFactoryImpl.SUPPORTS_COMMENTS, "true");
}
if (maxSize > 0) {
- config.put("org.apache.johnzon.max-string-length", maxSize);
+ config.put(JsonParserFactoryImpl.MAX_STRING_LENGTH, maxSize);
}
if (bufferSize > 0) {
- config.put("org.apache.johnzon.default-char-buffer", bufferSize);
+ config.put(JsonParserFactoryImpl.BUFFER_LENGTH, bufferSize);
}
if (readerFactory == null) {
readerFactory = provider.createReaderFactory(config);
}
}
- if (accessMode == null) {
+ if (builderConfig.getAccessMode() == null) {
if ("field".equalsIgnoreCase(accessModeName)) {
- this.accessMode = new FieldAccessMode(supportConstructors, supportHiddenAccess);
+ builderConfig.setAccessMode(new FieldAccessMode(supportConstructors, supportHiddenAccess));
} else if ("method".equalsIgnoreCase(accessModeName)) {
- this.accessMode = new MethodAccessMode(supportConstructors, supportHiddenAccess, true);
+ builderConfig.setAccessMode(new MethodAccessMode(supportConstructors, supportHiddenAccess, true));
} else if ("strict-method".equalsIgnoreCase(accessModeName)) {
- this.accessMode = new MethodAccessMode(supportConstructors, supportHiddenAccess, false);
+ builderConfig.setAccessMode(new MethodAccessMode(supportConstructors, supportHiddenAccess, false));
} else if ("both".equalsIgnoreCase(accessModeName)) {
- this.accessMode = new FieldAndMethodAccessMode(supportConstructors, supportHiddenAccess);
+ builderConfig.setAccessMode(new FieldAndMethodAccessMode(supportConstructors, supportHiddenAccess));
} else {
- this.accessMode = new MethodAccessMode(supportConstructors, supportHiddenAccess, useGetterForCollections);
+ builderConfig.setAccessMode(new MethodAccessMode(supportConstructors, supportHiddenAccess, useGetterForCollections));
}
}
+ // new config so builderConfig can get tweaked again.
+ MapperConfig config = builderConfig.clone();
+
return new Mapper(
readerFactory, generatorFactory,
- doCloseOnStreams,
+ config,
adapters,
version,
attributeOrder,
- skipNull, skipEmptyArray,
- accessMode,
- treatByteArrayAsBase64, treatByteArrayAsBase64URL,
- encoding,
- closeables,
- readAttributeBeforeWrite);
+ closeables);
}
public MapperBuilder addCloseable(final Closeable closeable) {
@@ -182,11 +174,11 @@ public class MapperBuilder {
}
public MapperBuilder setIgnoreFieldsForType(final Class<?> type, final String... fields) {
- if (BaseAccessMode.class.isInstance(accessMode)) {
+ if (BaseAccessMode.class.isInstance(builderConfig.getAccessMode())) {
if (fields == null || fields.length == 0) {
- BaseAccessMode.class.cast(accessMode).getFieldsToRemove().remove(type);
+ BaseAccessMode.class.cast(builderConfig.getAccessMode()).getFieldsToRemove().remove(type);
} else {
- BaseAccessMode.class.cast(accessMode).getFieldsToRemove().put(type, fields);
+ BaseAccessMode.class.cast(builderConfig.getAccessMode()).getFieldsToRemove().put(type, fields);
}
} else {
throw new IllegalStateException("AccessMode is not an BaseAccessMode");
@@ -200,12 +192,12 @@ public class MapperBuilder {
}
public MapperBuilder setSupportsComments(final boolean supportsComments) {
- this.supportsComments = supportsComments;
+ builderConfig.setSupportsComments(supportsComments);
return this;
}
public MapperBuilder setPretty(final boolean pretty) {
- this.pretty = pretty;
+ builderConfig.setPrettyPrint(pretty);
return this;
}
@@ -225,7 +217,7 @@ public class MapperBuilder {
}
public MapperBuilder setAccessMode(final AccessMode mode) {
- this.accessMode = mode;
+ builderConfig.setAccessMode(mode);
return this;
}
@@ -259,7 +251,7 @@ public class MapperBuilder {
}
public MapperBuilder setDoCloseOnStreams(final boolean doCloseOnStreams) {
- this.doCloseOnStreams = doCloseOnStreams;
+ builderConfig.setClose(doCloseOnStreams);
return this;
}
@@ -297,22 +289,22 @@ public class MapperBuilder {
}
public MapperBuilder setSkipNull(final boolean skipNull) {
- this.skipNull = skipNull;
+ builderConfig.setSkipNull(skipNull);
return this;
}
public MapperBuilder setSkipEmptyArray(final boolean skipEmptyArray) {
- this.skipEmptyArray = skipEmptyArray;
+ builderConfig.setSkipEmptyArray(skipEmptyArray);
return this;
}
public MapperBuilder setTreatByteArrayAsBase64(final boolean treatByteArrayAsBase64) {
- this.treatByteArrayAsBase64 = treatByteArrayAsBase64;
+ builderConfig.setTreatByteArrayAsBase64(treatByteArrayAsBase64);
return this;
}
public MapperBuilder setTreatByteArrayAsBase64URL(final boolean treatByteArrayAsBase64URL) {
- this.treatByteArrayAsBase64URL = treatByteArrayAsBase64URL;
+ builderConfig.setTreatByteArrayAsBase64URL(treatByteArrayAsBase64URL);
return this;
}
@@ -322,12 +314,12 @@ public class MapperBuilder {
}
public MapperBuilder setEncoding(final String encoding) {
- this.encoding = Charset.forName(encoding);
+ builderConfig.setEncoding(Charset.forName(encoding));
return this;
}
public MapperBuilder setReadAttributeBeforeWrite(final boolean readAttributeBeforeWrite) {
- this.readAttributeBeforeWrite = readAttributeBeforeWrite;
+ builderConfig.setReadAttributeBeforeWrite(readAttributeBeforeWrite);
return this;
}
}
http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/e41b829b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java
new file mode 100644
index 0000000..5c51e12
--- /dev/null
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.mapper;
+
+import java.nio.charset.Charset;
+
+import org.apache.johnzon.mapper.access.AccessMode;
+
+/**
+ * Contains internal configuration for all the mapper stuff
+ */
+class MapperConfig implements Cloneable {
+ private boolean close = false;
+ private boolean skipNull = true;
+ private boolean skipEmptyArray = false;
+ private boolean supportsComments = false;
+ private boolean treatByteArrayAsBase64;
+ private boolean treatByteArrayAsBase64URL;
+ private boolean readAttributeBeforeWrite;
+ private boolean prettyPrint;
+ private AccessMode accessMode;
+ private Charset encoding = Charset.forName(System.getProperty("johnzon.mapper.encoding", "UTF-8"));
+
+ MapperConfig() {
+ }
+
+ void setClose(boolean close) {
+ this.close = close;
+ }
+
+ public boolean isClose() {
+ return close;
+ }
+
+ public boolean isSkipNull() {
+ return skipNull;
+ }
+
+ void setSkipNull(boolean skipNull) {
+ this.skipNull = skipNull;
+ }
+
+ public boolean isSkipEmptyArray() {
+ return skipEmptyArray;
+ }
+
+ void setSkipEmptyArray(boolean skipEmptyArray) {
+ this.skipEmptyArray = skipEmptyArray;
+ }
+
+ public boolean isSupportsComments() {
+ return supportsComments;
+ }
+
+ void setSupportsComments(boolean supportsComments) {
+ this.supportsComments = supportsComments;
+ }
+
+ public boolean isTreatByteArrayAsBase64() {
+ return treatByteArrayAsBase64;
+ }
+
+ void setTreatByteArrayAsBase64(boolean treatByteArrayAsBase64) {
+ this.treatByteArrayAsBase64 = treatByteArrayAsBase64;
+ }
+
+ public boolean isTreatByteArrayAsBase64URL() {
+ return treatByteArrayAsBase64URL;
+ }
+
+ void setTreatByteArrayAsBase64URL(boolean treatByteArrayAsBase64URL) {
+ this.treatByteArrayAsBase64URL = treatByteArrayAsBase64URL;
+ }
+
+ public boolean isReadAttributeBeforeWrite() {
+ return readAttributeBeforeWrite;
+ }
+
+ void setReadAttributeBeforeWrite(boolean readAttributeBeforeWrite) {
+ this.readAttributeBeforeWrite = readAttributeBeforeWrite;
+ }
+
+ public boolean isPrettyPrint() {
+ return prettyPrint;
+ }
+
+ void setPrettyPrint(boolean prettyPrint) {
+ this.prettyPrint = prettyPrint;
+ }
+
+ public AccessMode getAccessMode() {
+ return accessMode;
+ }
+
+ void setAccessMode(AccessMode accessMode) {
+ this.accessMode = accessMode;
+ }
+
+ public Charset getEncoding() {
+ return encoding;
+ }
+
+ void setEncoding(Charset encoding) {
+ this.encoding = encoding;
+ }
+
+ @Override
+ public MapperConfig clone() {
+ try {
+ return (MapperConfig) super.clone();
+ } catch (CloneNotSupportedException e) {
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/e41b829b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGenerator.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGenerator.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGenerator.java
new file mode 100644
index 0000000..b3719a2
--- /dev/null
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGenerator.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.mapper;
+
+import javax.json.stream.JsonGenerator;
+
+/**
+ * Handles writing Json for Objects.
+ * Internally it uses a {@link JsonGenerator} to write JSON
+ *
+ * To write JSON-P structure elements you can use the {@link #getJsonGenerator()} method.
+ *
+ */
+public interface MappingGenerator {
+
+ /**
+ * @return the {@link JsonGenerator} used internally to write the JSON output.
+ */
+ JsonGenerator getJsonGenerator();
+
+ /**
+ * Write the given Object o into the current JSON layer.
+ * This will <em>not</em> open a new json layer ('{', '}')
+ * but really just write the attributes of o to the currently opened layer.
+ *
+ * Consider you have a class
+ * <pre>
+ * public class Customer {
+ * private String firstName;
+ * private String lastName;
+ * private Address address;
+ * ...
+ * }
+ * </pre>
+ * then the resulting JSON String will e.g. look like
+ * <pre>
+ * "firstName":"Karl", "lastName":"SomeName", "address":{"street":"mystreet"}
+ * </pre>
+ * @param o the object to write
+ * @return itself, for easier chaining of commands
+ */
+ MappingGenerator writeObject(Object o);
+
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/e41b829b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParser.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParser.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParser.java
new file mode 100644
index 0000000..615dfaf
--- /dev/null
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParser.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.mapper;
+
+import java.lang.reflect.Type;
+
+import javax.json.JsonReader;
+import javax.json.JsonValue;
+import javax.json.stream.JsonParser;
+
+/**
+ * Handles reading Json for Objects.
+ * Internally it uses a {@link JsonParser} to write JSON
+ *
+ * To write JSON-P structure elements you can use the {@link #getJsonParser()} ()} method.
+ *
+ */
+public interface MappingParser {
+
+ /**
+ * @return the {@link JsonParser} used internally to read the JSON input.
+ */
+ JsonParser getJsonParser();
+
+ /**
+ * @return the {@link JsonReader} to read in full {@link javax.json.JsonValue}s from the {@link #getJsonParser()}
+ */
+ JsonReader getJsonReader();
+
+ <T> T readObject(Type targetType);
+
+ <T> T readObject(JsonValue jsonValue, Type targetType);
+}
http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/e41b829b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java
new file mode 100644
index 0000000..da210fc
--- /dev/null
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java
@@ -0,0 +1,765 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.mapper;
+
+import org.apache.johnzon.mapper.access.AccessMode;
+import org.apache.johnzon.mapper.converter.DateWithCopyConverter;
+import org.apache.johnzon.mapper.converter.EnumConverter;
+import org.apache.johnzon.mapper.internal.AdapterKey;
+import org.apache.johnzon.mapper.internal.ConverterAdapter;
+import org.apache.johnzon.mapper.reflection.JohnzonParameterizedType;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Array;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import static java.util.Arrays.asList;
+import static org.apache.johnzon.mapper.reflection.Converters.matches;
+
+public class Mappings {
+ public static class ClassMapping {
+ final Class<?> clazz;
+ final AccessMode.Factory factory;
+ final Map<String, Getter> getters;
+ final Map<String, Setter> setters;
+
+
+ protected ClassMapping(final Class<?> clazz, final AccessMode.Factory factory,
+ final Map<String, Getter> getters, final Map<String, Setter> setters) {
+ this.clazz = clazz;
+ this.factory = factory;
+ this.getters = getters;
+ this.setters = setters;
+ }
+ }
+
+ public static class CollectionMapping {
+ final Class<?> raw;
+ final Type arg;
+ final boolean primitive;
+
+ public CollectionMapping(final boolean primitive, final Class<?> collectionType, final Type fieldArgType) {
+ this.raw = collectionType;
+ this.arg = fieldArgType;
+ this.primitive = primitive;
+ }
+ }
+
+ public static class Getter {
+ final AccessMode.Reader reader;
+ final int version;
+ final Adapter converter;
+ final Adapter itemConverter;
+ final boolean primitive;
+ final boolean array;
+ final boolean map;
+ final boolean collection;
+
+ public Getter(final AccessMode.Reader reader,
+ final boolean primitive, final boolean array,
+ final boolean collection, final boolean map,
+ final Adapter<?, ?> converter,
+ final int version) {
+ this.reader = reader;
+ this.version = version;
+ this.array = array;
+ this.collection = collection;
+ this.primitive = primitive;
+ if (converter != null && matches(reader.getType(), converter)) {
+ this.converter = converter;
+ this.itemConverter = null;
+ } else if (converter != null) {
+ this.converter = null;
+ this.itemConverter = converter;
+ } else {
+ this.converter = null;
+ this.itemConverter = null;
+ }
+ this.map = map && this.converter == null;
+ }
+
+ @Override
+ public String toString() {
+ return "Getter{" +
+ "reader=" + reader +
+ ", version=" + version +
+ ", converter=" + converter +
+ ", itemConverter=" + itemConverter +
+ ", primitive=" + primitive +
+ ", array=" + array +
+ ", map=" + map +
+ ", collection=" + collection +
+ '}';
+ }
+ }
+
+ public static class Setter {
+ public final AccessMode.Writer writer;
+ public final int version;
+ public final Type paramType;
+ public final Adapter<?, ?> converter;
+ public final Adapter<?, ?> itemConverter;
+ public final boolean primitive;
+ public final boolean array;
+
+ public Setter(final AccessMode.Writer writer, final boolean primitive, final boolean array,
+ final Type paramType, final Adapter<?, ?> converter,
+ final int version) {
+ this.writer = writer;
+ this.paramType = paramType;
+ this.version = version;
+ this.primitive = primitive;
+ this.array = array;
+ if (converter != null && matches(writer.getType(), converter)) {
+ this.converter = converter;
+ this.itemConverter = null;
+ } else if (converter != null) {
+ this.converter = null;
+ this.itemConverter = converter;
+ } else {
+ this.converter = null;
+ this.itemConverter = null;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Setter{" +
+ "writer=" + writer +
+ ", version=" + version +
+ ", paramType=" + paramType +
+ ", converter=" + converter +
+ ", itemConverter=" + itemConverter +
+ ", primitive=" + primitive +
+ ", array=" + array +
+ '}';
+ }
+ }
+
+ private static final JohnzonParameterizedType VIRTUAL_TYPE = new JohnzonParameterizedType(Map.class, String.class, Object.class);
+
+ protected final ConcurrentMap<Type, ClassMapping> classes = new ConcurrentHashMap<Type, ClassMapping>();
+ protected final ConcurrentMap<Type, CollectionMapping> collections = new ConcurrentHashMap<Type, CollectionMapping>();
+ protected final Comparator<String> fieldOrdering;
+ protected final ConcurrentMap<AdapterKey, Adapter<?, ?>> adapters;
+ private final AccessMode accessMode;
+ private final int version;
+
+ public Mappings(final Comparator<String> attributeOrder, final AccessMode accessMode,
+ final int version, final ConcurrentMap<AdapterKey, Adapter<?, ?>> adapters) {
+ this.fieldOrdering = attributeOrder;
+ this.accessMode = accessMode;
+ this.version = version;
+ this.adapters = adapters;
+ }
+
+ public <T> CollectionMapping findCollectionMapping(final ParameterizedType genericType) {
+ CollectionMapping collectionMapping = collections.get(genericType);
+ if (collectionMapping == null) {
+ collectionMapping = createCollectionMapping(genericType);
+ if (collectionMapping == null) {
+ return null;
+ }
+ final CollectionMapping existing = collections.putIfAbsent(genericType, collectionMapping);
+ if (existing != null) {
+ collectionMapping = existing;
+ }
+ }
+ return collectionMapping;
+ }
+
+ private <T> CollectionMapping createCollectionMapping(final ParameterizedType aType) {
+ final Type[] fieldArgTypes = aType.getActualTypeArguments();
+ final Type raw = aType.getRawType();
+ if (fieldArgTypes.length == 1 && Class.class.isInstance(raw)) {
+ final Class<?> r = Class.class.cast(raw);
+ final Class<?> collectionType;
+ if (List.class.isAssignableFrom(r)) {
+ collectionType = List.class;
+ } else if (SortedSet.class.isAssignableFrom(r)) {
+ collectionType = SortedSet.class;
+ } else if (Set.class.isAssignableFrom(r)) {
+ collectionType = Set.class;
+ } else if (Deque.class.isAssignableFrom(r)) {
+ collectionType = Deque.class;
+ } else if (Queue.class.isAssignableFrom(r)) {
+ collectionType = Queue.class;
+ } else if (Collection.class.isAssignableFrom(r)) {
+ collectionType = Collection.class;
+ } else {
+ return null;
+ }
+
+ final CollectionMapping mapping = new CollectionMapping(isPrimitive(fieldArgTypes[0]), collectionType, fieldArgTypes[0]);
+ collections.putIfAbsent(aType, mapping);
+ return mapping;
+ }
+ return null;
+ }
+
+ // has JSon API a method for this type
+ public static boolean isPrimitive(final Type type) {
+ if (type == String.class) {
+ return true;
+ } else if (type == char.class || type == Character.class) {
+ return true;
+ } else if (type == long.class || type == Long.class) {
+ return true;
+ } else if (type == int.class || type == Integer.class
+ || type == byte.class || type == Byte.class
+ || type == short.class || type == Short.class) {
+ return true;
+ } else if (type == double.class || type == Double.class
+ || type == float.class || type == Float.class) {
+ return true;
+ } else if (type == boolean.class || type == Boolean.class) {
+ return true;
+ } else if (type == BigDecimal.class) {
+ return true;
+ } else if (type == BigInteger.class) {
+ return true;
+ }
+ return false;
+ }
+
+ public ClassMapping getClassMapping(final Type clazz) {
+ return classes.get(clazz);
+ }
+
+ public ClassMapping findOrCreateClassMapping(final Type clazz) {
+ ClassMapping classMapping = classes.get(clazz);
+ if (classMapping == null) {
+ if (!Class.class.isInstance(clazz) || Map.class.isAssignableFrom(Class.class.cast(clazz))) {
+ return null;
+ }
+
+ classMapping = createClassMapping(Class.class.cast(clazz));
+ final ClassMapping existing = classes.putIfAbsent(clazz, classMapping);
+ if (existing != null) {
+ classMapping = existing;
+ }
+ }
+ return classMapping;
+ }
+
+ protected ClassMapping createClassMapping(final Class<?> inClazz) {
+ boolean copyDate = false;
+ for (final Class<?> itf : inClazz.getInterfaces()) {
+ if ("org.apache.openjpa.enhance.PersistenceCapable".equals(itf.getName())) {
+ copyDate = true;
+ break;
+ }
+ }
+ final Class<?> clazz = findModelClass(inClazz);
+
+ Comparator<String> fieldComparator = accessMode.fieldComparator(inClazz);
+ fieldComparator = fieldComparator == null ? fieldOrdering : fieldComparator;
+
+ final Map<String, Getter> getters = fieldComparator == null ? newOrderedMap(Getter.class) : new TreeMap<String, Getter>(fieldComparator);
+ final Map<String, Setter> setters = fieldComparator == null ? newOrderedMap(Setter.class) : new TreeMap<String, Setter>(fieldComparator);
+
+ final Map<String, AccessMode.Reader> readers = accessMode.findReaders(clazz);
+ final Map<String, AccessMode.Writer> writers = accessMode.findWriters(clazz);
+
+ final Collection<String> virtualFields = new HashSet<String>();
+ {
+ final JohnzonVirtualObjects virtualObjects = clazz.getAnnotation(JohnzonVirtualObjects.class);
+ if (virtualObjects != null) {
+ for (final JohnzonVirtualObject virtualObject : virtualObjects.value()) {
+ handleVirtualObject(virtualFields, virtualObject, getters, setters, readers, writers, copyDate);
+ }
+ }
+
+ final JohnzonVirtualObject virtualObject = clazz.getAnnotation(JohnzonVirtualObject.class);
+ if (virtualObject != null) {
+ handleVirtualObject(virtualFields, virtualObject, getters, setters, readers, writers, copyDate);
+ }
+ }
+
+ for (final Map.Entry<String, AccessMode.Reader> reader : readers.entrySet()) {
+ final String key = reader.getKey();
+ if (virtualFields.contains(key)) {
+ continue;
+ }
+ addGetterIfNeeded(getters, key, reader.getValue(), copyDate);
+ }
+
+ for (final Map.Entry<String, AccessMode.Writer> writer : writers.entrySet()) {
+ final String key = writer.getKey();
+ if (virtualFields.contains(key)) {
+ continue;
+ }
+ addSetterIfNeeded(setters, key, writer.getValue(), copyDate);
+ }
+ return new ClassMapping(clazz, accessMode.findFactory(clazz), getters, setters);
+ }
+
+ protected Class<?> findModelClass(final Class<?> inClazz) {
+ Class<?> clazz = inClazz;
+ // unproxy to get a clean model
+ while (clazz != null && clazz != Object.class
+ && (clazz.getName().contains("$$") || clazz.getName().contains("$proxy")
+ || clazz.getName().startsWith("org.apache.openjpa.enhance.") /* subclassing mode, not the default */)) {
+ clazz = clazz.getSuperclass();
+ }
+ if (clazz == null || clazz == Object.class) { // shouldn't occur but a NPE protection
+ clazz = inClazz;
+ }
+ return clazz;
+ }
+
+ private <T> Map<String, T> newOrderedMap(final Class<T> value) {
+ return fieldOrdering != null ? new TreeMap<String, T>(fieldOrdering) : new HashMap<String, T>();
+ }
+
+ private void addSetterIfNeeded(final Map<String, Setter> setters,
+ final String key,
+ final AccessMode.Writer value,
+ final boolean copyDate) {
+ final JohnzonIgnore writeIgnore = value.getAnnotation(JohnzonIgnore.class);
+ if (writeIgnore == null || writeIgnore.minVersion() >= 0) {
+ if (key.equals("metaClass")) {
+ return;
+ }
+ final Type param = value.getType();
+ final Class<?> returnType = Class.class.isInstance(param) ? Class.class.cast(param) : null;
+ final Setter setter = new Setter(
+ value, isPrimitive(param), returnType != null && returnType.isArray(), param,
+ findConverter(copyDate, value), writeIgnore != null ? writeIgnore.minVersion() : -1);
+ setters.put(key, setter);
+ }
+ }
+
+ private void addGetterIfNeeded(final Map<String, Getter> getters,
+ final String key,
+ final AccessMode.Reader value,
+ final boolean copyDate) {
+ final JohnzonIgnore readIgnore = value.getAnnotation(JohnzonIgnore.class);
+ if (readIgnore == null || readIgnore.minVersion() >= 0) {
+ final Class<?> returnType = Class.class.isInstance(value.getType()) ? Class.class.cast(value.getType()) : null;
+ final ParameterizedType pt = ParameterizedType.class.isInstance(value.getType()) ? ParameterizedType.class.cast(value.getType()) : null;
+ final Getter getter = new Getter(value, isPrimitive(returnType),
+ returnType != null && returnType.isArray(),
+ (pt != null && Collection.class.isAssignableFrom(Class.class.cast(pt.getRawType())))
+ || (returnType != null && Collection.class.isAssignableFrom(returnType)),
+ (pt != null && Map.class.isAssignableFrom(Class.class.cast(pt.getRawType())))
+ || (returnType != null && Map.class.isAssignableFrom(returnType)),
+ findConverter(copyDate, value),
+ readIgnore != null ? readIgnore.minVersion() : -1);
+ getters.put(key, getter);
+ }
+ }
+
+ // idea is quite trivial, simulate an object with a Map<String, Object>
+ private void handleVirtualObject(final Collection<String> virtualFields,
+ final JohnzonVirtualObject o,
+ final Map<String, Getter> getters,
+ final Map<String, Setter> setters,
+ final Map<String, AccessMode.Reader> readers,
+ final Map<String, AccessMode.Writer> writers,
+ final boolean copyDate) {
+ final String[] path = o.path();
+ if (path.length < 1) {
+ throw new IllegalArgumentException("@JohnzonVirtualObject need a path");
+ }
+
+ // add them to ignored fields
+ for (final JohnzonVirtualObject.Field f : o.fields()) {
+ virtualFields.add(f.value());
+ }
+
+ // build "this" model
+ final Map<String, Getter> objectGetters = newOrderedMap(Getter.class);
+ final Map<String, Setter> objectSetters = newOrderedMap(Setter.class);
+
+ for (final JohnzonVirtualObject.Field f : o.fields()) {
+ final String name = f.value();
+ if (f.read()) {
+ final AccessMode.Reader reader = readers.get(name);
+ if (reader != null) {
+ addGetterIfNeeded(objectGetters, name, reader, copyDate);
+ }
+ }
+ if (f.write()) {
+ final AccessMode.Writer writer = writers.get(name);
+ if (writer != null) {
+ addSetterIfNeeded(objectSetters, name, writer, copyDate);
+ }
+ }
+ }
+
+ final String key = path[0];
+
+ final Getter getter = getters.get(key);
+ final MapBuilderReader newReader = new MapBuilderReader(objectGetters, path, version);
+ getters.put(key, new Getter(getter == null ? newReader : new CompositeReader(getter.reader, newReader), false, false, false, true, null, -1));
+
+ final Setter newSetter = setters.get(key);
+ final MapUnwrapperWriter newWriter = new MapUnwrapperWriter(objectSetters, path);
+ setters.put(key, new Setter(newSetter == null ? newWriter : new CompositeWriter(newSetter.writer, newWriter), false, false, VIRTUAL_TYPE, null, -1));
+ }
+
+ private Adapter findConverter(final boolean copyDate, final AccessMode.DecoratedType decoratedType) {
+ Adapter converter = decoratedType.findConverter();
+ if (converter != null) {
+ return converter;
+ }
+
+ final JohnzonConverter annotation = decoratedType.getAnnotation(JohnzonConverter.class);
+
+ Type typeToTest = decoratedType.getType();
+ if (annotation != null) {
+ try {
+ converter = new ConverterAdapter(annotation.value().newInstance());
+ } catch (final Exception e) {
+ throw new IllegalArgumentException(e);
+ }
+ } else if (ParameterizedType.class.isInstance(decoratedType.getType())) {
+ final ParameterizedType type = ParameterizedType.class.cast(decoratedType.getType());
+ final Type rawType = type.getRawType();
+ if (Class.class.isInstance(rawType)
+ && Collection.class.isAssignableFrom(Class.class.cast(rawType))
+ && type.getActualTypeArguments().length >= 1) {
+ typeToTest = type.getActualTypeArguments()[0];
+ } // TODO: map
+ }
+ if (converter == null && Class.class.isInstance(typeToTest)) {
+ final Class type = Class.class.cast(typeToTest);
+ if (Date.class.isAssignableFrom(type) && copyDate) {
+ converter = new DateWithCopyConverter(Adapter.class.cast(adapters.get(new AdapterKey(Date.class, String.class))));
+ } else if (type.isEnum()) {
+ final AdapterKey key = new AdapterKey(String.class, type);
+ converter = adapters.get(key); // first ensure user didnt override it
+ if (converter == null) {
+ converter = new ConverterAdapter(new EnumConverter(type));
+ adapters.put(key, converter);
+ }
+ } else {
+ for (final Map.Entry<AdapterKey, Adapter<?, ?>> adapterEntry : adapters.entrySet()) {
+ if (adapterEntry.getKey().getFrom() == adapterEntry.getKey().getTo()) { // String -> String
+ continue;
+ }
+ if (adapterEntry.getKey().getFrom() == type && !(
+ // ignore internal converters to let primitives be correctly handled
+ ConverterAdapter.class.isInstance(adapterEntry.getValue()) &&
+ ConverterAdapter.class.cast(adapterEntry.getValue()).getConverter().getClass().getName().startsWith("org.apache.johnzon.mapper."))) {
+
+ if (converter != null) {
+ throw new IllegalArgumentException("Ambiguous adapter for " + decoratedType);
+ }
+ converter = adapterEntry.getValue();
+ }
+ }
+ }
+ }
+ return converter;
+ }
+
+ private static class MapBuilderReader implements AccessMode.Reader {
+ private final Map<String, Getter> getters;
+ private final Map<String, Object> template;
+ private final String[] paths;
+ private final int version;
+
+ public MapBuilderReader(final Map<String, Getter> objectGetters, final String[] paths, final int version) {
+ this.getters = objectGetters;
+ this.paths = paths;
+ this.template = new LinkedHashMap<String, Object>();
+ this.version = version;
+
+ Map<String, Object> last = this.template;
+ for (int i = 1; i < paths.length; i++) {
+ final Map<String, Object> newLast = new LinkedHashMap<String, Object>();
+ last.put(paths[i], newLast);
+ last = newLast;
+ }
+ }
+
+ @Override
+ public Object read(final Object instance) {
+ final Map<String, Object> map = new LinkedHashMap<String, Object>(template);
+ Map<String, Object> nested = map;
+ for (int i = 1; i < paths.length; i++) {
+ nested = Map.class.cast(nested.get(paths[i]));
+ }
+ for (final Map.Entry<String, Getter> g : getters.entrySet()) {
+ final Mappings.Getter getter = g.getValue();
+ final Object value = getter.reader.read(instance);
+ final Object val = value == null || getter.converter == null ? value : getter.converter.from(value);
+ if (val == null) {
+ continue;
+ }
+ if (getter.version >= 0 && version >= getter.version) {
+ continue;
+ }
+
+ nested.put(g.getKey(), val);
+ }
+ return map;
+ }
+
+ @Override
+ public Type getType() {
+ return VIRTUAL_TYPE;
+ }
+
+ @Override
+ public <T extends Annotation> T getAnnotation(final Class<T> clazz) {
+ throw new UnsupportedOperationException("getAnnotation shouldn't get called for virtual fields");
+ }
+
+ @Override
+ public <T extends Annotation> T getClassOrPackageAnnotation(final Class<T> clazz) {
+ return null;
+ }
+
+ @Override
+ public Adapter<?, ?> findConverter() {
+ return null;
+ }
+
+ @Override
+ public boolean isNillable() {
+ return false;
+ }
+ }
+
+ private static class MapUnwrapperWriter implements AccessMode.Writer {
+ private final Map<String, Setter> writers;
+ private final Map<String, Class<?>> componentTypes;
+ private final String[] paths;
+
+ public MapUnwrapperWriter(final Map<String, Setter> writers, final String[] paths) {
+ this.writers = writers;
+ this.paths = paths;
+ this.componentTypes = new HashMap<String, Class<?>>();
+
+ for (final Map.Entry<String, Setter> setter : writers.entrySet()) {
+ if (setter.getValue().array) {
+ componentTypes.put(setter.getKey(), Class.class.cast(setter.getValue().paramType).getComponentType());
+ }
+ }
+ }
+
+ @Override
+ public void write(final Object instance, final Object value) {
+ Map<String, Object> nested = null;
+ for (final String path : paths) {
+ nested = Map.class.cast(nested == null ? value : nested.get(path));
+ if (nested == null) {
+ return;
+ }
+ }
+
+ for (final Map.Entry<String, Setter> setter : writers.entrySet()) {
+ final Setter setterValue = setter.getValue();
+ final String key = setter.getKey();
+ final Object rawValue = nested.get(key);
+ Object val = value == null || setterValue.converter == null ?
+ rawValue : Converter.class.cast(setterValue.converter).toString(rawValue);
+ if (val == null) {
+ continue;
+ }
+
+ if (setterValue.array && Collection.class.isInstance(val)) {
+ final Collection<?> collection = Collection.class.cast(val);
+ final Object[] array = (Object[]) Array.newInstance(componentTypes.get(key), collection.size());
+ val = collection.toArray(array);
+ }
+
+ final AccessMode.Writer setterMethod = setterValue.writer;
+ setterMethod.write(instance, val);
+ }
+ }
+
+ @Override
+ public Type getType() {
+ return VIRTUAL_TYPE;
+ }
+
+ @Override
+ public <T extends Annotation> T getAnnotation(final Class<T> clazz) {
+ throw new UnsupportedOperationException("getAnnotation shouldn't get called for virtual fields");
+ }
+
+ @Override
+ public <T extends Annotation> T getClassOrPackageAnnotation(final Class<T> clazz) {
+ return null;
+ }
+
+ @Override
+ public Adapter<?, ?> findConverter() {
+ return null;
+ }
+
+ @Override
+ public boolean isNillable() {
+ return false;
+ }
+ }
+
+ private static class CompositeReader implements AccessMode.Reader {
+ private final AccessMode.Reader[] delegates;
+
+ public CompositeReader(final AccessMode.Reader... delegates) {
+ final Collection<AccessMode.Reader> all = new LinkedList<AccessMode.Reader>();
+ for (final AccessMode.Reader r : delegates) {
+ if (CompositeReader.class.isInstance(r)) {
+ all.addAll(asList(CompositeReader.class.cast(r).delegates));
+ } else {
+ all.add(r);
+ }
+ }
+ this.delegates = all.toArray(new AccessMode.Reader[all.size()]);
+ }
+
+ @Override
+ public Object read(final Object instance) {
+ final Map<String, Object> map = new LinkedHashMap<String, Object>();
+ for (final AccessMode.Reader reader : delegates) {
+ final Map<String, Object> readerMap = (Map<String, Object>) reader.read(instance);
+ for (final Map.Entry<String, Object> entry : readerMap.entrySet()) {
+ final Object o = map.get(entry.getKey());
+ if (o == null) {
+ map.put(entry.getKey(), entry.getValue());
+ } else if (Map.class.isInstance(o)) {
+ // TODO
+ } else {
+ throw new IllegalStateException(entry.getKey() + " is ambiguous");
+ }
+ }
+ }
+ return map;
+ }
+
+ @Override
+ public Type getType() {
+ return VIRTUAL_TYPE;
+ }
+
+ @Override
+ public <T extends Annotation> T getAnnotation(final Class<T> clazz) {
+ throw new UnsupportedOperationException("getAnnotation shouldn't get called for virtual fields");
+ }
+
+ @Override
+ public <T extends Annotation> T getClassOrPackageAnnotation(final Class<T> clazz) {
+ return null;
+ }
+
+ @Override
+ public Adapter<?, ?> findConverter() {
+ for (final AccessMode.Reader r : delegates) {
+ final Adapter<?, ?> converter = r.findConverter();
+ if (converter != null) {
+ return converter;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean isNillable() {
+ for (final AccessMode.Reader r : delegates) {
+ if (r.isNillable()) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ private static class CompositeWriter implements AccessMode.Writer {
+ private final AccessMode.Writer[] delegates;
+
+ public CompositeWriter(final AccessMode.Writer... writers) {
+ final Collection<AccessMode.Writer> all = new LinkedList<AccessMode.Writer>();
+ for (final AccessMode.Writer r : writers) {
+ if (CompositeWriter.class.isInstance(r)) {
+ all.addAll(asList(CompositeWriter.class.cast(r).delegates));
+ } else {
+ all.add(r);
+ }
+ }
+ this.delegates = all.toArray(new AccessMode.Writer[all.size()]);
+ }
+
+ @Override
+ public void write(final Object instance, final Object value) {
+ for (final AccessMode.Writer w : delegates) {
+ w.write(instance, value);
+ }
+ }
+
+ @Override
+ public Type getType() {
+ return VIRTUAL_TYPE;
+ }
+
+ @Override
+ public <T extends Annotation> T getAnnotation(final Class<T> clazz) {
+ throw new UnsupportedOperationException("getAnnotation shouldn't get called for virtual fields");
+ }
+
+ @Override
+ public <T extends Annotation> T getClassOrPackageAnnotation(final Class<T> clazz) {
+ return null;
+ }
+
+ @Override
+ public Adapter<?, ?> findConverter() {
+ for (final AccessMode.Writer r : delegates) {
+ final Adapter<?, ?> converter = r.findConverter();
+ if (converter != null) {
+ return converter;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean isNillable() {
+ for (final AccessMode.Writer r : delegates) {
+ if (r.isNillable()) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/e41b829b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/ObjectConverter.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/ObjectConverter.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/ObjectConverter.java
index e831e80..7f5d02d 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/ObjectConverter.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/ObjectConverter.java
@@ -20,9 +20,6 @@ package org.apache.johnzon.mapper;
import java.lang.reflect.Type;
-import javax.json.stream.JsonGenerator;
-import javax.json.stream.JsonParser;
-
/**
* Convert a given Java Type a nested JSON representation.
* And the other way around.
@@ -33,7 +30,7 @@ import javax.json.stream.JsonParser;
* @param <T>
*/
public interface ObjectConverter<T> {
- void writeJson(T instance, JsonbGenerator jsonbGenerator);
+ void writeJson(T instance, MappingGenerator jsonbGenerator);
- T fromJson(JsonbParser jsonbParser, Type targetType);
+ T fromJson(MappingParser jsonbParser, Type targetType);
}