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.util.regex.Pattern;
019import org.modeshape.schematic.document.Null;
020import org.modeshape.schematic.document.ObjectId;
021
022public class BsonUtils {
023
024   public static int regexFlagsFrom(String flags) {
025      if (flags == null)
026         return 0;
027      flags = flags.toLowerCase();
028      int value = 0;
029
030      for (int i = 0; i < flags.length(); i++) {
031         char c = flags.charAt(i);
032         switch (c) {
033            case 'i':
034               value |= Pattern.CASE_INSENSITIVE;
035               break;
036            case 'm':
037               value |= Pattern.MULTILINE;
038               break;
039            case 'd':
040               value |= Pattern.UNIX_LINES;
041               break;
042            case 'c':
043               value |= Pattern.CANON_EQ;
044               break;
045            case 's':
046               value |= Pattern.DOTALL;
047               break;
048            case 't':
049               value |= Pattern.LITERAL;
050               break;
051            case 'u':
052               value |= Pattern.UNICODE_CASE;
053               break;
054            case 'x':
055               value |= Pattern.COMMENTS;
056               break;
057            case 'g': // global flag
058               // ignore
059               break;
060            default:
061               throw new IllegalArgumentException("unrecognized regex flag '" + c + "'");
062         }
063      }
064      return value;
065   }
066
067   public static String regexFlagsFor(Pattern pattern) {
068      return regexFlagsFor(pattern.flags());
069   }
070
071   public static String regexFlagsFor(int flags) {
072      if (flags == 0)
073         return "";
074      StringBuilder sb = new StringBuilder();
075      // Order of characters is important here
076      if ((flags & Pattern.CANON_EQ) == Pattern.CANON_EQ)
077         sb.append('c');
078      if ((flags & Pattern.UNIX_LINES) == Pattern.UNIX_LINES)
079         sb.append('d');
080      if ((flags & Pattern.CASE_INSENSITIVE) == Pattern.CASE_INSENSITIVE)
081         sb.append('i');
082      if ((flags & Pattern.MULTILINE) == Pattern.MULTILINE)
083         sb.append('m');
084      if ((flags & Pattern.DOTALL) == Pattern.DOTALL)
085         sb.append('s');
086      if ((flags & Pattern.LITERAL) == Pattern.LITERAL)
087         sb.append('t');
088      if ((flags & Pattern.UNICODE_CASE) == Pattern.UNICODE_CASE)
089         sb.append('u');
090      if ((flags & Pattern.COMMENTS) == Pattern.COMMENTS)
091         sb.append('x');
092      return sb.toString();
093   }
094
095   /**
096    * The BSON specification (or rather the <a href="http://www.mongodb.org/display/DOCS/Object+IDs">MongoDB
097    * documentation</a>) defines the structure of this data:
098    * <p>
099    * <quote>"A BSON ObjectID is a 12-byte value consisting of a 4-byte timestamp (seconds since epoch), a 3-byte
100    * machine id, a 2-byte process id, and a 3-byte counter. Note that the timestamp and counter fields must be stored
101    * big endian unlike the rest of BSON. This is because they are compared byte-by-byte and we want to ensure a mostly
102    * increasing order."</quote>
103    * </p>
104    * 
105    * @param bytes
106    * @return the ObjectId
107    */
108   public static ObjectId readObjectId(byte[] bytes) {
109      // Compute the values in big-endian ...
110      int time = ((bytes[0] & 0xff) << 24) + ((bytes[1] & 0xff) << 16) + ((bytes[2] & 0xff) << 8)
111               + ((bytes[3] & 0xff) << 0);
112      int machine = ((bytes[4] & 0xff) << 16) + ((bytes[5] & 0xff) << 8) + ((bytes[6] & 0xff) << 0);
113      int process = ((bytes[7] & 0xff) << 8) + ((bytes[8] & 0xff) << 0);
114      int inc = ((bytes[9] & 0xff) << 16) + ((bytes[10] & 0xff) << 8) + ((bytes[11] & 0xff) << 0);
115      // Create the value object ...
116      return new ObjectId(time, machine, process, inc);
117   }
118
119   /**
120    * Read the {@link ObjectId} from the hexadecimal string representation of the ObjectId.
121    * 
122    * @param bytesInHex
123    *           the hexadecimal identifier
124    * @return the ObjectId
125    */
126   public static ObjectId readObjectId(String bytesInHex) {
127      byte[] bytes = new byte[12];
128      assert bytesInHex.length() == 24;
129      for (int i = 0, index = 0; i != 12; ++i) {
130         String hexChar = bytesInHex.substring(index, index += 2);
131         bytes[i] = (byte) Integer.parseInt(hexChar, 16);
132      }
133      return readObjectId(bytes);
134   }
135
136   /**
137    * Write the 12-byte representation of the ObjectId per the BSON specification (or rather the <a
138    * href="http://www.mongodb.org/display/DOCS/Object+IDs">MongoDB documentation</a>).
139    * 
140    * @param id
141    *           the ObjectId; may not be null
142    * @param b
143    *           the bytes into which the object ID should be written
144    */
145   public static void writeObjectId(ObjectId id, byte[] b) {
146      int time = id.getTime();
147      int machine = id.getMachine();
148      int process = id.getProcess();
149      int inc = id.getInc();
150      b[0] = (byte) ((time >>> 24) & 0xFF);
151      b[1] = (byte) ((time >>> 16) & 0xFF);
152      b[2] = (byte) ((time >>> 8) & 0xFF);
153      b[3] = (byte) ((time >>> 0) & 0xFF);
154      b[4] = (byte) ((machine >>> 16) & 0xFF);
155      b[5] = (byte) ((machine >>> 8) & 0xFF);
156      b[6] = (byte) ((machine >>> 0) & 0xFF);
157      b[7] = (byte) ((process >>> 8) & 0xFF);
158      b[8] = (byte) ((process >>> 0) & 0xFF);
159      b[9] = (byte) ((inc >>> 16) & 0xFF);
160      b[10] = (byte) ((inc >>> 8) & 0xFF);
161      b[11] = (byte) ((inc >>> 0) & 0xFF);
162   }
163
164   public static boolean valuesAreEqual(Object thisValue, Object thatValue) {
165      if (thisValue == thatValue) {
166         return true;
167      }
168      if (thisValue == null || thisValue instanceof Null) {
169         return Null.matches(thatValue);
170      }
171      if (thisValue instanceof Number && thatValue instanceof Number) {
172         // Need to handle float vs. doubles
173         Number thisNumber = (Number) thisValue;
174         Number thatNumber = (Number) thatValue;
175         return thisNumber.doubleValue() == thatNumber.doubleValue();
176      }
177      if (thisValue instanceof Pattern && thatValue instanceof Pattern) {
178         // java.util.regex.Pattern does not implement equals!!
179         Pattern thisPattern = (Pattern) thisValue;
180         Pattern thatPattern = (Pattern) thatValue;
181         return thisPattern.pattern().equals(thatPattern.pattern()) && thisPattern.flags() == thatPattern.flags();
182      }
183      return thisValue.equals(thatValue);
184   }
185
186}