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