blob: b74c10571c824676fbd1e02f02de0a200c2a063f [file] [log] [blame]
/*
* 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.internal.JsonPointerTracker;
import org.apache.johnzon.mapper.reflection.JohnzonCollectionType;
import org.apache.johnzon.mapper.util.ArrayUtil;
import javax.json.JsonArray;
import javax.json.JsonBuilderFactory;
import javax.json.JsonObject;
import javax.json.JsonReader;
import javax.json.JsonReaderFactory;
import javax.json.JsonString;
import javax.json.JsonStructure;
import javax.json.JsonValue;
import javax.json.spi.JsonProvider;
import javax.json.stream.JsonGenerator;
import javax.json.stream.JsonGeneratorFactory;
import javax.json.stream.JsonParser;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
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.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import static org.apache.johnzon.mapper.internal.Streams.noClose;
public class Mapper implements Closeable {
protected final MapperConfig config;
protected final Mappings mappings;
protected final JsonReaderFactory readerFactory;
protected final JsonGeneratorFactory generatorFactory;
protected final JsonBuilderFactory builderFactory;
protected final JsonProvider provider;
protected final Collection<Closeable> closeables;
protected final Charset charset;
Mapper(final JsonReaderFactory readerFactory, final JsonGeneratorFactory generatorFactory,
final JsonBuilderFactory builderFactory, final JsonProvider provider,
final MapperConfig config, final Collection<Closeable> closeables) {
this.readerFactory = readerFactory;
this.generatorFactory = generatorFactory;
this.builderFactory = builderFactory;
this.provider = provider;
this.config = config;
this.mappings = new Mappings(config);
this.closeables = closeables;
this.charset = config.getEncoding();
}
public <T> void writeArray(final Object object, final OutputStream stream) {
if (object instanceof short[]) {
writeObject(ArrayUtil.asList((short[]) object), stream);
} else if (object instanceof int[]) {
writeObject(ArrayUtil.asList((int[]) object), stream);
} else if (object instanceof long[]) {
writeObject(ArrayUtil.asList((long[]) object), stream);
} else if (object instanceof byte[]) {
writeObject(ArrayUtil.asList((byte[]) object), stream);
} else if (object instanceof char[]) {
writeObject(ArrayUtil.asList((char[]) object), stream);
} else if (object instanceof float[]) {
writeObject(ArrayUtil.asList((float[]) object), stream);
} else if (object instanceof double[]) {
writeObject(ArrayUtil.asList((double[]) object), stream);
} else {
writeObject(Arrays.asList((T[]) object), stream);
}
}
public <T> void writeArray(final T[] object, final OutputStream stream) {
writeObject(Arrays.asList(object), stream);
}
public <T> void writeArray(final T[] object, final Writer stream) {
writeObject(Arrays.asList(object), stream);
}
public <T> void writeArray(final Collection<T> object, final OutputStream stream) {
writeArray(object, new OutputStreamWriter(stream, config.getEncoding()));
}
public <T> void writeArray(final Collection<T> object, final Writer stream) {
try (final JsonGenerator generator = generatorFactory.createGenerator(stream(stream))) {
boolean dedup = Boolean.TRUE.equals(config.isDeduplicateObjects());
writeObject(object, generator, null, dedup ? new JsonPointerTracker(null, "/") : null);
}
}
public <T> void writeIterable(final Iterable<T> object, final OutputStream stream) {
writeIterable(object, new OutputStreamWriter(stream, config.getEncoding()));
}
public <T> void writeIterable(final Iterable<T> object, final Writer stream) {
try (final JsonGenerator generator = generatorFactory.createGenerator(stream(stream))) {
boolean dedup = Boolean.TRUE.equals(config.isDeduplicateObjects());
writeObject(object, generator, null, dedup ? new JsonPointerTracker(null, "/") : null);
}
}
public JsonValue toStructure(final Object object) {
if (object == null) {
return JsonValue.NULL;
}
if (JsonStructure.class.isInstance(object)) {
return JsonStructure.class.cast(object);
}
if (Boolean.class.isInstance(object)) {
return Boolean.class.cast(object) ? JsonValue.TRUE : JsonValue.FALSE;
}
if (String.class.isInstance(object)) {
return provider.createValue(String.class.cast(object));
}
if (Double.class.isInstance(object)) {
return provider.createValue(Double.class.cast(object));
}
if (Float.class.isInstance(object)) {
return provider.createValue(Float.class.cast(object));
}
if (Long.class.isInstance(object)) {
return provider.createValue(Long.class.cast(object));
}
if (Integer.class.isInstance(object)) {
return provider.createValue(Integer.class.cast(object));
}
if (BigDecimal.class.isInstance(object)) {
return provider.createValue(BigDecimal.class.cast(object));
}
if (BigInteger.class.isInstance(object)) {
return provider.createValue(BigInteger.class.cast(object));
}
final JsonObjectGenerator objectGenerator = new JsonObjectGenerator(builderFactory);
writeObject(object, objectGenerator, null,
isDedup(object.getClass()) ? new JsonPointerTracker(null, "/") : null);
return objectGenerator.getResult();
}
public void writeObject(final Object object, final Writer stream) {
if (object == null) {
try (final Writer w = stream) {
w.write("null");
} catch (final IOException e) {
throw new MapperException(e);
}
return;
}
final Adapter adapter = config.findAdapter(object.getClass());
if (adapter != null && TypeAwareAdapter.class.isInstance(adapter) && TypeAwareAdapter.class.cast(adapter).getTo() == JsonString.class) {
writeObject(adapter.from(object), stream);
return;
}
try (final JsonGenerator generator = generatorFactory.createGenerator(stream(stream))) {
final Object converted = adapter == null ? object : adapter.from(object);
if (CharSequence.class.isInstance(converted)) {
writeObjectWithGenerator(provider.createValue(converted.toString()), generator);
} else {
writeObjectWithGenerator(converted, generator);
}
}
}
public void writeObjectWithGenerator(final Object object, final JsonGenerator generator) {
writeObject(object, generator, null,
isDedup(object.getClass()) ? new JsonPointerTracker(null, "/") : null);
}
public void writeObject(final Object object, final OutputStream stream) {
Charset charset = config.getEncoding();
if (charset == null) {
charset = StandardCharsets.UTF_8;
}
writeObject(object, new OutputStreamWriter(stream, charset));
}
private void writeObject(final Object object, final JsonGenerator generator, final Collection<String> ignored, JsonPointerTracker jsonPointer) {
final MappingGeneratorImpl mappingGenerator = new MappingGeneratorImpl(config, generator, mappings, jsonPointer != null);
mappingGenerator.doWriteObject(object, generator, true, ignored, jsonPointer);
}
public String writeArrayAsString(final Collection<?> instance) {
final StringWriter writer = new StringWriter(2048);
writeArray(instance, writer);
return writer.toString();
}
public <T> String writeArrayAsString(final T[] instance) {
final StringWriter writer = new StringWriter(2048);
writeArray(instance, writer);
return writer.toString();
}
public String writeObjectAsString(final Object instance) {
final StringWriter writer = new StringWriter(2048);
writeObject(instance, writer);
return writer.toString();
}
public <T> T readObject(final JsonValue value, final Type clazz) {
return new MappingParserImpl(config, mappings, new JsonReader() {
@Override
public JsonStructure read() {
switch (value.getValueType()) {
case STRING:
case FALSE:
case TRUE:
case NULL:
case NUMBER:
throw new UnsupportedOperationException("use readValue()");
default:
return JsonStructure.class.cast(readValue());
}
}
@Override
public JsonValue readValue() {
return value;
}
@Override
public JsonObject readObject() {
return value.asJsonObject();
}
@Override
public JsonArray readArray() {
return value.asJsonArray();
}
@Override
public void close() {
// no-op
}
}, isDedup(clazz)).readObject(clazz);
}
public <T> T readObject(final String string, final Type clazz) {
return readObject(new StringReader(string), clazz);
}
public <T> T readObject(final Reader stream, final Type clazz) {
try (final JsonReader reader = readerFactory.createReader(stream(stream))) {
return mapObject(clazz, reader);
}
}
public <T> T readObject(final JsonParser stream, final Type clazz) {
try (final JsonReader reader = JohnzonCores.map(stream, readerFactory)) {
return mapObject(clazz, reader);
}
}
public <T> T readObject(final InputStream stream, final Type clazz) {
try (final JsonReader reader = charset == null ? readerFactory.createReader(stream(stream)) : readerFactory.createReader(
stream(stream), charset)) {
return mapObject(clazz, reader);
}
}
public <T> Collection<T> readCollection(final InputStream stream, final ParameterizedType genericType) {
try (final JsonReader reader = charset == null ? readerFactory.createReader(stream(stream)): readerFactory.createReader(stream(stream), charset)) {
return mapObject(genericType, reader);
}
}
public <T> T readJohnzonCollection(final InputStream stream, final JohnzonCollectionType<T> genericType) {
return (T) readCollection(stream, genericType);
}
public <T> T readJohnzonCollection(final Reader stream, final JohnzonCollectionType<T> genericType) {
return (T) readCollection(stream, genericType);
}
public <T> Collection<T> readCollection(final Reader stream, final ParameterizedType genericType) {
try (final JsonReader reader = readerFactory.createReader(stream(stream))) {
return mapObject(genericType, reader);
}
}
public <T> Collection<T> readCollection(final JsonParser stream, final ParameterizedType genericType) {
try (final JsonReader reader = JohnzonCores.map(stream, readerFactory)) {
return mapObject(genericType, reader);
}
}
public <T> T[] readArray(final Reader stream, final Class<T> clazz) {
try (final JsonReader reader = readerFactory.createReader(stream(stream))) {
return (T[]) mapArray(clazz, reader);
}
}
public <T> T readTypedArray(final InputStream stream, final Class<?> elementType, final Class<T> arrayType) {
try (final JsonReader reader = charset == null ? readerFactory.createReader(stream(stream)): readerFactory.createReader(stream(stream), charset)) {
return arrayType.cast(mapArray(elementType, reader));
}
}
public <T> T readTypedArray(final Reader stream, final Class<?> elementType, final Class<T> arrayType) {
try (final JsonReader reader = readerFactory.createReader(stream(stream))) {
return arrayType.cast(mapArray(elementType, reader));
}
}
public <T> T readTypedArray(final JsonParser parser, final Class<?> elementType, final Class<T> arrayType) {
try (final JsonReader reader = JohnzonCores.map(parser, readerFactory)) {
return arrayType.cast(mapArray(elementType, reader));
}
}
public JsonArray readJsonArray(final Reader stream) {
try (final JsonReader reader = readerFactory.createReader(stream(stream))) {
return reader.readArray();
}
}
public JsonArray readJsonArray(final JsonParser stream) {
try (final JsonReader reader = JohnzonCores.map(stream, readerFactory)) {
return reader.readArray();
}
}
public <T> T[] readArray(final InputStream stream, final Class<T> clazz) {
try (final JsonReader reader = charset == null ? readerFactory.createReader(stream(stream)): readerFactory.createReader(stream(stream), charset)) {
return (T[]) mapArray(clazz, reader);
}
}
public JsonArray readJsonArray(final InputStream stream) {
try (final JsonReader reader = charset == null ? readerFactory.createReader(stream(stream)): readerFactory.createReader(stream(stream), charset)) {
return reader.readArray();
}
}
private Object mapArray(final Class<?> clazz, final JsonReader reader) {
return mapObject(ArrayUtil.getArrayTypeFor(clazz), reader);
}
private <T> T mapObject(final Type clazz, final JsonReader reader) {
return new MappingParserImpl(config, mappings, reader, isDedup(clazz)).readObject(clazz);
}
private boolean isDedup(final Type clazz) {
if (clazz instanceof Class &&
JsonValue.class != clazz && JsonStructure.class != clazz &&
JsonObject.class != clazz && JsonArray.class != clazz) {
Boolean dedup = config.isDeduplicateObjects();
if (dedup == null) {
// TODO: never call it more than once per clazz, should be done after once ClassMapping is obtained, not here!
// -> revisit org.apache.johnzon.mapper.Mappings.findOrCreateClassMapping (isPrimitive should drop)
// -> revisit org.apache.johnzon.mapper.access.FieldAccessMode.isIgnored(java.lang.String, java.lang.Class<?>)
Mappings.ClassMapping classMapping = mappings.findOrCreateClassMapping(clazz);
if (classMapping != null) {
dedup = classMapping.isDeduplicateObjects();
}
}
return dedup != null ? dedup : false;
}
return false;
}
private Reader stream(final Reader stream) {
return !config.isClose() ? noClose(stream) : stream;
}
private Writer stream(final Writer stream) {
return !config.isClose() ? noClose(stream) : stream;
}
private InputStream stream(final InputStream stream) {
return !config.isClose() ? noClose(stream) : stream;
}
@Override
public synchronized void close() {
Collection<Exception> errors = null;
for (final Closeable c : closeables) {
try {
c.close();
} catch (final IOException e) {
if (errors == null) {
errors = new ArrayList<>();
}
errors.add(e);
}
}
closeables.clear();
if (errors != null) {
throw new IllegalStateException(errors.toString());
}
}
public JsonBuilderFactory getBuilderFactory() {
return builderFactory;
}
public JsonProvider getProvider() {
return provider;
}
}