Java tutorial
/* * Copyright 2014 Alfresco Software, Ltd. All rights reserved. * * License rights for this program may be obtained from Alfresco Software, Ltd. * pursuant to a written agreement and any use of this program without such an * agreement is prohibited. */ package org.alfresco.serializers; import java.io.IOException; import java.io.Serializable; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.Map; import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.events.node.types.DataType; import org.alfresco.events.node.types.Property; import org.alfresco.serializers.PropertyValue.ValueType; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.DictionaryException; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.repository.datatype.TypeConversionException; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.util.PropertyCheck; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.elasticsearch.common.xcontent.XContentBuilder; import com.mongodb.BasicDBObjectBuilder; import com.mongodb.DBObject; /** * * @author sglover * */ public class PropertySerializer { protected static final Log logger = LogFactory.getLog(PropertySerializer.class); private Set<QName> NUMBER_TYPES; private DictionaryService dictionaryService; private NamespaceService namespaceService; private NodePropertyHelper nodePropertyHelper; public PropertySerializer(DictionaryService dictionaryService, NamespaceService namespaceService) { this.dictionaryService = dictionaryService; this.namespaceService = namespaceService; this.nodePropertyHelper = new NodePropertyHelper(); init(); } private void init() { PropertyCheck.mandatory(this, "dictionaryService", dictionaryService); PropertyCheck.mandatory(this, "namespaceService", namespaceService); NUMBER_TYPES = new HashSet<QName>(4); NUMBER_TYPES.add(DataTypeDefinition.DOUBLE); NUMBER_TYPES.add(DataTypeDefinition.FLOAT); NUMBER_TYPES.add(DataTypeDefinition.INT); NUMBER_TYPES.add(DataTypeDefinition.LONG); } private void makeNodePropertyValue(PropertyDefinition propertyDef, Serializable value, XContentBuilder builder) throws IOException { String propName = propertyDef.getName().toPrefixString(namespaceService); PropertyValue propertyValue = nodePropertyHelper.makeNodePropertyValue(propertyDef, value); switch (propertyValue.getPersistedValueType()) { case NULL: break; case BOOLEAN: boolean boolValue = propertyValue.getBooleanValue(); builder.field(propName, boolValue); break; case LONG: long longValue = propertyValue.getLongValue(); builder.field(propName, longValue); break; case FLOAT: float floatValue = propertyValue.getFloatValue(); builder.field(propName, floatValue); break; case DOUBLE: double doubleValue = propertyValue.getDoubleValue(); builder.field(propName, doubleValue); break; case STRING: String stringValue = propertyValue.getStringValue(); builder.field(propName, stringValue); break; case JSONOBJECT: JSON json = propertyValue.getJSON(); XContentBuilder builder1 = json.makeXContent(); builder.field(propName, builder1); break; // case FIXED_POINT: // ret = propertyValue.getJSON(); // break; case SERIALIZABLE: // TODO // Serializable s = propertyValue.getSerializableValue(); // byte[] b = SerializationUtils.serialize(s); // ret = files.createFile(nodeId, nodeVersion, propertyQName.toString(), b); break; default: throw new AlfrescoRuntimeException("Unrecognised value type: " + propertyValue.getPersistedType()); } } private void makeNodePropertyValue(PropertyDefinition propertyDef, Serializable value, BasicDBObjectBuilder builder) { String propName = propertyDef.getName().toPrefixString(namespaceService); PropertyValue propertyValue = nodePropertyHelper.makeNodePropertyValue(propertyDef, value); switch (propertyValue.getPersistedValueType()) { case NULL: break; case BOOLEAN: boolean boolValue = propertyValue.getBooleanValue(); builder.append(propName, boolValue); break; case LONG: long longValue = propertyValue.getLongValue(); builder.append(propName, longValue); break; case FLOAT: float floatValue = propertyValue.getFloatValue(); builder.append(propName, floatValue); break; case DOUBLE: double doubleValue = propertyValue.getDoubleValue(); builder.append(propName, doubleValue); break; case STRING: String stringValue = propertyValue.getStringValue(); builder.append(propName, stringValue); break; case JSONOBJECT: JSON json = propertyValue.getJSON(); DBObject builder1 = json.makeDBObject(); builder.append(propName, builder1); break; // case FIXED_POINT: // ret = propertyValue.getJSON(); // break; case SERIALIZABLE: // TODO // Serializable s = propertyValue.getSerializableValue(); // byte[] b = SerializationUtils.serialize(s); // ret = files.createFile(nodeId, nodeVersion, propertyQName.toString(), b); break; default: throw new AlfrescoRuntimeException("Unrecognised value type: " + propertyValue.getPersistedType()); } } // private Serializable makeNodePropertyValue(PropertyDefinition propertyDef, Serializable value) // { // Serializable ret = null; // // PropertyValue propertyValue = nodePropertyHelper.makeNodePropertyValue(propertyDef, value); // switch(propertyValue.getPersistedValueType()) // { // case NULL: // break; // case BOOLEAN: // ret = propertyValue.getBooleanValue(); // break; // case LONG: // ret = propertyValue.getLongValue(); // break; // case FLOAT: // ret = propertyValue.getFloatValue(); // break; // case DOUBLE: // ret = propertyValue.getDoubleValue(); // break; // case STRING: // ret = propertyValue.getStringValue(); // break; // case JSONOBJECT: // JSON json = propertyValue.getJSON(); // ret = json; // break; //// case FIXED_POINT: //// ret = propertyValue.getJSON(); //// break; // case SERIALIZABLE: // // TODO //// Serializable s = propertyValue.getSerializableValue(); //// byte[] b = SerializationUtils.serialize(s); //// ret = files.createFile(nodeId, nodeVersion, propertyQName.toString(), b); // break; // default: // throw new AlfrescoRuntimeException("Unrecognised value type: " // + propertyValue.getPersistedType()); // } // // return ret; // } public Serializable serialize(QName propertyQName, Object value) { Serializable ret = null; PropertyDefinition propertyDef = dictionaryService.getProperty(propertyQName); if (value != null) { QName propertyTypeQName; if (propertyDef == null) // property not recognised { // allow it for now - persisting excess properties can be useful sometimes propertyTypeQName = DataTypeDefinition.ANY; } else { propertyTypeQName = propertyDef.getDataType().getName(); } // A property may appear to be multi-valued if the model definition is loose and // an unexploded collection is passed in. Otherwise, use the model-defined behaviour // strictly. boolean isMultiValued; if (propertyTypeQName.equals(DataTypeDefinition.ANY)) { // It is multi-valued if required (we are not in a collection and the property is a new collection) isMultiValued = (value != null) && (value instanceof Collection<?>); } else { isMultiValued = propertyDef.isMultiValued(); } // Handle different scenarios. // - Do we need to explode a collection? // - Does the property allow collections? if (value instanceof Collection<?>) { // We are not (yet) processing a collection and the property is a collection i.e. needs exploding // Check that multi-valued properties are supported if the property is a collection if (!isMultiValued) { throw new DictionaryException( "A single-valued property of this type may not be a collection: \n" + " Property: " + propertyDef + "\n" + " Type: " + propertyTypeQName + "\n" + " Value: " + value); } // We have an allowable collection. @SuppressWarnings("unchecked") Collection<Object> collectionValues = (Collection<Object>) value; LinkedList<Object> values = new LinkedList<>(); for (Object collectionValueObj : collectionValues) { if (collectionValueObj != null && !(collectionValueObj instanceof Serializable)) { throw new IllegalArgumentException("Node properties must be fully serializable, " + "including values contained in collections. \n" + " Property: " + propertyDef + "\n" + "\n" + " Value: " + collectionValueObj); } Serializable collectionValue = (Serializable) collectionValueObj; PropertyValue propertyValue = nodePropertyHelper.makeNodePropertyValue(propertyDef, collectionValue); switch (propertyValue.getPersistedValueType()) { case NULL: values.add(null); break; case BOOLEAN: boolean boolValue = propertyValue.getBooleanValue(); values.add(boolValue); break; case LONG: long longValue = propertyValue.getLongValue(); values.add(longValue); break; case FLOAT: float floatValue = propertyValue.getFloatValue(); values.add(floatValue); break; case DOUBLE: double doubleValue = propertyValue.getDoubleValue(); values.add(doubleValue); break; case STRING: String stringValue = propertyValue.getStringValue(); values.add(stringValue); break; case JSONOBJECT: JSON json = propertyValue.getJSON(); values.add(json); break; case SERIALIZABLE: values.add(propertyValue.getSerializableValue()); break; default: throw new AlfrescoRuntimeException( "Unrecognised value type: " + propertyValue.getPersistedType()); } } ret = values; } else { // We are either processing collection elements OR the property is not a collection // Collections of collections are only supported by type d:any if (value instanceof Collection<?> && !propertyTypeQName.equals(DataTypeDefinition.ANY)) { throw new DictionaryException( "Collections of collections (Serializable) are only supported by type 'd:any': \n" + " Property: " + propertyDef + "\n" + " Type: " + propertyTypeQName + "\n" + " Value: " + value); } PropertyValue propertyValue = nodePropertyHelper.makeNodePropertyValue(propertyDef, (Serializable) value); switch (propertyValue.getPersistedValueType()) { case NULL: ret = null; break; case BOOLEAN: ret = propertyValue.getBooleanValue(); break; case LONG: ret = propertyValue.getLongValue(); break; case FLOAT: ret = propertyValue.getFloatValue(); break; case DOUBLE: ret = propertyValue.getDoubleValue(); break; case STRING: ret = propertyValue.getStringValue(); break; case JSONOBJECT: ret = propertyValue.getJSON(); break; case SERIALIZABLE: ret = propertyValue.getSerializableValue(); break; default: throw new AlfrescoRuntimeException( "Unrecognised value type: " + propertyValue.getPersistedType()); } } } return ret; } public void serialize(QName propertyQName, Serializable value, XContentBuilder builder) throws IOException { PropertyDefinition propertyDef = dictionaryService.getProperty(propertyQName); Serializable val = serialize(propertyQName, value); makeNodePropertyValue(propertyDef, val, builder); } public void serialize(QName propertyQName, Serializable value, BasicDBObjectBuilder builder) { PropertyDefinition propertyDef = dictionaryService.getProperty(propertyQName); Serializable val = serialize(propertyQName, value); makeNodePropertyValue(propertyDef, val, builder); } private DataType getPropertyType(QName propertyName) { DataType dataType = null; PropertyDefinition def = dictionaryService.getProperty(propertyName); if (def != null) { DataTypeDefinition dataTypeDef = def.getDataType(); String dataTypeDefStr = dataTypeDef.getName().getPrefixString().substring(2); StringBuilder dataTypeName = new StringBuilder(dataTypeDefStr.substring(0, 1).toUpperCase()); dataTypeName.append(dataTypeDefStr.substring(1)); dataType = DataType.valueOf(dataTypeName.toString()); } return dataType; } public Map<String, Property> serialize(Map<QName, Serializable> properties) { Map<String, Property> ret = new HashMap<>(); for (Map.Entry<QName, Serializable> entry : properties.entrySet()) { QName propertyQName = entry.getKey(); String propName = propertyQName.toPrefixString(namespaceService); Serializable propValue = entry.getValue(); Serializable serializedPropValue = serialize(propertyQName, propValue); DataType type = getPropertyType(propertyQName); Property property = new Property(propName, serializedPropValue, type); ret.put(propName, property); } return ret; } public Serializable deserialize(QName propertyName, Object propValue) { PropertyDefinition propertyDef = dictionaryService.getProperty(propertyName); return deserialize(propertyDef, propValue); } @SuppressWarnings("unchecked") public Serializable deserialize(PropertyDefinition propertyDef, Object propValue) { Serializable value = null; if (propValue instanceof Collection<?>) { LinkedList<Object> list = new LinkedList<>(); value = list; Collection<Object> c = (Collection<Object>) propValue; for (Object o : c) { Object value1 = deserialize(propertyDef, o); list.add(value1); } } else { Serializable s = (Serializable) propValue; PropertyValue nodePropValue = new PropertyValue(); if (s instanceof String) { nodePropValue.setStringValue((String) s); nodePropValue.setPersistedType(ValueType.STRING.getOrdinalNumber()); } else if (s instanceof Boolean) { nodePropValue.setBooleanValue((Boolean) s); nodePropValue.setPersistedType(ValueType.BOOLEAN.getOrdinalNumber()); } else if (s instanceof Double) { nodePropValue.setDoubleValue((Double) s); nodePropValue.setPersistedType(ValueType.DOUBLE.getOrdinalNumber()); } else if (s instanceof Float) { nodePropValue.setFloatValue((Float) s); nodePropValue.setPersistedType(ValueType.FLOAT.getOrdinalNumber()); } else if (s instanceof Long) { nodePropValue.setLongValue((Long) s); nodePropValue.setPersistedType(ValueType.LONG.getOrdinalNumber()); } else if (s instanceof JSON) { nodePropValue.setJSON((JSON) s); nodePropValue.setPersistedType(ValueType.JSONOBJECT.getOrdinalNumber()); } else { nodePropValue.setSerializableValue(s); nodePropValue.setPersistedType(ValueType.SERIALIZABLE.getOrdinalNumber()); } // Do we definitely have MLText? // boolean isMLText = (propertyDef != null && propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT)); // Get the local entry value Serializable entryValue = makeSerializableValue(propertyDef, nodePropValue); value = entryValue; } // Done return value; } public Serializable makeSerializableValue(PropertyDefinition propertyDef, PropertyValue propertyValue) { if (propertyValue == null) { return null; } // get property attributes final QName propertyTypeQName; if (propertyDef == null) { // allow this for now propertyTypeQName = DataTypeDefinition.ANY; } else { propertyTypeQName = propertyDef.getDataType().getName(); } try { Serializable value = propertyValue.getValue(propertyTypeQName); // done return value; } catch (TypeConversionException e) { throw new TypeConversionException( "The property value is not compatible with the type defined for the property: \n" + " property: " + (propertyDef == null ? "unknown" : propertyDef) + "\n" + " property value: " + propertyValue, e); } } public Map<String, Serializable> deserialize(Map<String, Serializable> serializedProperties) { Map<String, Serializable> properties = new HashMap<>(); for (Map.Entry<String, Serializable> entry : serializedProperties.entrySet()) { String propName = entry.getKey(); Serializable propValue = entry.getValue(); QName propertyQName = QName.createQName(propName, namespaceService); PropertyDefinition propertyDef = dictionaryService.getProperty(propertyQName); Serializable deserializedPropValue = deserialize(propertyDef, propValue); properties.put(propName, deserializedPropValue); } return properties; } }