/* * (C) Copyright 2017 Nuxeo (http://nuxeo.com/) and others. * * 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. * * Contributors: * Funsho David * */ package org.nuxeo.directory.mongodb; import java.io.Serializable; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bson.Document; /** * Helper for serialization/deserialization of BSON objects * * @since 9.1 */ public class MongoDBSerializationHelper { public static final String MONGODB_ID = "_id"; public static final String MONGODB_SEQ = "seq"; private MongoDBSerializationHelper() { // empty } /** * Create a BSON object with a single field from a pair key/value * * @param key the key which corresponds to the field id in the object * @param value the value which corresponds to the field value in the object * @return the new BSON object */ public static Document fieldMapToBson(String key, Object value) { return fieldMapToBson(Collections.singletonMap(key, value)); } /** * Create a BSON object from a map * * @param fieldMap a map of keys/values * @return the new BSON object */ public static Document fieldMapToBson(Map<String, Object> fieldMap) { Document doc = new Document(); for (Map.Entry<String, Object> entry : fieldMap.entrySet()) { Object val = valueToBson(entry.getValue()); if (val != null) { doc.put(entry.getKey(), val); } } return doc; } /** * Cast an object according to its instance * * @param value the object to transform * @return the BSON object */ public static Object valueToBson(Object value) { if (value instanceof Map) { @SuppressWarnings("unchecked") Map<String, Object> map = (Map<String, Object>) value; return fieldMapToBson(map); } else if (value instanceof List) { @SuppressWarnings("unchecked") List<Object> values = (List<Object>) value; return listToBson(values); } else if (value instanceof Object[]) { return listToBson(Arrays.asList((Object[]) value)); } else { return serializableToBson(value); } } protected static List<Object> listToBson(List<Object> values) { List<Object> objects = new ArrayList<>(values.size()); for (Object value : values) { objects.add(valueToBson(value)); } return objects; } protected static Object serializableToBson(Object value) { if (value instanceof Calendar) { return ((Calendar) value).getTime(); } return value; } /** * Create a map from a BSON object * * @param doc the BSON object to parse * @return the new map */ public static Map<String, Object> bsonToFieldMap(Document doc) { Map<String, Object> fieldMap = new HashMap<>(); for (String key : doc.keySet()) { if (MONGODB_ID.equals(key)) { // skip native id continue; } fieldMap.put(key, bsonToValue(doc.get(key))); } return fieldMap; } protected static Serializable bsonToValue(Object value) { if (value instanceof List) { @SuppressWarnings("unchecked") List<Object> list = (List<Object>) value; if (list.isEmpty()) { return null; } else { Class<?> klass = Object.class; for (Object o : list) { if (o != null) { klass = scalarToSerializableClass(o.getClass()); break; } } if (Document.class.isAssignableFrom(klass)) { List<Serializable> l = new ArrayList<>(list.size()); for (Object o : list) { l.add((Serializable) bsonToFieldMap((Document) o)); } return (Serializable) l; } else { // turn the list into a properly-typed array Object[] array = (Object[]) Array.newInstance(klass, list.size()); int i = 0; for (Object o : list) { array[i++] = scalarToSerializable(o); } return array; } } } else if (value instanceof Document) { return (Serializable) bsonToFieldMap((Document) value); } else { return scalarToSerializable(value); } } protected static Class<?> scalarToSerializableClass(Class<?> klass) { if (Date.class.isAssignableFrom(klass)) { return Calendar.class; } return klass; } protected static Serializable scalarToSerializable(Object value) { if (value instanceof Date) { Calendar cal = Calendar.getInstance(); cal.setTime((Date) value); return cal; } return (Serializable) value; } }