package org.constellation.util.json;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import org.geotoolkit.utility.parameter.ParametersExt;
import org.opengis.parameter.GeneralParameterDescriptor;
import org.opengis.parameter.GeneralParameterValue;
import org.opengis.parameter.ParameterDescriptor;
import org.opengis.parameter.ParameterDescriptorGroup;
import org.opengis.parameter.ParameterValue;
import org.opengis.parameter.ParameterValueGroup;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Deserialize a {@link GeneralParameterValue} from a JSON matching specific {@link GeneralParameterDescriptor}.
* This deserializer work with array representation of parameters multi-occurrences.
* For example this JSON : {@code {"group":{"paramName":[5,6,55]}}} can be translate to a
* {@link ParameterValueGroup} containing three {@code paramName} (5,6,55).
* In case of "mono-occurrence", JSON can wrap or not the value in an array.
* For example : {@code {"paramName":[5]}} and {@code {"paramName":5}} are supported and deserialized in the same way.
*
* @author Quentin Boileau (Geomatys)
* @see org.constellation.util.json.ParameterValueJSONSerializer
*/
public class ParameterValueJSONDeserializer extends JsonDeserializer<GeneralParameterValue> {
private GeneralParameterDescriptor descriptor;
public ParameterValueJSONDeserializer(GeneralParameterDescriptor descriptor) {
this.descriptor = descriptor;
}
@Override
public GeneralParameterValue deserialize(JsonParser parser, DeserializationContext deserializationContext)
throws IOException, JsonProcessingException {
final JsonNode rootNode = parser.getCodec().readTree(parser);
if (!rootNode.isObject()) {
throw new IOException("Invalid JSON : Expecting JSON object as root node");
}
// special case when JSON doesn't contain root descriptor node.
if (descriptor instanceof ParameterDescriptorGroup) {
String rootParamName = descriptor.getName().getCode();
if (!rootNode.has(rootParamName)) {
// JSON exclude root
return nodeToParameterValueGroup((ParameterDescriptorGroup) descriptor, rootNode);
}
}
//standard case
List<GeneralParameterValue> generalParameterValues = readGeneralParameterValue(descriptor, rootNode);
if (generalParameterValues != null && !generalParameterValues.isEmpty()) {
return generalParameterValues.get(0);
}
// error ?
return null;
}
private List<GeneralParameterValue> readGeneralParameterValue(GeneralParameterDescriptor descriptor, JsonNode node) throws IOException {
if (descriptor instanceof ParameterDescriptorGroup) {
ParameterDescriptorGroup descriptorGroup = (ParameterDescriptorGroup) descriptor;
return readParameterValueGroup(descriptorGroup, node);
} else {
ParameterDescriptor parameterDesc = (ParameterDescriptor) descriptor;
return readParameterValue(parameterDesc, node);
}
}
private List<GeneralParameterValue> readParameterValueGroup(ParameterDescriptorGroup descriptor, JsonNode rootNode) throws IOException {
String paramName = descriptor.getName().getCode();
JsonNode paramNode = rootNode.get(paramName);
if (paramNode == null) {
throw new IOException("Parameter group "+paramName+" not found.");
}
final List<GeneralParameterValue> valueGroups = new ArrayList<>();
if (paramNode.isArray()) {
//multi-occurrences
final ArrayNode arrayNode = (ArrayNode) paramNode;
for (JsonNode occurrence : arrayNode) {
valueGroups.add(nodeToParameterValueGroup(descriptor, occurrence));
}
} else {
//mono-occurrences
valueGroups.add(nodeToParameterValueGroup(descriptor, paramNode));
}
return valueGroups;
}
private ParameterValueGroup nodeToParameterValueGroup(ParameterDescriptorGroup descriptor, JsonNode node) throws IOException {
final ParameterValueGroup valueGroup = descriptor.createValue();
if (!node.isObject()) {
throw new IOException("Invalid JSON node type. Expecting Object to build a ParameterValueGroup.");
}
List<GeneralParameterDescriptor> subDescriptors = descriptor.descriptors();
for (GeneralParameterDescriptor subDescriptor : subDescriptors) {
List<GeneralParameterValue> values = readGeneralParameterValue(subDescriptor, node);
setOrCreate(valueGroup, values, subDescriptor);
}
return valueGroup;
}
/**
* Re-use and update default created Parameters contained in {@code valueGroup} before append new GeneralParameterValue
* to parameters list.
*
* For example if a {@link ParameterDescriptorGroup} define a parameter with cardinality at (2-5), the created
* {@link ParameterValueGroup} will be initialized with two instances of this parameter.
* And if we read three instances of this parameter that we want to used in {@link ParameterValueGroup},
* we have to update the two default instances and add the last one. This is the job of this utility method.
*
* @param valueGroup
* @param values
* @param subDescriptor
*/
private void setOrCreate(ParameterValueGroup valueGroup, List<GeneralParameterValue> values, GeneralParameterDescriptor subDescriptor) {
final String paramName = subDescriptor.getName().getCode();
final List<GeneralParameterValue> defaultParameters = ParametersExt.getParameters(valueGroup, paramName);
for (int i = 0; i < values.size(); i++) {
if (i < defaultParameters.size()) {
//reuse parameters created by default
if (subDescriptor instanceof ParameterDescriptorGroup) {
final ParameterValueGroup defaultParamGroup = (ParameterValueGroup) defaultParameters.get(i);
final ParameterValueGroup newParamGroup = (ParameterValueGroup) values.get(i);
ParametersExt.deepCopy(newParamGroup, defaultParamGroup);
} else {
final ParameterValue defaultParamValue = (ParameterValue) defaultParameters.get(i);
final ParameterValue newParamValue = (ParameterValue) values.get(i);
defaultParamValue.setValue(newParamValue.getValue());
}
} else {
//add new value
valueGroup.values().add(values.get(i));
}
}
}
private List<GeneralParameterValue> readParameterValue(ParameterDescriptor descriptor, JsonNode rootNode) throws IOException {
final boolean mandatory = descriptor.getMinimumOccurs() > 0;
final String paramName = descriptor.getName().getCode();
final JsonNode paramNode = rootNode.get(paramName);
if (paramNode == null) {
if (mandatory) throw new IOException("Mandatory parameter "+paramName+" not found.");
// parameter optional and not found OK
return null;
}
final List<GeneralParameterValue> parameterValues = new ArrayList<>();
if (paramNode.isArray()) {
//multi-occurrences
final ArrayNode arrayNode = (ArrayNode) paramNode;
for (JsonNode occurrence : arrayNode) {
parameterValues.add(nodeToParameterValue(descriptor, occurrence));
}
} else {
//mono-occurrences
parameterValues.add(nodeToParameterValue(descriptor, paramNode));
}
return parameterValues;
}
private ParameterValue nodeToParameterValue(ParameterDescriptor descriptor, JsonNode paramNode) throws IOException {
final ParameterValue value = descriptor.createValue();
final String paramName = value.getDescriptor().getName().getCode();
value.setValue(JsonUtils.readValue(paramNode, descriptor.getValueClass(), paramName));
return value;
}
}