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.hbase.classification.InterfaceAudience;
22 import org.apache.hadoop.hbase.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){
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[start] +
184 "> at 0. " + (isSnapshot ? "Snapshot" : "User-space table") +
185 " qualifiers can only start with 'alphanumeric " +
186 "characters': i.e. [a-zA-Z_0-9]: " +
187 Bytes.toString(qualifierName, start, end));
188 }
189 for (int i = start; i < end; i++) {
190 if (Character.isLetterOrDigit(qualifierName[i]) ||
191 qualifierName[i] == '_' ||
192 qualifierName[i] == '-' ||
193 qualifierName[i] == '.') {
194 continue;
195 }
196 throw new IllegalArgumentException("Illegal character code:" + qualifierName[i] +
197 ", <" + (char) qualifierName[i] + "> at " + i +
198 ". " + (isSnapshot ? "Snapshot" : "User-space table") +
199 " qualifiers can only contain " +
200 "'alphanumeric characters': i.e. [a-zA-Z_0-9-.]: " +
201 Bytes.toString(qualifierName, start, end));
202 }
203 }
204 public static void isLegalNamespaceName(byte[] namespaceName) {
205 isLegalNamespaceName(namespaceName, 0, namespaceName.length);
206 }
207
208
209
210
211 public static void isLegalNamespaceName(final byte[] namespaceName,
212 final int start,
213 final int end) {
214 if(end - start < 1) {
215 throw new IllegalArgumentException("Namespace name must not be empty");
216 }
217 for (int i = start; i < end; i++) {
218 if (Character.isLetterOrDigit(namespaceName[i])|| namespaceName[i] == '_') {
219 continue;
220 }
221 throw new IllegalArgumentException("Illegal character <" + namespaceName[i] +
222 "> at " + i + ". Namespaces can only contain " +
223 "'alphanumeric characters': i.e. [a-zA-Z_0-9]: " + Bytes.toString(namespaceName,
224 start, end));
225 }
226 }
227
228 public byte[] getName() {
229 return name;
230 }
231
232 public String getNameAsString() {
233 return nameAsString;
234 }
235
236 public byte[] getNamespace() {
237 return namespace;
238 }
239
240 public String getNamespaceAsString() {
241 return namespaceAsString;
242 }
243
244 public byte[] getQualifier() {
245 return qualifier;
246 }
247
248 public String getQualifierAsString() {
249 return qualifierAsString;
250 }
251
252 public byte[] toBytes() {
253 return name;
254 }
255
256 public boolean isSystemTable() {
257 return systemTable;
258 }
259
260 @Override
261 public String toString() {
262 return nameAsString;
263 }
264
265
266
267
268
269 private TableName(ByteBuffer namespace, ByteBuffer qualifier) throws IllegalArgumentException {
270 this.qualifier = new byte[qualifier.remaining()];
271 qualifier.duplicate().get(this.qualifier);
272 this.qualifierAsString = Bytes.toString(this.qualifier);
273
274 if (qualifierAsString.equals(OLD_ROOT_STR)) {
275 throw new IllegalArgumentException(OLD_ROOT_STR + " has been deprecated.");
276 }
277 if (qualifierAsString.equals(OLD_META_STR)) {
278 throw new IllegalArgumentException(OLD_META_STR + " no longer exists. The table has been " +
279 "renamed to " + META_TABLE_NAME);
280 }
281
282 if (Bytes.equals(NamespaceDescriptor.DEFAULT_NAMESPACE_NAME, namespace)) {
283
284 this.namespace = NamespaceDescriptor.DEFAULT_NAMESPACE_NAME;
285 this.namespaceAsString = NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR;
286 this.systemTable = false;
287
288
289 this.nameAsString = qualifierAsString;
290 this.name = this.qualifier;
291 } else {
292 if (Bytes.equals(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME, namespace)) {
293 this.namespace = NamespaceDescriptor.SYSTEM_NAMESPACE_NAME;
294 this.namespaceAsString = NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR;
295 this.systemTable = true;
296 } else {
297 this.namespace = new byte[namespace.remaining()];
298 namespace.duplicate().get(this.namespace);
299 this.namespaceAsString = Bytes.toString(this.namespace);
300 this.systemTable = false;
301 }
302 this.nameAsString = namespaceAsString + NAMESPACE_DELIM + qualifierAsString;
303 this.name = Bytes.toBytes(nameAsString);
304 }
305
306 this.hashCode = nameAsString.hashCode();
307
308 isLegalNamespaceName(this.namespace);
309 isLegalTableQualifierName(this.qualifier);
310 }
311
312
313
314
315 private TableName(String qualifier) {
316 this.qualifier = Bytes.toBytes(qualifier);
317 this.qualifierAsString = qualifier;
318
319 this.namespace = NamespaceDescriptor.SYSTEM_NAMESPACE_NAME;
320 this.namespaceAsString = NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR;
321 this.systemTable = true;
322
323
324
325 this.nameAsString = namespaceAsString + NAMESPACE_DELIM + qualifierAsString;
326 this.name = this.qualifier;
327
328 this.hashCode = nameAsString.hashCode();
329 }
330
331
332
333
334
335
336
337
338 private static TableName createTableNameIfNecessary(ByteBuffer bns, ByteBuffer qns) {
339 for (TableName tn : tableCache) {
340 if (Bytes.equals(tn.getQualifier(), qns) && Bytes.equals(tn.getNamespace(), bns)) {
341 return tn;
342 }
343 }
344
345 TableName newTable = new TableName(bns, qns);
346 if (tableCache.add(newTable)) {
347 return newTable;
348 }
349
350
351 for (TableName tn : tableCache) {
352 if (Bytes.equals(tn.getQualifier(), qns) && Bytes.equals(tn.getNamespace(), bns)) {
353 return tn;
354 }
355 }
356
357 throw new IllegalStateException(newTable + " was supposed to be in the cache");
358 }
359
360
361
362
363
364
365
366 private static TableName getADummyTableName(String qualifier) {
367 return new TableName(qualifier);
368 }
369
370
371 public static TableName valueOf(String namespaceAsString, String qualifierAsString) {
372 if (namespaceAsString == null || namespaceAsString.length() < 1) {
373 namespaceAsString = NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR;
374 }
375
376 for (TableName tn : tableCache) {
377 if (qualifierAsString.equals(tn.getQualifierAsString()) &&
378 namespaceAsString.equals(tn.getNameAsString())) {
379 return tn;
380 }
381 }
382
383 return createTableNameIfNecessary(
384 ByteBuffer.wrap(Bytes.toBytes(namespaceAsString)),
385 ByteBuffer.wrap(Bytes.toBytes(qualifierAsString)));
386 }
387
388
389
390
391
392
393
394 public static TableName valueOf(byte[] fullName) throws IllegalArgumentException{
395 for (TableName tn : tableCache) {
396 if (Arrays.equals(tn.getName(), fullName)) {
397 return tn;
398 }
399 }
400
401 int namespaceDelimIndex = com.google.common.primitives.Bytes.lastIndexOf(fullName,
402 (byte) NAMESPACE_DELIM);
403
404 if (namespaceDelimIndex < 0) {
405 return createTableNameIfNecessary(
406 ByteBuffer.wrap(NamespaceDescriptor.DEFAULT_NAMESPACE_NAME),
407 ByteBuffer.wrap(fullName));
408 } else {
409 return createTableNameIfNecessary(
410 ByteBuffer.wrap(fullName, 0, namespaceDelimIndex),
411 ByteBuffer.wrap(fullName, namespaceDelimIndex + 1,
412 fullName.length - (namespaceDelimIndex + 1)));
413 }
414 }
415
416
417
418
419
420
421 public static TableName valueOf(String name) {
422 for (TableName tn : tableCache) {
423 if (name.equals(tn.getNameAsString())) {
424 return tn;
425 }
426 }
427
428 int namespaceDelimIndex = name.indexOf(NAMESPACE_DELIM);
429 byte[] nameB = Bytes.toBytes(name);
430
431 if (namespaceDelimIndex < 0) {
432 return createTableNameIfNecessary(
433 ByteBuffer.wrap(NamespaceDescriptor.DEFAULT_NAMESPACE_NAME),
434 ByteBuffer.wrap(nameB));
435 } else {
436 return createTableNameIfNecessary(
437 ByteBuffer.wrap(nameB, 0, namespaceDelimIndex),
438 ByteBuffer.wrap(nameB, namespaceDelimIndex + 1,
439 nameB.length - (namespaceDelimIndex + 1)));
440 }
441 }
442
443
444 public static TableName valueOf(byte[] namespace, byte[] qualifier) {
445 if (namespace == null || namespace.length < 1) {
446 namespace = NamespaceDescriptor.DEFAULT_NAMESPACE_NAME;
447 }
448
449 for (TableName tn : tableCache) {
450 if (Arrays.equals(tn.getQualifier(), qualifier) &&
451 Arrays.equals(tn.getNamespace(), namespace)) {
452 return tn;
453 }
454 }
455
456 return createTableNameIfNecessary(
457 ByteBuffer.wrap(namespace), ByteBuffer.wrap(qualifier));
458 }
459
460 public static TableName valueOf(ByteBuffer namespace, ByteBuffer qualifier) {
461 if (namespace == null || namespace.remaining() < 1) {
462 return createTableNameIfNecessary(
463 ByteBuffer.wrap(NamespaceDescriptor.DEFAULT_NAMESPACE_NAME), qualifier);
464 }
465
466 return createTableNameIfNecessary(namespace, qualifier);
467 }
468
469 @Override
470 public boolean equals(Object o) {
471 if (this == o) return true;
472 if (o == null || getClass() != o.getClass()) return false;
473
474 TableName tableName = (TableName) o;
475
476 return o.hashCode() == hashCode && nameAsString.equals(tableName.nameAsString);
477 }
478
479 @Override
480 public int hashCode() {
481 return hashCode;
482 }
483
484
485
486
487 @Override
488 public int compareTo(TableName tableName) {
489 if (this == tableName) return 0;
490 if (this.hashCode < tableName.hashCode()) {
491 return -1;
492 }
493 if (this.hashCode > tableName.hashCode()) {
494 return 1;
495 }
496 return this.nameAsString.compareTo(tableName.getNameAsString());
497 }
498
499
500
501
502
503
504 public KVComparator getRowComparator() {
505 if(TableName.META_TABLE_NAME.equals(this)) {
506 return KeyValue.META_COMPARATOR;
507 }
508 return KeyValue.COMPARATOR;
509 }
510 }