001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase; 019 020import com.google.errorprone.annotations.RestrictedApi; 021import java.nio.ByteBuffer; 022import java.nio.charset.StandardCharsets; 023import java.util.Arrays; 024import java.util.Set; 025import java.util.concurrent.CopyOnWriteArraySet; 026import org.apache.commons.lang3.ArrayUtils; 027import org.apache.hadoop.conf.Configuration; 028import org.apache.hadoop.hbase.util.Bytes; 029import org.apache.yetus.audience.InterfaceAudience; 030import org.slf4j.Logger; 031import org.slf4j.LoggerFactory; 032 033import org.apache.hbase.thirdparty.com.google.common.base.Preconditions; 034import org.apache.hbase.thirdparty.com.google.common.base.Strings; 035 036/** 037 * Immutable POJO class for representing a table name. Which is of the form: <table 038 * namespace>:<table qualifier> Two special namespaces: 1. hbase - system namespace, used 039 * to contain hbase internal tables 2. default - tables with no explicit specified namespace will 040 * automatically fall into this namespace. ie a) foo:bar, means namespace=foo and qualifier=bar b) 041 * bar, means namespace=default and qualifier=bar c) default:bar, means namespace=default and 042 * qualifier=bar 043 * <p> 044 * Internally, in this class, we cache the instances to limit the number of objects and make the 045 * "equals" faster. We try to minimize the number of objects created of the number of array copy to 046 * check if we already have an instance of this TableName. The code is not optimize for a new 047 * instance creation but is optimized to check for existence. 048 * </p> 049 */ 050@InterfaceAudience.Public 051public final class TableName implements Comparable<TableName> { 052 private static final Logger LOG = LoggerFactory.getLogger(TableName.class); 053 054 /** See {@link #createTableNameIfNecessary(ByteBuffer, ByteBuffer)} */ 055 private static final Set<TableName> tableCache = new CopyOnWriteArraySet<>(); 056 057 /** Namespace delimiter */ 058 // this should always be only 1 byte long 059 public final static char NAMESPACE_DELIM = ':'; 060 061 // A non-capture group so that this can be embedded. 062 // regex is a bit more complicated to support nuance of tables 063 // in default namespace 064 // Allows only letters, digits and '_' 065 public static final String VALID_NAMESPACE_REGEX = "(?:[_\\p{Digit}\\p{IsAlphabetic}]+)"; 066 // Allows only letters, digits, '_', '-' and '.' 067 public static final String VALID_TABLE_QUALIFIER_REGEX = 068 "(?:[_\\p{Digit}\\p{IsAlphabetic}][-_.\\p{Digit}\\p{IsAlphabetic}]*)"; 069 // Concatenation of NAMESPACE_REGEX and TABLE_QUALIFIER_REGEX, 070 // with NAMESPACE_DELIM as delimiter 071 public static final String VALID_USER_TABLE_REGEX = "(?:(?:(?:" + VALID_NAMESPACE_REGEX + "\\" 072 + NAMESPACE_DELIM + ")?)" + "(?:" + VALID_TABLE_QUALIFIER_REGEX + "))"; 073 public static final String VALID_META_TABLE_SUFFIX_REGEX = "[a-zA-Z0-9]+"; 074 075 /** 076 * The name of hbase meta table could either be hbase:meta_xxx or 'hbase:meta' otherwise. Config 077 * hbase.meta.table.suffix will govern the decision of adding suffix to the habase:meta 078 */ 079 public static final TableName META_TABLE_NAME; 080 static { 081 Configuration conf = HBaseConfiguration.create(); 082 META_TABLE_NAME = initializeHbaseMetaTableName(conf); 083 LOG.info("Meta table name: {}", META_TABLE_NAME); 084 } 085 086 /* Visible for testing only */ 087 @RestrictedApi(explanation = "Should only be called in tests", link = "", 088 allowedOnPath = ".*/src/test/.*") 089 public static TableName getDefaultNameOfMetaForReplica() { 090 return valueOf(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR, "meta"); 091 } 092 093 public static TableName initializeHbaseMetaTableName(Configuration conf) { 094 String suffix_val = conf.get(HConstants.HBASE_META_TABLE_SUFFIX, 095 HConstants.HBASE_META_TABLE_SUFFIX_DEFAULT_VALUE); 096 LOG.debug("[Read-replica feature] suffix value: {}", 097 (suffix_val == null || suffix_val.isEmpty()) ? "<blank>" : suffix_val); 098 if (Strings.isNullOrEmpty(suffix_val)) { 099 return valueOf(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR, "meta"); 100 } else { 101 if (!suffix_val.matches(VALID_META_TABLE_SUFFIX_REGEX)) { 102 throw new IllegalArgumentException("Invalid value '" + suffix_val + "' for config '" 103 + HConstants.HBASE_META_TABLE_SUFFIX + "'. Suffix must only contain ASCII letters and " 104 + "digits matching: " + VALID_META_TABLE_SUFFIX_REGEX); 105 } 106 return valueOf(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR, "meta_" + suffix_val); 107 } 108 } 109 110 /** 111 * The Namespace table's name. 112 * @deprecated since 3.0.0 and will be removed in 4.0.0. We have folded the data in namespace 113 * table into meta table, so do not use it any more. 114 * @see <a href="https://issues.apache.org/jira/browse/HBASE-21154">HBASE-21154</a> 115 */ 116 @Deprecated 117 public static final TableName NAMESPACE_TABLE_NAME = 118 valueOf(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR, "namespace"); 119 120 public static final String OLD_META_STR = ".META."; 121 public static final String OLD_ROOT_STR = "-ROOT-"; 122 123 /** One globally disallowed name */ 124 public static final String DISALLOWED_TABLE_NAME = "zookeeper"; 125 126 /** Returns True if <code>tn</code> is the hbase:meta table name. */ 127 public static boolean isMetaTableName(final TableName tn) { 128 return tn.equals(TableName.META_TABLE_NAME); 129 } 130 131 /** 132 * TableName for old -ROOT- table. It is used to read/process old WALs which have ROOT edits. 133 */ 134 public static final TableName OLD_ROOT_TABLE_NAME = getADummyTableName(OLD_ROOT_STR); 135 /** 136 * TableName for old .META. table. Used in testing. 137 */ 138 public static final TableName OLD_META_TABLE_NAME = getADummyTableName(OLD_META_STR); 139 140 private final byte[] name; 141 private final String nameAsString; 142 private final byte[] namespace; 143 private final String namespaceAsString; 144 private final byte[] qualifier; 145 private final String qualifierAsString; 146 private final boolean systemTable; 147 private final boolean backupsTable; 148 private final int hashCode; 149 150 /** 151 * Check passed byte array, "tableName", is legal user-space table name. 152 * @return Returns passed <code>tableName</code> param 153 * @throws IllegalArgumentException if passed a tableName is null or is made of other than 'word' 154 * characters or underscores: i.e. 155 * <code>[\p{IsAlphabetic}\p{Digit}.-:]</code>. The ':' is used 156 * to delimit the namespace from the table name and can be used 157 * for nothing else. Namespace names can only contain 'word' 158 * characters <code>[\p{IsAlphabetic}\p{Digit}]</code> or '_' 159 * Qualifier names can only contain 'word' characters 160 * <code>[\p{IsAlphabetic}\p{Digit}]</code> or '_', '.' or '-'. 161 * The name may not start with '.' or '-'. Valid fully qualified 162 * table names: foo:bar, namespace=>foo, table=>bar 163 * org:foo.bar, namespace=org, table=>foo.bar 164 */ 165 public static byte[] isLegalFullyQualifiedTableName(final byte[] tableName) { 166 if (tableName == null || tableName.length <= 0) { 167 throw new IllegalArgumentException("Name is null or empty"); 168 } 169 170 int namespaceDelimIndex = ArrayUtils.lastIndexOf(tableName, (byte) NAMESPACE_DELIM); 171 if (namespaceDelimIndex < 0) { 172 isLegalTableQualifierName(tableName); 173 } else { 174 isLegalNamespaceName(tableName, 0, namespaceDelimIndex); 175 isLegalTableQualifierName(tableName, namespaceDelimIndex + 1, tableName.length); 176 } 177 return tableName; 178 } 179 180 public static byte[] isLegalTableQualifierName(final byte[] qualifierName) { 181 isLegalTableQualifierName(qualifierName, 0, qualifierName.length, false); 182 return qualifierName; 183 } 184 185 public static byte[] isLegalTableQualifierName(final byte[] qualifierName, boolean isSnapshot) { 186 isLegalTableQualifierName(qualifierName, 0, qualifierName.length, isSnapshot); 187 return qualifierName; 188 } 189 190 /** 191 * Qualifier names can only contain 'word' characters <code>[\p{IsAlphabetic}\p{Digit}]</code> or 192 * '_', '.' or '-'. The name may not start with '.' or '-'. 193 * @param qualifierName byte array containing the qualifier name 194 * @param start start index 195 * @param end end index (exclusive) 196 */ 197 public static void isLegalTableQualifierName(final byte[] qualifierName, int start, int end) { 198 isLegalTableQualifierName(qualifierName, start, end, false); 199 } 200 201 public static void isLegalTableQualifierName(final byte[] qualifierName, int start, int end, 202 boolean isSnapshot) { 203 if (end - start < 1) { 204 throw new IllegalArgumentException( 205 isSnapshot ? "Snapshot" : "Table" + " qualifier must not be empty"); 206 } 207 String qualifierString = Bytes.toString(qualifierName, start, end - start); 208 if (qualifierName[start] == '.' || qualifierName[start] == '-') { 209 throw new IllegalArgumentException("Illegal first character <" + qualifierName[start] 210 + "> at 0. " + (isSnapshot ? "Snapshot" : "User-space table") 211 + " qualifiers can only start with 'alphanumeric " + "characters' from any language: " 212 + qualifierString); 213 } 214 if (qualifierString.equals(DISALLOWED_TABLE_NAME)) { 215 // Per https://zookeeper.apache.org/doc/r3.4.10/zookeeperProgrammers.html#ch_zkDataModel 216 // A znode named "zookeeper" is disallowed by zookeeper. 217 throw new IllegalArgumentException("Tables may not be named '" + DISALLOWED_TABLE_NAME + "'"); 218 } 219 for (int i = 0; i < qualifierString.length(); i++) { 220 // Treat the string as a char-array as some characters may be multi-byte 221 char c = qualifierString.charAt(i); 222 // Check for letter, digit, underscore, hyphen, or period, and allowed by ZK. 223 // ZooKeeper also has limitations, but Character.isAlphabetic omits those all 224 // See https://zookeeper.apache.org/doc/r3.4.10/zookeeperProgrammers.html#ch_zkDataModel 225 if (Character.isAlphabetic(c) || Character.isDigit(c) || c == '_' || c == '-' || c == '.') { 226 continue; 227 } 228 throw new IllegalArgumentException("Illegal character code:" + (int) c + ", <" + c + "> at " 229 + i + ". " + (isSnapshot ? "Snapshot" : "User-space table") 230 + " qualifiers may only contain 'alphanumeric characters' and digits: " + qualifierString); 231 } 232 } 233 234 public static void isLegalNamespaceName(byte[] namespaceName) { 235 isLegalNamespaceName(namespaceName, 0, namespaceName.length); 236 } 237 238 /** 239 * Valid namespace characters are alphabetic characters, numbers, and underscores. 240 */ 241 public static void isLegalNamespaceName(final byte[] namespaceName, final int start, 242 final int end) { 243 if (end - start < 1) { 244 throw new IllegalArgumentException("Namespace name must not be empty"); 245 } 246 String nsString = new String(namespaceName, start, (end - start), StandardCharsets.UTF_8); 247 if (nsString.equals(DISALLOWED_TABLE_NAME)) { 248 // Per https://zookeeper.apache.org/doc/r3.4.10/zookeeperProgrammers.html#ch_zkDataModel 249 // A znode named "zookeeper" is disallowed by zookeeper. 250 throw new IllegalArgumentException("Tables may not be named '" + DISALLOWED_TABLE_NAME + "'"); 251 } 252 for (int i = 0; i < nsString.length(); i++) { 253 // Treat the string as a char-array as some characters may be multi-byte 254 char c = nsString.charAt(i); 255 // ZooKeeper also has limitations, but Character.isAlphabetic omits those all 256 // See https://zookeeper.apache.org/doc/r3.4.10/zookeeperProgrammers.html#ch_zkDataModel 257 if (Character.isAlphabetic(c) || Character.isDigit(c) || c == '_') { 258 continue; 259 } 260 throw new IllegalArgumentException( 261 "Illegal character <" + c + "> at " + i + ". Namespaces may only contain " 262 + "'alphanumeric characters' from any language and digits: " + nsString); 263 } 264 } 265 266 public byte[] getName() { 267 return name; 268 } 269 270 public String getNameAsString() { 271 return nameAsString; 272 } 273 274 public byte[] getNamespace() { 275 return namespace; 276 } 277 278 public String getNamespaceAsString() { 279 return namespaceAsString; 280 } 281 282 /** 283 * Ideally, getNameAsString should contain namespace within it, but if the namespace is default, 284 * it just returns the name. This method takes care of this corner case. 285 */ 286 public String getNameWithNamespaceInclAsString() { 287 if (getNamespaceAsString().equals(NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR)) { 288 return NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR + TableName.NAMESPACE_DELIM 289 + getNameAsString(); 290 } 291 return getNameAsString(); 292 } 293 294 public byte[] getQualifier() { 295 return qualifier; 296 } 297 298 public String getQualifierAsString() { 299 return qualifierAsString; 300 } 301 302 /** Returns A pointer to TableName as String bytes. */ 303 public byte[] toBytes() { 304 return name; 305 } 306 307 public boolean isSystemTable() { 308 return systemTable; 309 } 310 311 public boolean isBackupsTable() { 312 return backupsTable; 313 } 314 315 @Override 316 public String toString() { 317 return nameAsString; 318 } 319 320 private TableName(ByteBuffer namespace, ByteBuffer qualifier) throws IllegalArgumentException { 321 this.qualifier = new byte[qualifier.remaining()]; 322 qualifier.duplicate().get(this.qualifier); 323 this.qualifierAsString = Bytes.toString(this.qualifier); 324 325 if (qualifierAsString.equals(OLD_ROOT_STR)) { 326 throw new IllegalArgumentException(OLD_ROOT_STR + " has been deprecated."); 327 } 328 if (qualifierAsString.equals(OLD_META_STR)) { 329 throw new IllegalArgumentException( 330 OLD_META_STR + " no longer exists. The table has been " + "renamed to " + META_TABLE_NAME); 331 } 332 333 if (Bytes.equals(NamespaceDescriptor.DEFAULT_NAMESPACE_NAME, namespace)) { 334 // Using the same objects: this will make the comparison faster later 335 this.namespace = NamespaceDescriptor.DEFAULT_NAMESPACE_NAME; 336 this.namespaceAsString = NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR; 337 this.systemTable = false; 338 this.backupsTable = false; 339 340 // The name does not include the namespace when it's the default one. 341 this.nameAsString = qualifierAsString; 342 this.name = this.qualifier; 343 } else { 344 if (Bytes.equals(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME, namespace)) { 345 this.namespace = NamespaceDescriptor.SYSTEM_NAMESPACE_NAME; 346 this.namespaceAsString = NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR; 347 this.systemTable = true; 348 this.backupsTable = false; 349 } else if (Bytes.equals(NamespaceDescriptor.BACKUP_NAMESPACE_NAME, namespace)) { 350 this.namespace = NamespaceDescriptor.BACKUP_NAMESPACE_NAME; 351 this.namespaceAsString = NamespaceDescriptor.BACKUP_NAMESPACE_NAME_STR; 352 this.systemTable = true; 353 this.backupsTable = true; 354 } else { 355 this.namespace = new byte[namespace.remaining()]; 356 namespace.duplicate().get(this.namespace); 357 this.namespaceAsString = Bytes.toString(this.namespace); 358 this.systemTable = false; 359 this.backupsTable = false; 360 } 361 this.nameAsString = namespaceAsString + NAMESPACE_DELIM + qualifierAsString; 362 this.name = Bytes.toBytes(nameAsString); 363 } 364 365 this.hashCode = nameAsString.hashCode(); 366 367 isLegalNamespaceName(this.namespace); 368 isLegalTableQualifierName(this.qualifier); 369 } 370 371 /** This is only for the old and meta tables. */ 372 private TableName(String qualifier) { 373 this.qualifier = Bytes.toBytes(qualifier); 374 this.qualifierAsString = qualifier; 375 376 this.namespace = NamespaceDescriptor.SYSTEM_NAMESPACE_NAME; 377 this.namespaceAsString = NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR; 378 this.systemTable = true; 379 this.backupsTable = false; 380 381 // WARNING: nameAsString is different than name for old meta & root! 382 // This is by design. 383 this.nameAsString = namespaceAsString + NAMESPACE_DELIM + qualifierAsString; 384 this.name = this.qualifier; 385 386 this.hashCode = nameAsString.hashCode(); 387 } 388 389 /** 390 * Check that the object does not exist already. There are two reasons for creating the objects 391 * only once: 1) With 100K regions, the table names take ~20MB. 2) Equals becomes much faster as 392 * it's resolved with a reference and an int comparison. 393 */ 394 private static TableName createTableNameIfNecessary(ByteBuffer bns, ByteBuffer qns) { 395 for (TableName tn : tableCache) { 396 if (Bytes.equals(tn.getQualifier(), qns) && Bytes.equals(tn.getNamespace(), bns)) { 397 return tn; 398 } 399 } 400 401 TableName newTable = new TableName(bns, qns); 402 if (tableCache.add(newTable)) { // Adds the specified element if it is not already present 403 return newTable; 404 } 405 406 // Someone else added it. Let's find it. 407 for (TableName tn : tableCache) { 408 if (Bytes.equals(tn.getQualifier(), qns) && Bytes.equals(tn.getNamespace(), bns)) { 409 return tn; 410 } 411 } 412 // this should never happen. 413 throw new IllegalStateException(newTable + " was supposed to be in the cache"); 414 } 415 416 /** 417 * It is used to create table names for old META, and ROOT table. These tables are not really 418 * legal tables. They are not added into the cache. 419 * @return a dummy TableName instance (with no validation) for the passed qualifier 420 */ 421 private static TableName getADummyTableName(String qualifier) { 422 return new TableName(qualifier); 423 } 424 425 public static TableName valueOf(String namespaceAsString, String qualifierAsString) { 426 if (namespaceAsString == null || namespaceAsString.length() < 1) { 427 namespaceAsString = NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR; 428 } 429 430 for (TableName tn : tableCache) { 431 if ( 432 qualifierAsString.equals(tn.getQualifierAsString()) 433 && namespaceAsString.equals(tn.getNamespaceAsString()) 434 ) { 435 return tn; 436 } 437 } 438 439 return createTableNameIfNecessary(ByteBuffer.wrap(Bytes.toBytes(namespaceAsString)), 440 ByteBuffer.wrap(Bytes.toBytes(qualifierAsString))); 441 } 442 443 /** 444 * Construct a TableName 445 * @param fullName will use the entire byte array 446 * @throws IllegalArgumentException if fullName equals old root or old meta. Some code depends on 447 * this. The test is buried in the table creation to save on 448 * array comparison when we're creating a standard table object 449 * that will be in the cache. 450 */ 451 public static TableName valueOf(byte[] fullName) throws IllegalArgumentException { 452 return valueOf(fullName, 0, fullName.length); 453 } 454 455 /** 456 * Construct a TableName 457 * @param fullName byte array to look into 458 * @param offset within said array 459 * @param length within said array 460 * @throws IllegalArgumentException if fullName equals old root or old meta. 461 */ 462 public static TableName valueOf(byte[] fullName, int offset, int length) 463 throws IllegalArgumentException { 464 Preconditions.checkArgument(offset >= 0, "offset must be non-negative but was %s", offset); 465 Preconditions.checkArgument(offset < fullName.length, "offset (%s) must be < array length (%s)", 466 offset, fullName.length); 467 Preconditions.checkArgument(length <= fullName.length, 468 "length (%s) must be <= array length (%s)", length, fullName.length); 469 for (TableName tn : tableCache) { 470 final byte[] tnName = tn.getName(); 471 if (Bytes.equals(tnName, 0, tnName.length, fullName, offset, length)) { 472 return tn; 473 } 474 } 475 476 int namespaceDelimIndex = 477 ArrayUtils.lastIndexOf(fullName, (byte) NAMESPACE_DELIM, offset + length - 1); 478 479 if (namespaceDelimIndex < offset) { 480 return createTableNameIfNecessary(ByteBuffer.wrap(NamespaceDescriptor.DEFAULT_NAMESPACE_NAME), 481 ByteBuffer.wrap(fullName, offset, length)); 482 } else { 483 return createTableNameIfNecessary(ByteBuffer.wrap(fullName, offset, namespaceDelimIndex), 484 ByteBuffer.wrap(fullName, namespaceDelimIndex + 1, length - (namespaceDelimIndex + 1))); 485 } 486 } 487 488 /** 489 * Construct a TableName 490 * @param fullname of a table, possibly with a leading namespace and ':' as delimiter. 491 * @throws IllegalArgumentException if fullName equals old root or old meta. 492 */ 493 public static TableName valueOf(ByteBuffer fullname) { 494 fullname = fullname.duplicate(); 495 fullname.mark(); 496 boolean miss = true; 497 while (fullname.hasRemaining() && miss) { 498 miss = ((byte) NAMESPACE_DELIM) != fullname.get(); 499 } 500 if (miss) { 501 fullname.reset(); 502 return valueOf(null, fullname); 503 } else { 504 ByteBuffer qualifier = fullname.slice(); 505 int delimiterIndex = fullname.position() - 1; 506 fullname.reset(); 507 // changing variable name for clarity 508 ByteBuffer namespace = fullname.duplicate(); 509 namespace.limit(delimiterIndex); 510 return valueOf(namespace, qualifier); 511 } 512 } 513 514 /** 515 * Construct a TableName 516 * @throws IllegalArgumentException if fullName equals old root or old meta. Some code depends on 517 * this. 518 */ 519 public static TableName valueOf(String name) { 520 for (TableName tn : tableCache) { 521 if (name.equals(tn.getNameAsString())) { 522 return tn; 523 } 524 } 525 526 final int namespaceDelimIndex = name.indexOf(NAMESPACE_DELIM); 527 528 if (namespaceDelimIndex < 0) { 529 return createTableNameIfNecessary(ByteBuffer.wrap(NamespaceDescriptor.DEFAULT_NAMESPACE_NAME), 530 ByteBuffer.wrap(Bytes.toBytes(name))); 531 } else { 532 // indexOf is by character, not byte (consider multi-byte characters) 533 String ns = name.substring(0, namespaceDelimIndex); 534 String qualifier = name.substring(namespaceDelimIndex + 1); 535 return createTableNameIfNecessary(ByteBuffer.wrap(Bytes.toBytes(ns)), 536 ByteBuffer.wrap(Bytes.toBytes(qualifier))); 537 } 538 } 539 540 public static TableName valueOf(byte[] namespace, byte[] qualifier) { 541 if (namespace == null || namespace.length < 1) { 542 namespace = NamespaceDescriptor.DEFAULT_NAMESPACE_NAME; 543 } 544 545 for (TableName tn : tableCache) { 546 if ( 547 Arrays.equals(tn.getQualifier(), qualifier) && Arrays.equals(tn.getNamespace(), namespace) 548 ) { 549 return tn; 550 } 551 } 552 553 return createTableNameIfNecessary(ByteBuffer.wrap(namespace), ByteBuffer.wrap(qualifier)); 554 } 555 556 public static TableName valueOf(ByteBuffer namespace, ByteBuffer qualifier) { 557 if (namespace == null || namespace.remaining() < 1) { 558 return createTableNameIfNecessary(ByteBuffer.wrap(NamespaceDescriptor.DEFAULT_NAMESPACE_NAME), 559 qualifier); 560 } 561 562 return createTableNameIfNecessary(namespace, qualifier); 563 } 564 565 @Override 566 public boolean equals(Object o) { 567 if (this == o) return true; 568 if (o == null || getClass() != o.getClass()) return false; 569 570 TableName tableName = (TableName) o; 571 572 return o.hashCode() == hashCode && nameAsString.equals(tableName.nameAsString); 573 } 574 575 @Override 576 public int hashCode() { 577 return hashCode; 578 } 579 580 @Override 581 public int compareTo(TableName tableName) { 582 // For performance reasons, the ordering is not lexicographic. 583 if (this == tableName) { 584 return 0; 585 } 586 if (this.hashCode < tableName.hashCode()) { 587 return -1; 588 } 589 if (this.hashCode > tableName.hashCode()) { 590 return 1; 591 } 592 return this.nameAsString.compareTo(tableName.getNameAsString()); 593 } 594 595}