package org.qrone.mongo;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import net.arnx.jsonic.JSON;
import org.bson.BSONObject;
import org.bson.types.BasicBSONList;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.NativeArray;
import org.mozilla.javascript.NativeJavaObject;
import org.mozilla.javascript.NativeObject;
import org.mozilla.javascript.ScriptRuntime;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import com.mongodb.BasicDBObject;
/**
* Direct conversion between native Rhino objects and BSON.
* <p>
* This class can be used directly in Rhino.
*
* @author Tal Liron
*/
public class BsonUtil
{
public static String stringify( Object s){
if(s.getClass().getName().equals("org.mozilla.javascript.xmlimpl.XML")){
return s.toString();
}
if(s instanceof Scriptable){
BSONObject bson = BsonUtil.to((Scriptable)s);
return com.mongodb.util.JSON.serialize(bson);
}
return JSON.encode(s);
}
//
// Static operations
//
/**
* Convert from native Rhino to BSON.
*
* @param object
* A Rhino native object
* @return A BSON object
*/
public static BSONObject to( Scriptable object )
{
Object[] ids = object.getIds();
boolean isArray = true;
for (int i = 0; i < ids.length; i++) {
if(!(ids[i] instanceof Integer)) isArray = false;
}
if(isArray){
BasicBSONList bson = new BasicBSONList();
for( Object id : ids )
{
if(id instanceof Integer){
bson.put((Integer)id, forBson( ScriptableObject.getProperty( object, (Integer)id )));
}
}
return bson;
}else{
BasicDBObject bson = new BasicDBObject();
for( Object id : ids )
{
String key = id.toString();
if(id instanceof String){
bson.put( key, forBson( ScriptableObject.getProperty( object, key )));
}
if(id instanceof Integer){
bson.put( key, forBson( ScriptableObject.getProperty( object, (Integer)id )));
}
}
return bson;
}
}
/**
* Convert from BSON to native Rhino.
*
* @param bson
* A BSON object
* @return A Rhino native object
*/
public static NativeObject from( BSONObject bson )
{
if( bson == null )
return null;
NativeObject object = new NativeObject();
for( String key : bson.keySet() )
{
Object value = forRhino( bson.get( key ) );
ScriptableObject.putProperty( object, key, value );
}
return object;
}
// //////////////////////////////////////////////////////////////////////////
// Private
/**
* If necessary, convert from native Rhino to a type supported by BSON.
*
* @param object
* An object
* @return An object ready to be put inside a BSON object
*/
private static Object forBson( Object object )
{
if( object instanceof NativeJavaObject )
{
// This happens either because the developer purposely creates a
// Java object, or because it was returned from a Java call and
// wrapped by Rhino.
return ( (NativeJavaObject) object ).unwrap();
}
else if( object instanceof NativeArray )
{
// Convert Rhino array to list
NativeArray array = (NativeArray) object;
int length = (int) array.getLength();
List<Object> list = new ArrayList<Object>( length );
for( int i = 0; i < length; i++ )
list.add( forBson( ScriptableObject.getProperty( array, i ) ) );
return list;
}
else if( object instanceof Scriptable )
{
Scriptable scriptable = (Scriptable) object;
if( scriptable.getClassName().equals( "Date" ) )
{
// The NativeDate class is private in Rhino, but we can access
// it like a regular object.
Object time = ScriptableObject.callMethod( scriptable, "getTime", null );
if( time instanceof Number )
return new Date( ( (Number) time ).longValue() );
}
// Convert
return to( scriptable );
}
else
return object;
}
/**
* If necessary, convert from BSON to a type supported by Rhino.
*
* @param object
* An object
* @return An object ready to be put inside a Rhino object
*/
private static Object forRhino( Object object )
{
// System.out.println( object.getClass() );
if( object instanceof BSONObject )
{
// Convert
return from( (BSONObject) object );
}
else if( object instanceof List<?> )
{
// Convert list to NativeArray
List<?> list = (List<?>) object;
NativeArray array = new NativeArray( list.size() );
int index = 0;
for( Object item : list )
ScriptableObject.putProperty( array, index++, forRhino( item ) );
return array;
}
else if( object instanceof Date )
{
// The NativeDate class is private in Rhino, but we can create
// it like a regular object.
Date date = (Date) object;
Context context = Context.getCurrentContext();
Scriptable scope = ScriptRuntime.getTopCallScope( context );
Scriptable nativeDate = context.newObject( scope, "Date", new Object[]
{
date.getTime()
} );
return nativeDate;
}
else
return object;
}
}