// RawDBObject.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 com.mongodb; import static com.mongodb.util.MyAsserts.assertEquals; import static org.bson.BSON.ARRAY; import static org.bson.BSON.BINARY; import static org.bson.BSON.BOOLEAN; import static org.bson.BSON.CODE; import static org.bson.BSON.CODE_W_SCOPE; import static org.bson.BSON.DATE; import static org.bson.BSON.EOO; import static org.bson.BSON.MAXKEY; import static org.bson.BSON.MINKEY; import static org.bson.BSON.NULL; import static org.bson.BSON.NUMBER; import static org.bson.BSON.NUMBER_INT; import static org.bson.BSON.NUMBER_LONG; import static org.bson.BSON.OBJECT; import static org.bson.BSON.OID; import static org.bson.BSON.REF; import static org.bson.BSON.REGEX; import static org.bson.BSON.STRING; import static org.bson.BSON.SYMBOL; import static org.bson.BSON.TIMESTAMP; import static org.bson.BSON.UNDEFINED; import java.nio.ByteBuffer; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.bson.BSONObject; import org.bson.types.ObjectId; /** * This object wraps the binary object format ("BSON") used for the transport of serialized objects to / from the Mongo database. */ public class RawDBObject implements DBObject { RawDBObject( ByteBuffer buf ){ this( buf , 0 ); assertEquals( _end , _buf.limit() ); } RawDBObject( ByteBuffer buf , int offset ){ _buf = buf; _offset = offset; _end = _buf.getInt( _offset ); } public Object get( String key ){ Element e = findElement( key ); if ( e == null ) return null; return e.getObject(); } @SuppressWarnings("unchecked") public Map toMap() { Map m = new HashMap(); Iterator i = this.keySet().iterator(); while (i.hasNext()) { Object s = i.next(); m.put(s, this.get(String.valueOf(s))); } return m; } public Object put( String key , Object v ){ throw new RuntimeException( "read only" ); } public void putAll( BSONObject o ){ throw new RuntimeException( "read only" ); } public void putAll( Map m ){ throw new RuntimeException( "read only" ); } public Object removeField( String key ){ throw new RuntimeException( "read only" ); } /** * @deprecated */ @Deprecated public boolean containsKey( String key ){ return containsField(key); } public boolean containsField( String field ){ return findElement( field ) != null; } public Set<String> keySet(){ Set<String> keys = new HashSet<String>(); ElementIter i = new ElementIter(); while ( i.hasNext() ){ Element e = i.next(); if ( e.eoo() ) break; keys.add( e.fieldName() ); } return keys; } String _readCStr( final int start ){ return _readCStr( start , null ); } String _readCStr( final int start , final int[] end ){ synchronized ( _cStrBuf ){ int pos = 0; while ( _buf.get( pos + start ) != 0 ){ _cStrBuf[pos] = _buf.get( pos + start ); pos++; if ( pos >= _cStrBuf.length ) throw new IllegalArgumentException( "c string too big for RawDBObject. so far[" + new String( _cStrBuf ) + "]" ); if ( pos + start >= _buf.limit() ){ StringBuilder sb = new StringBuilder(); for ( int x=0; x<10; x++ ){ int y = start + x; if ( y >= _buf.limit() ) break; sb.append( (char)_buf.get( y ) ); } throw new IllegalArgumentException( "can't find end of cstring. start:" + start + " pos: " + pos + " [" + sb + "]" ); } } if ( end != null && end.length > 0 ) end[0] = start + pos; return new String( _cStrBuf , 0 , pos ); } } String _readJavaString( final int start ){ int size = _buf.getInt( start ) - 1; byte[] b = new byte[size]; int old = _buf.position(); _buf.position( start + 4 ); _buf.get( b , 0 , b.length ); _buf.position( old ); try { return new String( b , "UTF-8" ); } catch ( java.io.UnsupportedEncodingException uee ){ return new String( b ); } } /** * includes 0 at end */ int _cStrLength( final int start ){ int end = start; while ( _buf.get( end ) != 0 ) end++; return 1 + ( end - start ); } Element findElement( String name ){ ElementIter i = new ElementIter(); while ( i.hasNext() ){ Element e = i.next(); if ( e.fieldName().equals( name ) ) return e; } return null; } public boolean isPartialObject(){ return false; } public void markAsPartialObject(){ throw new RuntimeException( "RawDBObject can't be a partial object" ); } @Override public String toString(){ return "Object"; } class Element { Element( final int start ){ _start = start; _type = _buf.get( _start ); int end[] = new int[1]; _name = eoo() ? "" : _readCStr( _start + 1 , end ); int size = 1 + ( end[0] - _start); // 1 for the end of the string _dataStart = _start + size; switch ( _type ){ case MAXKEY: case MINKEY: case EOO: case UNDEFINED: case NULL: break; case BOOLEAN: size += 1; break; case DATE: case NUMBER: case NUMBER_LONG: size += 8; break; case NUMBER_INT: size += 4; break; case OID: size += 12; break; case REF: size += 12; size += 4 + _buf.getInt( _dataStart ); break; case SYMBOL: case CODE: case STRING: size += 4 + _buf.getInt( _dataStart ); break; case CODE_W_SCOPE: case ARRAY: case OBJECT: size += _buf.getInt( _dataStart ); break; case BINARY: size += 4 + _buf.getInt( _dataStart ) + 1; break; case REGEX: int first = _cStrLength( _dataStart ); int second = _cStrLength( _dataStart + first ); size += first + second; break; case TIMESTAMP: size += 8; break; default: throw new RuntimeException( "RawDBObject can't size type " + _type ); } _size = size; } String fieldName(){ return _name; } boolean eoo(){ return _type == EOO || _type == MAXKEY; } int size(){ return _size; } Object getObject(){ if ( _cached != null ) return _cached; switch ( _type ){ case NUMBER: return _buf.getDouble( _dataStart ); case NUMBER_INT: return _buf.getInt( _dataStart ); case OID: return new ObjectId( _buf.getInt( _dataStart ) , _buf.getInt( _dataStart + 4 ) , _buf.getInt( _dataStart + 8 ) ); case CODE: case CODE_W_SCOPE: throw new RuntimeException( "can't handle code" ); case SYMBOL: case STRING: return _readJavaString( _dataStart ); case DATE: return new Date( _buf.getLong( _dataStart ) ); case REGEX: //int[] endPos = new int[1]; //String first = _readCStr( _dataStart , endPos ); //return new JSRegex( first , _readCStr( 1 + endPos[0] ) ); throw new RuntimeException( "can't handle regex" ); case BINARY: throw new RuntimeException( "can't inspect binary in db" ); case BOOLEAN: return _buf.get( _dataStart ) > 0; case ARRAY: case OBJECT: throw new RuntimeException( "can't handle emebdded objects" ); case NULL: case EOO: case MAXKEY: case MINKEY: case UNDEFINED: return null; } throw new RuntimeException( "can't decode type " + _type ); } final int _start; final byte _type; final String _name; final int _dataStart; final int _size; Object _cached; } class ElementIter { ElementIter(){ _pos = _offset + 4; } boolean hasNext(){ return ! _done && _pos < _buf.limit(); } Element next(){ Element e = new Element( _pos ); _done = e.eoo(); _pos += e.size(); return e; } int _pos; boolean _done = false; } final ByteBuffer _buf; final int _offset; final int _end; private final static byte[] _cStrBuf = new byte[1024]; }