View Javadoc
1   package com.foxinmy.weixin4j.util;
2   
3   /*
4    * Copyright (c) 2008-2014 MongoDB, Inc.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  import java.net.NetworkInterface;
20  import java.nio.ByteBuffer;
21  import java.util.Date;
22  import java.util.Enumeration;
23  import java.util.Random;
24  import java.util.concurrent.atomic.AtomicInteger;
25  import java.util.logging.Level;
26  import java.util.logging.Logger;
27  
28  /**
29   * <p>
30   * A globally unique identifier for objects.
31   * </p>
32   * 
33   * <p>
34   * Consists of 12 bytes, divided as follows:
35   * </p>
36   * <table border="1">
37   * <caption>ObjectID layout</caption>
38   * <tr>
39   * <td>0</td>
40   * <td>1</td>
41   * <td>2</td>
42   * <td>3</td>
43   * <td>4</td>
44   * <td>5</td>
45   * <td>6</td>
46   * <td>7</td>
47   * <td>8</td>
48   * <td>9</td>
49   * <td>10</td>
50   * <td>11</td>
51   * </tr>
52   * <tr>
53   * <td colspan="4">time</td>
54   * <td colspan="3">machine</td>
55   * <td colspan="2">pid</td>
56   * <td colspan="3">inc</td>
57   * </tr>
58   * </table>
59   * 
60   * <p>
61   * Instances of this class are immutable.
62   * </p>
63   * 
64   * @mongodb.driver.manual core/object-id ObjectId
65   */
66  public class ObjectId implements Comparable<ObjectId>, java.io.Serializable {
67  
68  	private static final long serialVersionUID = -4415279469780082174L;
69  
70  	static final Logger LOGGER = Logger.getLogger("org.bson.ObjectId");
71  
72  	/**
73  	 * Gets a new object id.
74  	 * 
75  	 * @return the new id
76  	 */
77  	public static ObjectId get() {
78  		return new ObjectId();
79  	}
80  
81  	/**
82  	 * Checks if a string could be an {@code ObjectId}.
83  	 * 
84  	 * @param s
85  	 *            a potential ObjectId as a String.
86  	 * @return whether the string could be an object id
87  	 * @throws IllegalArgumentException
88  	 *             if hexString is null
89  	 */
90  	public static boolean isValid(String s) {
91  		if (s == null)
92  			return false;
93  
94  		final int len = s.length();
95  		if (len != 24)
96  			return false;
97  
98  		for (int i = 0; i < len; i++) {
99  			char c = s.charAt(i);
100 			if (c >= '0' && c <= '9')
101 				continue;
102 			if (c >= 'a' && c <= 'f')
103 				continue;
104 			if (c >= 'A' && c <= 'F')
105 				continue;
106 
107 			return false;
108 		}
109 
110 		return true;
111 	}
112 
113 	/**
114 	 * Constructs an ObjectId given its 12-byte binary representation.
115 	 * 
116 	 * @param b
117 	 *            a byte array of length 12
118 	 */
119 	public ObjectId(byte[] b) {
120 		if (b.length != 12)
121 			throw new IllegalArgumentException("need 12 bytes");
122 		ByteBuffer bb = ByteBuffer.wrap(b);
123 		_time = bb.getInt();
124 		_machine = bb.getInt();
125 		_inc = bb.getInt();
126 		_new = false;
127 	}
128 
129 	/**
130 	 * Create a new object id.
131 	 */
132 	public ObjectId() {
133 		_time = (int) (System.currentTimeMillis() / 1000);
134 		_machine = _genmachine;
135 		_inc = _nextInc.getAndIncrement();
136 		_new = true;
137 	}
138 
139 	@Override
140 	public int hashCode() {
141 		int x = _time;
142 		x += (_machine * 111);
143 		x += (_inc * 17);
144 		return x;
145 	}
146 
147 	@Override
148 	public boolean equals(Object o) {
149 		if (this == o)
150 			return true;
151 		if (!(o instanceof ObjectId)) {
152 			return false;
153 		}
154 		ObjectId other = (ObjectId) o;
155 		return _time == other._time && _machine == other._machine
156 				&& _inc == other._inc;
157 	}
158 
159 	/**
160 	 * Converts this instance into a 24-byte hexadecimal string representation.
161 	 * 
162 	 * @return a string representation of the ObjectId in hexadecimal format
163 	 */
164 	public String toHexString() {
165 		final StringBuilder buf = new StringBuilder(24);
166 
167 		for (final byte b : toByteArray()) {
168 			buf.append(String.format("%02x", b & 0xff));
169 		}
170 
171 		return buf.toString();
172 	}
173 
174 	/**
175 	 * Convert to a byte array. Note that the numbers are stored in big-endian
176 	 * order.
177 	 * 
178 	 * @return the byte array
179 	 */
180 	public byte[] toByteArray() {
181 		byte b[] = new byte[12];
182 		ByteBuffer bb = ByteBuffer.wrap(b);
183 		// by default BB is big endian like we need
184 		bb.putInt(_time);
185 		bb.putInt(_machine);
186 		bb.putInt(_inc);
187 		return b;
188 	}
189 
190 	static String _pos(String s, int p) {
191 		return s.substring(p * 2, (p * 2) + 2);
192 	}
193 
194 	public String toString() {
195 		byte b[] = toByteArray();
196 
197 		StringBuilder buf = new StringBuilder(24);
198 
199 		for (int i = 0; i < b.length; i++) {
200 			int x = b[i] & 0xFF;
201 			String s = Integer.toHexString(x);
202 			if (s.length() == 1)
203 				buf.append("0");
204 			buf.append(s);
205 		}
206 
207 		return buf.toString();
208 	}
209 
210 	int _compareUnsigned(int i, int j) {
211 		long li = 0xFFFFFFFFL;
212 		li = i & li;
213 		long lj = 0xFFFFFFFFL;
214 		lj = j & lj;
215 		long diff = li - lj;
216 		if (diff < Integer.MIN_VALUE)
217 			return Integer.MIN_VALUE;
218 		if (diff > Integer.MAX_VALUE)
219 			return Integer.MAX_VALUE;
220 		return (int) diff;
221 	}
222 
223 	public int compareTo(ObjectId id) {
224 		if (id == null)
225 			return -1;
226 
227 		int x = _compareUnsigned(_time, id._time);
228 		if (x != 0)
229 			return x;
230 
231 		x = _compareUnsigned(_machine, id._machine);
232 		if (x != 0)
233 			return x;
234 
235 		return _compareUnsigned(_inc, id._inc);
236 	}
237 
238 	/**
239 	 * Gets the timestamp (number of seconds since the Unix epoch).
240 	 * 
241 	 * @return the timestamp
242 	 */
243 	public int getTimestamp() {
244 		return _time;
245 	}
246 
247 	/**
248 	 * Gets the timestamp as a {@code Date} instance.
249 	 * 
250 	 * @return the Date
251 	 */
252 	public Date getDate() {
253 		return new Date(_time * 1000L);
254 	}
255 
256 	/**
257 	 * Gets the current value of the auto-incrementing counter.
258 	 * 
259 	 * @return the current counter value.
260 	 */
261 	public static int getCurrentCounter() {
262 		return _nextInc.get();
263 	}
264 
265 	final int _time;
266 	final int _machine;
267 	final int _inc;
268 
269 	boolean _new;
270 
271 	private static AtomicInteger _nextInc = new AtomicInteger(
272 			(new java.util.Random()).nextInt());
273 
274 	private static final int _genmachine;
275 	static {
276 		try {
277 			// build a 2-byte machine piece based on NICs info
278 			int machinePiece;
279 			{
280 				try {
281 					StringBuilder sb = new StringBuilder();
282 					Enumeration<NetworkInterface> e = NetworkInterface
283 							.getNetworkInterfaces();
284 					while (e.hasMoreElements()) {
285 						NetworkInterface ni = e.nextElement();
286 						sb.append(ni.toString());
287 					}
288 					machinePiece = sb.toString().hashCode() << 16;
289 				} catch (Throwable e) {
290 					// exception sometimes happens with IBM JVM, use random
291 					LOGGER.log(Level.WARNING, e.getMessage(), e);
292 					machinePiece = (new Random().nextInt()) << 16;
293 				}
294 				LOGGER.fine("machine piece post: "
295 						+ Integer.toHexString(machinePiece));
296 			}
297 
298 			// add a 2 byte process piece. It must represent not only the JVM
299 			// but the class loader.
300 			// Since static var belong to class loader there could be collisions
301 			// otherwise
302 			final int processPiece;
303 			{
304 				int processId = new java.util.Random().nextInt();
305 				try {
306 					processId = java.lang.management.ManagementFactory
307 							.getRuntimeMXBean().getName().hashCode();
308 				} catch (Throwable t) {
309 				}
310 
311 				ClassLoader loader = ObjectId.class.getClassLoader();
312 				int loaderId = loader != null ? System.identityHashCode(loader)
313 						: 0;
314 
315 				StringBuilder sb = new StringBuilder();
316 				sb.append(Integer.toHexString(processId));
317 				sb.append(Integer.toHexString(loaderId));
318 				processPiece = sb.toString().hashCode() & 0xFFFF;
319 				LOGGER.fine("process piece: "
320 						+ Integer.toHexString(processPiece));
321 			}
322 
323 			_genmachine = machinePiece | processPiece;
324 			LOGGER.fine("machine : " + Integer.toHexString(_genmachine));
325 		} catch (Exception e) {
326 			throw new RuntimeException(e);
327 		}
328 
329 	}
330 }