001/* 002 * (C) Copyright 2017 Nuxeo SA (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.runtime.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; 035 036/** 037 * Helper for serialization/deserialization of BSON objects 038 * 039 * @since 9.1 040 */ 041public class MongoDBSerializationHelper { 042 043 public static final String MONGODB_ID = "_id"; 044 045 private MongoDBSerializationHelper() { 046 // empty 047 } 048 049 /** 050 * Create a BSON object with a single field from a pair key/value 051 * 052 * @param key the key which corresponds to the field id in the object 053 * @param value the value which corresponds to the field value in the object 054 * @return the new BSON object 055 */ 056 public static Document fieldMapToBson(String key, Object value) { 057 return fieldMapToBson(Collections.singletonMap(key, value)); 058 } 059 060 /** 061 * Create a BSON object from a map 062 * 063 * @param fieldMap a map of keys/values 064 * @return the new BSON object 065 */ 066 public static Document fieldMapToBson(Map<String, Object> fieldMap) { 067 Document doc = new Document(); 068 for (Map.Entry<String, Object> entry : fieldMap.entrySet()) { 069 Object val = valueToBson(entry.getValue()); 070 if (val != null) { 071 doc.put(entry.getKey(), val); 072 } 073 } 074 return doc; 075 } 076 077 /** 078 * Cast an object according to its instance 079 * 080 * @param value the object to transform 081 * @return the BSON object 082 */ 083 public static Object valueToBson(Object value) { 084 if (value instanceof Map) { 085 @SuppressWarnings("unchecked") 086 Map<String, Object> map = (Map<String, Object>) value; 087 return fieldMapToBson(map); 088 } else if (value instanceof List) { 089 @SuppressWarnings("unchecked") 090 List<Object> values = (List<Object>) value; 091 return listToBson(values); 092 } else if (value instanceof Object[]) { 093 return listToBson(Arrays.asList((Object[]) value)); 094 } else { 095 return serializableToBson(value); 096 } 097 } 098 099 protected static List<Object> listToBson(List<Object> values) { 100 List<Object> objects = new ArrayList<>(values.size()); 101 for (Object value : values) { 102 objects.add(valueToBson(value)); 103 } 104 return objects; 105 } 106 107 protected static Object serializableToBson(Object value) { 108 if (value instanceof Calendar) { 109 return ((Calendar) value).getTime(); 110 } 111 return value; 112 } 113 114 /** 115 * Create a map from a BSON object 116 * 117 * @param doc the BSON object to parse 118 * @return the new map 119 */ 120 public static Map<String, Object> bsonToFieldMap(Document doc) { 121 Map<String, Object> fieldMap = new HashMap<>(); 122 for (String key : doc.keySet()) { 123 if (MONGODB_ID.equals(key)) { 124 // skip native id 125 continue; 126 } 127 fieldMap.put(key, bsonToValue(doc.get(key))); 128 } 129 return fieldMap; 130 } 131 132 protected static Serializable bsonToValue(Object value) { 133 if (value instanceof List) { 134 @SuppressWarnings("unchecked") 135 List<Object> list = (List<Object>) value; 136 if (list.isEmpty()) { 137 return null; 138 } else { 139 Class<?> klass = Object.class; 140 for (Object o : list) { 141 if (o != null) { 142 klass = scalarToSerializableClass(o.getClass()); 143 break; 144 } 145 } 146 if (Document.class.isAssignableFrom(klass)) { 147 List<Serializable> l = new ArrayList<>(list.size()); 148 for (Object o : list) { 149 l.add((Serializable) bsonToFieldMap((Document) o)); 150 } 151 return (Serializable) l; 152 } else { 153 // turn the list into a properly-typed array 154 Object[] array = (Object[]) Array.newInstance(klass, list.size()); 155 int i = 0; 156 for (Object o : list) { 157 array[i++] = scalarToSerializable(o); 158 } 159 return array; 160 } 161 } 162 } else if (value instanceof Document) { 163 return (Serializable) bsonToFieldMap((Document) value); 164 } else { 165 return scalarToSerializable(value); 166 } 167 } 168 169 protected static Class<?> scalarToSerializableClass(Class<?> klass) { 170 if (Date.class.isAssignableFrom(klass)) { 171 return Calendar.class; 172 } 173 return klass; 174 } 175 176 protected static Serializable scalarToSerializable(Object value) { 177 if (value instanceof Date) { 178 Calendar cal = Calendar.getInstance(); 179 cal.setTime((Date) value); 180 return cal; 181 } 182 return (Serializable) value; 183 } 184 185}