/*
* Copyright 2010 Outerthought bvba
*
* 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 org.lilyproject.tools.import_.json;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.node.ArrayNode;
import org.codehaus.jackson.node.JsonNodeFactory;
import org.codehaus.jackson.node.ObjectNode;
import org.joda.time.DateTime;
import org.lilyproject.bytes.api.ByteArray;
import org.lilyproject.repository.api.Blob;
import org.lilyproject.repository.api.FieldType;
import org.lilyproject.repository.api.HierarchyPath;
import org.lilyproject.repository.api.LRepository;
import org.lilyproject.repository.api.Metadata;
import org.lilyproject.repository.api.QName;
import org.lilyproject.repository.api.Record;
import org.lilyproject.repository.api.RepositoryException;
import org.lilyproject.repository.api.Scope;
import org.lilyproject.repository.api.ValueType;
public class RecordWriter implements EntityWriter<Record> {
public static final RecordWriter INSTANCE = new RecordWriter();
@Override
public ObjectNode toJson(Record record, WriteOptions options, LRepository repository) throws RepositoryException,
InterruptedException {
Namespaces namespaces = new NamespacesImpl(options != null ? options.getUseNamespacePrefixes() :
NamespacesImpl.DEFAULT_USE_PREFIXES);
ObjectNode recordNode = toJson(record, options, namespaces, repository);
if (namespaces.usePrefixes()) {
recordNode.put("namespaces", NamespacesConverter.toJson(namespaces));
}
return recordNode;
}
@Override
public ObjectNode toJson(Record record, WriteOptions options, Namespaces namespaces, LRepository repository)
throws RepositoryException, InterruptedException {
JsonNodeFactory factory = JsonNodeFactory.instance;
ObjectNode recordNode = factory.objectNode();
if (record.getId() != null) {
recordNode.put("id", record.getId().toString());
}
if (record.getVersion() != null) {
recordNode.put("version", record.getVersion());
}
if (record.getRecordTypeName() != null) {
recordNode.put("type", typeToJson(record.getRecordTypeName(), record.getRecordTypeVersion(), namespaces));
}
QName versionedTypeName = record.getRecordTypeName(Scope.VERSIONED);
if (versionedTypeName != null) {
long version = record.getRecordTypeVersion(Scope.VERSIONED);
recordNode.put("versionedType", typeToJson(versionedTypeName, version, namespaces));
}
QName versionedMutableTypeName = record.getRecordTypeName(Scope.VERSIONED_MUTABLE);
if (versionedMutableTypeName != null) {
long version = record.getRecordTypeVersion(Scope.VERSIONED_MUTABLE);
recordNode.put("versionedMutableType", typeToJson(versionedMutableTypeName, version, namespaces));
}
Map<QName, Object> fields = record.getFields();
if (fields.size() > 0) {
ObjectNode fieldsNode = recordNode.putObject("fields");
ObjectNode schemaNode = null;
if (options.getIncludeSchema()) {
schemaNode = recordNode.putObject("schema");
}
for (Map.Entry<QName, Object> field : fields.entrySet()) {
FieldType fieldType = repository.getTypeManager().getFieldTypeByName(field.getKey());
String fieldName = QNameConverter.toJson(fieldType.getName(), namespaces);
// fields entry
fieldsNode.put(
fieldName,
valueToJson(field.getValue(), fieldType.getValueType(), options, namespaces, repository));
// schema entry
if (schemaNode != null) {
schemaNode.put(fieldName, FieldTypeWriter.toJson(fieldType, namespaces, false));
}
}
}
Map<String, String> attributes = record.getAttributes();
if (attributes.size() > 0) {
ObjectNode attributesNode = recordNode.putObject("attributes");
for (String key : attributes.keySet()) {
attributesNode.put(key, attributes.get(key));
}
}
Map<QName, Metadata> metadatas = record.getMetadataMap();
if (!metadatas.isEmpty()) {
ObjectNode metadatasNode = recordNode.putObject("metadata");
for (Map.Entry<QName, Metadata> entry : metadatas.entrySet()) {
String fieldName = QNameConverter.toJson(entry.getKey(), namespaces);
ObjectNode metadataNode = metadatasNode.putObject(fieldName);
for (Map.Entry<String, Object> metadata : entry.getValue().getMap().entrySet()) {
Object value = metadata.getValue();
if (value instanceof String) {
metadataNode.put(metadata.getKey(), (String)value);
} else if (value instanceof Integer) {
metadataNode.put(metadata.getKey(), (Integer)value);
} else if (value instanceof Long) {
metadataNode.put(metadata.getKey(), (Long)value);
} else if (value instanceof Float) {
metadataNode.put(metadata.getKey(), (Float)value);
} else if (value instanceof Double) {
metadataNode.put(metadata.getKey(), (Double)value);
} else if (value instanceof Boolean) {
metadataNode.put(metadata.getKey(), (Boolean)value);
} else if (value instanceof ByteArray) {
ObjectNode binaryNode = metadataNode.putObject(metadata.getKey());
binaryNode.put("type", "binary");
binaryNode.put("value", ((ByteArray)value).getBytes());
} else if (value instanceof DateTime){
ObjectNode dateTimeNode = metadataNode.putObject(metadata.getKey());
dateTimeNode.put("type", "datetime");
dateTimeNode.put("value", value.toString());
} else {
throw new RuntimeException("Unsupported type of metadata value: " + value.getClass().getName()
+ " for value '" + value + "' in metadata field '" + metadata.getKey()
+ "' of record field " + entry.getKey());
}
}
}
ObjectNode metadataToDeleteNode = null;
for (Map.Entry<QName, Metadata> entry : metadatas.entrySet()) {
Set<String> fieldsToDelete = entry.getValue().getFieldsToDelete();
if (!fieldsToDelete.isEmpty()) {
if (metadataToDeleteNode == null) {
metadataToDeleteNode = recordNode.putObject("metadataToDelete");
}
String fieldName = QNameConverter.toJson(entry.getKey(), namespaces);
ArrayNode array = metadataToDeleteNode.putArray(fieldName);
for (String name : fieldsToDelete) {
array.add(name);
}
}
}
}
return recordNode;
}
private JsonNode listToJson(Object value, ValueType valueType, WriteOptions options, Namespaces namespaces,
LRepository repository) throws RepositoryException, InterruptedException {
List list = (List)value;
ArrayNode array = JsonNodeFactory.instance.arrayNode();
for (Object item : list) {
array.add(valueToJson(item, valueType, options, namespaces, repository));
}
return array;
}
private JsonNode pathToJson(Object value, ValueType valueType, WriteOptions options, Namespaces namespaces,
LRepository repository) throws RepositoryException, InterruptedException {
HierarchyPath path = (HierarchyPath)value;
ArrayNode array = JsonNodeFactory.instance.arrayNode();
for (Object element : path.getElements()) {
array.add(valueToJson(element, valueType, options, namespaces, repository));
}
return array;
}
public JsonNode valueToJson(Object value, ValueType valueType, WriteOptions options, Namespaces namespaces,
LRepository repository) throws RepositoryException, InterruptedException {
String name = valueType.getBaseName();
JsonNodeFactory factory = JsonNodeFactory.instance;
JsonNode result;
if (name.equals("LIST")) {
result = listToJson(value, valueType.getNestedValueType(), options, namespaces, repository);
} else if (name.equals("PATH")) {
result = pathToJson(value, valueType.getNestedValueType(), options, namespaces, repository);
} else if (name.equals("STRING")) {
result = factory.textNode((String)value);
} else if (name.equals("LONG")) {
result = factory.numberNode((Long)value);
} else if (name.equals("DOUBLE")) {
result = factory.numberNode((Double)value);
} else if (name.equals("BOOLEAN")) {
result = factory.booleanNode((Boolean)value);
} else if (name.equals("INTEGER")) {
result = factory.numberNode((Integer)value);
} else if (name.equals("URI") || name.equals("DATETIME") || name.equals("DATE") || name.equals("LINK")) {
result = factory.textNode(value.toString());
} else if (name.equals("DECIMAL")) {
result = factory.numberNode((BigDecimal)value);
} else if (name.equals("BLOB")) {
Blob blob = (Blob)value;
result = BlobConverter.toJson(blob);
} else if (name.equals("RECORD")){
result = toJson((Record)value, options, namespaces, repository);
} else if (name.equals("BYTEARRAY")) {
result = factory.binaryNode(((ByteArray) value).getBytes());
} else {
throw new RuntimeException("Unsupported value type: " + name);
}
return result;
}
private static JsonNode typeToJson(QName name, Long version, Namespaces namespaces) {
JsonNodeFactory factory = JsonNodeFactory.instance;
ObjectNode jsonType = factory.objectNode();
jsonType.put("name", QNameConverter.toJson(name, namespaces));
if (version != null) {
jsonType.put("version", version);
}
return jsonType;
}
}