content: /*
   * Copyright 1999-2012 Alibaba Group.
   *  
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *  
   *      http://www.apache.org/licenses/LICENSE-2.0
   *  
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  /**
   * (created at 2011-1-23)
   */
  package com.alibaba.cobar.parser.ast.expression.primary;
  
  import com.alibaba.cobar.parser.visitor.SQLASTVisitor;
  
  /**
   * @author <a href="mailto:shuo.qius@alibaba-inc.com">QIU Shuo</a>
   */
  public class Identifier extends PrimaryExpression {
      public static String unescapeName(String name) {
          return unescapeName(name, false);
      }
  
      public static String unescapeName(String name, boolean toUppercase) {
          if (name == null || name.length() <= 0) {
              return name;
          }
          if (name.charAt(0) != '`') {
              return toUppercase ? name.toUpperCase() : name;
          }
          if (name.charAt(name.length() - 1) != '`') {
              throw new IllegalArgumentException("id start with a '`' must end with a '`', id: " + name);
          }
          StringBuilder sb = new StringBuilder(name.length() - 2);
          final int endIndex = name.length() - 1;
          boolean hold = false;
          for (int i = 1; i < endIndex; ++i) {
              char c = name.charAt(i);
              if (c == '`' && !hold) {
                  hold = true;
                  continue;
              }
              hold = false;
              if (toUppercase && c >= 'a' && c <= 'z') {
                  c -= 32;
              }
              sb.append(c);
          }
          return sb.toString();
      }
  
      /** null if no parent */
      protected Identifier parent;
      /** e.g. "id1", "`id1`" */
      protected final String idText;
      protected final String idTextUpUnescape;
  
      public Identifier(Identifier parent, String idText) {
          this(parent, idText, idText.toUpperCase());
      }
  
      public Identifier(Identifier parent, String idText, String idTextUp) {
          this.parent = parent;
          this.idText = idText;
          this.idTextUpUnescape = unescapeName(idTextUp);
      }
  
      public String getLevelUnescapeUpName(int level) {
          Identifier id = this;
          for (int i = level; i > 1 && id != null; --i) {
              id = id.parent;
          }
          if (id != null) {
              return id.idTextUpUnescape;
          }
          return null;
      }
  
      /** trim not happen because parent in given level is not exist */
      public static final int PARENT_ABSENT = 0;
      /** trim happen */
      public static final int PARENT_TRIMED = 1;
      /** trim not happen because parent in given not equals to given name */
      public static final int PARENT_IGNORED = 2;
  
      /**
       * @param level At most how many levels left after trim, must be a positive
       *            integer. e.g. level = 2 for "schema1.tb1.c1", "tb1.c1" is left
       * @param trimSchema upper-case. Assumed that top trimmed parent is schema,
       *            if that equals given schema, do not trim
       * @return {@link #PARENT_ABSENT} or {@link #PARENT_TRIMED}or
       *         {@link #PARENT_IGNORED}
       */
      public int trimParent(int level, String trimSchema) {
          Identifier id = this;
          for (int i = 1; i < level; ++i) {
              if (id.parent == null) {
                  return PARENT_ABSENT;
              }
              id = id.parent;
          }
          if (id.parent == null) {
              return PARENT_ABSENT;
          }
          if (trimSchema != null && !trimSchema.equals(id.parent.idTextUpUnescape)) {
              return PARENT_IGNORED;
          } else {
              id.parent = null;
              return PARENT_TRIMED;
          }
      }
  
      public void setParent(Identifier parent) {
          this.parent = parent;
      }
  
      public Identifier getParent() {
          return parent;
      }
  
      public String getIdText() {
          return idText;
      }
  
      public String getIdTextUpUnescape() {
          return idTextUpUnescape;
      }
  
      @Override
      public String toString() {
          StringBuilder sb = new StringBuilder("ID:");
          if (parent != null) {
              sb.append(parent).append('.');
          }
          return sb.append(idText).toString();
      }
  
      @Override
      public int hashCode() {
          final int constant = 37;
          int hash = 17;
          if (parent == null) {
              hash += constant;
          } else {
              hash = hash * constant + parent.hashCode();
          }
          if (idText == null) {
              hash += constant;
          } else {
              hash = hash * constant + idText.hashCode();
          }
          return hash;
      }
  
      @Override
      public boolean equals(Object obj) {
          if (obj == this)
              return true;
          if (obj instanceof Identifier) {
              Identifier that = (Identifier) obj;
              return objEquals(this.parent, that.parent) && objEquals(this.idText, that.idText);
          }
          return false;
      }
  
      private static boolean objEquals(Object obj, Object obj2) {
          if (obj == obj2)
              return true;
          if (obj == null)
              return obj2 == null;
          return obj.equals(obj2);
      }
  
      @Override
      public void accept(SQLASTVisitor visitor) {
          visitor.visit(this);
      }
  }
  