Skip to content

Commit f2240d5

Browse files
committed
Added Bson support
1 parent 9925769 commit f2240d5

File tree

6 files changed

+298
-0
lines changed

6 files changed

+298
-0
lines changed

pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@
4646
<artifactId>snakeyaml</artifactId>
4747
<version>1.29</version>
4848
</dependency>
49+
<dependency>
50+
<groupId>org.mongodb</groupId>
51+
<artifactId>bson</artifactId>
52+
<version>4.4.1</version>
53+
<optional>true</optional>
54+
</dependency>
4955
</dependencies>
5056

5157
<build>

src/main/java/org/javawebstack/abstractdata/AbstractElement.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package org.javawebstack.abstractdata;
22

3+
import org.bson.BsonValue;
4+
import org.javawebstack.abstractdata.bson.BsonConverter;
5+
import org.javawebstack.abstractdata.bson.BsonTypeAdapter;
36
import org.javawebstack.abstractdata.json.JsonDumper;
47
import org.javawebstack.abstractdata.json.JsonParser;
58
import org.javawebstack.abstractdata.util.QueryString;
@@ -73,6 +76,14 @@ default Number number() {
7376
return primitive().number();
7477
}
7578

79+
default BsonValue toBson() {
80+
return new BsonConverter().toBson(this);
81+
}
82+
83+
default byte[] toBsonBytes() {
84+
return (byte[]) new BsonTypeAdapter().fromAbstract(null, this, byte[].class);
85+
}
86+
7687
default String toJsonString(boolean pretty) {
7788
return new JsonDumper().setPretty(pretty).dump(this);
7889
}
@@ -154,6 +165,14 @@ default String toFormDataString() {
154165
return toFormData().toString();
155166
}
156167

168+
static AbstractElement fromBson(BsonValue value) {
169+
return new BsonConverter().toAbstract(value);
170+
}
171+
172+
static AbstractElement fromBson(byte[] value) {
173+
return new BsonTypeAdapter().toAbstract(null, value);
174+
}
175+
157176
AbstractElement clone();
158177

159178
Map<String[], Object> toTree();
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
package org.javawebstack.abstractdata.bson;
2+
3+
import org.bson.*;
4+
import org.bson.internal.Base64;
5+
import org.bson.types.Decimal128;
6+
import org.bson.types.ObjectId;
7+
import org.javawebstack.abstractdata.*;
8+
9+
import java.text.DateFormat;
10+
import java.text.ParseException;
11+
import java.text.SimpleDateFormat;
12+
import java.util.Date;
13+
14+
public class BsonConverter {
15+
16+
static {
17+
System.out.println("Hello");
18+
}
19+
20+
private DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
21+
22+
public BsonConverter dateFormat(DateFormat dateFormat) {
23+
this.dateFormat = dateFormat;
24+
return this;
25+
}
26+
27+
public BsonConverter dateFormat(String format) {
28+
return dateFormat(new SimpleDateFormat(format));
29+
}
30+
31+
public AbstractObject toAbstract(ObjectId value) {
32+
return new AbstractObject().set("$oid", value.toHexString());
33+
}
34+
35+
public AbstractObject toAbstract(Decimal128 value) {
36+
return new AbstractObject().set("$numberDecimal", value.toString());
37+
}
38+
39+
public AbstractElement toAbstract(BsonValue value) {
40+
switch (value.getBsonType()) {
41+
case NULL:
42+
return AbstractNull.INSTANCE;
43+
case STRING:
44+
return new AbstractPrimitive(value.asString().getValue());
45+
case BOOLEAN:
46+
return new AbstractPrimitive(value.asBoolean().getValue());
47+
case INT32:
48+
return new AbstractPrimitive(value.asInt32().getValue());
49+
case INT64:
50+
return new AbstractPrimitive(value.asInt64().getValue());
51+
case DOUBLE:
52+
return new AbstractPrimitive(value.asDouble().getValue());
53+
case OBJECT_ID:
54+
return toAbstract(value.asObjectId().getValue());
55+
case DATE_TIME:
56+
return new AbstractObject().set("$date", dateFormat.format(new Date(value.asDateTime().getValue())));
57+
case UNDEFINED:
58+
return new AbstractObject().set("$undefined", true);
59+
case SYMBOL:
60+
return new AbstractObject().set("$symbol", value.asSymbol().getSymbol());
61+
case MIN_KEY:
62+
return new AbstractObject().set("$minKey", 1);
63+
case MAX_KEY:
64+
return new AbstractObject().set("$maxKey", 1);
65+
case JAVASCRIPT:
66+
return new AbstractObject().set("$code", value.asJavaScript().getCode());
67+
case JAVASCRIPT_WITH_SCOPE:
68+
return new AbstractObject()
69+
.set("$code", value.asJavaScriptWithScope().getCode())
70+
.set("$scope", toAbstract(value.asJavaScriptWithScope().getScope()));
71+
case DECIMAL128:
72+
return toAbstract(value.asDecimal128().getValue());
73+
case REGULAR_EXPRESSION:
74+
return new AbstractObject().set("$regularExpression", new AbstractObject()
75+
.set("pattern", value.asRegularExpression().getPattern())
76+
.set("options", value.asRegularExpression().getOptions())
77+
);
78+
case TIMESTAMP:
79+
return new AbstractObject().set("$timestamp", new AbstractObject()
80+
.set("t", Integer.toUnsignedLong(value.asTimestamp().getTime()))
81+
.set("i", Integer.toUnsignedLong(value.asTimestamp().getInc()))
82+
);
83+
case BINARY:
84+
return new AbstractObject().set("$binary", new AbstractObject()
85+
.set("base64", Base64.encode(value.asBinary().getData()))
86+
.set("subType", String.format("%02x", value.asBinary().getType()))
87+
);
88+
case ARRAY: {
89+
AbstractArray a = new AbstractArray();
90+
value.asArray().forEach(v -> a.add(toAbstract(v)));
91+
return a;
92+
}
93+
case DOCUMENT: {
94+
AbstractObject o = new AbstractObject();
95+
value.asDocument().forEach((k, v) -> o.set(k, toAbstract(v)));
96+
return o;
97+
}
98+
}
99+
throw new UnsupportedOperationException("Unsupported Bson Type: " + value.getBsonType().name());
100+
}
101+
102+
public BsonValue toBson(AbstractElement element) {
103+
if(element == null || element.isNull())
104+
return BsonNull.VALUE;
105+
if(element.isString())
106+
return new BsonString(element.string());
107+
if(element.isBoolean())
108+
return new BsonBoolean(element.bool());
109+
if(element.isNumber()) {
110+
Number n = element.number();
111+
if(n instanceof Integer || n instanceof Short || n instanceof Byte) {
112+
return new BsonInt32(n.intValue());
113+
} else if(n instanceof Long) {
114+
return new BsonInt64(n.longValue());
115+
} else if(n instanceof Float || n instanceof Double) {
116+
return new BsonDouble(n.doubleValue());
117+
}
118+
}
119+
if(element.isArray()) {
120+
BsonArray a = new BsonArray();
121+
for(AbstractElement e : element.array())
122+
a.add(toBson(e));
123+
return a;
124+
}
125+
AbstractObject o = element.object();
126+
if(o.size() == 1 && o.has("$oid") && o.get("$oid").isString())
127+
return new BsonObjectId(new ObjectId(o.string("$oid")));
128+
if(o.size() == 1 && o.has("$undefined") && o.get("$undefined").isBoolean())
129+
return new BsonUndefined();
130+
if(o.size() == 1 && o.has("$date") && o.get("$date").isString()) {
131+
try {
132+
return new BsonDateTime(dateFormat.parse(o.string("$date")).getTime());
133+
} catch (ParseException ignored) {}
134+
}
135+
if(o.size() == 1 && o.has("$numberDecimal") && o.get("$numberDecimal").isString())
136+
return new BsonDecimal128(Decimal128.parse(o.string("$numberDecimal")));
137+
if(o.size() == 1 && o.has("$minKey") && o.get("$minKey").isNumber())
138+
return new BsonMinKey();
139+
if(o.size() == 1 && o.has("$maxKey") && o.get("$maxKey").isNumber())
140+
return new BsonMinKey();
141+
if(o.size() == 1 && o.has("$symbol") && o.get("$symbol").isString())
142+
return new BsonSymbol(o.string("$symbol"));
143+
if(o.size() == 1 && o.has("$code") && o.get("$code").isString())
144+
return new BsonJavaScript(o.string("$code"));
145+
if(o.size() == 2 && o.has("$code") && o.has("$scope") && o.get("$code").isString() && o.get("$scope").isObject())
146+
return new BsonJavaScriptWithScope(o.string("$code"), toBson(o.get("$scope")).asDocument());
147+
if(o.size() == 1 && o.has("$timestamp") && o.get("$timestamp").isObject()) {
148+
AbstractObject ts = o.object("$timestamp");
149+
if(ts.has("t") && ts.has("i") && ts.get("t").isNumber() && ts.get("i").isNumber()) {
150+
return new BsonTimestamp((int) ts.number("t").longValue(), (int) ts.number("i").longValue());
151+
}
152+
}
153+
if(o.size() == 1 && o.has("$regularExpression") && o.get("$regularExpression").isObject()) {
154+
AbstractObject re = o.object("$regularExpression");
155+
if(re.has("pattern") && re.has("options") && re.get("pattern").isString() && (re.get("options").isString() || re.get("options").isNull())) {
156+
return new BsonRegularExpression(re.string("pattern"), (String) re.toObject());
157+
}
158+
}
159+
if(o.size() == 1 && o.has("$binary") && o.get("$binary").isObject()) {
160+
AbstractObject bin = o.object("$binary");
161+
if(bin.has("base64") && bin.has("subType") && bin.get("base64").isString() && bin.get("subType").isString()) {
162+
byte[] data = Base64.decode(bin.string("base64"));
163+
byte type = (byte) Integer.parseInt(bin.string("subType"), 16);
164+
return new BsonBinary(type, data);
165+
}
166+
}
167+
BsonDocument doc = new BsonDocument(o.size());
168+
for(String k : o.keys())
169+
doc.put(k, toBson(o.get(k)));
170+
return doc;
171+
}
172+
173+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package org.javawebstack.abstractdata.bson;
2+
3+
import org.bson.*;
4+
import org.bson.codecs.BsonDocumentCodec;
5+
import org.bson.codecs.DecoderContext;
6+
import org.bson.codecs.EncoderContext;
7+
import org.bson.io.BasicOutputBuffer;
8+
import org.bson.types.Decimal128;
9+
import org.bson.types.ObjectId;
10+
import org.javawebstack.abstractdata.AbstractElement;
11+
import org.javawebstack.abstractdata.mapper.MapperContext;
12+
import org.javawebstack.abstractdata.mapper.MapperTypeAdapter;
13+
import org.javawebstack.abstractdata.mapper.exception.MapperException;
14+
15+
import java.nio.ByteBuffer;
16+
import java.nio.ByteOrder;
17+
18+
public class BsonTypeAdapter implements MapperTypeAdapter {
19+
20+
private final BsonConverter converter;
21+
22+
public BsonTypeAdapter() {
23+
this(new BsonConverter());
24+
}
25+
26+
public BsonTypeAdapter(BsonConverter converter) {
27+
this.converter = converter;
28+
}
29+
30+
public AbstractElement toAbstract(MapperContext context, Object value) throws MapperException {
31+
if(value instanceof byte[]) {
32+
ByteBuffer buffer = ByteBuffer.wrap((byte[]) value);
33+
buffer.order(ByteOrder.LITTLE_ENDIAN);
34+
return converter.toAbstract(new BsonDocumentCodec().decode(new BsonBinaryReader(buffer), DecoderContext.builder().build()));
35+
}
36+
if(value instanceof ObjectId)
37+
return converter.toAbstract((ObjectId) value);
38+
if(value instanceof Decimal128)
39+
return converter.toAbstract((Decimal128) value);
40+
return converter.toAbstract((BsonValue) value);
41+
}
42+
43+
public Object fromAbstract(MapperContext context, AbstractElement element, Class<?> type) throws MapperException {
44+
BsonValue value = converter.toBson(element);
45+
if(value instanceof BsonNull && !BsonNull.class.equals(type))
46+
return null;
47+
if(byte[].class.equals(type)) {
48+
BasicOutputBuffer outputBuffer = new BasicOutputBuffer();
49+
new BsonDocumentCodec().encode(new BsonBinaryWriter(outputBuffer), value.asDocument(), EncoderContext.builder().build());
50+
return outputBuffer.toByteArray();
51+
}
52+
if(ObjectId.class.equals(type))
53+
return value.asObjectId().getValue();
54+
if(Decimal128.class.equals(type))
55+
return value.asDecimal128().getValue();
56+
return value;
57+
}
58+
59+
public Class<?>[] getSupportedTypes() {
60+
return new Class[] {
61+
ObjectId.class,
62+
Decimal128.class,
63+
BsonValue.class,
64+
BsonDocument.class,
65+
BsonArray.class,
66+
BsonObjectId.class,
67+
BsonJavaScript.class,
68+
BsonJavaScriptWithScope.class,
69+
BsonTimestamp.class,
70+
BsonDateTime.class,
71+
BsonInt32.class,
72+
BsonInt64.class,
73+
BsonDouble.class,
74+
BsonString.class,
75+
BsonBoolean.class,
76+
BsonBinary.class,
77+
BsonNull.class,
78+
BsonDecimal128.class,
79+
BsonSymbol.class,
80+
BsonRegularExpression.class,
81+
BsonMinKey.class,
82+
BsonMaxKey.class,
83+
BsonNumber.class,
84+
BsonUndefined.class
85+
};
86+
}
87+
88+
}

src/main/java/org/javawebstack/abstractdata/mapper/Mapper.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,15 @@ public Mapper adapter(Class<?> type, MapperTypeAdapter adapter) {
111111
return this;
112112
}
113113

114+
public Mapper adapter(MapperTypeAdapter adapter) {
115+
Class<?>[] types = adapter.getSupportedTypes();
116+
if(types != null) {
117+
for(Class<?> type : types)
118+
adapter(type, adapter);
119+
}
120+
return this;
121+
}
122+
114123
public Map<Class<?>, MapperTypeAdapter> getAdapters() {
115124
return adapters;
116125
}

src/main/java/org/javawebstack/abstractdata/mapper/MapperTypeAdapter.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,8 @@ public interface MapperTypeAdapter {
77

88
AbstractElement toAbstract(MapperContext context, Object value) throws MapperException;
99
Object fromAbstract(MapperContext context, AbstractElement element, Class<?> type) throws MapperException;
10+
default Class<?>[] getSupportedTypes() {
11+
return null;
12+
}
1013

1114
}

0 commit comments

Comments
 (0)