jclass clazz = (*env)->GetObjectClass(env, jdata);
jmethodID putMethodID = (*env)->GetMethodID(env, clazz, "put", "(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;");
+ jfieldID idKeyID = (*env)->GetStaticFieldID(env, clazz, "ID_KEY", "Ljava/lang/String;");
+ jstring oidField = (*env)->GetStaticObjectField(env, clazz, idKeyID);
- jstring oidField = (*env)->NewStringUTF(env, "_id");
(*env)->CallObjectMethod(env, jdata, putMethodID, oidField, result);
- (*env)->DeleteLocalRef(env, oidField);
-
update_coll_meta(env, obj, coll);
return result;
package org.ejdb.bson;
+import org.ejdb.bson.io.InputBuffer;
+import org.ejdb.bson.types.ObjectId;
+import org.ejdb.bson.util.RegexFlag;
+
+import java.util.Date;
+import java.util.regex.Pattern;
+
/**
* @author Tyutyunkov Vyacheslav (tve@softmotions.com)
* @version $Id$
*/
-public class BSONDecoder {
+class BSONDecoder {
+
+ private InputBuffer input;
public BSONObject decode(byte[] data) {
- return new BSONObject();
+ if (isBusy()) {
+ throw new IllegalStateException("other decoding in process");
+ }
+
+ if (data == null) {
+ throw new IllegalArgumentException("can not read object from null");
+ }
+
+ input = InputBuffer.createFromByteArray(data);
+ BSONObject result = read();
+ input = null;
+
+ return result;
}
- public int readInt(byte[] data) {
- return readInt(data, 0);
+ public boolean isBusy() {
+ return input != null;
}
- public int readInt(byte[] data, int offset) {
- int result = 0;
- for (int i = 0; i < 4; ++i) {
- result |= (0xFF & data[offset + i]) << (i * 8);
+
+ protected BSONObject read() {
+ int length = input.readInt();
+
+ BSONObject result = this.readObject(input.subBuffer(length - 5));
+
+ if (0x00 != input.read()) {
+ throw new IllegalArgumentException("unexpected end of document");
}
return result;
}
- public long readLong(byte[] data) {
- return readLong(data, 0);
- }
+ protected BSONObject readObject(InputBuffer input) {
+ BSONObject result = new BSONObject();
+ while (input.isAvailable()) {
+ byte type = input.read();
+ String name = input.readString();
+ switch (type) {
+ case BSON.DOUBLE:
+ result.put(name, Double.longBitsToDouble(input.readLong()));
+ break;
+
+ case BSON.STRING:
+ int strlen = input.readInt();
+ result.put(name, input.readString(strlen));
+ break;
+
+ case BSON.OBJECT:
+ int objlen = input.readInt();
+ result.put(name, readObject(input.subBuffer(objlen - 5)));
+ input.read();
+ break;
+
+ case BSON.ARRAY:
+ int arrlen = input.readInt();
+ BSONObject arrObject = readObject(input.subBuffer(arrlen - 5));
+ Object[] array = new Object[arrObject.size()];
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = arrObject.get(String.valueOf(i));
+ }
+ result.put(name, array);
+ input.read();
+ break;
+
+ case BSON.BINARY:
+ int binlen = input.readInt();
+ byte subtype = input.read();
+ if (0x00 != subtype) {
+ throw new IllegalArgumentException("unexpected binary type: " + subtype);
+ }
+ result.put(name, input.readBytes(binlen));
+ break;
+
+ case BSON.OBJECT_ID:
+ result.put(name, new ObjectId(input.readBytes(12)));
+ break;
+
+ case BSON.BOOLEAN:
+ byte bvalue = input.read();
+ if (0x00 != bvalue && 0x01 != bvalue) {
+ throw new IllegalArgumentException("unexpected boolean value");
+ }
+ result.put(name, 0x01 == bvalue);
+ break;
+
+ case BSON.DATE:
+ result.put(name, new Date(input.readLong()));
+ break;
+
+ case BSON.NULL:
+ result.put(name, null);
+ break;
+
+ case BSON.REGEX:
+ //noinspection MagicConstant
+ result.put(name, Pattern.compile(input.readString(), RegexFlag.stringToRegexFlags(input.readString())));
+ break;
+
+ case BSON.INT:
+ result.put(name, input.readInt());
+ break;
+
+ case BSON.LONG:
+ result.put(name, input.readLong());
+ break;
- public long readLong(byte[] data, int offset) {
- long result = 0;
- for (int i = 0; i < 8; ++i) {
- result |= (0xFFL & data[offset + i]) << (i * 8);
+ default:
+ throw new IllegalArgumentException("unexpected type: " + type);
+ }
}
return result;
* @author Tyutyunkov Vyacheslav (tve@softmotions.com)
* @version $Id$
*/
-public class BSONEncoder {
+class BSONEncoder {
private OutputBuffer output;
writeField(field, object.get(field));
}
+ output.write((byte) 0x00);
+
int end = output.getPosition();
output.writeIntAt(start, end - start);
writeField(String.valueOf(i), value.next());
++i;
}
+ output.write((byte) 0x00);
output.writeIntAt(sp, output.getPosition() - sp);
}
import org.ejdb.bson.types.ObjectId;
-import java.util.Collections;
+import java.lang.reflect.Array;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.Map;
import java.util.Set;
* @version $Id$
*/
public class BSONObject {
- private static final String ID_KEY = "_id";
+ public static final String ID_KEY = "_id";
private Map<String, Object> data;
return data.get(key);
}
+ public ObjectId getId() {
+ return (ObjectId) get(ID_KEY);
+ }
+
+ public int size() {
+ return data.size();
+ }
+
public boolean containsField(String key) {
return data.containsKey(key);
}
@Override
+ public boolean equals(Object o) {
+ if (this != o && (null == o || !(o instanceof BSONObject))) {
+ return false;
+ }
+
+ Map<String, Object> thatData = ((BSONObject) o).data;
+
+ if (thatData.size() != data.size()) {
+ return false;
+ }
+
+ try {
+ Iterator<Map.Entry<String, Object>> i = data.entrySet().iterator();
+ while (i.hasNext()) {
+ Map.Entry<String, Object> e = i.next();
+ String key = e.getKey();
+ Object value = e.getValue();
+ if (value == null) {
+ if (!(thatData.get(key) == null && thatData.containsKey(key))) {
+ return false;
+ }
+ } else {
+ if (!equalObjects(value, thatData.get(key))) {
+ return false;
+ }
+ }
+ }
+ } catch (ClassCastException unused) {
+ return false;
+ } catch (NullPointerException unused) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private boolean equalObjects(Object o1, Object o2) {
+ if (o1.getClass().isArray()) {
+ int len = Array.getLength(o1);
+ if (len != Array.getLength(o2)) {
+ return false;
+ }
+
+ for (int i = 0; i < len; ++i) {
+ Object item1 = Array.get(o1, i);
+ Object item2 = Array.get(o2, i);
+
+ boolean isEquals = item1 == null ? item2 == null : equalObjects(item1, item2);
+ if (!isEquals) {
+ return false;
+ }
+ }
+
+ return true;
+ } else {
+ return !o1.equals(o2) ? false : true;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return data.hashCode();
+ }
+
+ @Override
public String toString() {
return data.toString();
}
--- /dev/null
+package org.ejdb.bson.io;
+
+import org.ejdb.bson.BSONObject;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * @author Tyutyunkov Vyacheslav (tve@softmotions.com)
+ * @version $Id$
+ */
+public class InputBuffer {
+ private byte[] data;
+ private int offset;
+ private int position;
+ private int limit;
+
+ private InputBuffer(byte[] data, int offset, int limit) {
+ this.data = data;
+ this.position = 0;
+ this.offset = offset;
+ this.limit = limit;
+ }
+
+ public byte read() {
+ ensure(1);
+
+ byte result = data[offset + position];
+ position += 1;
+
+ return result;
+ }
+
+ public int readInt() {
+ ensure(4);
+
+ int result = 0;
+ for (int i = 0; i < 4; ++i) {
+ result |= (0xFF & data[offset + position + i]) << (i * 8);
+ }
+ position += 4;
+
+ return result;
+ }
+
+ public long readLong() {
+ ensure(8);
+
+ long result = 0;
+ for (int i = 0; i < 8; ++i) {
+ result |= (0xFFL & data[offset + position + i]) << (i * 8);
+ }
+ position += 8;
+
+ return result;
+ }
+
+ public byte[] readBytes(int count) {
+ ensure(count);
+ byte[] bytes = new byte[count];
+ System.arraycopy(data, offset + position, bytes, 0, count);
+ position += count;
+
+ return bytes;
+ }
+
+ public String readString() {
+ return readString(0);
+ }
+
+ public String readString(int length) {
+ if (length > 0) {
+ ensure(length);
+ if ((byte) 0x00 != data[offset + position + length - 1]) {
+ throw new IllegalArgumentException("unexpected end of string");
+ }
+
+ try {
+ String s = new String(data, offset + position, length - 1, "UTF-8");
+ position += length;
+ return s;
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException("can not decode string", e);
+ }
+ }
+
+ length = 0;
+ while (position + length < limit && data[offset + position + length] != (byte)0x00) {
+ ++length;
+ }
+
+ if (position + length > limit) {
+ throw new IllegalArgumentException("unexpected end of string");
+ }
+
+ String s = new String(data, offset + position, length);
+ position += length + 1;
+ return s;
+ }
+
+ public boolean isAvailable() {
+ return position < limit;
+ }
+
+ public InputBuffer subBuffer(int limit) {
+ ensure(limit);
+
+ InputBuffer result = new InputBuffer(data, offset + position, limit);
+ position += limit;
+
+ return result;
+ }
+
+ protected void ensure(int size) {
+ if (size > limit - position) {
+ throw new IllegalArgumentException("can not allocate sub buffer: not enought bytes");
+ }
+ }
+
+ public static InputBuffer createFromByteArray(byte[] data) {
+ return new InputBuffer(data, 0, data.length);
+ }
+}
package org.ejdb.bson.types;
+import java.util.Arrays;
+
/**
* @author Tyutyunkov Vyacheslav (tve@softmotions.com)
* @version $Id$
}
@Override
+ public boolean equals(Object o) {
+ return this == o || null != o && o instanceof ObjectId && Arrays.equals(data, ((ObjectId) o).data);
+
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(data);
+ }
+
+ @Override
public String toString() {
StringBuilder builder = new StringBuilder(34);
builder.append("ObjectId(");
public static String regexFlagsToString(int flags) {
StringBuilder result = new StringBuilder();
- for (RegexFlag regexFlag : regexFlags) {
- if ((flags & regexFlag.getFlag()) > 0 && regexFlag.isSupported()) {
- result.append(regexFlag.getCharacter());
+ for (RegexFlag rf : regexFlags) {
+ if ((flags & rf.getFlag()) > 0 && rf.isSupported()) {
+ result.append(rf.getCharacter());
}
}
return result.toString();
}
+ public static int stringToRegexFlags(String str) {
+ int flags = 0;
+
+ for (int i = 0; i < str.length(); ++i) {
+ RegexFlag rf = characterToRegexFlagMap.get(str.charAt(i));
+ if (rf != null && rf.isSupported()) {
+ flags |= rf.getFlag();
+ }
+ }
+
+ return flags;
+ }
+
public static void registerRegexFlag(int flag, char character, boolean supported) {
RegexFlag rf = new RegexFlag(flag, character, supported);
regexFlags.add(rf);
BSONObject obj = new BSONObject("test", "test").append("test2", 123);
-
ObjectId oid = coll.save(obj);
assertNotNull(oid);
+ assertEquals(oid, obj.getId());
BSONObject lobj = coll.load(oid);
assertNotNull(lobj);
- assertEquals(lobj.get("_id"), oid);
- assertEquals(obj.get("test"), lobj.get("test"));
- assertEquals(obj.get("test2"), lobj.get("test2"));
+ assertEquals(obj, lobj);
+// assertEquals(lobj.get("_id"), oid);
+// assertEquals(obj.get("test"), lobj.get("test"));
+// assertEquals(obj.get("test2"), lobj.get("test2"));
EJDBQuery query = coll.createQuery(new BSONObject());
EJDBResultSet rs = query.find();
assertNotNull(ss.get(2));
BSONObject obj12 = parrots.load(ss.get(0));
- assertEquals(ss.get(0), obj12.get("_id"));
+ assertNotNull(obj12);
+ assertEquals(ss.get(0), obj12.getId());
+ assertEquals(obj1, obj12);
EJDBQuery query = parrots.createQuery(new BSONObject());
EJDBResultSet rs = query.find();
assertEquals(rs.length(), 2);
rs.close();
-
query = parrots.createQuery(new BSONObject("name", Pattern.compile("(grenny|bounty)", Pattern.CASE_INSENSITIVE)),
new BSONObject("$orderby", new BSONObject("name", 1)));
assertFalse(bars.isTransactionActive());
BSONObject bar2 = bars.load(boid);
assertNotNull(bar2);
- assertEquals(bar2.get("_id"), boid);
- assertEquals(bar2.get("foo"), bar.get("foo"));
+ assertEquals(bar2.getId(), boid);
+ assertEquals(bar2, bar);
}
}