content: /*****************************************************************
   *   Licensed to the Apache Software Foundation (ASF) under one
   *  or more contributor license agreements.  See the NOTICE file
   *  distributed with this work for additional information
   *  regarding copyright ownership.  The ASF licenses this file
   *  to you 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
   *
   *    https://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.
   ****************************************************************/
  
  package org.apache.cayenne.map;
  
  import org.apache.cayenne.configuration.ConfigurationNode;
  import org.apache.cayenne.configuration.ConfigurationNodeVisitor;
  import org.apache.cayenne.dba.TypesMapping;
  import org.apache.cayenne.map.event.AttributeEvent;
  import org.apache.cayenne.util.XMLEncoder;
  
  /**
   * A DbAttribute defines a descriptor for a single database table column.
   */
  public class DbAttribute extends Attribute<DbEntity, DbAttribute, DbRelationship> implements ConfigurationNode {
  
      /**
       * Defines JDBC type of the column.
       */
      protected int type = TypesMapping.NOT_DEFINED;
  
      /**
       * Defines whether the attribute allows nulls.
       */
      protected boolean mandatory;
  
      /**
       * Defines whether the attribute is a part of the table primary key.
       */
      protected boolean primaryKey;
  
      /**
       * Defines whether this column value is generated by the database. Other
       * terms for such columns are "auto-increment" or "identity".
       * 
       * @since 1.2
       */
      protected boolean generated;
  
      // The length of CHAR or VARCHAR or max num of digits for DECIMAL.
      protected int maxLength = -1;
  
      /**
       * @since 3.0
       */
      protected int scale = -1;
  
      /**
       * @since 3.0
       */
      // must call it 'attributePrecision' as 'precision' in 1.2 really meant 'scale'
      protected int attributePrecision = -1;
  
      public DbAttribute() {
          super();
      }
  
      public DbAttribute(String name) {
          super(name);
      }
  
      public DbAttribute(String name, int type, DbEntity entity) {
          this.setName(name);
          this.setType(type);
          this.setEntity(entity);
      }
  
      @Override
      public DbEntity getEntity() {
          return (DbEntity) super.getEntity();
      }
  
      public <T> T acceptVisitor(ConfigurationNodeVisitor<T> visitor) {
          return visitor.visitDbAttribute(this);
      }
  
      /**
       * Prints itself as XML to the provided XMLEncoder.
       * 
       * @since 1.1
       */
      @Override
      public void encodeAsXML(XMLEncoder encoder, ConfigurationNodeVisitor delegate) {
  
          encoder.start("db-attribute").attribute("name", getName());
  
          String type = TypesMapping.getSqlNameByType(getType());
          encoder.attribute("type", type);
  
          if (isPrimaryKey()) {
              encoder.attribute("isPrimaryKey", true);
  
              // only allow generated if an attribute is a PK.
              if (isGenerated()) {
                  encoder.attribute("isGenerated", true);
              }
          }
  
          if (isMandatory()) {
              encoder.attribute("isMandatory", true);
          }
  
          if (getMaxLength() > 0) {
              encoder.attribute("length", getMaxLength());
          }
  
          if (getScale() > 0) {
              encoder.attribute("scale", getScale());
          }
  
          if (getAttributePrecision() > 0) {
              encoder.attribute("attributePrecision", getAttributePrecision());
          }
  
          delegate.visitDbAttribute(this);
          encoder.end();
      }
  
      public String getAliasedName(String alias) {
          return (alias != null) ? alias + '.' + this.getName() : this.getName();
      }
  
      /**
       * Returns the SQL type of the column.
       * 
       * @see java.sql.Types
       */
      public int getType() {
          return type;
      }
  
      /**
       * Sets the SQL type for the column.
       * 
       * @see java.sql.Types
       */
      public void setType(int type) {
          this.type = type;
      }
  
      public boolean isPrimaryKey() {
          return primaryKey;
      }
  
      /**
       * Returns <code>true</code> if the DB column represented by this attribute
       * is a foreign key, referencing another table.
       * 
       * @since 1.1
       */
      public boolean isForeignKey() {
          String name = getName();
          if (name == null) {
              // won't be able to match joins...
              return false;
          }
  
          for (DbRelationship relationship : getEntity().getRelationships()) {
              for (DbJoin join : relationship.getJoins()) {
                  if (name.equals(join.getSourceName())) {
                      DbAttribute target = join.getTarget();
                      if (target != null && target.isPrimaryKey()) {
                          return true;
                      }
                  }
              }
          }
  
          return false;
      }
  
      /**
       * Updates attribute "primaryKey" property.
       */
      public void setPrimaryKey(boolean primaryKey) {
          if (this.primaryKey != primaryKey) {
              this.primaryKey = primaryKey;
  
              DbEntity e = this.getEntity();
              if (e != null) {
                  e.dbAttributeChanged(new AttributeEvent(this, this, e));
              }
          }
      }
  
      public boolean isMandatory() {
          return mandatory;
      }
  
      public void setMandatory(boolean mandatory) {
          this.mandatory = mandatory;
      }
  
      /**
       * Returns the length of database column described by this attribute.
       */
      public int getMaxLength() {
          return maxLength;
      }
  
      /**
       * Sets the length of character or binary type or max num of digits for
       * DECIMAL.
       */
      public void setMaxLength(int maxLength) {
          this.maxLength = maxLength;
      }
  
      /**
       * Returns true if this column value is generated by the database. Other
       * terms for such columns are "auto-increment" or "identity".
       * 
       * @since 1.2
       */
      public boolean isGenerated() {
          return generated;
      }
  
      /**
       * Updates attribute "generated" property.
       * 
       * @since 1.2
       */
      public void setGenerated(boolean generated) {
          if (this.generated != generated) {
              this.generated = generated;
  
              DbEntity e = this.getEntity();
              if (e != null) {
                  e.dbAttributeChanged(new AttributeEvent(this, this, e));
              }
          }
      }
  
      /**
       * @since 3.0
       */
      public int getAttributePrecision() {
          return attributePrecision;
      }
  
      /**
       * @since 3.0
       */
      public void setAttributePrecision(int attributePrecision) {
          this.attributePrecision = attributePrecision;
      }
  
      /**
       * Returns the number of digits after period for decimal attributes. Returns
       * "-1" if not set.
       * 
       * @since 3.0
       */
      public int getScale() {
          return scale;
      }
  
      /**
       * @since 3.0
       */
      public void setScale(int scale) {
          this.scale = scale;
      }
  
      /**
       * @return java class guessed by the JDBC type of this attribute
       * @since 5.0
       */
      public String getJavaClass() {
          return TypesMapping.getJavaBySqlType(this);
      }
  
      @Override
      public String toString() {
          String res = "DbAttr: ";
          String type = TypesMapping.getSqlNameByType(getType());
  
          res += type != null ? type : "type(" + getType() + ")";
          res += " " + name;
  
          if (scale > -1 || attributePrecision > -1) {
              res += "[" + scale + ", " + attributePrecision + "]";
          }
  
          if (maxLength > -1) {
              res += "[" + maxLength + "]";
          }
  
          if (primaryKey) {
              res += " Primary Key ";
          } else if (mandatory) {
              res += " Mandatory ";
          }
  
          return res;
      }
  }
  