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.document; 017 018import java.io.DataInput; 019import java.io.IOException; 020import java.io.InputStream; 021import java.io.ObjectOutput; 022import java.io.OutputStream; 023import java.lang.ref.SoftReference; 024import java.text.DateFormat; 025import java.text.SimpleDateFormat; 026import java.util.Date; 027import java.util.GregorianCalendar; 028import java.util.List; 029import java.util.SimpleTimeZone; 030import java.util.regex.Pattern; 031import org.modeshape.schematic.internal.document.BsonReader; 032import org.modeshape.schematic.internal.document.BsonWriter; 033 034/** 035 * A utility class for working with BSON documents. 036 * 037 * @author Randall Hauch <rhauch@redhat.com> (C) 2011 Red Hat Inc. 038 */ 039public class Bson { 040 041 public static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; 042 private static final ThreadLocal<SoftReference<DateFormat>> dateFormatter = new ThreadLocal<SoftReference<DateFormat>>(); 043 044 protected static final String DATE_FORMAT_FOR_PARSING = "yyyy-MM-dd'T'HH:mm:ssz"; 045 private static final ThreadLocal<SoftReference<DateFormat>> dateFormatterForParsing = new ThreadLocal<SoftReference<DateFormat>>(); 046 047 /** 048 * Obtain a {@link DateFormat} object that can be used within the current thread to format {@link Date} objects. 049 * 050 * @return the formatter; never null 051 */ 052 public static DateFormat getDateFormatter() { 053 SoftReference<DateFormat> ref = dateFormatter.get(); 054 DateFormat formatter = ref != null ? ref.get() : null; 055 if (formatter == null) { 056 formatter = new SimpleDateFormat(DATE_FORMAT); 057 formatter.setCalendar(new GregorianCalendar(new SimpleTimeZone(0, "GMT"))); 058 dateFormatter.set(new SoftReference<DateFormat>(formatter)); 059 } 060 return formatter; 061 } 062 063 public static DateFormat getDateParsingFormatter() { 064 SoftReference<DateFormat> ref = dateFormatterForParsing.get(); 065 DateFormat formatter = ref != null ? ref.get() : null; 066 if (formatter == null) { 067 formatter = new SimpleDateFormat(DATE_FORMAT_FOR_PARSING); 068 formatter.setCalendar(new GregorianCalendar(new SimpleTimeZone(0, "GMT"))); 069 dateFormatterForParsing.set(new SoftReference<DateFormat>(formatter)); 070 } 071 return formatter; 072 } 073 074 /** 075 * Byte used for the end of a document within a BSON stream. 076 */ 077 public static final byte END_OF_DOCUMENT = 0x00; 078 079 /** 080 * Byte used for the end of a string within a BSON stream. 081 */ 082 public static final byte END_OF_STRING = 0x00; 083 084 /** 085 * The bytes used for the types within a BSON stream. 086 */ 087 public static final class Type { 088 public static final byte DOUBLE = 0x01; 089 public static final byte STRING = 0x02; 090 public static final byte DOCUMENT = 0x03; 091 public static final byte ARRAY = 0x04; 092 public static final byte BINARY = 0x05; 093 public static final byte UNDEFINED = 0x06; 094 public static final byte OBJECTID = 0x07; 095 public static final byte BOOLEAN = 0x08; 096 public static final byte DATETIME = 0x09; 097 public static final byte NULL = 0x0A; 098 public static final byte REGEX = 0x0B; 099 public static final byte DBPOINTER = 0x0C; 100 public static final byte JAVASCRIPT = 0x0D; 101 public static final byte SYMBOL = 0x0E; 102 public static final byte JAVASCRIPT_WITH_SCOPE = 0x0F; 103 public static final byte INT32 = 0x10; 104 public static final byte TIMESTAMP = 0x11; 105 public static final byte INT64 = 0x12; 106 public static final byte MINKEY = (byte)0xFF; 107 public static final byte MAXKEY = 0x7f; 108 } 109 110 /** 111 * The bytes used for the subtypes of a binary value within a BSON stream. 112 */ 113 public static final class BinaryType { 114 /** 115 * The most common binary subtype, and the one should be used as the default. 116 */ 117 public static final byte GENERAL = 0x00; 118 /** 119 * The binary subtype that represents functions. 120 */ 121 public static final byte FUNCTION = 0x01; 122 /** 123 * The old generic subtype. This used to be the default subtype, but was deprecated in favor of {@link #GENERAL}. Drivers 124 * and tools should be sure to handle this type appropriately. 125 * 126 * @deprecated Use {@link #GENERAL} instead 127 */ 128 @Deprecated 129 public static final byte BINARY = 0x02; 130 public static final byte UUID = 0x03; 131 public static final byte MD5 = 0x05; 132 public static final byte USER_DEFINED = (byte)0x80; 133 } 134 135 private static final BsonWriter SHARED_WRITER = new BsonWriter(); 136 137 protected static BsonWriter getBsonWriter() { 138 return SHARED_WRITER; 139 } 140 141 /** 142 * Write to the supplied stream the binary BSON representation of the supplied in-memory {@link Document}. 143 * 144 * @param bson the BSON object or BSON value; may not be null 145 * @param stream the output stream; may not be null 146 * @throws IOException if there was a problem writing to the stream 147 */ 148 public static void write( Document bson, 149 OutputStream stream ) throws IOException { 150 getBsonWriter().write(bson, stream); 151 } 152 153 /** 154 * Write to the supplied output the binary BSON representation of the supplied in-memory {@link Document}. 155 * 156 * @param bson the BSON object or BSON value; may not be null 157 * @param output the output; may not be null 158 * @throws IOException if there was a problem writing to the ObjectOutput 159 */ 160 public static void write( Document bson, 161 ObjectOutput output ) throws IOException { 162 getBsonWriter().write(bson, output); 163 } 164 165 /** 166 * Return the array of bytes containing the standard BSON binary form of the supplied in-memory {@link Document}. 167 * 168 * @param object the BSON object or BSON value; may not be null 169 * @return the bytes 170 * @throws IOException if there was a problem reading from the stream 171 */ 172 public static byte[] write( Object object ) throws IOException { 173 return getBsonWriter().write(object); 174 } 175 176 private static final BsonReader SHARED_READER = new BsonReader(); 177 178 protected static BsonReader getReader() { 179 return SHARED_READER; 180 } 181 182 /** 183 * Read the binary BSON representation from supplied input stream and construct the {@link Document} representation. 184 * 185 * @param stream the input stream; may not be null 186 * @return the in-memory {@link Document} representation 187 * @throws IOException if there was a problem reading from the stream 188 */ 189 public static Document read( InputStream stream ) throws IOException { 190 return SHARED_READER.read(stream); 191 } 192 193 /** 194 * Read the binary BSON representation from supplied data input and construct the {@link Document} representation. 195 * 196 * @param input the data input; may not be null 197 * @return the in-memory {@link Document} representation 198 * @throws IOException if there was a problem reading from the stream 199 */ 200 public static Document read( DataInput input ) throws IOException { 201 return SHARED_READER.read(input); 202 } 203 204 /** 205 * Get the {@link Type} constant that describes the type of value for the given field name. 206 * 207 * @param value The value 208 * @return the {@link Type} constant describing the value 209 */ 210 public static int getTypeForValue( Object value ) { 211 if (value == null) return Type.NULL; 212 if (value instanceof String) return Type.STRING; 213 if (value instanceof Symbol) return Type.SYMBOL; 214 if (value instanceof Integer) return Type.INT32; 215 if (value instanceof Long) return Type.INT64; 216 if (value instanceof Double) return Type.DOUBLE; 217 if (value instanceof List) return Type.ARRAY; 218 if (value instanceof Document) return Type.DOCUMENT; 219 if (value instanceof Boolean) return Type.BOOLEAN; 220 if (value instanceof Binary) return Type.BINARY; 221 if (value instanceof ObjectId) return Type.OBJECTID; 222 if (value instanceof Date) return Type.DATETIME; 223 if (value instanceof Null) return Type.NULL; 224 if (value instanceof Pattern) return Type.REGEX; 225 if (value instanceof Symbol) return Type.DBPOINTER; 226 if (value instanceof Code) return Type.JAVASCRIPT; 227 if (value instanceof CodeWithScope) return Type.JAVASCRIPT_WITH_SCOPE; 228 if (value instanceof Timestamp) return Type.TIMESTAMP; 229 if (value instanceof MinKey) return Type.MINKEY; 230 if (value instanceof MaxKey) return Type.MAXKEY; 231 return Type.UNDEFINED; 232 } 233}