// BSONEncoder.java package org.bson; import org.bson.io.*; import org.bson.types.*; import static org.bson.BSON.*; import java.util.*; import java.util.regex.*; import java.util.concurrent.atomic.*; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.*; import java.nio.charset.*; /** * this is meant to be pooled or cached * there is some per instance memory for string conversion, etc... */ public class BSONEncoder { static final boolean DEBUG = false; public BSONEncoder(){ } public byte[] encode( BSONObject o ){ if ( _buf == null ){ BasicOutputBuffer buf = new BasicOutputBuffer(); set( buf ); } putObject( o ); byte[] ret = _buf.toByteArray(); done(); return ret; } public void set( OutputBuffer out ){ if ( _buf != null ) throw new IllegalStateException( "in the middle of something" ); _buf = out; } public void done(){ _buf = null; } /** * @return true if object was handled */ protected boolean handleSpecialObjects( String name , BSONObject o ){ return false; } protected boolean putSpecial( String name , Object o ){ return false; } /** Encodes a <code>BSONObject</code>. * This is for the higher level api calls * @param o the object to encode * @return the number of characters in the encoding */ public int putObject( BSONObject o ){ return putObject( null , o ); } /** * this is really for embedded objects */ int putObject( String name , BSONObject o ){ if ( o == null ) throw new NullPointerException( "can't save a null object" ); if ( DEBUG ) System.out.println( "putObject : " + name + " [" + o.getClass() + "]" + " # keys " + o.keySet().size() ); final int start = _buf.getPosition(); byte myType = OBJECT; if ( o instanceof List ) myType = ARRAY; if ( handleSpecialObjects( name , o ) ) return _buf.getPosition() - start; if ( name != null ){ _put( myType , name ); } final int sizePos = _buf.getPosition(); _buf.writeInt( 0 ); // leaving space for this. set it at the end List transientFields = null; boolean rewriteID = myType == OBJECT && name == null; if ( myType == OBJECT ) { if ( rewriteID && o.containsField( "_id" ) ) _putObjectField( "_id" , o.get( "_id" ) ); { Object temp = o.get( "_transientFields" ); if ( temp instanceof List ) transientFields = (List)temp; } } for ( String s : o.keySet() ){ if ( rewriteID && s.equals( "_id" ) ) continue; if ( transientFields != null && transientFields.contains( s ) ) continue; Object val = o.get( s ); _putObjectField( s , val ); } _buf.write( EOO ); _buf.writeInt( sizePos , _buf.getPosition() - sizePos ); return _buf.getPosition() - start; } protected void _putObjectField( String name , Object val ){ if ( name.equals( "_transientFields" ) ) return; if ( DEBUG ) System.out.println( "\t put thing : " + name ); if ( name.equals( "$where") && val instanceof String ){ _put( CODE , name ); _putValueString( val.toString() ); return; } val = BSON.applyEncodingHooks( val ); if ( val == null ) putNull(name); else if ( val instanceof Date ) putDate( name , (Date)val ); else if ( val instanceof Number ) putNumber(name, (Number)val ); else if ( val instanceof String ) putString(name, val.toString() ); else if ( val instanceof ObjectId ) putObjectId(name, (ObjectId)val ); else if ( val instanceof BSONObject ) putObject(name, (BSONObject)val ); else if ( val instanceof Boolean ) putBoolean(name, (Boolean)val ); else if ( val instanceof Pattern ) putPattern(name, (Pattern)val ); else if ( val instanceof Map ) putMap( name , (Map)val ); else if ( val instanceof List ) putList( name , (List)val ); else if ( val instanceof byte[] ) putBinary( name , (byte[])val ); else if ( val instanceof Binary ) putBinary( name , (Binary)val ); else if ( val.getClass().isArray() ) putList( name , Arrays.asList( (Object[])val ) ); else if (val instanceof Symbol) { putSymbol(name, (Symbol) val); } else if (val instanceof BSONTimestamp) { putTimestamp( name , (BSONTimestamp)val ); } else if (val instanceof CodeWScope) { putCodeWScope( name , (CodeWScope)val ); } else if ( putSpecial( name , val ) ){ // no-op } else { throw new IllegalArgumentException( "can't serialize " + val.getClass() ); } } private void putList( String name , List l ){ _put( ARRAY , name ); final int sizePos = _buf.getPosition(); _buf.writeInt( 0 ); for ( int i=0; i<l.size(); i++ ) _putObjectField( String.valueOf( i ) , l.get( i ) ); _buf.write( EOO ); _buf.writeInt( sizePos , _buf.getPosition() - sizePos ); } private void putMap( String name , Map m ){ _put( OBJECT , name ); final int sizePos = _buf.getPosition(); _buf.writeInt( 0 ); for ( Map.Entry entry : (Set<Map.Entry>)m.entrySet() ) _putObjectField( entry.getKey().toString() , entry.getValue() ); _buf.write( EOO ); _buf.writeInt( sizePos , _buf.getPosition() - sizePos ); } protected void putNull( String name ){ _put( NULL , name ); } protected void putUndefined(String name){ _put(UNDEFINED, name); } protected void putTimestamp(String name, BSONTimestamp ts ){ _put( TIMESTAMP , name ); _buf.writeInt( ts.getInc() ); _buf.writeInt( ts.getTime() ); } protected void putCodeWScope( String name , CodeWScope code ){ _put( CODE_W_SCOPE , name ); int temp = _buf.getPosition(); _buf.writeInt( 0 ); _putValueString( code.getCode() ); putObject( code.getScope() ); _buf.writeInt( temp , _buf.getPosition() - temp ); } protected void putBoolean( String name , Boolean b ){ int start = _buf.getPosition(); _put( BOOLEAN , name ); _buf.write( b ? (byte)0x1 : (byte)0x0 ); } protected void putDate( String name , Date d ){ int start = _buf.getPosition(); _put( DATE , name ); _buf.writeLong( d.getTime() ); } protected void putNumber( String name , Number n ){ if ( n instanceof Integer || n instanceof Short || n instanceof Byte || n instanceof AtomicInteger ){ _put( NUMBER_INT , name ); _buf.writeInt( n.intValue() ); } else if ( n instanceof Long || n instanceof AtomicLong ) { _put( NUMBER_LONG , name ); _buf.writeLong( n.longValue() ); } else { _put( NUMBER , name ); _buf.writeDouble( n.doubleValue() ); } } protected void putBinary( String name , byte[] data ){ _put( BINARY , name ); _buf.writeInt( 4 + data.length ); _buf.write( B_BINARY ); _buf.writeInt( data.length ); int before = _buf.getPosition(); _buf.write( data ); int after = _buf.getPosition(); //com.mongodb.util.MyAsserts.assertEquals( after - before , data.length ); } protected void putBinary( String name , Binary val ){ _put( BINARY , name ); _buf.writeInt( val.length() ); _buf.write( val.getType() ); _buf.write( val.getData() ); } protected void putSymbol( String name , Symbol s ){ _putString(name, s.getSymbol(), SYMBOL); } protected void putString(String name, String s) { _putString(name, s, STRING); } private void _putString( String name , String s, byte type ){ _put( type , name ); _putValueString( s ); } protected void putObjectId( String name , ObjectId oid ){ _put( OID , name ); _buf.writeInt( oid._time() ); _buf.writeInt( oid._machine() ); _buf.writeInt( oid._inc() ); } private void putPattern( String name, Pattern p ) { _put( REGEX , name ); _put( p.pattern() ); _put( regexFlags( p.flags() ) ); } // ---------------------------------------------- /** * Encodes the type and key. * */ protected void _put( byte type , String name ){ _buf.write( type ); _put( name ); } protected void _putValueString( String s ){ int lenPos = _buf.getPosition(); _buf.writeInt( 0 ); // making space for size int strLen = _put( s ); _buf.writeInt( lenPos , strLen ); } void _reset( Buffer b ){ b.position(0); b.limit( b.capacity() ); } /** * puts as utf-8 string */ protected int _put( String str ){ int total = 0; final int len = str.length(); int pos = 0; while ( pos < len ){ _reset( _stringC ); _reset( _stringB ); int toEncode = Math.min( _stringC.capacity() - 1, len - pos ); _stringC.put( str , pos , pos + toEncode ); _stringC.flip(); CoderResult cr = _encoder.encode( _stringC , _stringB , false ); if ( cr.isMalformed() || cr.isUnmappable() ) throw new IllegalArgumentException( "malforumed string" ); if ( cr.isOverflow() ) throw new RuntimeException( "overflor should be impossible" ); if ( cr.isError() ) throw new RuntimeException( "should never get here" ); if ( ! cr.isUnderflow() ) throw new RuntimeException( "this should always be true" ); total += _stringB.position(); _buf.write( _stringB.array() , 0 , _stringB.position() ); pos += toEncode; } _buf.write( (byte)0 ); total++; return total; } public void writeInt( int x ){ _buf.writeInt( x ); } public void writeLong( long x ){ _buf.writeLong( x ); } public void writeCString( String s ){ _put( s ); } protected OutputBuffer _buf; private CharBuffer _stringC = CharBuffer.wrap( new char[256 + 1] ); private ByteBuffer _stringB = ByteBuffer.wrap( new byte[1024 + 1] ); private CharsetEncoder _encoder = BSON._utf8.newEncoder(); public static void main(String[] args) throws IOException{ BSONEncoder encoder = new BSONEncoder(); BasicOutputBuffer buffer = new BasicOutputBuffer(); //encoder.set(buffer); BSONDecoder decoder = new BSONDecoder(); BasicBSONCallback callback = new BasicBSONCallback(); BasicBSONObject bson = new BasicBSONObject(); CodeWScope scope = new CodeWScope("helloCode",new BasicBSONObject("x",1)); bson.put("key", scope); bson.put("key1", "asdfqwerqwer"); System.out.println(bson); decoder.decode(encoder.encode(bson), callback); System.out.println(callback.get()); } }