Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 44 additions & 1 deletion bson/src/main/org/bson/AbstractBsonReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,12 @@ protected boolean isClosed() {
*/
protected abstract BsonBinary doReadBinaryData();

/**
* Handles the logic to peek at the binary subtype.
*
* @return the binary subtype
*/
protected abstract byte doPeekBinarySubType();

/**
* Handles the logic to read booleans
Expand Down Expand Up @@ -261,6 +267,12 @@ public BsonBinary readBinaryData() {
return doReadBinaryData();
}

@Override
public byte peekBinarySubType() {
checkPreconditions("readBinaryData", BsonType.BINARY);
return doPeekBinarySubType();
}

@Override
public boolean readBoolean() {
checkPreconditions("readBoolean", BsonType.BOOLEAN);
Expand Down Expand Up @@ -725,13 +737,44 @@ private void setStateOnEnd() {
throw new BSONException(format("Unexpected ContextType %s.", getContext().getContextType()));
}
}
protected class Mark {
private State state;
private Context parentContext;
private BsonContextType contextType;
private BsonType currentBsonType;
private String currentName;

protected Context getParentContext() {
return parentContext;
}

protected BsonContextType getContextType() {
return contextType;
}

protected Mark() {
state = AbstractBsonReader.this.state;
parentContext = AbstractBsonReader.this.context.parentContext;
contextType = AbstractBsonReader.this.context.contextType;
currentBsonType = AbstractBsonReader.this.currentBsonType;
currentName = AbstractBsonReader.this.currentName;
}

protected void reset() {
AbstractBsonReader.this.state = state;
AbstractBsonReader.this.currentBsonType = currentBsonType;
AbstractBsonReader.this.currentName = currentName;
}
}


/**
* The context for the reader. Records the parent context, creating a bread crumb trail to trace back up to the root context of the
* reader. Also records the {@link org.bson.BsonContextType}, indicating whether the reader is reading a document, array, or other
* complex sub-structure.
*/
protected static class Context {
protected abstract class Context {

private final Context parentContext;
private final BsonContextType contextType;

Expand Down
44 changes: 43 additions & 1 deletion bson/src/main/org/bson/BsonBinaryReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public class BsonBinaryReader extends AbstractBsonReader {

private final BsonInput bsonInput;
private final boolean closeInput;
private Mark mark;

/**
* Construct an instance.
Expand Down Expand Up @@ -129,6 +130,15 @@ protected BsonBinary doReadBinaryData() {
return new BsonBinary(type, bytes);
}

@Override
protected byte doPeekBinarySubType() {
mark();
bsonInput.readInt32();
byte type = bsonInput.readByte();
reset();
return type;
}

@Override
protected boolean doReadBoolean() {
return bsonInput.readByte() == 0x1;
Expand Down Expand Up @@ -338,9 +348,41 @@ private int readSize() {
protected Context getContext() {
return (Context) super.getContext();
}
@Override
public void mark() {
if (mark != null) {
throw new BSONException("A mark already exists; it needs to be reset before creating a new one");
}
mark = new Mark();
}

@Override
public void reset() {
if (mark == null) {
throw new BSONException("trying to reset a mark before creating it");
}
mark.reset();
mark = null;
}

private static class Context extends AbstractBsonReader.Context {
protected class Mark extends AbstractBsonReader.Mark {
private int startPosition;
private int size;

protected Mark() {
super();
startPosition = BsonBinaryReader.this.getContext().startPosition;
size = BsonBinaryReader.this.getContext().size;
BsonBinaryReader.this.bsonInput.mark(Integer.MAX_VALUE);
}

protected void reset() {
super.reset();
BsonBinaryReader.this.bsonInput.reset();
BsonBinaryReader.this.setContext(new Context((Context) getParentContext(), getContextType(), startPosition, size));
}
}
protected class Context extends AbstractBsonReader.Context {
private final int startPosition;
private final int size;

Expand Down
140 changes: 135 additions & 5 deletions bson/src/main/org/bson/BsonDocumentReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import org.bson.types.ObjectId;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
Expand All @@ -16,6 +18,7 @@
*/
public class BsonDocumentReader extends AbstractBsonReader {
private BsonValue currentValue;
private Mark mark;

/**
* Construct a new instance.
Expand All @@ -33,6 +36,11 @@ protected BsonBinary doReadBinaryData() {
return currentValue.asBinary();
}

@Override
protected byte doPeekBinarySubType() {
return currentValue.asBinary().getType();
}

@Override
protected boolean doReadBoolean() {
return currentValue.asBoolean().getValue();
Expand Down Expand Up @@ -200,23 +208,122 @@ public BsonType readBsonType() {
return getCurrentBsonType();
}

@Override
public void mark() {
if (mark != null) {
throw new BSONException("A mark already exists; it needs to be reset before creating a new one");
}
mark = new Mark();
}

@Override
public void reset() {
if (mark == null) {
throw new BSONException("trying to reset a mark before creating it");
}
mark.reset();
mark = null;
}

@Override
protected Context getContext() {
return (Context) super.getContext();
}
protected class Mark extends AbstractBsonReader.Mark {
private BsonValue currentValue;
private Context context;

protected Mark() {
super();
currentValue = BsonDocumentReader.this.currentValue;
context = BsonDocumentReader.this.getContext();
context.mark();
}

protected static class Context extends AbstractBsonReader.Context {
private Iterator<Map.Entry<String, BsonValue>> documentIterator;
private Iterator<BsonValue> arrayIterator;
protected void reset() {
super.reset();
BsonDocumentReader.this.currentValue = currentValue;
BsonDocumentReader.this.setContext(context);
context.reset();
}
}

private static class BsonDocumentMarkableIterator<T> implements Iterator<T> {

private Iterator<T> baseIterator;
private List<T> markIterator = new ArrayList<T>();
private int curIndex; // index of the cursor
private boolean marking;

protected BsonDocumentMarkableIterator(final Iterator<T> baseIterator) {
this.baseIterator = baseIterator;
curIndex = 0;
marking = false;
}

/**
*
*/
protected void mark() {
marking = true;
}

/**
*
*/
protected void reset() {
curIndex = 0;
marking = false;
}


@Override
public boolean hasNext() {
return baseIterator.hasNext() || curIndex < markIterator.size();
}

@Override
public T next() {
T value;
//TODO: check closed
if (curIndex < markIterator.size()) {
value = markIterator.get(curIndex);
if (marking) {
curIndex++;
} else {
markIterator.remove(0);
}
} else {
value = baseIterator.next();
if (marking) {
markIterator.add(value);
curIndex++;
}
}


return value;
}

@Override
public void remove() {
// iterator is read only
}
}

protected class Context extends AbstractBsonReader.Context {

private BsonDocumentMarkableIterator<Map.Entry<String, BsonValue>> documentIterator;
private BsonDocumentMarkableIterator<BsonValue> arrayIterator;

protected Context(final Context parentContext, final BsonContextType contextType, final BsonArray array) {
super(parentContext, contextType);
arrayIterator = array.iterator();
arrayIterator = new BsonDocumentMarkableIterator<BsonValue>(array.iterator());
}

protected Context(final Context parentContext, final BsonContextType contextType, final BsonDocument document) {
super(parentContext, contextType);
documentIterator = document.entrySet().iterator();
documentIterator = new BsonDocumentMarkableIterator<Map.Entry<String, BsonValue>>(document.entrySet().iterator());
}

public Map.Entry<String, BsonValue> getNextElement() {
Expand All @@ -226,6 +333,29 @@ public Map.Entry<String, BsonValue> getNextElement() {
return null;
}
}
protected void mark() {
if (documentIterator != null) {
documentIterator.mark();
} else {
arrayIterator.mark();
}

if (getParentContext() != null) {
((Context) getParentContext()).mark();
}
}

protected void reset() {
if (documentIterator != null) {
documentIterator.reset();
} else {
arrayIterator.reset();
}

if (getParentContext() != null) {
((Context) getParentContext()).reset();
}
}

public BsonValue getNextValue() {
if (arrayIterator.hasNext()) {
Expand Down
9 changes: 9 additions & 0 deletions bson/src/main/org/bson/BsonDocumentWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ public BsonDocumentWriter(final BsonDocument document) {
setContext(new Context());
}

/**
* Gets the document that the writer is writing to.
*
* @return the document
*/
public BsonDocument getDocument() {
return document;
}

@Override
protected void doWriteStartDocument() {
switch (getState()) {
Expand Down
22 changes: 22 additions & 0 deletions bson/src/main/org/bson/BsonReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ public interface BsonReader {
*/
BsonBinary readBinaryData();

/**
* Peeks the subtype of the binary data that the reader is positioned at. This operation is not permitted if the mark is already set.
*
* @return the subtype
* @see #mark()
*/
byte peekBinarySubType();

/**
* Reads a BSON Binary data element from the reader.
*
Expand Down Expand Up @@ -344,4 +352,18 @@ public interface BsonReader {
* Skips the value (reader must be positioned on a value).
*/
void skipValue();

/**
* Creates a bookmark in the BsonReader's input
*
* The previous mark must be cleared before creating a new one
*/
void mark();

/**
* Go back to the state at the last mark and removes the mark
*
* @throws org.bson.BSONException if no mark has been set
*/
void reset();
}
Loading