/* * Copyright (C) 2006-2015 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.datamodel.internal; import java.io.IOException; import java.util.Date; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.JsonParseException; import org.codehaus.jackson.JsonProcessingException; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.node.ArrayNode; import org.codehaus.jackson.node.ObjectNode; import org.codehaus.jackson.node.TextNode; import de.rcenvironment.core.datamodel.api.DataType; import de.rcenvironment.core.datamodel.api.TypedDatum; import de.rcenvironment.core.datamodel.api.TypedDatumSerializer; import de.rcenvironment.core.datamodel.types.api.BooleanTD; import de.rcenvironment.core.datamodel.types.api.DateTimeTD; import de.rcenvironment.core.datamodel.types.api.DirectoryReferenceTD; import de.rcenvironment.core.datamodel.types.api.FileReferenceTD; import de.rcenvironment.core.datamodel.types.api.FloatTD; import de.rcenvironment.core.datamodel.types.api.IntegerTD; import de.rcenvironment.core.datamodel.types.api.MatrixTD; import de.rcenvironment.core.datamodel.types.api.NotAValueTD; import de.rcenvironment.core.datamodel.types.api.ShortTextTD; import de.rcenvironment.core.datamodel.types.api.SmallTableTD; import de.rcenvironment.core.datamodel.types.api.VectorTD; import de.rcenvironment.core.utils.common.JsonUtils; import de.rcenvironment.core.utils.common.StringUtils; /** * Default {@link TypedDatumSerializer} implementation. * * @author Jan Flink * @author Doreen Seider */ public class DefaultTypedDatumSerializer implements TypedDatumSerializer { private static final String UNABLE_TO_SERIALIZE_STRING = "Serialization of %s is not supported."; private static final String UNABLE_TO_DESERIALIZE_STRING = "Could not deserialize \"%s\""; private static final String FILE_NAME_STRING = "fileName"; private static final String FILE_REFERENCE_STRING = "fileReference"; private static final String FILE_SIZE_STRING = "fileSize"; private static final String DIRECTORY_NAME_STRING = "directoryName"; private static final String DIRECTORY_REFERENCE_STRING = "directoryReference"; private static final String DIRECTORY_SIZE_STRING = "directorySize"; private static final String LAST_MODIFIED_STRING = "lastModified"; private static final String TYPE_STRING = "t"; private static final String VALUE_STRING = "v"; private static final String ROW_STRING = "r"; private static final String COLUMN_STRING = "c"; private static final String ID_STRING = "id"; private static final Log LOGGER = LogFactory.getLog(DefaultTypedDatumSerializer.class); private static final ObjectMapper MAPPER = JsonUtils.getDefaultObjectMapper(); @Override public TypedDatum deserialize(String input) { TypedDatum returnDatum = null; if (input.length() == 0) { throw new IllegalArgumentException(StringUtils.format(UNABLE_TO_DESERIALIZE_STRING, input.toString())); } try { JsonNode rootNode = MAPPER.readTree(input); DataType dataType = DataType.byShortName(rootNode.get(TYPE_STRING).getTextValue()); JsonNode valueNode = rootNode.get(VALUE_STRING); returnDatum = getTypedDatumFromNode(dataType, rootNode, valueNode); } catch (JsonParseException e) { LOGGER.error(StringUtils.format(UNABLE_TO_DESERIALIZE_STRING, input), e); throw new IllegalArgumentException(StringUtils.format(UNABLE_TO_DESERIALIZE_STRING, input)); } catch (JsonProcessingException e) { LOGGER.error(StringUtils.format(UNABLE_TO_DESERIALIZE_STRING, input), e); throw new IllegalArgumentException(StringUtils.format(UNABLE_TO_DESERIALIZE_STRING, input)); } catch (IOException e) { LOGGER.error(StringUtils.format(UNABLE_TO_DESERIALIZE_STRING, input), e); throw new IllegalArgumentException(StringUtils.format(UNABLE_TO_DESERIALIZE_STRING, input)); } return returnDatum; } private TypedDatum getTypedDatumFromNode(DataType dataType, JsonNode rootNode, JsonNode valueNode) throws JsonProcessingException, IOException { TypedDatum returnDatum; DefaultTypedDatumFactory factory = new DefaultTypedDatumFactory(); switch (dataType) { case Boolean: returnDatum = factory.createBoolean(valueNode.asBoolean()); break; case ShortText: returnDatum = factory.createShortText(valueNode.asText()); break; case Integer: returnDatum = factory.createInteger(valueNode.asLong()); break; case Float: returnDatum = factory.createFloat(valueNode.asDouble()); break; case DateTime: returnDatum = factory.createDateTime(valueNode.asLong()); break; case Vector: VectorTD vector = factory.createVector(valueNode.size()); for (int i = 0; i < valueNode.size(); i++) { vector.setFloatTDForElement(factory.createFloat(valueNode.get(i).asDouble()), i); } returnDatum = vector; break; case Matrix: ArrayNode matrixArray = (ArrayNode) valueNode; MatrixTD matrix = factory.createMatrix(rootNode.get(ROW_STRING).asInt(), rootNode.get(COLUMN_STRING).asInt()); for (int i = 0; i < matrixArray.size(); i++) { ArrayNode matrixRowArray = (ArrayNode) matrixArray.get(i); for (int j = 0; j < matrixRowArray.size(); j++) { FloatTD value = factory.createFloat(matrixRowArray.get(j).asDouble()); matrix.setFloatTDForElement(value, i, j); } } returnDatum = matrix; break; case SmallTable: SmallTableTD smallTable = factory.createSmallTable(rootNode.get(ROW_STRING).asInt(), rootNode.get(COLUMN_STRING).asInt()); ArrayNode tableArray = (ArrayNode) valueNode; for (int i = 0; i < tableArray.size(); i++) { ArrayNode tableRowArray = (ArrayNode) tableArray.get(i); for (int j = 0; j < tableRowArray.size(); j++) { // To remain compatibility to all versions below 7.1, there must this diffenerce in deserializing small table if (tableRowArray.get(j) instanceof TextNode) { smallTable.setTypedDatumForCell(deserialize(tableRowArray.get(j).asText()), i, j); } else { DataType dataTypeTableEntry = DataType.byShortName(tableRowArray.get(j).get(TYPE_STRING).asText()); JsonNode valueNodeTableEntry = tableRowArray.get(j).get(VALUE_STRING); smallTable.setTypedDatumForCell( getTypedDatumFromNode(dataTypeTableEntry, tableRowArray.get(j), valueNodeTableEntry), i, j); } } } returnDatum = smallTable; break; case NotAValue: // backward-compatibility to RCE version < 8.0.0 if value is read from data management if (valueNode.isValueNode()) { String id = valueNode.getTextValue(); final String failureCaseSuffix = "_flr"; if (id.endsWith(failureCaseSuffix)) { returnDatum = factory.createNotAValue(id, NotAValueTD.Cause.Failure); } else { returnDatum = factory.createNotAValue(id, NotAValueTD.Cause.InvalidInputs); } } else { NotAValueTD notAValue = factory.createNotAValue(valueNode.get(ID_STRING).getTextValue(), NotAValueTD.Cause.valueOf(valueNode.get(TYPE_STRING).getTextValue())); returnDatum = notAValue; } break; case Empty: returnDatum = factory.createEmpty(); break; case FileReference: FileReferenceTD fileReference = factory.createFileReference(valueNode.get(FILE_REFERENCE_STRING).getTextValue(), valueNode.get(FILE_NAME_STRING).getTextValue()); fileReference.setFileSize(valueNode.get(FILE_SIZE_STRING).getLongValue()); if (valueNode.has(LAST_MODIFIED_STRING) && !valueNode.get(LAST_MODIFIED_STRING).isNull()) { fileReference.setLastModified(new Date(valueNode.get(LAST_MODIFIED_STRING).asLong())); } returnDatum = fileReference; break; case DirectoryReference: DirectoryReferenceTD directoryReference = factory.createDirectoryReference(valueNode.get(DIRECTORY_REFERENCE_STRING).getTextValue(), valueNode.get(DIRECTORY_NAME_STRING).getTextValue()); directoryReference.setDirectorySize(valueNode.get(DIRECTORY_SIZE_STRING).asLong()); returnDatum = directoryReference; break; case StructuredData: case BigTable: default: throw new IllegalArgumentException(StringUtils.format(UNABLE_TO_DESERIALIZE_STRING, rootNode.toString())); } return returnDatum; } @Override public String serialize(TypedDatum input) { ObjectMapper mapper = new ObjectMapper(); ObjectNode rootNode = mapper.createObjectNode(); if (input == null || input.getDataType() == null) { throw new NullPointerException(); } rootNode.put(TYPE_STRING, input.getDataType().getShortName()); switch (input.getDataType()) { case Boolean: rootNode.put(VALUE_STRING, ((BooleanTD) input).getBooleanValue()); break; case ShortText: rootNode.put(VALUE_STRING, ((ShortTextTD) input).getShortTextValue()); break; case Integer: IntegerTD integer = (IntegerTD) input; rootNode.put(VALUE_STRING, integer.getIntValue()); break; case Float: FloatTD floatData = (FloatTD) input; rootNode.put(VALUE_STRING, floatData.getFloatValue()); break; case DateTime: DateTimeTD dateTime = (DateTimeTD) input; rootNode.put(VALUE_STRING, dateTime.getDateTimeInMilliseconds()); break; case Vector: VectorTD vector = (VectorTD) input; ArrayNode vectorArray = mapper.createArrayNode(); for (int i = 0; i < vector.getRowDimension(); i++) { vectorArray.add(vector.getFloatTDOfElement(i).getFloatValue()); } rootNode.put(VALUE_STRING, vectorArray); break; case Matrix: MatrixTD matrix = (MatrixTD) input; rootNode.put(ROW_STRING, matrix.getRowDimension()); rootNode.put(COLUMN_STRING, matrix.getColumnDimension()); ArrayNode matrixArray = mapper.createArrayNode(); for (int i = 0; i < matrix.getRowDimension(); i++) { ArrayNode matrixRowArray = mapper.createArrayNode(); for (int j = 0; j < matrix.getColumnDimension(); j++) { matrixRowArray.add(matrix.getFloatTDOfElement(i, j).getFloatValue()); } matrixArray.add(matrixRowArray); } rootNode.put(VALUE_STRING, matrixArray); break; case SmallTable: SmallTableTD smallTable = (SmallTableTD) input; rootNode.put(ROW_STRING, smallTable.getRowCount()); rootNode.put(COLUMN_STRING, smallTable.getColumnCount()); ArrayNode smallTableArray = mapper.createArrayNode(); for (int i = 0; i < smallTable.getRowCount(); i++) { ArrayNode smallTableRowArray = mapper.createArrayNode(); for (int j = 0; j < smallTable.getColumnCount(); j++) { smallTableRowArray.addPOJO(serialize(smallTable.getTypedDatumOfCell(i, j))); } smallTableArray.add(smallTableRowArray); } rootNode.put(VALUE_STRING, smallTableArray); break; case FileReference: ObjectNode fileObjectNode = mapper.createObjectNode(); FileReferenceTD fileReference = (FileReferenceTD) input; fileObjectNode.put(FILE_REFERENCE_STRING, fileReference.getFileReference()); fileObjectNode.put(FILE_NAME_STRING, fileReference.getFileName()); fileObjectNode.put(FILE_SIZE_STRING, fileReference.getFileSizeInBytes()); if (fileReference.getLastModified() != null) { fileObjectNode.put(LAST_MODIFIED_STRING, fileReference.getLastModified().getTime()); } rootNode.put(VALUE_STRING, fileObjectNode); break; case DirectoryReference: ObjectNode dirObjectNode = mapper.createObjectNode(); DirectoryReferenceTD directoryReference = (DirectoryReferenceTD) input; dirObjectNode.put(DIRECTORY_REFERENCE_STRING, directoryReference.getDirectoryReference()); dirObjectNode.put(DIRECTORY_NAME_STRING, directoryReference.getDirectoryName()); dirObjectNode.put(DIRECTORY_SIZE_STRING, directoryReference.getDirectorySizeInBytes()); rootNode.put(VALUE_STRING, dirObjectNode); break; case NotAValue: ObjectNode notAValueObjectNode = mapper.createObjectNode(); NotAValueTD notAValue = (NotAValueTD) input; notAValueObjectNode.put(ID_STRING, notAValue.getIdentifier()); notAValueObjectNode.put(TYPE_STRING, notAValue.getCause().name()); rootNode.put(VALUE_STRING, notAValueObjectNode); break; case Empty: break; case BigTable: case StructuredData: default: throw new IllegalArgumentException(StringUtils.format(UNABLE_TO_SERIALIZE_STRING, input.getDataType().getDisplayName())); } try { return mapper.writeValueAsString(rootNode); } catch (IOException e) { throw new IllegalArgumentException(StringUtils.format(UNABLE_TO_SERIALIZE_STRING, input.getDataType().getDisplayName()), e); } } }