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.dbsync.reverse.dbload;
  
  import java.sql.DatabaseMetaData;
  import java.sql.Types;
  
  import org.apache.cayenne.dba.TypesMapping;
  import org.apache.cayenne.map.DbAttribute;
  import org.apache.cayenne.map.DbEntity;
  import org.junit.Test;
  
  import static org.junit.Assert.assertEquals;
  import static org.junit.Assert.assertFalse;
  import static org.junit.Assert.assertNotNull;
  import static org.junit.Assert.assertTrue;
  
  public class AttributeLoaderIT extends BaseLoaderIT {
  
      @Test
      public void testAttributeLoad() throws Exception {
          createDbEntities();
  
          AttributeLoader loader = new AttributeLoader(adapter, EMPTY_CONFIG, new DefaultDbLoaderDelegate());
          loader.load(connection.getMetaData(), store);
  
          DbEntity artist = getDbEntity("ARTIST");
          DbAttribute a = getDbAttribute(artist, "ARTIST_ID");
          assertNotNull(a);
          if (accessStackAdapter.onlyGenericNumberType()) {
              // All integer types are mapped to NUMERIC in Oracle DB.
              assertEquals(Types.NUMERIC, a.getType());
          } else {
              assertEquals(Types.BIGINT, a.getType());
          }
          assertTrue(a.isMandatory());
          assertFalse(a.isGenerated());
  
          a = getDbAttribute(artist, "ARTIST_NAME");
          assertNotNull(a);
          assertEquals(Types.CHAR, a.getType());
          assertEquals(254, a.getMaxLength());
          assertTrue(a.isMandatory());
  
          a = getDbAttribute(artist, "DATE_OF_BIRTH");
          assertNotNull(a);
          if(accessStackAdapter.onlyGenericDateType()) {
              assertTrue(Types.DATE == a.getType() || Types.TIMESTAMP == a.getType());
          } else {
              assertEquals(Types.DATE, a.getType());
          }
          assertFalse(a.isMandatory());
  
          if(accessStackAdapter.supportsLobs()) {
              assertLobDbEntities();
          }
  
          if (adapter.supportsGeneratedKeys()) {
              assertGenerated();
          }
      }
  
      @Test
      public void testAttributeLoadTypes() throws Exception {
          DatabaseMetaData metaData = connection.getMetaData();
          DbLoaderDelegate delegate = new DefaultDbLoaderDelegate();
  
          // We need all data to check relationships, so simply load it all
          EntityLoader entityLoader = new EntityLoader(adapter, EMPTY_CONFIG, delegate);
          AttributeLoader attributeLoader = new AttributeLoader(adapter, EMPTY_CONFIG, delegate);
  
          entityLoader.load(metaData, store);
          attributeLoader.load(metaData, store);
  
          DbEntity dbe = getDbEntity("PAINTING");
          DbEntity floatTest = getDbEntity("FLOAT_TEST");
          DbEntity smallintTest = getDbEntity("SMALLINT_TEST");
          DbAttribute integerAttr = getDbAttribute(dbe, "PAINTING_ID");
          DbAttribute decimalAttr = getDbAttribute(dbe, "ESTIMATED_PRICE");
          DbAttribute varcharAttr = getDbAttribute(dbe, "PAINTING_TITLE");
          DbAttribute floatAttr = getDbAttribute(floatTest, "FLOAT_COL");
          DbAttribute smallintAttr = getDbAttribute(smallintTest, "SMALLINT_COL");
  
          // check decimal
          assertTrue(msgForTypeMismatch(Types.DECIMAL, decimalAttr), Types.DECIMAL == decimalAttr.getType()
                  || Types.NUMERIC == decimalAttr.getType());
          assertEquals(2, decimalAttr.getScale());
  
          // check varchar
          assertEquals(msgForTypeMismatch(Types.VARCHAR, varcharAttr), Types.VARCHAR, varcharAttr.getType());
          assertEquals(255, varcharAttr.getMaxLength());
  
          // check integer
          // All integer types are mapped to NUMERIC in Oracle DB.
          if (accessStackAdapter.onlyGenericNumberType()) {
              assertEquals(msgForTypeMismatch(Types.NUMERIC, integerAttr), Types.NUMERIC, integerAttr.getType());
          } else {
              assertEquals(msgForTypeMismatch(Types.INTEGER, integerAttr), Types.INTEGER, integerAttr.getType());
          }
  
          // check float
          assertTrue(msgForTypeMismatch(Types.FLOAT, floatAttr), Types.FLOAT == floatAttr.getType()
                  || Types.DOUBLE == floatAttr.getType() || Types.REAL == floatAttr.getType());
  
          // check smallint
          // All integer types are mapped to NUMERIC in Oracle DB.
          if (accessStackAdapter.onlyGenericNumberType()) {
              assertEquals(msgForTypeMismatch(Types.NUMERIC, smallintAttr), Types.NUMERIC, smallintAttr.getType());
          } else {
              assertTrue(msgForTypeMismatch(Types.SMALLINT, smallintAttr),
                         Types.SMALLINT == smallintAttr.getType() || Types.INTEGER == smallintAttr.getType());
          }
      }
  
      private void assertGenerated() {
          DbEntity bag = getDbEntity("GENERATED_COLUMN_TEST");
          DbAttribute id = getDbAttribute(bag, "GENERATED_COLUMN");
          assertTrue(id.isGenerated());
      }
  
      private void assertLobDbEntities() {
          DbEntity blobEnt = getDbEntity("BLOB_TEST");
          assertNotNull(blobEnt);
          DbAttribute blobAttr = getDbAttribute(blobEnt, "BLOB_COL");
          assertNotNull(blobAttr);
          assertTrue(msgForTypeMismatch(Types.BLOB, blobAttr),
                  Types.BLOB == blobAttr.getType()
                          || Types.VARBINARY == blobAttr.getType()
                          || Types.LONGVARBINARY == blobAttr.getType());
  
          DbEntity clobEnt = getDbEntity("CLOB_TEST");
          assertNotNull(clobEnt);
          DbAttribute clobAttr = getDbAttribute(clobEnt, "CLOB_COL");
          assertNotNull(clobAttr);
          assertTrue(msgForTypeMismatch(Types.CLOB, clobAttr),
                  Types.CLOB == clobAttr.getType()
                          || Types.VARCHAR == clobAttr.getType()
                          || Types.LONGVARCHAR == clobAttr.getType());
      }
  
  
  
      private DbAttribute getDbAttribute(DbEntity ent, String name) {
          DbAttribute da = ent.getAttribute(name);
          // sometimes table names get converted to lowercase
          if (da == null) {
              da = ent.getAttribute(name.toLowerCase());
          }
  
          return da;
      }
  
      private static String msgForTypeMismatch(int origType, DbAttribute newAttr) {
          String nt = TypesMapping.getSqlNameByType(newAttr.getType());
          String ot = TypesMapping.getSqlNameByType(origType);
          return attrMismatch(newAttr.getName(), "expected type: <" + ot + ">, but was <" + nt + ">");
      }
  
      private static String attrMismatch(String attrName, String msg) {
          return "[Error loading attribute '" + attrName + "': " + msg + "]";
      }
  }
  