1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase;
20
21 import org.apache.hadoop.classification.InterfaceAudience;
22 import org.apache.hadoop.classification.InterfaceStability;
23 import org.apache.hadoop.hbase.KeyValue.KVComparator;
24 import org.apache.hadoop.hbase.util.Bytes;
25
26 import java.nio.ByteBuffer;
27 import java.util.Arrays;
28 import java.util.Set;
29 import java.util.concurrent.CopyOnWriteArraySet;
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55 @InterfaceAudience.Public
56 @InterfaceStability.Evolving
57 public final class TableName implements Comparable<TableName> {
58
59
60 private static final Set<TableName> tableCache = new CopyOnWriteArraySet<TableName>();
61
62
63
64 public final static char NAMESPACE_DELIM = ':';
65
66
67
68
69
70 public static final String VALID_NAMESPACE_REGEX =
71 "(?:[a-zA-Z_0-9]+)";
72
73 public static final String VALID_TABLE_QUALIFIER_REGEX =
74 "(?:[a-zA-Z_0-9][a-zA-Z_0-9-.]*)";
75
76
77 public static final String VALID_USER_TABLE_REGEX =
78 "(?:(?:(?:"+VALID_NAMESPACE_REGEX+"\\"+NAMESPACE_DELIM+")?)" +
79 "(?:"+VALID_TABLE_QUALIFIER_REGEX+"))";
80
81
82 public static final TableName META_TABLE_NAME =
83 valueOf(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR, "meta");
84
85
86 public static final TableName NAMESPACE_TABLE_NAME =
87 valueOf(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR, "namespace");
88
89 public static final String OLD_META_STR = ".META.";
90 public static final String OLD_ROOT_STR = "-ROOT-";
91
92
93
94
95
96
97
98 public static final TableName OLD_ROOT_TABLE_NAME = getADummyTableName(OLD_ROOT_STR);
99
100
101
102 public static final TableName OLD_META_TABLE_NAME = getADummyTableName(OLD_META_STR);
103
104 private final byte[] name;
105 private final String nameAsString;
106 private final byte[] namespace;
107 private final String namespaceAsString;
108 private final byte[] qualifier;
109 private final String qualifierAsString;
110 private final boolean systemTable;
111 private final int hashCode;
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132 public static byte [] isLegalFullyQualifiedTableName(final byte[] tableName) {
133 if (tableName == null || tableName.length <= 0) {
134 throw new IllegalArgumentException("Name is null or empty");
135 }
136
137 int namespaceDelimIndex = com.google.common.primitives.Bytes.lastIndexOf(tableName,
138 (byte) NAMESPACE_DELIM);
139 if (namespaceDelimIndex == 0 || namespaceDelimIndex == -1){
140 isLegalTableQualifierName(tableName);
141 } else {
142 isLegalNamespaceName(tableName, 0, namespaceDelimIndex);
143 isLegalTableQualifierName(tableName, namespaceDelimIndex + 1, tableName.length);
144 }
145 return tableName;
146 }
147
148 public static byte [] isLegalTableQualifierName(final byte[] qualifierName) {
149 isLegalTableQualifierName(qualifierName, 0, qualifierName.length, false);
150 return qualifierName;
151 }
152
153 public static byte [] isLegalTableQualifierName(final byte[] qualifierName, boolean isSnapshot) {
154 isLegalTableQualifierName(qualifierName, 0, qualifierName.length, isSnapshot);
155 return qualifierName;
156 }
157
158
159
160
161
162
163
164
165
166
167
168 public static void isLegalTableQualifierName(final byte[] qualifierName,
169 int start,
170 int end) {
171 isLegalTableQualifierName(qualifierName, start, end, false);
172 }
173
174 public static void isLegalTableQualifierName(final byte[] qualifierName,
175 int start,
176 int end,
177 boolean isSnapshot) {
178 if(end - start < 1) {
179 throw new IllegalArgumentException(isSnapshot ? "Snapshot" : "Table" + " qualifier must not be empty");
180 }
181
182 if (qualifierName[start] == '.' || qualifierName[start] == '-') {
183 throw new IllegalArgumentException("Illegal first character <" + qualifierName[0] +
184 "> at 0. Namespaces can only start with alphanumeric " +
185 "characters': i.e. [a-zA-Z_0-9]: " +
186 Bytes.toString(qualifierName));
187 }
188 for (int i = start; i < end; i++) {
189 if (Character.isLetterOrDigit(qualifierName[i]) ||
190 qualifierName[i] == '_' ||
191 qualifierName[i] == '-' ||
192 qualifierName[i] == '.') {
193 continue;
194 }
195 throw new IllegalArgumentException("Illegal character code:" + qualifierName[i] +
196 ", <" + (char) qualifierName[i] + "> at " + i +
197 ". " + (isSnapshot ? "snapshot" : "User-space table") +
198 " qualifiers can only contain " +
199 "'alphanumeric characters': i.e. [a-zA-Z_0-9-.]: " +
200 Bytes.toString(qualifierName, start, end));
201 }
202 }
203 public static void isLegalNamespaceName(byte[] namespaceName) {
204 isLegalNamespaceName(namespaceName, 0, namespaceName.length);
205 }
206
207
208
209
210 public static void isLegalNamespaceName(byte[] namespaceName, int offset, int length) {
211 for (int i = offset; i < length; i++) {
212 if (Character.isLetterOrDigit(namespaceName[i])|| namespaceName[i] == '_') {
213 continue;
214 }
215 throw new IllegalArgumentException("Illegal character <" + namespaceName[i] +
216 "> at " + i + ". Namespaces can only contain " +
217 "'alphanumeric characters': i.e. [a-zA-Z_0-9]: " + Bytes.toString(namespaceName,
218 offset, length));
219 }
220 }
221
222 public byte[] getName() {
223 return name;
224 }
225
226 public String getNameAsString() {
227 return nameAsString;
228 }
229
230 public byte[] getNamespace() {
231 return namespace;
232 }
233
234 public String getNamespaceAsString() {
235 return namespaceAsString;
236 }
237
238 public byte[] getQualifier() {
239 return qualifier;
240 }
241
242 public String getQualifierAsString() {
243 return qualifierAsString;
244 }
245
246 public byte[] toBytes() {
247 return name;
248 }
249
250 public boolean isSystemTable() {
251 return systemTable;
252 }
253
254 @Override
255 public String toString() {
256 return nameAsString;
257 }
258
259
260
261
262
263 private TableName(ByteBuffer namespace, ByteBuffer qualifier) throws IllegalArgumentException {
264 this.qualifier = new byte[qualifier.remaining()];
265 qualifier.duplicate().get(this.qualifier);
266 this.qualifierAsString = Bytes.toString(this.qualifier);
267
268 if (qualifierAsString.equals(OLD_ROOT_STR)) {
269 throw new IllegalArgumentException(OLD_ROOT_STR + " has been deprecated.");
270 }
271 if (qualifierAsString.equals(OLD_META_STR)) {
272 throw new IllegalArgumentException(OLD_META_STR + " no longer exists. The table has been " +
273 "renamed to " + META_TABLE_NAME);
274 }
275
276 if (Bytes.equals(NamespaceDescriptor.DEFAULT_NAMESPACE_NAME, namespace)) {
277
278 this.namespace = NamespaceDescriptor.DEFAULT_NAMESPACE_NAME;
279 this.namespaceAsString = NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR;
280 this.systemTable = false;
281
282
283 this.nameAsString = qualifierAsString;
284 this.name = this.qualifier;
285 } else {
286 if (Bytes.equals(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME, namespace)) {
287 this.namespace = NamespaceDescriptor.SYSTEM_NAMESPACE_NAME;
288 this.namespaceAsString = NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR;
289 this.systemTable = true;
290 } else {
291 this.namespace = new byte[namespace.remaining()];
292 namespace.duplicate().get(this.namespace);
293 this.namespaceAsString = Bytes.toString(this.namespace);
294 this.systemTable = false;
295 }
296 this.nameAsString = namespaceAsString + NAMESPACE_DELIM + qualifierAsString;
297 this.name = Bytes.toBytes(nameAsString);
298 }
299
300 this.hashCode = nameAsString.hashCode();
301
302 isLegalNamespaceName(this.namespace);
303 isLegalTableQualifierName(this.qualifier);
304 }
305
306
307
308
309 private TableName(String qualifier) {
310 this.qualifier = Bytes.toBytes(qualifier);
311 this.qualifierAsString = qualifier;
312
313 this.namespace = NamespaceDescriptor.SYSTEM_NAMESPACE_NAME;
314 this.namespaceAsString = NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR;
315 this.systemTable = true;
316
317
318
319 this.nameAsString = namespaceAsString + NAMESPACE_DELIM + qualifierAsString;
320 this.name = this.qualifier;
321
322 this.hashCode = nameAsString.hashCode();
323 }
324
325
326
327
328
329
330
331
332 private static TableName createTableNameIfNecessary(ByteBuffer bns, ByteBuffer qns) {
333 for (TableName tn : tableCache) {
334 if (Bytes.equals(tn.getQualifier(), qns) && Bytes.equals(tn.getNamespace(), bns)) {
335 return tn;
336 }
337 }
338
339 TableName newTable = new TableName(bns, qns);
340 if (tableCache.add(newTable)) {
341 return newTable;
342 }
343
344
345 for (TableName tn : tableCache) {
346 if (Bytes.equals(tn.getQualifier(), qns) && Bytes.equals(tn.getNamespace(), bns)) {
347 return tn;
348 }
349 }
350
351 throw new IllegalStateException(newTable + " was supposed to be in the cache");
352 }
353
354
355
356
357
358
359
360 private static TableName getADummyTableName(String qualifier) {
361 return new TableName(qualifier);
362 }
363
364
365 public static TableName valueOf(String namespaceAsString, String qualifierAsString) {
366 if (namespaceAsString == null || namespaceAsString.length() < 1) {
367 namespaceAsString = NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR;
368 }
369
370 for (TableName tn : tableCache) {
371 if (qualifierAsString.equals(tn.getQualifierAsString()) &&
372 namespaceAsString.equals(tn.getNameAsString())) {
373 return tn;
374 }
375 }
376
377 return createTableNameIfNecessary(
378 ByteBuffer.wrap(Bytes.toBytes(namespaceAsString)),
379 ByteBuffer.wrap(Bytes.toBytes(qualifierAsString)));
380 }
381
382
383
384
385
386
387
388 public static TableName valueOf(byte[] fullName) throws IllegalArgumentException{
389 for (TableName tn : tableCache) {
390 if (Arrays.equals(tn.getName(), fullName)) {
391 return tn;
392 }
393 }
394
395 int namespaceDelimIndex = com.google.common.primitives.Bytes.lastIndexOf(fullName,
396 (byte) NAMESPACE_DELIM);
397
398 if (namespaceDelimIndex < 0) {
399 return createTableNameIfNecessary(
400 ByteBuffer.wrap(NamespaceDescriptor.DEFAULT_NAMESPACE_NAME),
401 ByteBuffer.wrap(fullName));
402 } else {
403 return createTableNameIfNecessary(
404 ByteBuffer.wrap(fullName, 0, namespaceDelimIndex),
405 ByteBuffer.wrap(fullName, namespaceDelimIndex + 1,
406 fullName.length - (namespaceDelimIndex + 1)));
407 }
408 }
409
410
411
412
413
414
415 public static TableName valueOf(String name) {
416 for (TableName tn : tableCache) {
417 if (name.equals(tn.getNameAsString())) {
418 return tn;
419 }
420 }
421
422 int namespaceDelimIndex = name.indexOf(NAMESPACE_DELIM);
423 byte[] nameB = Bytes.toBytes(name);
424
425 if (namespaceDelimIndex < 0) {
426 return createTableNameIfNecessary(
427 ByteBuffer.wrap(NamespaceDescriptor.DEFAULT_NAMESPACE_NAME),
428 ByteBuffer.wrap(nameB));
429 } else {
430 return createTableNameIfNecessary(
431 ByteBuffer.wrap(nameB, 0, namespaceDelimIndex),
432 ByteBuffer.wrap(nameB, namespaceDelimIndex + 1,
433 nameB.length - (namespaceDelimIndex + 1)));
434 }
435 }
436
437
438 public static TableName valueOf(byte[] namespace, byte[] qualifier) {
439 if (namespace == null || namespace.length < 1) {
440 namespace = NamespaceDescriptor.DEFAULT_NAMESPACE_NAME;
441 }
442
443 for (TableName tn : tableCache) {
444 if (Arrays.equals(tn.getQualifier(), namespace) &&
445 Arrays.equals(tn.getNamespace(), namespace)) {
446 return tn;
447 }
448 }
449
450 return createTableNameIfNecessary(
451 ByteBuffer.wrap(namespace), ByteBuffer.wrap(qualifier));
452 }
453
454 public static TableName valueOf(ByteBuffer namespace, ByteBuffer qualifier) {
455 if (namespace == null || namespace.remaining() < 1) {
456 return createTableNameIfNecessary(
457 ByteBuffer.wrap(NamespaceDescriptor.DEFAULT_NAMESPACE_NAME), qualifier);
458 }
459
460 return createTableNameIfNecessary(namespace, qualifier);
461 }
462
463 @Override
464 public boolean equals(Object o) {
465 if (this == o) return true;
466 if (o == null || getClass() != o.getClass()) return false;
467
468 TableName tableName = (TableName) o;
469
470 return o.hashCode() == hashCode && nameAsString.equals(tableName.nameAsString);
471 }
472
473 @Override
474 public int hashCode() {
475 return hashCode;
476 }
477
478
479
480
481 @Override
482 public int compareTo(TableName tableName) {
483 if (this == tableName) return 0;
484 if (this.hashCode < tableName.hashCode()) {
485 return -1;
486 }
487 if (this.hashCode > tableName.hashCode()) {
488 return 1;
489 }
490 return this.nameAsString.compareTo(tableName.getNameAsString());
491 }
492
493
494
495
496
497
498 public KVComparator getRowComparator() {
499 if(TableName.META_TABLE_NAME.equals(this)) {
500 return KeyValue.META_COMPARATOR;
501 }
502 return KeyValue.COMPARATOR;
503 }
504 }