/**
*
* Copyright (c) 2006-2017, Speedment, Inc. All Rights Reserved.
*
* Licensed 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 com.speedment.plugins.json.internal;
import com.speedment.plugins.json.JsonCollector;
import com.speedment.plugins.json.JsonEncoder;
import com.speedment.runtime.config.Project;
import com.speedment.runtime.config.identifier.TableIdentifier;
import com.speedment.runtime.core.manager.Manager;
import com.speedment.runtime.field.*;
import com.speedment.runtime.field.method.*;
import com.speedment.runtime.field.trait.HasFinder;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Stream;
import static com.speedment.common.invariant.NullUtil.requireNonNulls;
import static com.speedment.plugins.json.internal.JsonUtil.jsonField;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.joining;
/**
* The default implementation of the {@link JsonEncoder} interface.
*
* @param <ENTITY> the entity type
*
* @author Emil Forslund
* @since 1.0.0
*/
final class JsonEncoderImpl<ENTITY> implements JsonEncoder<ENTITY> {
private final Map<String, Function<ENTITY, String>> getters;
private final Project project;
private final Manager<ENTITY> manager;
/**
* Constructs an empty JsonEncoder with no fields added to the output
* renderer.
*/
JsonEncoderImpl(Project project, Manager<ENTITY> manager) {
this.getters = new LinkedHashMap<>();
this.project = requireNonNull(project);
this.manager = requireNonNull(manager);
}
/**************************************************************************/
/* Getters */
/**************************************************************************/
@Override
public Manager<ENTITY> getManager() {
return manager;
}
/**************************************************************************/
/* Field Putters */
/**************************************************************************/
@Override
public <D, V> JsonEncoder<ENTITY> put(ReferenceField<ENTITY, D, V> field) {
return putHelper(field, ReferenceField::getter, this::put);
}
@Override
public <D> JsonEncoder<ENTITY> putByte(ByteField<ENTITY, D> field) {
return putHelper(field, ByteField::getter, this::putByte);
}
@Override
public <D> JsonEncoder<ENTITY> putShort(ShortField<ENTITY, D> field) {
return putHelper(field, ShortField::getter, this::putShort);
}
@Override
public <D> JsonEncoder<ENTITY> putInt(IntField<ENTITY, D> field) {
return putHelper(field, IntField::getter, this::putInt);
}
@Override
public <D> JsonEncoder<ENTITY> putLong(LongField<ENTITY, D> field) {
return putHelper(field, LongField::getter, this::putLong);
}
@Override
public <D> JsonEncoder<ENTITY> putFloat(FloatField<ENTITY, D> field) {
return putHelper(field, FloatField::getter, this::putFloat);
}
@Override
public <D> JsonEncoder<ENTITY> putDouble(DoubleField<ENTITY, D> field) {
return putHelper(field, DoubleField::getter, this::putDouble);
}
@Override
public <D> JsonEncoder<ENTITY> putChar(CharField<ENTITY, D> field) {
return putHelper(field, CharField::getter, this::putChar);
}
@Override
public <D> JsonEncoder<ENTITY> putBoolean(BooleanField<ENTITY, D> field) {
return putHelper(field, BooleanField::getter, this::putBoolean);
}
private <F extends Field<ENTITY>, G extends Getter<ENTITY>> JsonEncoder<ENTITY> putHelper(
F field, Function<F, G> getter, BiFunction<String, G, JsonEncoder<ENTITY>> putter) {
requireNonNulls(field, getter, putter);
final String columnName = jsonField(project, field.identifier());
return putter.apply(columnName, getter.apply(field));
}
/**************************************************************************/
/* Put Labels with Getters */
/**************************************************************************/
@Override
public <T> JsonEncoder<ENTITY> put(String label, ReferenceGetter<ENTITY, T> getter) {
return putHelper(label, e -> jsonValue(getter.apply(e)));
}
@Override
public JsonEncoder<ENTITY> putByte(String label, ByteGetter<ENTITY> getter) {
return putHelper(label, e -> jsonValue(getter.applyAsByte(e)));
}
@Override
public JsonEncoder<ENTITY> putShort(String label, ShortGetter<ENTITY> getter) {
return putHelper(label, e -> jsonValue(getter.applyAsShort(e)));
}
@Override
public JsonEncoder<ENTITY> putInt(String label, IntGetter<ENTITY> getter) {
return putHelper(label, e -> jsonValue(getter.applyAsInt(e)));
}
@Override
public JsonEncoder<ENTITY> putLong(String label, LongGetter<ENTITY> getter) {
return putHelper(label, e -> jsonValue(getter.applyAsLong(e)));
}
@Override
public JsonEncoder<ENTITY> putFloat(String label, FloatGetter<ENTITY> getter) {
return putHelper(label, e -> jsonValue(getter.applyAsFloat(e)));
}
@Override
public JsonEncoder<ENTITY> putDouble(String label, DoubleGetter<ENTITY> getter) {
return putHelper(label, e -> jsonValue(getter.applyAsDouble(e)));
}
@Override
public JsonEncoder<ENTITY> putChar(String label, CharGetter<ENTITY> getter) {
return putHelper(label, e -> jsonValue(getter.applyAsChar(e)));
}
@Override
public JsonEncoder<ENTITY> putBoolean(String label, BooleanGetter<ENTITY> getter) {
return putHelper(label, e -> jsonValue(getter.applyAsBoolean(e)));
}
private JsonEncoder<ENTITY> putHelper(String label, Function<ENTITY, String> jsonValue) {
requireNonNull(label);
getters.put(label, e -> "\"" + label + "\":" + jsonValue.apply(e));
return this;
}
/**************************************************************************/
/* Put Fields with Finders */
/**************************************************************************/
@Override
public <FK_ENTITY, FIELD extends Field<ENTITY> & HasFinder<ENTITY, FK_ENTITY>>
JsonEncoder<ENTITY> put(FIELD field, JsonEncoder<FK_ENTITY> encoder) {
requireNonNulls(field, encoder);
final String columnName = jsonField(project, field.identifier());
final Manager<FK_ENTITY> fkManager = encoder.getManager();
final TableIdentifier<FK_ENTITY> identifier = fkManager.getTableIdentifier();
final FindFrom<ENTITY, FK_ENTITY> entityFinder = field.finder(identifier, () -> fkManager.stream());
return put(columnName, entityFinder, encoder);
}
/**************************************************************************/
/* Put Labels with Finders */
/**************************************************************************/
@Override
public <FK_ENTITY> JsonEncoder<ENTITY> put(
String label,
FindFrom<ENTITY, FK_ENTITY> finder,
JsonEncoder<FK_ENTITY> fkEncoder) {
requireNonNulls(label, finder, fkEncoder);
getters.put(label, e -> "\"" + label + "\":" +
fkEncoder.apply(finder.apply(e))
);
return this;
}
/**************************************************************************/
/* Put Labels with Find Many */
/**************************************************************************/
@Override
public <FK_ENTITY> JsonEncoder<ENTITY> putStreamer(
String label,
Function<ENTITY, Stream<FK_ENTITY>> streamer,
JsonEncoder<FK_ENTITY> fkEncoder) {
requireNonNulls(label, streamer, fkEncoder);
getters.put(label, e -> "\"" + label + "\":[" +
streamer.apply(e).map(fkEncoder::apply).collect(joining(",")) +
"]"
);
return this;
}
@Override
public <FK_ENTITY> JsonEncoder<ENTITY> putStreamer(
String label,
Function<ENTITY, Stream<FK_ENTITY>> streamer,
Function<FK_ENTITY, String> fkEncoder) {
requireNonNulls(label, streamer, fkEncoder);
getters.put(label, e -> "\"" + label + "\":[" +
streamer.apply(e).map(fkEncoder).collect(joining(",")) +
"]"
);
return this;
}
/**************************************************************************/
/* Remove by Label */
/**************************************************************************/
@Override
public JsonEncoder<ENTITY> remove(String label) {
requireNonNull(label);
getters.remove(label);
return this;
}
@Override
public JsonEncoder<ENTITY> remove(Field<ENTITY> field) {
requireNonNull(field);
getters.remove(jsonField(project, field.identifier()));
return this;
}
/**************************************************************************/
/* Encode */
/**************************************************************************/
@Override
public String apply(ENTITY entity) {
return entity == null ? "null" : "{"
+ getters.values().stream()
.map(g -> g.apply(entity))
.collect(joining(","))
+ "}";
}
@Override
public JsonCollector<ENTITY> collector() {
return JsonCollector.toJson(this);
}
/**************************************************************************/
/* Protected and Private Helper Methods */
/**************************************************************************/
/**
* Parse the specified value into JSON.
*
* @param value the value
* @return the JSON encoded value
*/
private static String jsonValue(byte value) {
return String.valueOf(value);
}
/**
* Parse the specified value into JSON.
*
* @param value the value
* @return the JSON encoded value
*/
private static String jsonValue(short value) {
return String.valueOf(value);
}
/**
* Parse the specified value into JSON.
*
* @param value the value
* @return the JSON encoded value
*/
private static String jsonValue(int value) {
return String.valueOf(value);
}
/**
* Parse the specified value into JSON.
*
* @param value the value
* @return the JSON encoded value
*/
private static String jsonValue(long value) {
return String.valueOf(value);
}
/**
* Parse the specified value into JSON.
*
* @param value the value
* @return the JSON encoded value
*/
private static String jsonValue(float value) {
return String.valueOf(value);
}
/**
* Parse the specified value into JSON.
*
* @param value the value
* @return the JSON encoded value
*/
private static String jsonValue(double value) {
return String.valueOf(value);
}
/**
* Parse the specified value into JSON.
*
* @param value the value
* @return the JSON encoded value
*/
private static String jsonValue(char value) {
return String.valueOf(value);
}
/**
* Parse the specified value into JSON.
*
* @param value the value
* @return the JSON encoded value
*/
private static String jsonValue(boolean value) {
return String.valueOf(value);
}
/**
* Parse the specified value into JSON.
*
* @param in the value
* @return the JSON encoded value
*/
private static String jsonValue(Object in) {
// in is nullable, a field can certainly be null
final String value;
if (in instanceof Optional<?>) {
final Optional<?> o = (Optional<?>) in;
return o.map(JsonEncoderImpl::jsonValue).orElse("null");
} else if (in == null) {
value = "null";
} else if (in instanceof Byte
|| in instanceof Short
|| in instanceof Integer
|| in instanceof Long
|| in instanceof Boolean
|| in instanceof Float
|| in instanceof Double) {
value = String.valueOf(in);
} else {
value = "\"" + String.valueOf(in).replace("\"", "\\\"") + "\"";
}
return value;
}
}