All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bson.BasicBSONObject Maven / Gradle / Ivy

There is a newer version: 5.10
Show newest version
// BasicBSONObject.java

/**
 *      Copyright (C) 2008 10gen Inc.
 *
 *   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.
 */

package org.bson;

// BSON
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.UUID;
import java.util.regex.Pattern;

import org.bson.types.BSONTimestamp;
import org.bson.types.BasicBSONList;
import org.bson.types.Binary;
import org.bson.types.Code;
import org.bson.types.CodeWScope;
import org.bson.types.MaxKey;
import org.bson.types.MinKey;
import org.bson.types.ObjectId;
import org.bson.types.Symbol;
import org.bson.util.JSON;

// Java

/**
 * A simple implementation of DBObject. A DBObject can
 * be created as follows, using this class: 
* *
 * DBObject obj = new BasicBSONObject();
 * obj.put("foo", "bar");
 * 
* *
*/ public class BasicBSONObject implements Map, BSONObject { private static final long serialVersionUID = -4415279469780082174L; private Map _objectMap = null ; /** * Creates an empty object. * * @param sort * true: key will be sorted * false: key won't be sorted. */ public BasicBSONObject(boolean sort) { if (sort) { _objectMap = new TreeMap() ; } else { _objectMap = new LinkedHashMap() ; } } /** * Creates an empty object. by default, key won't be sorted */ public BasicBSONObject() { this(false); } public BasicBSONObject(int size) { this(false); } public boolean isEmpty() { return _objectMap.size() == 0; } /** * Convenience CTOR * * @param key * key under which to store * @param value * value to stor */ public BasicBSONObject(String key, Object value) { this(false); put(key, value); } /** * Creates a DBObject from a map. * * @param m * map to convert */ @SuppressWarnings({"unchecked"}) public BasicBSONObject(Map m) { _objectMap = new LinkedHashMap(m) ; } /** * Converts a DBObject to a map. * * @return the DBObject */ //@Override public Map toMap() { if (_objectMap instanceof LinkedHashMap) { return new LinkedHashMap(_objectMap); } else { return new TreeMap(_objectMap); } } /** * Deletes a field from this object. * * @param key * the field name to remove * @return the object removed */ //@Override public Object removeField(String key) { return _objectMap.remove(key); } /** * Checks if this object contains a given field * * @param field * field name * @return if the field exists */ //@Override public boolean containsField(String field) { return _objectMap.containsKey(field); } /** * @deprecated */ //@Override @Deprecated public boolean containsKey(String key) { return containsField(key); } /** * Gets a value from this object * * @param key * field name * @return the value */ //@Override public Object get(String key) { return _objectMap.get(key); } /** * Returns the value of a field as an int. * * @param key * the field to look for * @return the field value (or default) */ public int getInt(String key) { Object o = get(key); if (o == null) throw new NullPointerException("no value for: " + key); return BSON.toInt(o); } /** * Returns the value of a field as an int. * * @param key * the field to look for * @param def * the default to return * @return the field value (or default) */ public int getInt(String key, int def) { Object foo = get(key); if (foo == null) return def; return BSON.toInt(foo); } /** * Returns the value of a field as a long. * * @param key * the field to return * @return the field value */ public long getLong(String key) { Object foo = get(key); return ((Number) foo).longValue(); } /** * Returns the value of a field as an long. * * @param key * the field to look for * @param def * the default to return * @return the field value (or default) */ public long getLong(String key, long def) { Object foo = get(key); if (foo == null) return def; return ((Number) foo).longValue(); } /** * Returns the value of a field as a double. * * @param key * the field to return * @return the field value */ public double getDouble(String key) { Object foo = get(key); return ((Number) foo).doubleValue(); } /** * Returns the value of a field as an double. * * @param key * the field to look for * @param def * the default to return * @return the field value (or default) */ public double getDouble(String key, double def) { Object foo = get(key); if (foo == null) return def; return ((Number) foo).doubleValue(); } /** * Returns the value of a field as a string * * @param key * the field to look up * @return the value of the field, converted to a string */ public String getString(String key) { Object foo = get(key); if (foo == null) return null; return foo.toString(); } /** * Returns the value of a field as a string * * @param key * the field to look up * @param def * the default to return * @return the value of the field, converted to a string */ public String getString(String key, final String def) { Object foo = get(key); if (foo == null) return def; return foo.toString(); } /** * Returns the value of a field as a boolean. * * @param key * the field to look up * @return the value of the field, or false if field does not exist */ public boolean getBoolean(String key) { return getBoolean(key, false); } /** * Returns the value of a field as a boolean * * @param key * the field to look up * @param def * the default value in case the field is not found * @return the value of the field, converted to a string */ public boolean getBoolean(String key, boolean def) { Object foo = get(key); if (foo == null) return def; if (foo instanceof Number) return ((Number) foo).intValue() > 0; if (foo instanceof Boolean) return ((Boolean) foo).booleanValue(); throw new IllegalArgumentException("can't coerce to bool:" + foo.getClass()); } /** * Returns the object id or null if not set. * * @param field * The field to return * @return The field object value or null if not found (or if null :-^). */ public ObjectId getObjectId(final String field) { return (ObjectId) get(field); } /** * Returns the object id or def if not set. * * @param field * The field to return * @param def * the default value in case the field is not found * @return The field object value or def if not set. */ public ObjectId getObjectId(final String field, final ObjectId def) { final Object foo = get(field); return (foo != null) ? (ObjectId) foo : def; } /** * Returns the date or null if not set. * * @param field * The field to return * @return The field object value or null if not found. */ public Date getDate(final String field) { return (Date) get(field); } /** * Returns the date or def if not set. * * @param field * The field to return * @param def * the default value in case the field is not found * @return The field object value or def if not set. */ public Date getDate(final String field, final Date def) { final Object foo = get(field); return (foo != null) ? (Date) foo : def; } /** * Add a key/value pair to this object * * @param key * the field name * @param val * the field value * @return the val parameter */ //@Override public Object put(String key, Object val) { return _objectMap.put(key, val); } //@Override @SuppressWarnings({ "unchecked", "rawtypes" }) public void putAll(Map m) { for (Map.Entry entry : (Set) m.entrySet()) { put(entry.getKey().toString(), entry.getValue()); } } //@Override public void putAll(BSONObject o) { for (String k : o.keySet()) { put(k, o.get(k)); } } /** * Add a key/value pair to this object * * @param key * the field name * @param val * the field value * @return this */ public BasicBSONObject append(String key, Object val) { put(key, val); return this; } /** * Returns a JSON serialization of this object * * @return JSON serialization */ //@Override public String toString() { return JSON.serialize(this); } //@Override public boolean equals(Object o) { if (!(o instanceof BSONObject)) return false; BSONObject other = (BSONObject) o; if (!keySet().equals(other.keySet())) return false; for (String key : keySet()) { Object a = get(key); Object b = other.get(key); if (a == null) { if (b != null) return false; } if (b == null) { if (a != null) return false; } else if (a instanceof Number && b instanceof Number) { if (((Number) a).doubleValue() != ((Number) b).doubleValue()) return false; } else if (a instanceof Pattern && b instanceof Pattern) { Pattern p1 = (Pattern) a; Pattern p2 = (Pattern) b; if (!p1.pattern().equals(p2.pattern()) || p1.flags() != p2.flags()) return false; } else { if (!a.equals(b)) return false; } } return true; } @SuppressWarnings({"rawtypes"}) public boolean BasicTypeWrite(Object object, Object field, Method method) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { // Get type of write method's first parameter. Class paramType = method.getParameterTypes()[0]; boolean result = true; boolean numberCompare = false ; if (paramType.isPrimitive()) { // if (!(field instanceof Number) && !(field instanceof Character)) { // throw new IllegalArgumentException( // "The method: " // + method.getName() // + " Expected parameter type:Number does not match with the actual type:" // + field.getClass().getName()); // } if (paramType.getName().equals("int")) { method.invoke(object, ((Number) field).intValue()); } else if (paramType.getName().equals("long")) { method.invoke(object, ((Number) field).longValue()); } else if (paramType.getName().equals("byte")) { method.invoke(object, ((Number) field).byteValue()); } else if (paramType.getName().equals("double")) { method.invoke(object, ((Number) field).doubleValue()); } else if (paramType.getName().equals("float")) { method.invoke(object, ((Number) field).floatValue()); } else if (paramType.getName().equals("short")) { method.invoke(object, ((Number) field).shortValue()); } else if (paramType.getName().equals("char")) { method.invoke(object, ((Character) field).charValue()); } else if(paramType.getName().equals("boolean")) { method.invoke(object, ((Boolean) field).booleanValue());//TODO }else{ result = false; } return result; } // make sure paramType and field are both number if ( ( paramType.getName().equals("java.lang.Integer") || paramType.getName().equals("java.lang.Long") || paramType.getName().equals("java.lang.Float") || paramType.getName().equals("java.lang.Double") ) && ( field.getClass().getName().equals("java.lang.Integer") || field.getClass().getName().equals("java.lang.Long") || field.getClass().getName().equals("java.lang.Float") || field.getClass().getName().equals("java.lang.Double") ) ) { numberCompare = true ; } // for number compare, we always cast to Number then cast back if (!numberCompare && !paramType.isInstance(field)) { throw new IllegalArgumentException("The method: " + method.getName() + " Expected parameter type:" + paramType.getName() + " does not match with the actual type:" + field.getClass().getName()); } result = true; if (String.class.isAssignableFrom(paramType)) { method.invoke(object, (String) field); } else if (Date.class.isAssignableFrom(paramType)) { method.invoke(object, (Date) field); } else if (Integer.class.isAssignableFrom(paramType)) { method.invoke(object, new Integer(((Number)field).intValue())); } else if (Long.class.isAssignableFrom(paramType)) { method.invoke(object, new Long(((Number)field).longValue())); } else if (Double.class.isAssignableFrom(paramType)) { method.invoke(object, new Double(((Number)field).doubleValue())); } else if (Float.class.isAssignableFrom(paramType)) { method.invoke(object, new Float(((Number)field).floatValue())); } else if (Character.class.isAssignableFrom(paramType)) { method.invoke(object, (Character) field); } else if (ObjectId.class.isAssignableFrom(paramType)) { method.invoke(object, (ObjectId) field); } else if (Boolean.class.isAssignableFrom(paramType)) { method.invoke(object, (Boolean) field); } else if (Pattern.class.isAssignableFrom(paramType)) { method.invoke(object, (Pattern) field); // } else if (Map.class.isAssignableFrom(paramType)) { // method.invoke(object, (Map) field); // } else if (paramType.isAssignableFrom(Iterable.class)) { // method.invoke(object, (Iterable) field); } else if (byte[].class.isAssignableFrom(paramType)) { method.invoke(object, (byte[]) field); } else if (Binary.class.isAssignableFrom(paramType)) { method.invoke(object, (Binary) field); } else if (UUID.class.isAssignableFrom(paramType)) { method.invoke(object, (UUID) field); // } else if (paramType.getClass().isArray()) { // TODO } else if (Symbol.class.isAssignableFrom(paramType)) { method.invoke(object, (Symbol) field); } else if (BSONTimestamp.class.isAssignableFrom(paramType)) { method.invoke(object, (BSONTimestamp) field); } else if (CodeWScope.class.isAssignableFrom(paramType)) { method.invoke(object, (CodeWScope) field); } else if (Code.class.isAssignableFrom(paramType)) { method.invoke(object, (Code) field); } else if (MinKey.class.isAssignableFrom(paramType)) method.invoke(object, (MinKey) field); else if (MaxKey.class.isAssignableFrom(paramType)) { method.invoke(object, (MaxKey) field); } else { result = false; } return result; } /** * Returns an instance of the class "type" only for BasicBsonObject * * @param type * @return the instance of the class * @throws Exception */ //@Override public T as(Class type) throws Exception { return as(type, null); } @SuppressWarnings({"unchecked"}) //@Override public T as(Class type, Type eleType) throws Exception { boolean hasConsturctor = false; T result = null; for (Constructor con : type.getConstructors()) { if (con.getParameterTypes().length == 0) { result = (T) con.newInstance(); hasConsturctor = true; break; } } if (hasConsturctor == false) { throw new Exception("Class " + type.getName() + " does not exist an default constructor method"); } if (BSON.IsBasicType(result)) { throw new IllegalArgumentException( "Not support as to basici type. type=" + type.getName()); } else if (Collection.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type) || type.isArray()) { throw new IllegalArgumentException( "Not support as to Collection/Map/Array type. type=" + type.getName()); } else { BeanInfo bi = Introspector.getBeanInfo(type); PropertyDescriptor[] props = bi.getPropertyDescriptors(); Object field = null; for (PropertyDescriptor p : props) { if (this.containsField(p.getName())) { Method writeMethod = p.getWriteMethod(); if (writeMethod == null) { throw new IllegalArgumentException("The property:" + type.getName() + "." + p.getName() + " have not set method."); } field = this.get(p.getName()); //如果属性是map if (field == null) { continue; }else if(p.getPropertyType().equals(java.util.Map.class)){ //TODO //取得map的泛型对象 Field mapField=type.getDeclaredField(p.getName()); Type generictype=mapField.getGenericType(); Type valueType=null; if(generictype instanceof ParameterizedType){ Type[] types=((ParameterizedType)generictype).getActualTypeArguments(); valueType=types[1]; } //bson对象转为Map对象 Map map=((BSONObject) field).toMap(); Map realMap=new HashMap(); Set> set=map.entrySet(); Iterator> iterator=set.iterator(); while(iterator.hasNext()){ Map.Entry entry=iterator.next(); String key =entry.getKey().toString(); if(((Class)valueType).isPrimitive()||((Class)valueType).equals(java.lang.String.class)){ realMap.put(key,((BSONObject) field).get(key)); }else{ realMap.put(key,((BSONObject)((BSONObject) field).get(key)).as((Class)valueType)); } } writeMethod.invoke(result,realMap); }else if (field instanceof BasicBSONObject) { // bson <=> // Object writeMethod.invoke(result, ((BSONObject) field).as(p.getPropertyType())); } else if (field instanceof BasicBSONList) { // bsonlist <=> // Collection Field f = type.getDeclaredField(p.getName()); if (f == null) continue; Type _type = f.getGenericType(); Type fileType = null; if (_type != null && _type instanceof ParameterizedType) { fileType = ((ParameterizedType) _type) .getActualTypeArguments()[0]; } else { throw new IllegalArgumentException( "Current version only support parameterized type Collection(List/Set/Queue) field. unknow type=" + eleType.toString()); } writeMethod.invoke(result, ((BSONObject) field).as( p.getPropertyType(), fileType)); } else if (BasicTypeWrite(result, field, writeMethod)) { continue; } else { continue; } } } } return result; } @SuppressWarnings({"rawtypes"}) public static BSONObject typeToBson(Object object) throws IntrospectionException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { BSONObject result = null; if (object == null) { result = null; } else if (BSON.IsBasicType(object)) { throw new IllegalArgumentException( "Current version is not support basice type to bson in the top level."); } else if (object instanceof List) { BSONObject listObj = new BasicBSONList(); List list = (List) object; int index = 0; for (Object obj : list) { if (BSON.IsBasicType(obj)) { listObj.put(Integer.toString(index), obj); } else { listObj.put(Integer.toString(index), typeToBson(obj)); } ++index; } result = listObj; } else if (object instanceof Map) { BSONObject mapObj=new BasicBSONObject(); Map map=(Map)object; Set> set=map.entrySet(); Iterator> iterator=set.iterator(); while(iterator.hasNext()){ Map.Entry entry=iterator.next(); String key =entry.getKey().toString(); Object value=entry.getValue(); if(BSON.IsBasicType(value)){ mapObj.put(key, value); }else{ mapObj.put(key, typeToBson(value)); } } result = mapObj; }else if(object.getClass().isArray()){ throw new IllegalArgumentException( "Current version is not support Map/Array type field."); }else if (object instanceof BSONObject) { result = (BSONObject) object; } else if (object.getClass().getName() == "java.lang.Class") { throw new IllegalArgumentException( "Current version is not support java.lang.Class type field."); } else { // User define type. result = new BasicBSONObject(); Class cl = object.getClass(); BeanInfo bi = Introspector.getBeanInfo(cl); PropertyDescriptor[] props = bi.getPropertyDescriptors(); for (PropertyDescriptor p : props) { Class type = p.getPropertyType(); Object propObj = p.getReadMethod().invoke(object); if (BSON.IsBasicType(propObj)) { result.put(p.getName(), propObj); } else if (type.getName() == "java.lang.Class") { continue; } else { result.put(p.getName(), typeToBson(propObj)); } } } return result; } @Override public Set keySet() { return _objectMap.keySet(); } public Set> entrySet() { return _objectMap.entrySet(); } @Override public int size() { return _objectMap.size(); } @Override public boolean containsKey(Object key) { return _objectMap.containsKey(key); } @Override public boolean containsValue(Object value) { return _objectMap.containsValue(value); } @Override public Object remove(Object key) { return _objectMap.remove(key); } @Override public void clear() { _objectMap.clear(); } @Override public Collection values() { return _objectMap.values(); } @Override public Object get(Object key) { return _objectMap.get(key); } }