001/* 002 * ModeShape (http://www.modeshape.org) 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.modeshape.schematic.internal.document; 017 018import java.io.DataInput; 019import java.io.DataInputStream; 020import java.io.IOException; 021import java.io.InputStream; 022import java.util.UUID; 023import org.modeshape.schematic.annotation.ThreadSafe; 024import org.modeshape.schematic.document.Array; 025import org.modeshape.schematic.document.Bson; 026import org.modeshape.schematic.document.Bson.BinaryType; 027import org.modeshape.schematic.document.Document; 028import org.modeshape.schematic.document.MaxKey; 029import org.modeshape.schematic.document.MinKey; 030import org.modeshape.schematic.internal.io.BsonDataInput; 031 032/** 033 * A component that reads BSON representations and constructs the in-memory {@link Document} representation. 034 * 035 * @author Randall Hauch <rhauch@redhat.com> (C) 2011 Red Hat Inc. 036 */ 037@ThreadSafe 038public class BsonReader { 039 040 protected static final DocumentValueFactory VALUE_FACTORY = new DefaultDocumentValueFactory(); 041 042 /** 043 * Read the binary BSON representation from supplied input stream and construct the {@link Document} representation. 044 * 045 * @param stream the input stream; may not be null 046 * @return the in-memory {@link Document} representation 047 * @throws IOException if there was a problem reading from the stream 048 */ 049 public Document read( InputStream stream ) throws IOException { 050 // Create an object so that this reader is thread safe ... 051 DocumentValueFactory valueFactory = VALUE_FACTORY; 052 Reader reader = new Reader(new BsonDataInput(new DataInputStream(stream)), valueFactory); 053 reader.startDocument(); 054 return reader.endDocument(); 055 } 056 057 /** 058 * Read the binary BSON representation from supplied input stream and construct the {@link Document} representation. 059 * 060 * @param input the input stream; may not be null 061 * @return the in-memory {@link Document} representation 062 * @throws IOException if there was a problem reading from the stream 063 */ 064 public Document read( DataInput input ) throws IOException { 065 // Create an object so that this reader is thread safe ... 066 DocumentValueFactory valueFactory = VALUE_FACTORY; 067 Reader reader = new Reader(new BsonDataInput(input), valueFactory); 068 reader.startDocument(); 069 return reader.endDocument(); 070 } 071 072 /** 073 * Read the binary BSON representation from supplied input stream and construct the {@link Array} representation. 074 * 075 * @param stream the input stream; may not be null 076 * @return the in-memory {@link Document} representation 077 * @throws IOException if there was a problem reading from the stream 078 */ 079 public Array readArray( InputStream stream ) throws IOException { 080 // Create an object so that this reader is thread safe ... 081 DocumentValueFactory valueFactory = VALUE_FACTORY; 082 Reader reader = new Reader(new BsonDataInput(new DataInputStream(stream)), valueFactory); 083 reader.startArray(); 084 return (Array)reader.endDocument(); 085 } 086 087 /** 088 * Read the binary BSON representation from supplied input stream and construct the {@link Document} representation. 089 * 090 * @param input the input stream; may not be null 091 * @return the in-memory {@link Document} representation 092 * @throws IOException if there was a problem reading from the stream 093 */ 094 public Array readArray( DataInput input ) throws IOException { 095 // Create an object so that this reader is thread safe ... 096 DocumentValueFactory valueFactory = VALUE_FACTORY; 097 Reader reader = new Reader(new BsonDataInput(input), valueFactory); 098 reader.startArray(); 099 return (Array)reader.endDocument(); 100 } 101 102 protected static class Reader { 103 private final BsonDataInput data; 104 private MutableDocument object; 105 private DocumentValueFactory values; 106 107 // private final BsonEditor editor; 108 109 protected Reader( BsonDataInput data, 110 DocumentValueFactory valueFactory ) { 111 this.data = data; 112 this.values = valueFactory; 113 } 114 115 protected void startDocument() throws IOException { 116 object = readDocument(false); 117 } 118 119 protected void startArray() throws IOException { 120 object = readDocument(true); 121 } 122 123 protected MutableDocument readDocument( boolean array ) throws IOException { 124 // Read the size int32, but we don't care about the value 'cuz it's in bytes ... 125 int length = data.readInt(); 126 int startingIndex = data.getTotalBytesRead(); 127 int endingIndex = startingIndex + length; 128 MutableDocument doc = array ? new BasicArray() : new BasicDocument(); 129 // Read the elements ... 130 while (data.getTotalBytesRead() < endingIndex) { 131 byte type = data.readByte(); 132 if (type == Bson.END_OF_DOCUMENT) break; 133 readElement(type, doc); 134 } 135 return doc; 136 } 137 138 protected void readElement( byte type, 139 MutableDocument bson ) throws IOException { 140 String name = readCString(); 141 Object value = null; 142 switch (type) { 143 case Bson.Type.ARRAY: 144 value = readDocument(true); 145 break; 146 case Bson.Type.BINARY: 147 int length = data.readInt(); 148 byte subtype = data.readByte(); 149 if (subtype == BinaryType.UUID) { 150 long mostSig = data.readLong(); 151 long leastSig = data.readLong(); 152 value = new UUID(mostSig, leastSig); 153 } else { 154 byte[] bytes = new byte[length]; 155 data.readFully(bytes); 156 value = values.createBinary(subtype, bytes); 157 } 158 break; 159 case Bson.Type.BOOLEAN: 160 value = values.createBoolean(data.readBoolean()); 161 break; 162 case Bson.Type.DATETIME: 163 value = values.createDate(data.readLong()); 164 break; 165 case Bson.Type.DBPOINTER: 166 // Deprecated, so ignore ... 167 break; 168 case Bson.Type.DOCUMENT: 169 value = readDocument(false); 170 break; 171 case Bson.Type.DOUBLE: 172 value = values.createDouble(data.readDouble()); 173 break; 174 case Bson.Type.INT32: 175 value = values.createInt(data.readInt()); 176 break; 177 case Bson.Type.INT64: 178 value = values.createLong(data.readLong()); 179 break; 180 case Bson.Type.JAVASCRIPT: 181 value = values.createCode(readString()); 182 break; 183 case Bson.Type.JAVASCRIPT_WITH_SCOPE: 184 data.readInt(); // the length, but we don't use this 185 String code = readString(); 186 Document scope = readDocument(false); 187 value = values.createCode(code, scope); 188 break; 189 case Bson.Type.MAXKEY: 190 value = MaxKey.getInstance(); 191 break; 192 case Bson.Type.MINKEY: 193 value = MinKey.getInstance(); 194 break; 195 case Bson.Type.NULL: 196 value = values.createNull(); 197 break; 198 case Bson.Type.OBJECTID: 199 byte[] objectIdBytes = new byte[12]; 200 data.readFully(objectIdBytes); 201 value = values.createObjectId(objectIdBytes); 202 break; 203 case Bson.Type.REGEX: 204 value = values.createRegex(readCString(), readCString()); 205 break; 206 case Bson.Type.STRING: 207 value = readString(); 208 break; 209 case Bson.Type.SYMBOL: 210 value = readString(); 211 break; 212 case Bson.Type.TIMESTAMP: 213 int inc = data.readInt(); 214 int time = data.readInt(); 215 value = values.createTimestamp(time, inc); 216 break; 217 case Bson.Type.UNDEFINED: 218 // ignore ... 219 break; 220 } 221 bson.put(name, value); 222 } 223 224 protected String readCString() throws IOException { 225 return data.readUTF(-1); // this reads the zero-byte terminator 226 } 227 228 protected String readString() throws IOException { 229 int length = data.readInt(); 230 String result = values.createString(data.readUTF(length - 1)); // don't read the zero-byte 231 data.readByte(); // reads the zero-byte terminator 232 return result; 233 } 234 235 protected Document endDocument() { 236 return object; 237 } 238 239 } 240 241}