Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.github.nlloyd.hornofmongo.util.BSONizer Maven / Gradle / Ivy
/**
* Copyright (c) 2013 Nick Lloyd
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.github.nlloyd.hornofmongo.util;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.regex.Pattern;
import org.bson.BSON;
import org.bson.BSONObject;
import org.bson.types.BSONTimestamp;
import org.bson.types.Binary;
import org.bson.types.Code;
import org.bson.types.Symbol;
import org.mozilla.javascript.BaseFunction;
import org.mozilla.javascript.ConsString;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.NativeArray;
import org.mozilla.javascript.NativeObject;
import org.mozilla.javascript.ScriptRuntime;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.Undefined;
import org.mozilla.javascript.Wrapper;
import org.mozilla.javascript.regexp.NativeRegExp;
import com.github.nlloyd.hornofmongo.MongoRuntime;
import com.github.nlloyd.hornofmongo.MongoScope;
import com.github.nlloyd.hornofmongo.action.MongoAction;
import com.github.nlloyd.hornofmongo.action.NewInstanceAction;
import com.github.nlloyd.hornofmongo.adaptor.BinData;
import com.github.nlloyd.hornofmongo.adaptor.DBPointer;
import com.github.nlloyd.hornofmongo.adaptor.DBRef;
import com.github.nlloyd.hornofmongo.adaptor.MaxKey;
import com.github.nlloyd.hornofmongo.adaptor.MinKey;
import com.github.nlloyd.hornofmongo.adaptor.NumberInt;
import com.github.nlloyd.hornofmongo.adaptor.NumberLong;
import com.github.nlloyd.hornofmongo.adaptor.ObjectId;
import com.github.nlloyd.hornofmongo.adaptor.ScriptableMongoObject;
import com.github.nlloyd.hornofmongo.adaptor.Timestamp;
import com.github.nlloyd.hornofmongo.exception.MongoScopeException;
import com.mongodb.BasicDBObject;
import com.mongodb.Bytes;
/**
* @author nlloyd
*
*/
public class BSONizer {
public static Object convertJStoBSON(Object jsObject, boolean isJsObj) {
Object bsonObject = null;
if (jsObject instanceof NativeArray) {
NativeArray jsArray = (NativeArray) jsObject;
List bsonArray = new ArrayList(Long.valueOf(
jsArray.getLength()).intValue());
for (Object jsEntry : jsArray) {
bsonArray.add(convertJStoBSON(jsEntry, isJsObj));
}
bsonObject = bsonArray;
} else if (jsObject instanceof NativeRegExp) {
Object source = ScriptableObject.getProperty((Scriptable) jsObject,
"source");
String fullRegex = (String) Context
.jsToJava(jsObject, String.class);
String options = fullRegex
.substring(fullRegex.lastIndexOf("/") + 1);
bsonObject = Pattern.compile(source.toString(),
Bytes.regexFlags(options));
;
} else if (jsObject instanceof NativeObject) {
BasicDBObject bson = new BasicDBObject();
bsonObject = bson;
NativeObject rawJsObject = (NativeObject) jsObject;
for (Object key : rawJsObject.keySet()) {
Object value = extractJSProperty(rawJsObject, key);
bson.put(key.toString(), convertJStoBSON(value, isJsObj));
}
} else if (jsObject instanceof ScriptableMongoObject) {
bsonObject = convertScriptableMongoToBSON(
(ScriptableMongoObject) jsObject, isJsObj);
} else if (jsObject instanceof BaseFunction) {
BaseFunction funcObject = (BaseFunction) jsObject;
Object classPrototype = ScriptableObject.getClassPrototype(
funcObject, funcObject.getFunctionName());
if ((classPrototype instanceof MinKey)
|| (classPrototype instanceof MaxKey)) {
// this is a special case handler for instances where MinKey or
// MaxKey are provided without explicit constructor calls
// index_check3.js does this
bsonObject = convertScriptableMongoToBSON(
(ScriptableMongoObject) classPrototype, isJsObj);
} else {
// comes from eval calls
String decompiledCode = (String) MongoRuntime
.call(new JSDecompileAction(funcObject));
bsonObject = new Code(decompiledCode);
}
} else if (jsObject instanceof ScriptableObject) {
// we found a ScriptableObject that isn't any of the concrete
// ScriptableObjects above...
String jsClassName = ((ScriptableObject) jsObject).getClassName();
if ("Date".equals(jsClassName)) {
bsonObject = Context.jsToJava(jsObject, Date.class);
} else {
Context.throwAsScriptRuntimeEx(new MongoScopeException(
"bsonizer couldnt convert js class: " + jsClassName));
bsonObject = jsObject;
}
} else if (jsObject instanceof ConsString) {
bsonObject = jsObject.toString();
} else if (jsObject instanceof Undefined) {
bsonObject = jsObject;
} else if (jsObject instanceof Integer) {
// this may seem strange, but JavaScript only knows about the number
// type
// which means in the official client we need to pass a Double
// this applies to Long and Integer values
bsonObject = Double.valueOf((Integer) jsObject);
} else if (jsObject instanceof Long) {
bsonObject = Double.valueOf((Long) jsObject);
} else {
bsonObject = jsObject;
}
return bsonObject;
}
@SuppressWarnings("deprecation")
public static Object convertBSONtoJS(MongoScope mongoScope,
Object bsonObject) {
Object jsObject = null;
if (bsonObject instanceof List>) {
List> bsonList = (List>) bsonObject;
Scriptable jsArray = (Scriptable) MongoRuntime
.call(new NewInstanceAction(mongoScope, bsonList.size()));
int index = 0;
for (Object bsonEntry : bsonList) {
ScriptableObject.putProperty(jsArray, index,
convertBSONtoJS(mongoScope, bsonEntry));
index++;
}
jsObject = jsArray;
} else if (bsonObject instanceof BSONObject) {
Scriptable jsObj = (Scriptable) MongoRuntime
.call(new NewInstanceAction(mongoScope));
BSONObject bsonObj = (BSONObject) bsonObject;
for (String key : bsonObj.keySet()) {
Object value = convertBSONtoJS(mongoScope, bsonObj.get(key));
MongoRuntime.call(new JSPopulatePropertyAction(jsObj, key,
value));
}
jsObject = jsObj;
} else if (bsonObject instanceof Symbol) {
jsObject = ((Symbol) bsonObject).getSymbol();
} else if (bsonObject instanceof Date) {
jsObject = MongoRuntime.call(new NewInstanceAction(mongoScope,
"Date", new Object[] { ((Date) bsonObject).getTime() }));
} else if (bsonObject instanceof Pattern) {
Pattern regex = (Pattern) bsonObject;
String source = regex.pattern();
String options = Bytes.regexFlags(regex.flags());
jsObject = MongoRuntime.call(new NewInstanceAction(mongoScope,
"RegExp", new Object[] { source, options }));
} else if (bsonObject instanceof org.bson.types.ObjectId) {
jsObject = MongoRuntime.call(new NewInstanceAction(mongoScope,
"ObjectId"));
((ObjectId) jsObject)
.setRealObjectId((org.bson.types.ObjectId) bsonObject);
} else if (bsonObject instanceof org.bson.types.MinKey) {
jsObject = MongoRuntime.call(new NewInstanceAction(mongoScope,
"MinKey"));
} else if (bsonObject instanceof org.bson.types.MaxKey) {
jsObject = MongoRuntime.call(new NewInstanceAction(mongoScope,
"MaxKey"));
} else if (bsonObject instanceof com.mongodb.DBRef) {
com.mongodb.DBRef dbRef = (com.mongodb.DBRef) bsonObject;
Object id = convertBSONtoJS(mongoScope, dbRef.getId());
jsObject = MongoRuntime.call(new NewInstanceAction(mongoScope,
"DBRef", new Object[] { dbRef.getRef(), id }));
} else if (bsonObject instanceof com.mongodb.DBPointer) {
com.mongodb.DBPointer dbPointer = (com.mongodb.DBPointer) bsonObject;
ObjectId oid = (ObjectId) MongoRuntime.call(new NewInstanceAction(
mongoScope, "ObjectId"));
oid.setRealObjectId(dbPointer.getId());
jsObject = MongoRuntime.call(new NewInstanceAction(mongoScope,
"DBPointer", new Object[] { dbPointer.getRef(), oid }));
} else if (bsonObject instanceof BSONTimestamp) {
BSONTimestamp bsonTstamp = (BSONTimestamp) bsonObject;
jsObject = MongoRuntime.call(new NewInstanceAction(mongoScope,
"Timestamp", new Object[] { bsonTstamp.getTime(),
bsonTstamp.getInc() }));
} else if (bsonObject instanceof Long) {
jsObject = MongoRuntime.call(new NewInstanceAction(mongoScope,
"NumberLong"));
((NumberLong) jsObject).setRealLong((Long) bsonObject);
} else if (bsonObject instanceof Integer) {
jsObject = Double.valueOf((Integer) bsonObject);
} else if (bsonObject instanceof Code) {
jsObject = ((Code) bsonObject).getCode();
} else if (bsonObject instanceof byte[]) {
jsObject = MongoRuntime.call(new NewInstanceAction(mongoScope,
"BinData"));
((BinData) jsObject).setValues(0, (byte[]) bsonObject);
} else if (bsonObject instanceof Binary) {
jsObject = MongoRuntime.call(new NewInstanceAction(mongoScope,
"BinData"));
((BinData) jsObject).setValues(((Binary) bsonObject).getType(),
((Binary) bsonObject).getData());
} else if (bsonObject instanceof UUID) {
jsObject = MongoRuntime.call(new NewInstanceAction(mongoScope,
"BinData"));
UUID uuid = (UUID) bsonObject;
ByteBuffer dataBuffer = ByteBuffer.allocate(16);
// mongodb wire protocol is little endian
dataBuffer.order(ByteOrder.LITTLE_ENDIAN);
dataBuffer.putLong(uuid.getMostSignificantBits());
dataBuffer.putLong(uuid.getLeastSignificantBits());
((BinData) jsObject).setValues(BSON.B_UUID, dataBuffer.array());
} else {
jsObject = bsonObject;
}
return jsObject;
}
/**
* Ammended form of the {@link ScriptableObject#get(Object)} method that
* will return {@link Undefined} property values instead of null.
*
* @param jsObject
* @param key
* @return
*/
private static Object extractJSProperty(ScriptableObject jsObject,
Object key) {
Object value = null;
if (key instanceof String) {
value = jsObject.get((String) key, jsObject);
} else if (key instanceof Number) {
value = jsObject.get(((Number) key).intValue(), jsObject);
}
if (value == Scriptable.NOT_FOUND) {
return null;
} else if (value instanceof Wrapper) {
return ((Wrapper) value).unwrap();
} else {
return value;
}
}
@SuppressWarnings("deprecation")
private static Object convertScriptableMongoToBSON(
ScriptableMongoObject jsMongoObj, boolean isJsObj) {
Object bsonObject = null;
if (jsMongoObj instanceof ObjectId) {
bsonObject = ((ObjectId) jsMongoObj).getRealObjectId();
} else if (jsMongoObj instanceof BinData) {
BinData binData = (BinData) jsMongoObj;
byte type = new Integer(binData.getType()).byteValue();
byte[] data = binData.getDataBytes();
if (type == BSON.B_UUID) {
ByteBuffer dataBuffer = ByteBuffer.wrap(data);
// mongodb wire protocol is little endian
dataBuffer.order(ByteOrder.LITTLE_ENDIAN);
long mostSigBits = dataBuffer.getLong();
long leastSigBits = dataBuffer.getLong();
bsonObject = new UUID(mostSigBits, leastSigBits);
} else
bsonObject = new org.bson.types.Binary(type, data);
} else if (jsMongoObj instanceof MinKey) {
bsonObject = new org.bson.types.MinKey();
} else if (jsMongoObj instanceof MaxKey) {
bsonObject = new org.bson.types.MaxKey();
} else if (jsMongoObj instanceof NumberInt) {
bsonObject = Integer.valueOf(((NumberInt) jsMongoObj).getRealInt());
} else if (jsMongoObj instanceof NumberLong) {
bsonObject = Long.valueOf(((NumberLong) jsMongoObj).getRealLong());
} else if (jsMongoObj instanceof DBRef) {
DBRef jsRef = (DBRef) jsMongoObj;
Object id = convertJStoBSON(jsRef.getId(), isJsObj);
bsonObject = new com.mongodb.DBRef(null, jsRef.getNs(), id);
} else if (jsMongoObj instanceof DBPointer) {
DBPointer jsPointer = (DBPointer) jsMongoObj;
bsonObject = new com.mongodb.DBPointer(jsPointer.getNs(), jsPointer
.getId().getRealObjectId());
} else if (jsMongoObj instanceof Timestamp) {
bsonObject = convertTimestampToBSONTimestamp((Timestamp) jsMongoObj);
}
return bsonObject;
}
/**
* seconds since epoch, used for Timestamp to BSONTimestamp conversion
*/
private static int lastSecFromEpoch;
/**
* ordinal used for Timestamp to BSONTimestamp conversion
*/
private static int timestampIncrementer = 1;
private static synchronized BSONTimestamp convertTimestampToBSONTimestamp(
Timestamp tstamp) {
BSONTimestamp bsTstamp;
int newTimeInSec = (int) tstamp.getT();
if (newTimeInSec == 0) {
newTimeInSec = (int) (new Date().getTime() / 1000);
// seconds from epoch has changed, reset ordinal and set the new
// lastSecFromEpoch value
if (newTimeInSec != lastSecFromEpoch) {
lastSecFromEpoch = newTimeInSec;
timestampIncrementer = 1;
} else
timestampIncrementer++;
bsTstamp = new BSONTimestamp(lastSecFromEpoch, timestampIncrementer);
} else
bsTstamp = new BSONTimestamp(newTimeInSec, (int) tstamp.getI());
return bsTstamp;
}
private static class JSPopulatePropertyAction extends MongoAction {
private Scriptable obj;
private Object key;
private Object value;
public JSPopulatePropertyAction(Scriptable obj, Object key, Object value) {
super(null);
this.obj = obj;
this.key = key;
this.value = value;
}
@Override
public Object doRun(Context cx) {
return ScriptRuntime.setObjectElem(obj, key, value, cx);
}
}
private static class JSDecompileAction extends MongoAction {
private BaseFunction toDecompile;
public JSDecompileAction(BaseFunction toDecompile) {
super(null);
this.toDecompile = toDecompile;
}
@Override
public Object doRun(Context cx) {
return cx.decompileFunction(toDecompile, 2);
}
}
}