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}