001/* 002 * (C) Copyright 2017 Nuxeo (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Funsho David 018 * 019 */ 020 021package org.nuxeo.directory.mongodb; 022 023import java.io.Serializable; 024import java.lang.reflect.Array; 025import java.util.ArrayList; 026import java.util.Arrays; 027import java.util.Calendar; 028import java.util.Collections; 029import java.util.Date; 030import java.util.HashMap; 031import java.util.List; 032import java.util.Map; 033 034import org.bson.Document; 035import org.nuxeo.ecm.core.schema.types.Type; 036import org.nuxeo.ecm.core.schema.types.primitives.IntegerType; 037import org.nuxeo.ecm.core.schema.types.primitives.LongType; 038 039/** 040 * Helper for serialization/deserialization of BSON objects 041 * 042 * @since 9.1 043 */ 044public class MongoDBSerializationHelper { 045 046 public static final String MONGODB_ID = "_id"; 047 048 public static final String MONGODB_SEQ = "seq"; 049 050 private MongoDBSerializationHelper() { 051 // empty 052 } 053 054 /** 055 * Create a BSON object with a single field from a pair key/value 056 * 057 * @param key the key which corresponds to the field id in the object 058 * @param value the value which corresponds to the field value in the object 059 * @return the new BSON object 060 */ 061 public static Document fieldMapToBson(String key, Object value) { 062 return fieldMapToBson(Collections.singletonMap(key, value)); 063 } 064 065 /** 066 * Create a BSON object from a map 067 * 068 * @param fieldMap a map of keys/values 069 * @return the new BSON object 070 */ 071 public static Document fieldMapToBson(Map<String, Object> fieldMap) { 072 Document doc = new Document(); 073 for (Map.Entry<String, Object> entry : fieldMap.entrySet()) { 074 Object val = valueToBson(entry.getValue()); 075 if (val != null) { 076 doc.put(entry.getKey(), val); 077 } 078 } 079 return doc; 080 } 081 082 /** 083 * Cast an object according to its instance 084 * 085 * @param value the object to transform 086 * @return the BSON object 087 */ 088 public static Object valueToBson(Object value) { 089 if (value instanceof Map) { 090 @SuppressWarnings("unchecked") 091 Map<String, Object> map = (Map<String, Object>) value; 092 return fieldMapToBson(map); 093 } else if (value instanceof List) { 094 @SuppressWarnings("unchecked") 095 List<Object> values = (List<Object>) value; 096 return listToBson(values); 097 } else if (value instanceof Object[]) { 098 return listToBson(Arrays.asList((Object[]) value)); 099 } else { 100 return serializableToBson(value); 101 } 102 } 103 104 /** 105 * Cast an object according to its instance ans its type 106 * 107 * @param value the object to transform 108 * @param type the object type 109 * @return the BSON object 110 * @since 9.2 111 */ 112 public static Object valueToBson(Object value, Type type) { 113 if (value != null && type instanceof IntegerType) { 114 return Integer.valueOf(String.valueOf(value)); 115 } else if (value != null && type instanceof LongType) { 116 return Long.valueOf(String.valueOf(value)); 117 } else { 118 return valueToBson(value); 119 } 120 } 121 122 protected static List<Object> listToBson(List<Object> values) { 123 List<Object> objects = new ArrayList<>(values.size()); 124 for (Object value : values) { 125 objects.add(valueToBson(value)); 126 } 127 return objects; 128 } 129 130 protected static Object serializableToBson(Object value) { 131 if (value instanceof Calendar) { 132 return ((Calendar) value).getTime(); 133 } 134 return value; 135 } 136 137 /** 138 * Create a map from a BSON object 139 * 140 * @param doc the BSON object to parse 141 * @return the new map 142 */ 143 public static Map<String, Object> bsonToFieldMap(Document doc) { 144 Map<String, Object> fieldMap = new HashMap<>(); 145 for (String key : doc.keySet()) { 146 if (MONGODB_ID.equals(key)) { 147 // skip native id 148 continue; 149 } 150 fieldMap.put(key, bsonToValue(doc.get(key))); 151 } 152 return fieldMap; 153 } 154 155 protected static Serializable bsonToValue(Object value) { 156 if (value instanceof List) { 157 @SuppressWarnings("unchecked") 158 List<Object> list = (List<Object>) value; 159 if (list.isEmpty()) { 160 return null; 161 } else { 162 Class<?> klass = Object.class; 163 for (Object o : list) { 164 if (o != null) { 165 klass = scalarToSerializableClass(o.getClass()); 166 break; 167 } 168 } 169 if (Document.class.isAssignableFrom(klass)) { 170 List<Serializable> l = new ArrayList<>(list.size()); 171 for (Object o : list) { 172 l.add((Serializable) bsonToFieldMap((Document) o)); 173 } 174 return (Serializable) l; 175 } else { 176 // turn the list into a properly-typed array 177 Object[] array = (Object[]) Array.newInstance(klass, list.size()); 178 int i = 0; 179 for (Object o : list) { 180 array[i++] = scalarToSerializable(o); 181 } 182 return array; 183 } 184 } 185 } else if (value instanceof Document) { 186 return (Serializable) bsonToFieldMap((Document) value); 187 } else { 188 return scalarToSerializable(value); 189 } 190 } 191 192 protected static Class<?> scalarToSerializableClass(Class<?> klass) { 193 if (Date.class.isAssignableFrom(klass)) { 194 return Calendar.class; 195 } 196 return klass; 197 } 198 199 protected static Serializable scalarToSerializable(Object value) { 200 if (value instanceof Date) { 201 Calendar cal = Calendar.getInstance(); 202 cal.setTime((Date) value); 203 return cal; 204 } 205 return (Serializable) value; 206 } 207 208}