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=&gt;foo, table=&gt;bar
163   *                                  org:foo.bar, namespace=org, table=&gt;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}