Package org.eclipse.persistence.platform.database.oracle.publisher.sqlrefl

Source Code of org.eclipse.persistence.platform.database.oracle.publisher.sqlrefl.SqlReflector

/*******************************************************************************
* Copyright (c) 1998, 2012 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
*     Mike Norman - from Proof-of-concept, become production code
******************************************************************************/
package org.eclipse.persistence.platform.database.oracle.publisher.sqlrefl;

//javase imports
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

//EclipseLink imports
import org.eclipse.persistence.platform.database.oracle.publisher.MethodFilter;
import org.eclipse.persistence.platform.database.oracle.publisher.PublisherException;
import org.eclipse.persistence.platform.database.oracle.publisher.Util;
import org.eclipse.persistence.platform.database.oracle.publisher.viewcache.AllCollTypes;
import org.eclipse.persistence.platform.database.oracle.publisher.viewcache.AllTypes;
import org.eclipse.persistence.platform.database.oracle.publisher.viewcache.ElemInfo;
import org.eclipse.persistence.platform.database.oracle.publisher.viewcache.FieldInfo;
import org.eclipse.persistence.platform.database.oracle.publisher.viewcache.RowtypeInfo;
import org.eclipse.persistence.platform.database.oracle.publisher.viewcache.SingleColumnViewRow;
import org.eclipse.persistence.platform.database.oracle.publisher.viewcache.UserArguments;
import org.eclipse.persistence.platform.database.oracle.publisher.viewcache.ViewCache;
import org.eclipse.persistence.platform.database.oracle.publisher.viewcache.ViewCacheManager;
import org.eclipse.persistence.platform.database.oracle.publisher.viewcache.ViewRow;
import static org.eclipse.persistence.platform.database.oracle.publisher.Util.ALL_ARGUMENTS;
import static org.eclipse.persistence.platform.database.oracle.publisher.Util.ALL_COLL_TYPES;
import static org.eclipse.persistence.platform.database.oracle.publisher.Util.ALL_OBJECTS;
import static org.eclipse.persistence.platform.database.oracle.publisher.Util.ALL_TYPES;
import static org.eclipse.persistence.platform.database.oracle.publisher.Util.ALL_TYPE_ATTRS;
import static org.eclipse.persistence.platform.database.oracle.publisher.Util.ATTR_NAME;
import static org.eclipse.persistence.platform.database.oracle.publisher.Util.ATTR_TYPE_NAME;
import static org.eclipse.persistence.platform.database.oracle.publisher.Util.DEFAULT_VARCHAR_LEN;
import static org.eclipse.persistence.platform.database.oracle.publisher.Util.ELEM_TYPE_NAME;
import static org.eclipse.persistence.platform.database.oracle.publisher.Util.IS_COLLECTION;
import static org.eclipse.persistence.platform.database.oracle.publisher.Util.IS_PACKAGE;
import static org.eclipse.persistence.platform.database.oracle.publisher.Util.IS_TOPLEVEL;
import static org.eclipse.persistence.platform.database.oracle.publisher.Util.IS_TYPE;
import static org.eclipse.persistence.platform.database.oracle.publisher.Util.MAX_IDENTIFIER_LENGTH;
import static org.eclipse.persistence.platform.database.oracle.publisher.Util.OBJECT_NAME;
import static org.eclipse.persistence.platform.database.oracle.publisher.Util.OVERLOAD;
import static org.eclipse.persistence.platform.database.oracle.publisher.Util.OWNER;
import static org.eclipse.persistence.platform.database.oracle.publisher.Util.PACKAGE_NAME;
import static org.eclipse.persistence.platform.database.oracle.publisher.Util.TOPLEVEL;
import static org.eclipse.persistence.platform.database.oracle.publisher.Util.TYPE_NAME;
import static org.eclipse.persistence.platform.database.oracle.publisher.Util.getDefaultTypeLen;

/*
* SQL Type Reflection Facility
*/
public class SqlReflector {

    public static final String CHAR_CS = "CHAR_CS";
    public static final String NCHAR_CS = "NCHAR_CS";
    public static final String ROWTYPE = "ROWTYPE";
    public static final String ROWTYPE_PL = ROWTYPE + "_PL";
    public static final String ROWTYPE_SQL = ROWTYPE + "_SQL";
    private static final int MAGIC_NUMBER = 0;

    public static final SqlType BFILE_TYPE = new SqlType("BFILE", OracleTypes.BFILE);
    public static final SqlType BINARY_INTEGER_TYPE =
    // new SqlType("BINARY_INTEGER", OracleTypes.INTEGER);
    new SqlType(new SqlName("BINARY_INTEGER", "INTEGER"), OracleTypes.INTEGER);
    public static final SqlType BLOB_TYPE = new SqlType("BLOB", OracleTypes.BLOB);
    // public static final SqlType BOOLEAN_TYPE =
    // new SqlType("BOOLEAN", SqlType.OracleTypes_BOOLEAN);
    public static final SqlType CHAR_TYPE = new SqlType("CHAR", OracleTypes.CHAR);
    public static final SqlType CLOB_TYPE = new SqlType("CLOB", OracleTypes.CLOB);
    public static final SqlType DATE_TYPE = new SqlType("DATE", OracleTypes.DATE);
    public static final SqlType NCHAR_TYPE = new SqlType("NCHAR", SqlType.ORACLE_TYPES_NCHAR);
    public static final SqlType NCLOB_TYPE = new SqlType("NCLOB", SqlType.ORACLE_TYPES_NCLOB);
    /*
     * public static final SqlType TIME_TYPE = new SqlType("TIME", OracleTypes.TIME); public static
     * final SqlType TIME_WTZ_TYPE = new SqlType("TIME WITH TIME ZONE", OracleTypes.TIME); public
     * static final SqlType TIMETZ_TYPE = // alternate spelling new SqlType("TIME WITH TZ",
     * OracleTypes.TIME);
     */
    public static final SqlType TIMESTAMP_TYPE = new SqlType("TIMESTAMP", OracleTypes.TIMESTAMP);
    public static final SqlType TIMESTAMP_WTZ_TYPE =
        new SqlType("TIMESTAMP WITH TZ", -101 ); // ==OracleTypes.TIMESTAMPTZ
    public static final SqlType TIMESTAMPTZ_TYPE = // alternate spelling
        new SqlType("TIMESTAMP WITH TIME ZONE", -101 ); // ==OracleTypes.TIMESTAMPTZ
    public static final SqlType TIMESTAMPTZ_TYPE0 = // alternate spelling
        new SqlType("TIMESTAMPTZ", -101 ); // ==OracleTypes.TIMESTAMPTZ
    public static final SqlType TIMESTAMP_WLTZ_TYPE =
        new SqlType("TIMESTAMP WITH LOCAL TZ", -102 ); // ==OracleTypes.TIMESTAMPLTZ
    public static final SqlType TIMESTAMPLTZ_TYPE = // alternate spelling
    new SqlType("TIMESTAMP WITH LOCAL TIME ZONE", -102 ); // ==OracleTypes.TIMESTAMPLTZ
    public static final SqlType TIMESTAMPLTZ_TYPE0 = // alternate spelling
    new SqlType("TIMESTAMPLTZ", -102 ); // ==OracleTypes.TIMESTAMPLTZ
    public static final SqlType DECIMAL_TYPE = new SqlType("DECIMAL", OracleTypes.DECIMAL);
    public static final SqlType DOUBLE_PRECISION_TYPE = new SqlType("DOUBLE PRECISION",
        OracleTypes.DOUBLE);
    public static final SqlType FLOAT_TYPE = new SqlType("FLOAT", OracleTypes.FLOAT);
    public static final SqlType FLOAT38TYPE = new SqlType("FLOAT38", OracleTypes.NUMBER);
    // TBD: Using the two types, 101 (OracleTypes.BDOUBLE),
    // 100 (OracleType.BFLOAT), results in <unsupported type>,
    // e.g., for the command
    // jpub -u scott/tiger -sql=sys.xmltype:XMLType
    public static final SqlType BINARY_DOUBLE_TYPE = new SqlType("BINARY_DOUBLE", 101); // OracleTypes.BDOUBLE
    public static final SqlType BINARY_FLOAT_TYPE = new SqlType("BINARY_FLOAT", 100); // OracleTypes.BFLOAT
    // Note: not referencing OracleTypes.BDOULBE and BFLOAT is
    // to avoid incompatible with pre-10i JDBC drivers
    public static final SqlType INTEGER_TYPE = new SqlType("INTEGER", OracleTypes.INTEGER);
    public static final SqlType INT_TYPE = new SqlType("INT", OracleTypes.INTEGER);
    public static final SqlType LONG_TYPE =
    // new SqlType("LONG", OracleTypes.LONGVARBINARY);
    new SqlType(new SqlName("LONG", "VARCHAR2"), OracleTypes.VARCHAR);
    public static final SqlType LONG_RAW_TYPE =
    // new SqlType("LONG RAW", OracleTypes.RAW);
    new SqlType(new SqlName("LONG RAW", "RAW"), OracleTypes.RAW);
    public static final SqlType NUMBER_TYPE = new SqlType("NUMBER", OracleTypes.NUMBER);
    public static final SqlType NUMERIC_TYPE = new SqlType("NUMERIC", OracleTypes.NUMERIC);
    public static final SqlType NVARCHAR2_TYPE = new SqlType("NVARCHAR2",
        SqlType.ORACLE_TYPES_NCHAR);
    public static final SqlType PLS_INTEGER_TYPE =
    // new SqlType("PLS_INTEGER", OracleTypes.INTEGER);
    new SqlType(new SqlName("PLS_INTEGER", "INTEGER"), OracleTypes.INTEGER);
    // public static final SqlType PLSQL_RECORD_TYPE =
    // new SqlType("PL/SQL RECORD", OracleTypes.UNSUPPORTED);
    public static final SqlType RAW_TYPE = new SqlType("RAW", OracleTypes.RAW);
    public static final SqlType REAL_TYPE = new SqlType("REAL", OracleTypes.REAL);
    public static final SqlType REF_CURSOR_TYPE = new SqlType("REF CURSOR", OracleTypes.CURSOR);
    public static final SqlType PLSQL_REF_CURSOR_TYPE = new SqlType("PL/SQL REF CURSOR",
        OracleTypes.CURSOR);
    public static final SqlType ROWID_TYPE =
    // new SqlType("ROWID", OracleTypes.ROWID);
    new SqlType(new SqlName("ROWID", "VARCHAR2"), OracleTypes.VARCHAR);
    public static final SqlType SMALLINT_TYPE = new SqlType("SMALLINT", OracleTypes.SMALLINT);
    public static final SqlType STRING_TYPE = new SqlType("STRING", OracleTypes.VARCHAR);
    public static final SqlType UROWID_TYPE =
    // new SqlType("UROWID", OracleTypes.ROWID);
    new SqlType(new SqlName("UROWID", "VARCHAR2"), OracleTypes.VARCHAR);
    public static final SqlType VARCHAR_TYPE = new SqlType("VARCHAR", OracleTypes.VARCHAR);
    public static final SqlType VARCHAR2_TYPE = new SqlType("VARCHAR2", OracleTypes.VARCHAR);
    public static SqlType plsqlTableDouble = null;
    public static SqlType plsqlTableFloat = null;
    public static SqlType plsqlTableInt = null;
    public static SqlType plsqlTableShort = null;
    public static SqlType plsqlTableJldouble = null;
    public static SqlType plsqlTableJlfloat = null;
    public static SqlType plsqlTableJlinteger = null;
    public static SqlType plsqlTableJlshort = null;
    public static SqlType plsqlTableJmbigdecimal = null;
    public static SqlType plsqlTableString = null;
    public static final SqlType UNKNOWN_TYPE = new SqlType("<unknown type or type not found>",
        OracleTypes.UNSUPPORTED);

    protected ViewCacheManager m_viewCacheManager;
    protected ViewCache m_viewCache;
    protected Connection m_conn;
    protected Boolean m_isPre920 = null;
    protected boolean m_geq9i;
    protected boolean m_transitive = true;
    protected String m_user;
    // A hashtable holding both predefined types and user types
    // for quick looking up.
    protected Map<Name, TypeClass> m_allTypes;
    protected Map<Name, TypeClass> m_predefTypes;
    /*
     * User types to be published can be added into m_userTypes in difference phases of publishing:
     * (1) First the types specified by the -sql option in the jpub command line are added (2)
     * JavaPublisher starts to publish the types in m_userTypes (3) When publishing a type, the
     * types on which that types depend on are reflected and added to the m_userTypes (4) On
     * finishing a type, JavaPublisher publishes the next type in the m_userTypes list. The
     * publishing order seems to be breadth-first.
     */
    protected List<TypeClass> m_userTypes;
    // -overwritedbtypes=false
    protected HashSet<String> m_allTypeNames;
    protected HashSet<String> m_allGeneratedTypeNames;
    protected int m_allGeneratedTypeNamesMagicNumber;
    protected Map<String, String> m_allDefaultArgsHolderTypeNames;
    protected HashSet<String>m_isReused;
    protected boolean m_getTypeCodeWarning = false;
    protected SqlStmtType m_sqlStmtType;
    protected int m_rowtypeDistinguisher = 0;
    protected WrapperPackageMetadata m_wrapperPackageMetadata;
    protected Map<String, SqlType> m_typeMap = new HashMap<String, SqlType>();

    public SqlReflector(Connection conn, String user) {
        m_viewCacheManager = new ViewCacheManager(conn);
        m_user = user;
        reset(conn);
    }

    public void reset(Connection conn) {
        if (m_conn != null) {
            try {
                m_conn.close();
            }
            catch (SQLException e) {
                // Closing resources. OK to ignore exception.
            }
        }
        m_conn = conn;

        m_viewCache = m_viewCacheManager.get(m_user);
        if (m_viewCache == null) {
            m_viewCache = new ViewCache(m_conn, m_user);
            m_viewCacheManager.add(m_viewCache);
        }
        else {
            m_viewCache.reset(m_conn);
        }

        m_allTypes = new HashMap<Name, TypeClass>();
        m_predefTypes = new HashMap<Name, TypeClass>();
        m_userTypes = new ArrayList<TypeClass>();
        sqlTypeInit();

        m_allTypeNames = new HashSet<String>();
        m_allGeneratedTypeNames = new HashSet<String>();
        m_allGeneratedTypeNamesMagicNumber = MAGIC_NUMBER;
        m_allDefaultArgsHolderTypeNames = new HashMap<String, String>();
        m_sqlStmtType = null;

        m_isReused = new HashSet<String>();
        m_wrapperPackageMetadata = null;

        if (conn != null) {
            m_geq9i = SqlReflector.geqOracle9(conn);
        }
    }

    public void loadAllTypeNames() {
        try {
            Iterator<ViewRow> iter = m_viewCache.getRows(ALL_TYPES, new String[]{TYPE_NAME},
                new String[0], new Object[0], new String[0]);
            while (iter.hasNext()) {
                SingleColumnViewRow row = (SingleColumnViewRow)iter.next();
                m_allTypeNames.add(row.getValue());
            }
        }
        catch (Exception e) { /* Cannot access ALL_TYPES table */
            e.printStackTrace();;
        }
    }

    // Determine the SQL type name for a PL/SQL type
    public String determineSqlName(String packageName, String[] sourceName, TypeClass parentType,
        boolean[] isRowType, List<AttributeField> fields, SqlType elemType, SqlType valueType)
        throws SQLException {
        String parentName = null; // Java name for the PL/SQL package
        if (parentType != null) {
            SqlName parentSqlName = (SqlName)parentType.getNameObject();
            if (parentSqlName != null) {
                parentName = parentSqlName.getTypeName().toUpperCase();
            }
        }
        String name = sourceName[0];
        m_allGeneratedTypeNames.add(name);
        if (name.indexOf('.') >= 0 && parentName != null) {
            // Replace the package name with the correpsonding Java name
            name = parentName + "_" + name.substring(name.indexOf('.') + 1);
            m_allGeneratedTypeNames.add(parentName);
        }
        name = name.replace('.', '_').replace(' ', '_');

        boolean toBeDistinguished = false;
        if (sourceName[0].equals("PL/SQL RECORD")) { // %ROWTYPE
            isRowType[0] = true;
            sourceName[0] = ROWTYPE_PL + (m_rowtypeDistinguisher++);
            name = ROWTYPE_SQL;
            if (packageName != null && !packageName.equals("")) {
                if (parentName != null) {
                    name = parentName + "_" + name;
                }
                else {
                    name = packageName + "_" + name;
                }
            }
            toBeDistinguished = true;
        }
        return determineSqlName(name, toBeDistinguished, fields, elemType, valueType);
    }

    private String determineSqlName(String name, boolean toBeDistinguished,
        List<AttributeField> fields, SqlType elemType, SqlType valueType) throws SQLException {

        String origName = name;
        if (valueType != null) {
            String uniqueName = (String)m_allDefaultArgsHolderTypeNames.get(origName);
            if (uniqueName != null) {
                return uniqueName;
            }
        }

        if (m_allTypeNames.contains(name)) {
            boolean match = true;
            Iterator<ViewRow> iter;
            if (fields != null) {
                HashSet<String> hs = new HashSet<String>();
                for (int i = 0; i < fields.size(); i++) {
                    AttributeField af = fields.get(i);
                    String fieldType = ((SqlName)af.getType().getNameObject())
                        .getTargetTypeName();
                    hs.add(fieldType + af.getName());
                }
                iter = m_viewCache.getRows(ALL_TYPE_ATTRS, new String[]{"CONCAT("
                    + ATTR_TYPE_NAME + "," + ATTR_NAME + ")"},
                    new String[]{TYPE_NAME}, new Object[]{name}, new String[0]);
                int count = 0;
                while (iter.hasNext()) {
                    String attr = ((SingleColumnViewRow)iter.next()).getValue();
                    if (!hs.contains(attr)) {
                        match = false;
                    }
                    count++;
                }
                match = match && (count == hs.size());
            }
            else if (elemType != null) {
                iter = m_viewCache.getRows(ALL_COLL_TYPES, new String[]{ELEM_TYPE_NAME},
                    new String[]{TYPE_NAME}, new Object[]{name}, new String[0]);
                if (iter.hasNext()) {
                    String elemTypeNameInDb = ((SingleColumnViewRow)iter.next()).getValue();
                    String elemTypeName = elemType.getSqlName().getTargetTypeName();
                    match = elemTypeNameInDb.equalsIgnoreCase(elemTypeName);
                }
                else {
                    match = false;
                }
            }
            else if (valueType != null) {
                iter = m_viewCache.getRows(ALL_TYPE_ATTRS, new String[]{ATTR_TYPE_NAME},
                    new String[]{TYPE_NAME}, new Object[]{name}, new String[0]);
                if (iter.hasNext()) {
                    String valueTypeNameInDb = ((SingleColumnViewRow)iter.next()).getValue();
                    String valueTypeName = valueType.getSqlName().getTargetTypeName();
                    match = valueTypeNameInDb.equalsIgnoreCase(valueTypeName);
                }
                else {
                    match = false;
                }
            }
            if (match) {
                m_isReused.add(name);
                return name;
            }
        }
        String uniqueName = name.toUpperCase();
        if (m_allTypeNames.contains(uniqueName)
            || (toBeDistinguished && m_allGeneratedTypeNames.contains(uniqueName))) {
            do {
                int len1 = name.length();
                int len2 = Integer.toString(m_allGeneratedTypeNamesMagicNumber++).length();
                if ((len1 + len2) > MAX_IDENTIFIER_LENGTH) {
                    name = name.substring(0, MAX_IDENTIFIER_LENGTH - len2);
                }
                uniqueName = name.toUpperCase() + m_allGeneratedTypeNamesMagicNumber;
            }
            while (m_allTypeNames.contains(uniqueName)
                || (toBeDistinguished && m_allGeneratedTypeNames.contains(uniqueName)));
        }
        m_allGeneratedTypeNames.add(uniqueName);
        if (valueType != null) {
            m_allDefaultArgsHolderTypeNames.put(origName, uniqueName);
        }

        return uniqueName;
    }

    void addAllGeneratedTypeNames(String name) {
        m_allGeneratedTypeNames.add(name.toUpperCase());
    }

    public boolean isReused(String name) {
        return m_isReused.contains(name);
    }

    /*
     * public String determineDefaultArgsHolderSqlName(String name) throws SQLException { String
     * unique = (String) m_allDefaultArgsHolderTypeNames.get(name); if (unique == null) { unique =
     * determineSqlName( name, true, null , null , name); m_allDefaultArgsHolderTypeNames.put(name,
     * unique); } return unique; }
     */
    public void setViewCachePoolCapacity(int size) {
        m_viewCacheManager.setViewCachePoolCapacity(size);
    }

    private void sqlTypeInit() {
        if (!m_allTypes.isEmpty()) {
            m_allTypes = new HashMap<Name, TypeClass>();
        }
        m_allTypes.put(BFILE_TYPE.m_name, BFILE_TYPE);
        m_allTypes.put(BINARY_INTEGER_TYPE.m_name, BINARY_INTEGER_TYPE);
        m_allTypes.put(BLOB_TYPE.m_name, BLOB_TYPE);
        // m_allTypes.put(BOOLEAN_TYPE.m_name, BOOLEAN_TYPE);
        m_allTypes.put(CHAR_TYPE.m_name, CHAR_TYPE);
        m_allTypes.put(CLOB_TYPE.m_name, CLOB_TYPE);
        m_allTypes.put(DATE_TYPE.m_name, DATE_TYPE);
        m_allTypes.put(NCHAR_TYPE.m_name, NCHAR_TYPE);
        m_allTypes.put(NCLOB_TYPE.m_name, NCLOB_TYPE);
        /*
         * m_allTypes.put(TIME_TYPE.m_name, TIME_TYPE);
         * m_allTypes.put(TIME_WTZ_TYPE.m_name,TIME_WTZ_TYPE);
         * m_allTypes.put(TIMETZ_TYPE.m_name, TIMETZ_TYPE);
         */
        m_allTypes.put(TIMESTAMP_TYPE.m_name, TIMESTAMP_TYPE);
        m_allTypes.put(TIMESTAMP_WTZ_TYPE.m_name, TIMESTAMP_WTZ_TYPE);
        m_allTypes.put(TIMESTAMPTZ_TYPE.m_name, TIMESTAMPTZ_TYPE);
        m_allTypes.put(TIMESTAMPTZ_TYPE0.m_name, TIMESTAMPTZ_TYPE0);
        m_allTypes.put(TIMESTAMP_WLTZ_TYPE.m_name, TIMESTAMP_WLTZ_TYPE);
        m_allTypes.put(TIMESTAMPLTZ_TYPE.m_name, TIMESTAMPLTZ_TYPE);
        m_allTypes.put(TIMESTAMPLTZ_TYPE0.m_name, TIMESTAMPLTZ_TYPE0);
        /*
         * m_allTypes.put(INTERVAL_YM_TYPE.m_name, INTERVAL_YM_TYPE);
         * m_allTypes.put(INTERVAL_YM_TYPE.m_name, INTERVAL_YM_TYPE);
         */
        m_allTypes.put(DECIMAL_TYPE.m_name, DECIMAL_TYPE);
        m_allTypes.put(BINARY_DOUBLE_TYPE.m_name, BINARY_DOUBLE_TYPE);
        m_allTypes.put(DOUBLE_PRECISION_TYPE.m_name, DOUBLE_PRECISION_TYPE);
        m_allTypes.put(FLOAT_TYPE.m_name, FLOAT_TYPE);
        m_allTypes.put(FLOAT38TYPE.m_name, FLOAT38TYPE);
        m_allTypes.put(BINARY_FLOAT_TYPE.m_name, BINARY_FLOAT_TYPE);
        m_allTypes.put(INTEGER_TYPE.m_name, INTEGER_TYPE);
        m_allTypes.put(INT_TYPE.m_name, INT_TYPE);
        m_allTypes.put(LONG_TYPE.m_name, LONG_TYPE);
        m_allTypes.put(LONG_RAW_TYPE.m_name, LONG_RAW_TYPE);
        m_allTypes.put(NUMBER_TYPE.m_name, NUMBER_TYPE);
        m_allTypes.put(NUMERIC_TYPE.m_name, NUMERIC_TYPE);
        m_allTypes.put(NVARCHAR2_TYPE.m_name, NVARCHAR2_TYPE);
        m_allTypes.put(PLS_INTEGER_TYPE.m_name, PLS_INTEGER_TYPE);
        m_allTypes.put(RAW_TYPE.m_name, RAW_TYPE);
        m_allTypes.put(REAL_TYPE.m_name, REAL_TYPE);
        m_allTypes.put(REF_CURSOR_TYPE.m_name, REF_CURSOR_TYPE);
        m_allTypes.put(PLSQL_REF_CURSOR_TYPE.m_name, PLSQL_REF_CURSOR_TYPE);
        m_allTypes.put(ROWID_TYPE.m_name, ROWID_TYPE);
        m_allTypes.put(SMALLINT_TYPE.m_name, SMALLINT_TYPE);
        m_allTypes.put(STRING_TYPE.m_name, STRING_TYPE);
        m_allTypes.put(UROWID_TYPE.m_name, UROWID_TYPE);
        m_allTypes.put(VARCHAR_TYPE.m_name, VARCHAR_TYPE);
        m_allTypes.put(VARCHAR2_TYPE.m_name, VARCHAR2_TYPE);

        String name;
        SqlName sqlName;

        name = "PLSQL_TABLE_DOUBLE";
        sqlName = new SqlName(name, name);
        sqlName.setAnnotation(new JavaName("", "double[]", null, "double[]", null));
        plsqlTableDouble = new PlsqlIndexTableType(sqlName, true);

        name = "PLSQL_TABLE_FLOAT";
        sqlName = new SqlName(name, name);
        sqlName.setAnnotation(new JavaName("", "float[]", null, "float[]", null));
        plsqlTableFloat = new PlsqlIndexTableType(sqlName, true);

        name = "PLSQL_TABLE_INT";
        sqlName = new SqlName(name, name);
        sqlName.setAnnotation(new JavaName("", "int[]", null, "int[]", null));
        plsqlTableInt = new PlsqlIndexTableType(sqlName, true);

        name = "PLSQL_TABLE_SHORT";
        sqlName = new SqlName(name, name);
        sqlName.setAnnotation(new JavaName("", "short[]", null, "short[]", null));
        plsqlTableShort = new PlsqlIndexTableType(sqlName, true);

        name = "PLSQL_TABLE_JLDOUBLE";
        sqlName = new SqlName(name, name);
        sqlName.setAnnotation(new JavaName("", "Double[]", null, "Double[]", null));
        plsqlTableJldouble = new PlsqlIndexTableType(sqlName, true);

        name = "PLSQL_TABLE_JLFLOAT";
        sqlName = new SqlName(name, name);
        sqlName.setAnnotation(new JavaName("", "Float[]", null, "Float[]", null));
        plsqlTableJlfloat = new PlsqlIndexTableType(sqlName, true);

        name = "PLSQL_TABLE_JLINTEGER";
        sqlName = new SqlName(name, name);
        sqlName.setAnnotation(new JavaName("", "Integer[]", null, "Integer[]", null));
        plsqlTableJlinteger = new PlsqlIndexTableType(sqlName, true);

        name = "PLSQL_TABLE_JLSHORT";
        sqlName = new SqlName(name, name);
        sqlName.setAnnotation(new JavaName("", "Short[]", null, "Short[]", null));
        plsqlTableJlshort = new PlsqlIndexTableType(sqlName, true);

        name = "PLSQL_TABLE_JLINTEGER";
        sqlName = new SqlName(name, name);
        sqlName.setAnnotation(new JavaName("", "Integer[]", null, "Integer[]", null));
        plsqlTableJlinteger = new PlsqlIndexTableType(sqlName, true);

        name = "PLSQL_TABLE_JMBIGDECIMAL";
        sqlName = new SqlName(name, name);
        sqlName.setAnnotation(new JavaName("", "java.math.BigDecimal[]", null,
            "java.math.BigDecimal[]", null));
        plsqlTableJmbigdecimal = new PlsqlIndexTableType(sqlName, true);
        name = "PLSQL_TABLE_STRING";
        sqlName = new SqlName(name, name);
        sqlName.setAnnotation(new JavaName("", "String[]", null, "String[]", null));
        plsqlTableString = new PlsqlIndexTableType(sqlName, false);

        m_allTypes.put(plsqlTableDouble.m_name, plsqlTableDouble);
        m_allTypes.put(plsqlTableFloat.m_name, plsqlTableFloat);
        m_allTypes.put(plsqlTableInt.m_name, plsqlTableInt);
        m_allTypes.put(plsqlTableShort.m_name, plsqlTableShort);
        m_allTypes.put(plsqlTableJldouble.m_name, plsqlTableJldouble);
        m_allTypes.put(plsqlTableJlfloat.m_name, plsqlTableJlfloat);
        m_allTypes.put(plsqlTableJlshort.m_name, plsqlTableJlshort);
        m_allTypes.put(plsqlTableJlinteger.m_name, plsqlTableJlinteger);
        m_allTypes.put(plsqlTableJmbigdecimal.m_name, plsqlTableJmbigdecimal);
        m_allTypes.put(plsqlTableString.m_name, plsqlTableString);
        String hint = "/*[32767]*/";
        plsqlTableDouble.setHint(hint);
        plsqlTableFloat.setHint(hint);
        plsqlTableInt.setHint(hint);
        plsqlTableShort.setHint(hint);
        plsqlTableJldouble.setHint(hint);
        plsqlTableJlfloat.setHint(hint);
        plsqlTableJlinteger.setHint(hint);
        plsqlTableJlshort.setHint(hint);
        plsqlTableString.setHint(hint);
        plsqlTableJmbigdecimal.setHint(hint);

        m_allTypes.put(UNKNOWN_TYPE.m_name, UNKNOWN_TYPE);
    }

    public Connection getConnection() {
        return m_conn;
    }

    public SqlType addSqlType(SqlName sqlName, int whatIsIt, boolean mustBeNew, boolean generateMe,
        SqlType parentType, String modifier) throws SQLException, PublisherException {
        return addSqlType(sqlName, whatIsIt, mustBeNew, generateMe, parentType, modifier, false,
            null);
    }

    public SqlType addSqlType(SqlName sqlName, int whatIsIt, boolean mustBeNew, boolean generateMe,
        SqlType parentType, String modifier, boolean ncharFormOfUse, MethodFilter signatureFilter)
        throws SQLException, PublisherException {
        SqlType result = null;
        try {
            if (ncharFormOfUse) {
                if (sqlName.getTypeName().equals("CHAR")) {
                    sqlName = NCHAR_TYPE.getSqlName();
                }
                else if (sqlName.getTypeName().equals("VARCHAR2")) {
                    sqlName = NVARCHAR2_TYPE.getSqlName();
                }
                else if (sqlName.getTypeName().equals("CLOB")) {
                    sqlName = NCLOB_TYPE.getSqlName();
                }
            }

            SqlType t = findType(sqlName);
            boolean predefined = false;
            if (t == null) {
                t = findPredefType(sqlName);
                predefined = true;
            }

            if (t != null) {
                result = t;
                if (mustBeNew && predefined) {
                    throw new PublisherException("duplicate type " + result.toString());
                }
                else if (mustBeNew) {
                    t.setNameObject(sqlName);
                }
                return result;
            }

            String schema = sqlName.getSchemaName();
            String type = sqlName.getTypeName();
            if (whatIsIt == IS_TOPLEVEL) {
                return new SqlToplevelType(sqlName, parentType, signatureFilter, this);
            }
            else if ((whatIsIt & IS_PACKAGE) != 0) {
                Iterator<ViewRow> rowIter = m_viewCache.getRows(ALL_OBJECTS,
                    new String[]{"'PACKAGE' AS TYPECODE"}, new String[]{OWNER, OBJECT_NAME,
                        "OBJECT_TYPE", "STATUS"}, new Object[]{schema, type, "PACKAGE", "VALID"},
                    new String[0]);

                if (rowIter.hasNext()) {
                    m_viewCache.fetch(type, signatureFilter);
                    result = new SqlPackageType(sqlName, parentType, signatureFilter, this);
                }
                if (result != null) {
                    return result;
                }
                if (whatIsIt == IS_PACKAGE) {
                    throw new PublisherException("package not found " + sqlName.toString());
                }
            }

            /* determine whether this is an opaque type */
            Iterator<ViewRow> iter = m_viewCache.getRows(ALL_TYPES, new String[0], new String[]{
                OWNER, TYPE_NAME, "PREDEFINED"}, new Object[]{schema, type, "NO"},
                new String[0]);
            if (iter.hasNext()) {
                AllTypes allTypes = (AllTypes)iter.next();
                String typeCode = allTypes.typeCode;
                byte[] typeOID = allTypes.typeOid;
                int dbTypeCode = 0;
                int kind = 0;
                if (!m_getTypeCodeWarning) {
                    try {
                        Object[] outParams = m_viewCache.getOutParameters(
                            "BEGIN sys.sqljutl.get_typecode(:1, :2, :3, :4); END;",
                            new Object[]{typeOID}, new int[]{OracleTypes.INTEGER,
                                OracleTypes.VARCHAR, OracleTypes.INTEGER});
                        if (outParams == null) {
                            throw new SQLException("no data from sqljutl.get_typecode call");
                        }
                        dbTypeCode = ((Integer)outParams[0]).intValue();
                        kind = ((Integer)outParams[2]).intValue();
                    }
                    catch (SQLException exn) {
                        String msg = exn.getMessage();
                        if (isPre920()) {
                            // an older database version will not have GET_TYPECODE
                            msg = null;
                            m_getTypeCodeWarning = true;
                        }
                        else if (msg.indexOf("PLS-00201") > 0) {
                            msg = "cannot determine type " + type;
                            m_getTypeCodeWarning = true;
                        }
                        else {
                            msg = "error determining type " + type;
                        }
                        if (msg != null) {
                            throw new PublisherException(msg);
                        }
                    }
                }

                if (dbTypeCode == SqlType.CODE_OPAQUE) {
                    result = new SqlObjectType(sqlName, OracleTypes.OPAQUE, generateMe, parentType,
                        this);
                }
                else if (dbTypeCode == SqlType.CODE_SQLJTYPE && kind != 0) {
                    result = new SqlSqljType(sqlName, kind, parentType, this);
                }
                else if (typeCode.equals("OBJECT")) {
                    result = new SqlObjectType(sqlName, generateMe, parentType, this);
                    if (SqlName.langIsOtt()) {
                        /*
                         * For C only. Maura - I'm not sure about this, but
                         * it seems that a ref typedef has to be generated
                         * for every struct. That's the purpose of the
                         * following SqlRefType
                         */
                        new SqlRefType(sqlName, result, parentType, generateMe, this);
                    }
                    if (PublisherModifier.isIncomplete(result.getModifiers())) {
                        /*
                         * give warning
                         * about incomplete
                         * type and
                         * continue
                         */
                        int line = sqlName.getLine();
                        int column = sqlName.getColumn();
                        String mesg = "incomplete type " + sqlName.toString();

                        if (line > 0 || column > 0) {
                            mesg = "" + line + "." + column + ": " + mesg;
                        }
                        System.err.println(mesg);
                    }
                }
                else if (typeCode.equals("COLLECTION")) {
                    if ((whatIsIt & IS_COLLECTION) == 0) {
                        throw new PublisherException("collection found " + sqlName.toString());
                    }
                    Iterator<ViewRow> iter3 = m_viewCache.getRows(ALL_COLL_TYPES, new String[0],
                        new String[]{OWNER, TYPE_NAME}, new Object[]{schema, type},
                        new String[0]);
                    if (iter3.hasNext()) {
                        AllCollTypes act = (AllCollTypes)iter3.next();
                        String collTypeCode = act.collType;
                        if (collTypeCode.equals("TABLE")) {
                            result = new SqlTableType(sqlName, generateMe, parentType, this);
                        }
                        else {
                            // "VARYING ARRAY"
                            result = new SqlArrayType(sqlName, generateMe, parentType, this);
                        }
                    }
                }
            }
            if (result != null) {
                return result;
            }

            if (result != null) {
                return result;
            }
            else {
                // Note that this type is not supported, so we will not look
                // it up again.
                result = addPredefType(sqlName, OracleTypes.UNSUPPORTED);
                // Now add an error message
                if ((whatIsIt & IS_PACKAGE) != 0) {
                    throw new PublisherException("type not found " + sqlName.toString());
                }
            }
        }
        catch (SQLException e) {
            System.err.println(e.getMessage());
        }
        catch (PublisherException ex) {
            System.err.println(ex.getMessage());
        }
        return result;
    }

    public SqlType addSqlUserType(String schema, String type, int whatIsIt, boolean mustBeNew,
        int line, int col, MethodFilter signatureFilter) throws SQLException, PublisherException {
        SqlName sn = new SqlName(schema, type, false, line, col, this);
        return addSqlType(sn, whatIsIt, mustBeNew, true, null, null, false, signatureFilter);
    }

    private SqlType addSqlDBType(SqlName sqlName, String modifier, boolean ncharFormOfUse,
        SqlType parentType) throws SQLException, PublisherException {

        /* constructed types are not hashed */
        if (modifier != null && modifier.equals("REF")) {
            SqlType t = addSqlType(sqlName, IS_TYPE, false, m_transitive, parentType, modifier);
            SqlType rt = new SqlRefType(sqlName, t, parentType, m_transitive, this);
            return rt;
        }
        else {
            return addSqlType(sqlName, IS_TYPE, false, m_transitive, parentType, modifier,
                ncharFormOfUse, null);
        }
    }

    // Used for adding types other than PL/SQL types
    public SqlType addSqlDBType(String schema, String type, String subtype, String modifier,
        boolean ncharFormOfUse, SqlType parentType) throws SQLException, PublisherException {
        int line = 0;
        int col = 0;

        if (parentType != null) {
            SqlName psqlname = (SqlName)parentType.getNameObject();
            line = psqlname.getLine();
            col = psqlname.getColumn();
        }
        if (subtype != null) {
            if (type != null) {
                type = type + "." + subtype;
            }
            else {
                type = subtype;
            }
            if (schema != null) {
                type = schema + "." + type;
                schema = null;
            }
        }
        SqlName sn = new SqlName(schema, type, true, line, col, this);
        sn.setAnnotation(genPattern((LangName)sn.getAnnotation(), sn.getSimpleName(), true));

        return addSqlDBType(sn, modifier, ncharFormOfUse, parentType);
    }

    /*
     * Add SQL or PL/SQL types from PL/SQL packages (1) For SQL types, delegate to addSqlDBType (2)
     * For PL/SQL, create PlsqlRecorType or PlsqlTableType
     */
    public SqlType addPlsqlDBType(String schema, String type, String subtype, String modifier,
        boolean ncharFormOfUse, String packageName, String methodName, String methodNo,
        int sequence, SqlType parentType) throws SQLException, PublisherException {
        return addPlsqlDBType(schema, type, subtype, modifier, ncharFormOfUse, packageName,
            methodName, methodNo, sequence, parentType, false);
    }

    public SqlType addPlsqlDBType(String schema, String type, String subtype, String modifier,
        boolean ncharFormOfUse, String packageName, String methodName, String methodNo,
        int sequence, SqlType parentType, boolean isGrandparent) throws SQLException,
        PublisherException {
        int line = 0;
        int col = 0;

        if (parentType != null) {
            SqlName psqlname = (SqlName)parentType.getNameObject();
            line = psqlname.getLine();
            col = psqlname.getColumn();
        }
        if (subtype != null) {
            if (type != null) {
                type = type + "." + subtype;
            }
            else {
                type = subtype;
            }
            if (schema != null) {
                type = schema + "." + type;
                schema = null;
            }
        }
        // For SQL types, delegate to addSqlDBType
        if (modifier == null
            || (!modifier.equals("PL/SQL RECORD") && !modifier.equals("PL/SQL TABLE")
                && (!modifier.equals("TABLE") || (subtype == null && type.indexOf(".") == -1)) && (!modifier
                .equals("VARRAY") || (subtype == null && type.indexOf(".") == -1)))) {
            SqlName sn = new SqlName(schema, type, true, line, col, this);
            sn.setAnnotation(genPattern((LangName)sn.getAnnotation(), sn.getSimpleName(), true));
            return addSqlDBType(sn, modifier, ncharFormOfUse, parentType);
        }

        // Start processing PL/SQL types
        // Check wether the ROWTYPE has bee published
        List<RowtypeInfo> rowtypeInfoA = null;
        if (modifier != null && type != null && modifier.equals("PL/SQL RECORD")
            && type.equals("PL/SQL RECORD")) {
            rowtypeInfoA = reflectRowtypeInfo(packageName, methodName, methodNo, sequence);
            for (int i = 0; i < m_userTypes.size(); i++) {
                boolean found = true;
                TypeClass p = m_userTypes.get(i);
                if (!(p instanceof PlsqlRecordType)) {
                    continue;
                }
                List<RowtypeInfo> rowtypeInfoB = ((PlsqlRecordType)p).getRowtypeInfo();
                if (rowtypeInfoB == null || rowtypeInfoA == null
                    || rowtypeInfoA.size() != rowtypeInfoB.size()) {
                    found = false;
                    continue;
                }
                for (int ja = 0; ja < rowtypeInfoA.size(); ja++) {
                    boolean microFound = false;
                    for (int jb = 0; jb < rowtypeInfoB.size(); jb++) {
                        if (rowtypeInfoA.get(ja).equals(rowtypeInfoB.get(jb))) {
                            microFound = true;
                            break;
                        }
                    }
                    if (!microFound) {
                        found = false;
                        break;
                    }
                }
                if (found) {
                    return (SqlType)p;
                }
            }
        }

        // Create the PL/SQL type
        SqlType result = findPredefType(schema, type);
        boolean predefined = (result != null);
        if (!predefined) {
            result = findType(schema, type);
        }
        if (result != null) {
            if (result.getTypecode() != SqlType.ORACLE_TYPES_TBD) {
                return result;
            }
            else if (!predefined) {
                // Deals with PL/SQL types: Augument the predefined type
                // by -typemap with more specific PL/SQL type information.
                if (modifier != null && modifier.equals("PL/SQL RECORD")) {
                    result.setTypecode(OracleTypes.PLSQL_RECORD);
                }
                else if (modifier != null && modifier.equals("PL/SQL TABLE")) {
                    result.setTypecode(OracleTypes.PLSQL_INDEX_TABLE);
                }
                else if (modifier != null && modifier.equals("TABLE")) {
                    result.setTypecode(OracleTypes.PLSQL_NESTED_TABLE);
                }
                else if (modifier != null && modifier.equals("VARRAY")) {
                    result.setTypecode(OracleTypes.PLSQL_VARRAY_TABLE);
                }
                return result;
            }
        }

        // Case 1: result is null
        // Case 2: result not null, predefined, OracleTypes_TBD
        if (modifier != null && modifier.equals("PL/SQL RECORD")) {
            List<FieldInfo> fieldInfo = PlsqlRecordType.getFieldInfo(packageName, methodName, methodNo,
                sequence, this);
            List<AttributeField> fields = PlsqlRecordType.reflectFields(false, fieldInfo, this,
                parentType, true /* isGrandparent */);
            // if predefined, overwrite entry already in m_predefiendTypes
            if (predefined) {
                String hint = result.getHint();
                result = new PlsqlRecordType(result.getSqlName(), fieldInfo, fields, rowtypeInfoA,
                    false /* generateMe */, parentType, this);
                result.setHint(hint);
            }
            else {
                // Prepare SqlName for the PL/SQL type
                SqlName sqlName = new PlsqlRecordName(schema, type, true, line, col, packageName,
                    parentType, fields, this);
                sqlName.setAnnotation(genPattern((LangName)sqlName.getAnnotation(), sqlName
                    .getSimpleName(), true));

                result = new PlsqlRecordType(sqlName, fieldInfo, fields, rowtypeInfoA,
                    true /* generateMe */, parentType, this);
            }
        }
        else {
            int oracleType = OracleTypes.UNSUPPORTED;
            if (modifier != null && modifier.equals("PL/SQL TABLE")) {
                oracleType = OracleTypes.PLSQL_INDEX_TABLE;
            }
            else if (modifier != null && modifier.equals("TABLE") && type.indexOf(".") > -1) {
                oracleType = OracleTypes.PLSQL_NESTED_TABLE;
            }
            else if (modifier != null && modifier.equals("VARRAY") && type.indexOf(".") > -1) {
                oracleType = OracleTypes.PLSQL_VARRAY_TABLE;
            }
            if (oracleType != OracleTypes.UNSUPPORTED) {
                ElemInfo elemInfo = PlsqlTableType.getElemInfo(schema, type, packageName,
                    methodName, methodNo, m_viewCache);
                int[] details = new int[PlsqlTableType.NUMBER_OF_DETAILS];

                SqlType elemType = (SqlType)PlsqlTableType.getComponentType(elemInfo, this,
                    parentType, details);

                if (predefined) {
                    String hint = result.getHint();
                    result = PlsqlTableType.newInstance(result.getSqlName(), oracleType, elemInfo,
                        elemType, details, false, parentType, isGrandparent, this);
                    result.setHint(hint);
                }
                else {
                    // Prepare SqlName for the PL/SQL type
                    SqlName sqlName = new PlsqlTableName(schema, type, true, line, col,
                        packageName, parentType, elemType, this);
                    sqlName.setAnnotation(genPattern((LangName)sqlName.getAnnotation(), sqlName
                        .getSimpleName(), true));

                    result = PlsqlTableType.newInstance(sqlName, oracleType, elemInfo, elemType,
                        details, true, parentType, isGrandparent, this);
                }
            }
        }
        if (result != null) {
            return result;
        }
        throw new PublisherException("unsupported Type " + schema + "." + type);
    }

    /*
     * look up rowtype columns
     */
    List<RowtypeInfo> reflectRowtypeInfo(String packageName, String methodName, String methodNo,
        int sequence) throws SQLException {
        // Although package_name and type_name derived from getPlsqlTypeName()
        // can be used for the queries, we use method and sequence
        // to be more general. If this type is defined via CURSOR%ROWTYPE,
        // method, method_no and sequence has to be used to
        // identify this type in ALL_ARGUMENTS.
        Iterator<ViewRow> iter = m_viewCache.getRows(ALL_ARGUMENTS, new String[0], new String[]{
           PACKAGE_NAME, OBJECT_NAME, OVERLOAD}, new Object[]{packageName, methodName,
            methodNo}, new String[0]);
        ArrayList<ViewRow> viewRows = new ArrayList<ViewRow>();
        while (iter.hasNext()) { // DISTINCT not enforced
            UserArguments item = (UserArguments)iter.next();
            viewRows.add(item);
        }
        List<RowtypeInfo> rowtypeInfoA = RowtypeInfo.getRowtypeInfo(viewRows);
        int data_level = 0;
        for (int i = 0; i < rowtypeInfoA.size(); i++) {
            RowtypeInfo rti = rowtypeInfoA.get(i);
            if (sequence == -1 || sequence == rti.sequence()) {
                data_level = rti.data_level();
                break;
            }
        }
        int next_rec_sequence = -1;
        for (int i = 0; i < rowtypeInfoA.size(); i++) {
            RowtypeInfo rti = rowtypeInfoA.get(i);
            if (data_level == rti.data_level() && (sequence == -1 || sequence < rti.sequence())) {
                next_rec_sequence = rti.sequence();
                break;
            }
        }
        data_level++;
        ArrayList<RowtypeInfo> rowtypeInfoW = new ArrayList<RowtypeInfo>();
        for (int i = 0; i < rowtypeInfoA.size(); i++) {
            RowtypeInfo rti = rowtypeInfoA.get(i);
            if ((sequence == -1 || sequence < rti.sequence()) && data_level == rti.data_level()
            // && data_level <= rti.data_level()
                && (next_rec_sequence == -1 || next_rec_sequence > rti.sequence())) {
                rowtypeInfoW.add(rti);
            }
        }
        return rowtypeInfoW;
    }

    public SqlType addPredefType(String schema, String type, int typecode, String javaName,
        String convertInto, String convertOutOf, String conversionTarget) throws PublisherException {
        SqlName sn = new SqlName(schema, type, false, false, true, convertInto, convertOutOf,
            conversionTarget, this);

        // Does the Java type contain a type annotation?
        int pos;
        String annotation;
        boolean isPlsqlIndexTable = false;
        int maxLen = Integer.valueOf(DEFAULT_VARCHAR_LEN);
        int maxElemLen = -1;
        boolean isNumeric = true;
        if ((pos = javaName.indexOf("[")) >= 0) {
            annotation = "/*" + javaName.substring(pos).trim() + "*/";
            isPlsqlIndexTable = true;
            int pos1 = pos;
            int pos2 = javaName.indexOf("]");
            if (pos2 > -1) {
                try {
                    maxLen = Integer.parseInt(javaName.substring(pos1 + 1, pos2));
                }
                catch (NumberFormatException e) {
                    System.err.println("ERROR: number format error: " + javaName);
                }
            }
            pos1 = javaName.indexOf("(");
            pos2 = javaName.indexOf(")");
            if (pos1 > -1 && pos2 > -1) {
                try {
                    maxElemLen = Integer.parseInt(javaName.substring(pos1 + 1, pos2));
                }
                catch (NumberFormatException e) {
                    System.err.println("ERROR: number format error: " + javaName);
                }
            }
            javaName = javaName.substring(0, pos);
            if ("String".equals(javaName) || "java.lang.String".equals(javaName)) {
                isNumeric = false;
            }
            javaName = javaName + "[]";
            if (maxElemLen == -1) {
                maxElemLen = Integer.valueOf(getDefaultTypeLen("VARCHAR2"));
            }
        }
        else {
            annotation = "";
        }

        sn.setLangName(null, javaName, null, null, null, null, null, null, true);

        SqlType st = null;
        if (isPlsqlIndexTable) {
            st = new PlsqlIndexTableType(sn, isNumeric, maxLen, maxElemLen);
            m_predefTypes.put(sn, st);
        }
        else {
            st = addPredefType(sn, typecode);
        }
        st.setHint(annotation);
        return st;
    }

    public SqlType addPredefType(SqlName name, int typecode) throws PublisherException {
        // To determine whether the Java type implements ORAData or CustomData
        boolean isPrimitive = true;
        String declClassName = ((name.getDeclPackage() != null && name.getDeclPackage().length() > 0) ? (name
            .getDeclPackage() + ".")
            : "")
            + name.getDeclClass();
        Class<?> declClass = null;
        try {
            declClass = Class.forName(declClassName);
        }
        catch (Throwable t) {
            try {
                declClass = Class.forName("java.lang." + declClassName);
            }
            catch (Throwable t2) {
                // class did not check out as a primitive class
            }
        }
        if (declClass == null) {
            // Cannot load that class. Assume it is not primitive.
            isPrimitive = false;
        }
        else if (declClass != null) {
            try {
                declClass.getField("_SQL_TYPENAME");
                isPrimitive = false;
            }
            catch (Throwable t) {
                try {
                    declClass.getField("_SQL_NAME");
                    isPrimitive = false;
                }
                catch (Throwable t2) {
                    // class has neither _SQL_TYPENAME nor _SQL_NAME
                }
            }
        }
        if (declClassName.equals("boolean") || declClassName.equals("int")
            || declClassName.equals("short") || declClassName.equals("double")
            || declClassName.equals("float") || declClassName.equals("long")
            || declClassName.equals("byte") || declClassName.equals("char")
            || declClassName.endsWith("[]")) {
            isPrimitive = true;
        }

        if (typecode == OracleTypes.UNSUPPORTED || typecode == SqlType.ORACLE_TYPES_TBD) {
            if ("INTEGER".equals(name.getTargetTypeName())) {
                typecode = OracleTypes.INTEGER;
            }
            if ("CHAR".equals(name.getTargetTypeName())) {
                typecode = OracleTypes.CHAR;
            }
        }

        SqlType st = null;
        if (!isPrimitive) { // for sure this is a SQL Object Type
            try {
                st = new SqlObjectType(name, typecode, false, null, this);
            }
            catch (Exception e) {
                System.err.println(e.getMessage());
            }
        }
        else {
            st = new SqlType(name, typecode, false, isPrimitive, null, this);
        }
        if (st != null) {
            m_predefTypes.put(name, st);
        }
        else {
            throw new PublisherException("Cannot find Type " + name);
        }
        return st;
    }

    public void addType(Name name, TypeClass type, boolean generateMe) {
        if (generateMe) {
            m_userTypes.add(type);
        }
        if (name instanceof SqlName && type instanceof SqlType) {
            m_allTypes.put((SqlName)name, (SqlType)type);
        }
    }

    /*
     * Add default parameter holder types for PL/SQL methods
     */
    public SqlType addDefaultArgsHolderType(SqlType valueType, String packageName,
        SqlType parentType, boolean ncharFormOfUse) throws SQLException, PublisherException {
        String typeName = "";
        if (valueType.getSqlName().getTypeName().indexOf(".") > -1) {
            typeName += valueType.getSqlName().getSimpleName();
        }
        else {
            typeName += valueType.getSqlName().getTypeName();
        }
        SqlName sqlName = new DefaultArgsHolderName(null, typeName, true, 0, 0, packageName,
            parentType, valueType, this);
        SqlType result = findType(sqlName);
        if (result == null) {
            sqlName.setAnnotation(genPattern((LangName)sqlName.getAnnotation(), sqlName
                .getSimpleName(), true));
            result = new DefaultArgsHolderType(sqlName, valueType, ncharFormOfUse, this);
        }
        return result;
    }

    public boolean isUserType(TypeClass t) {
        if (t instanceof SqlType) {
            for (int i = 0; i < m_userTypes.size(); i++) {
                if (m_userTypes.get(i).equals(t)) {
                    return true;
                }
            }
        }
        return false;
    }

    private SqlType findType(SqlName sqlName) {
        return _findType(m_allTypes, sqlName);
    }

    private SqlType findType(String schema, String type) {
        SqlName sqlName = new SqlName(Util.getSchema(schema, type), Util.getType(schema, type),
            false, this);
        return _findType(m_allTypes, sqlName);
    }

    private SqlType findPredefType(SqlName sqlName) {
        return _findType(m_predefTypes, sqlName);
    }

    private SqlType findPredefType(String schema, String type) {
        SqlName sqlName = new SqlName(schema, type, false, this);
        return _findType(m_predefTypes, sqlName);
    }

    private SqlType _findType(Map<Name, TypeClass> ht, SqlName sqlName) {
        return (SqlType)ht.get(sqlName);
        /*
         * Enumeration keys = ht.keys(); while (keys.hasMoreElements()) { Object key =
         * keys.nextElement(); if (sqlName.equals(key)) { return (SqlType) ht.get(key); } } return
         * null;
         */
    }

    public SqlType findType(String name) {
        String schema = Util.getSchema(null, name);
        name = Util.getType(null, name);
        SqlName sqlName = new SqlName(schema, name, false, this);
        SqlType sqlType = findType(sqlName);
        if (sqlType == null) {
            sqlType = findPredefType(sqlName);
        }
        return sqlType;
    }

    /**
     * Add all types declared in the given schema to the set of types to be translated.
     */
    public void addAllTypes(String schema) throws SQLException, PublisherException {
        if (m_conn != null) {
            Iterator<ViewRow> iter = m_viewCache
                .getRows(ALL_TYPES, new String[0], new String[]{OWNER, "PREDEFINED"},
                    new Object[]{schema, "NO"}, new String[]{TYPE_NAME});
            while (iter.hasNext()) {
                AllTypes allTypes = (AllTypes)iter.next();
                addSqlDBType(schema, allTypes.typeName, null, "", false, null);
            }
        }
    }

    public void addAllPackages(String schema) throws SQLException, PublisherException {
        if (m_conn != null) {
            PreparedStatement stmt = m_conn.prepareStatement("SELECT OBJECT_NAME AS TYPE_NAME FROM " +
                    "ALL_OBJECTS WHERE OWNER = :1 AND OBJECT_TYPE = 'PACKAGE' AND STATUS='VALID'");
            stmt.setString(1, schema);
            ResultSet rs = stmt.executeQuery();
            while (rs.next()) {
                SqlName sn = new SqlName(schema, rs.getString(1), true, this);
                // determine generated Java names using -genPattern setting.
                sn
                    .setAnnotation(genPattern((LangName)sn.getAnnotation(), sn.getSimpleName(),
                        true));
                addSqlType(sn, Util.IS_PACKAGE, false, true, null, null);
            }
            rs.close();
            stmt.close();

            stmt = m_conn
                .prepareStatement("SELECT COUNT(OBJECT_NAME) AS ARG_COUNT FROM ALL_ARGUMENTS WHERE OWNER = :1 AND PACKAGE_NAME IS NULL AND DATA_LEVEL = 0");
            stmt.setString(1, schema);
            rs = stmt.executeQuery();
            if (rs.next()) {
                int count = rs.getInt(1);
                if (count > 0) {
                    SqlType t = addSqlUserType(schema, "", Util.IS_TOPLEVEL, true, 0, 0, null);
                    String userName = SqlName.sqlIdToJavaId(TOPLEVEL, true);
                    t.getSqlName().setLangName("", userName, null, null, null, null, null, null,
                        true);
                }
            }
            rs.close();
            stmt.close();
        }
    }

    /* Add Java non-array types for publishing */
    public JavaType addJavaType(String typeName, List<AttributeField> fields,
        List<ProcedureMethod> methods, boolean genPattern, TypeClass sqlType) throws SQLException {
        if (typeName == null) {
            return null;
        }
        JavaType jt = null;
        for (int i = 0; i < m_userTypes.size(); i++) {
            if (m_userTypes.get(i) instanceof JavaType) {
                JavaType jTypeTmp = (JavaType)m_userTypes.get(i);
                JavaName jNameTmp = new JavaName(null, typeName, null, null, null);
                if (jTypeTmp.getJavaName().equals(jNameTmp)) {
                    jt = jTypeTmp;
                }
            }
        }
        if (jt != null) {
            return jt;
        }
        JavaName javaName = new JavaName(null, typeName, null, null, null);
        if (genPattern) {
            javaName = (JavaName)genPattern(javaName, typeName, false);
        }
        jt = new JavaBaseType(javaName, fields, methods, sqlType);
        m_userTypes.add(jt);
        return jt;
    }

    /**
     * Search among userTypes, and return a boolean indicating whether a subclass of the receiver
     * type has methods
     */
    public boolean hasMethodsInSubclasses(TypeClass who) throws SQLException, PublisherException {
        Iterator<TypeClass> iter = m_userTypes.iterator();
        while (iter.hasNext()) {
            TypeClass t = iter.next();
            if (!(t instanceof SqlType)) {
                continue;
            }
            SqlType st = (SqlType)t;
            boolean stHasMethods = st.hasMethods();
            while ((st = (SqlType)st.getSupertype()) != null) {
                if (who.getName() != null && who.getName().equals(st.getName())) {
                    if (stHasMethods) {
                        return true;
                    }
                    break;
                }
            }
        }
        return false;
    }

    public void createSqlStmtType(SqlName sqlName) throws SQLException {
        if (m_sqlStmtType == null) {
            m_sqlStmtType = new SqlStmtType(sqlName, this);
        }
    }

    public boolean isPre920() {
        if (m_isPre920 == null) {
            try {
                String v = m_conn.getMetaData().getDatabaseProductVersion().toUpperCase();
                if (v.startsWith("ORACLE DATABASE 10G") || v.startsWith("ORACLE DATABASE 11G")) {
                    m_isPre920 = Boolean.valueOf(false);
                    return false;
                }
                int pos = v.indexOf("ORACLE");
                if (0 < pos) {
                    v = v.substring(pos);
                }
                String vp = v.substring(0, "ORACLExx".length()).toUpperCase();
                if (vp.equals("ORACLE12")
                    || vp.equals("ORACLE11")
                    || vp.equals("ORACLE10")
                    || (vp.equals("ORACLE9I") && (v.indexOf("9.2.") > 0 || v.indexOf("9.3.") > 0 || v
                        .indexOf("9.4.") > 0))) {
                    m_isPre920 = Boolean.valueOf(false);
                }
            }
            catch (Exception e) {
                // Connection is pre 9.2.0
            }
            if (m_isPre920 == null) {
                m_isPre920 = Boolean.valueOf(true);
            }
        }
        return m_isPre920.booleanValue();
    }

    public boolean geqOracle9() {
        return m_geq9i;
    }

    public static boolean geqOracle9(Connection conn) {
        boolean geq9i = false;
        try {
            String dv = conn.getMetaData().getDatabaseProductVersion().toUpperCase();

            int pos = dv.indexOf("ORACLE");
            if (0 < pos) {
                dv = dv.substring(pos);
            }
            geq9i = dv.startsWith("ORACLE9") || dv.startsWith("ORACLE DATABASE 10G")
                || dv.startsWith("ORACLE DATABASE 11G") || dv.startsWith("ORACLE1")
                || dv.startsWith("ORACLE2") || dv.startsWith("ORACLE3");
        }
        catch (SQLException se) {
            /* assume the database is old */
        }
        return geq9i;
    }

    public void setTransitive(boolean transitive) {
        m_transitive = transitive;
    }

    // Return true if a column is null
    public static boolean isNull(String col) {
        return (col == null || col.length() == 0);
    }

    public boolean noUserTypes() {
        return m_userTypes.isEmpty();
    }

    public Map<String, SqlType> getTypeMap() {
        return m_typeMap;
    }

    /**
     * Used for implicitly publishing SQL types Based on the -genPattern setting, sets the LangName
     * associated with this SqlName.
     */
    private LangName genPattern(LangName langName, String simpleName, boolean sql2Java) {
        String genPattern = null;
        if (genPattern == null) {
            return langName;
        }
        String useName = simpleName;
        if (sql2Java) {
            useName = SqlName.sqlIdToJavaId(simpleName, true);
        }
        // return sqlstring entry (e.g., a:b:c#d) according to m_genPattern.
        // Entries in itemTokens are used to replace %?
        // in m_genPattern. For instance, itemTokens can be
        // - {sqlTypeName, JavaTypeName, JavaSubclassName, JavaItf}
        // - {sqlTypeName}
        String sql = genPattern;
        for (int i = genPattern.length() - 1; i >= 0; i--) {
            if (genPattern.charAt(i) == '%' && i != genPattern.length() - 1) {
                int j = genPattern.charAt(i + 1) - '0';
                sql = sql.substring(0, i) + ((j == 1) ? simpleName : useName)
                    + sql.substring(i + 2);
                // System.out.println("SqlName: sql = "+sql); //D+
            }
        }
        // Derive useName, subclassName, interface name
        String useItf = null;
        String generatedName = null;
        String generatedItf = null;
        StringTokenizer st = new StringTokenizer(sql, "#");
        String[] tokens = new String[st.countTokens()];
        for (int i = 0; 0 < st.countTokens(); i++) {
            tokens[i] = st.nextToken();
            if (i == 1) {
                generatedItf = tokens[1];
                useItf = tokens[1];
            }
        }
        st = new StringTokenizer(tokens[0], ":");
        tokens = new String[st.countTokens()];
        for (int i = 0; 0 < st.countTokens(); i++) {
            tokens[i] = st.nextToken();
            if (i == 0) {
                useName = tokens[0];
            }
            else if (i == 1) {
                useName = tokens[1];
                generatedName = tokens[0];
            }
        }
        if (useName == null) {
            useItf = null;
        }
        if (generatedName == null) {
            generatedItf = null;
        }
        if (useItf != null) {
            generatedItf = null;
        }
        JavaName retJavaName = new JavaName("", useName, useItf, generatedName, generatedItf);
        return retJavaName;
    }

    public String determineSqlName(String name, boolean toBeDistinguished) {
        // toBeDistinguished is a marker indicating that the name is chopped
        // or post-fixed due to length over-limit or name conflicts. If true,
        // the new name needs to go through another round of conflicts checking.
        if (name.length() > MAX_IDENTIFIER_LENGTH) {
            name = name.substring(0, MAX_IDENTIFIER_LENGTH);
            toBeDistinguished = true;
        }
        String uniqueName = name.toUpperCase();
        if (m_allTypeNames.contains(uniqueName)
            || (toBeDistinguished && m_allGeneratedTypeNames.contains(uniqueName))) {
            do {
                int len1 = name.length();
                int len2 = Integer.toString(m_allGeneratedTypeNamesMagicNumber++).length();
                if ((len1 + len2) > MAX_IDENTIFIER_LENGTH) {
                    name = name.substring(0, MAX_IDENTIFIER_LENGTH - len2);
                }
                uniqueName = name.toUpperCase() + m_allGeneratedTypeNamesMagicNumber;
            }
            while (m_allTypeNames.contains(uniqueName)
                || (toBeDistinguished && m_allGeneratedTypeNames.contains(uniqueName)));
        }
        m_allGeneratedTypeNames.add(uniqueName);
        return uniqueName;
    }


    public ViewCache getViewCache() {
        return m_viewCache;
    }

    public void getViewCache(ViewCache vc) {
        m_viewCache = vc;
    }

    public void close() {
        try {
            if (m_conn != null) {
                m_conn.close();
            }
            if (m_viewCache != null) {
                m_viewCache.close();
            }
        }
        catch (Exception e) {
            // Closing resources. OK to ignore exception.
        }
    }

    /**
     * Returns all the user-defined SqlTypes. New types may be appended to the end of the
     * enumeration as the existing types are published or navigated.
     */
    public Iterator<TypeClass> getUserTypes() {
        return m_userTypes.iterator();
    }

    /**
     * @return metadata for generated PL/SQL wrapper package
     */

    public WrapperPackageMetadata getWrapperPackageMetadata() {
        return m_wrapperPackageMetadata;
    }

    public void addWrapperMethodMetadata(String name, String[] paramTypes, String[] paramNames,
        String returnType) {
        if (m_wrapperPackageMetadata == null) {
            m_wrapperPackageMetadata = new WrapperPackageMetadata("");
        }
        m_wrapperPackageMetadata.addMethod(new WrapperMethodMetadata(name, paramTypes, paramNames,
            returnType));
    }

}
TOP

Related Classes of org.eclipse.persistence.platform.database.oracle.publisher.sqlrefl.SqlReflector

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.