/******************************************************************************* * Copyright (c) 2013, 2015 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: * David McCann - 2.6.0 - July 09, 2013 - Initial Implementation ******************************************************************************/ package org.eclipse.persistence.tools.metadata.generation; import static org.eclipse.persistence.internal.helper.ClassConstants.Object_Class; import static org.eclipse.persistence.platform.database.oracle.plsql.OraclePLSQLTypes.XMLType; import java.sql.Types; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.persistence.internal.databaseaccess.DatabasePlatform; import org.eclipse.persistence.internal.helper.ClassConstants; import org.eclipse.persistence.tools.oracleddl.metadata.DatabaseType; import org.eclipse.persistence.tools.oracleddl.metadata.FieldType; import org.eclipse.persistence.tools.oracleddl.metadata.NumericType; import org.eclipse.persistence.tools.oracleddl.metadata.PLSQLCursorType; import org.eclipse.persistence.tools.oracleddl.metadata.PLSQLType; import org.eclipse.persistence.tools.oracleddl.metadata.ProcedureType; import org.eclipse.persistence.tools.oracleddl.metadata.SizedType; /** * Utility class typically used when constructing JPA/JAXB metadata from a * list of meta-model database types. * */ public class Util { public static final String ARRAY_STR = "ARRAY"; public static final String BIGINT_STR = "BIGINT"; public static final String BINARY_STR = "BINARY"; public static final String BLOB_STR = "BLOB"; public static final String BOOLEAN_STR = "BOOLEAN"; public static final String CHAR_STR = "CHAR"; public static final String CLOB_STR = "CLOB"; public static final String DATE_STR = "DATE"; public static final String DECIMAL_STR = "DECIMAL"; public static final String DOUBLE_STR = "DOUBLE"; public static final String FLOAT_STR = "FLOAT"; public static final String INTEGER_STR = "INTEGER"; public static final String LONG_STR = "LONG"; public static final String LONGRAW_STR = "LONG RAW"; public static final String LONGVARBINARY_STR = "LONGVARBINARY"; public static final String NCHAR_STR = "NCHAR"; public static final String NCLOB_STR = "NCLOB"; public static final String NUMBER_STR = "NUMBER"; public static final String NUMERIC_STR = "NUMERIC"; public static final String NVARCHAR_STR = "NVARCHAR"; public static final String NVARCHAR2_STR = "NVARCHAR2"; public static final String OTHER_STR = "OTHER"; public static final String RAW_STR = "RAW"; public static final String REAL_STR = "REAL"; public static final String ROWID_STR = "ROWID"; public static final String ROWTYPE_STR = "%ROWTYPE"; public static final String SMALLINT_STR = "SMALLINT"; public static final String STRUCT_STR = "STRUCT"; public static final String TIME_STR = "TIME"; public static final String TIMESTAMP_STR = "TIMESTAMP"; public static final String TINYINT_STR = "TINYINT"; public static final String UROWID_STR = "UROWID"; public static final String VARBINARY_STR = "VARBINARY"; public static final String VARCHAR_STR = "VARCHAR"; public static final String VARCHAR2_STR = "VARCHAR2"; public static final String BINARY_INTEGER_STR = "BINARY_INTEGER"; public static final String PLS_INTEGER_STR = "PLS_INTEGER"; public static final String NATURAL_STR = "NATURAL"; public static final String POSITIVE_STR = "POSITIVE"; public static final String SIGNTYPE_STR = "SIGNTYPE"; public static final String BINARY_INTEGER_TYPE_STR = "BinaryInteger"; public static final String PLS_BOOLEAN_TYPE_STR = "PLSQLBoolean"; public static final String PLS_INTEGER_TYPE_STR = "PLSQLInteger"; public static final String NATURAL_TYPE_STR = "Natural"; public static final String POSITIVE_TYPE_STR = "Positive"; public static final String SIGNTYPE_TYPE_STR = "SignType"; public static final String SYS_XMLTYPE_STR = "SYS.XMLTYPE"; public static final String XMLTYPE_STR = "XMLTYPE"; public static final String _TYPE_STR = "_TYPE"; public static final String COMMA = ","; public static final String SINGLE_SPACE = " "; public static final String COMMA_SPACE_STR = COMMA + SINGLE_SPACE; // for CRUD operations public static final String ALL_QUERYNAME = "findAll"; public static final String PK_QUERYNAME = "findByPrimaryKey"; public static final String CREATE_OPERATION_NAME = "create"; public static final String UPDATE_OPERATION_NAME = "update"; public static final String REMOVE_OPERATION_NAME = "delete"; public static final String OPEN_BRACKET = "("; public static final String CLOSE_BRACKET = ")"; public static final String TYPE_STR = "Type"; public static final String SELECT_FROM_STR = "SELECT * FROM "; public static final String WHERE_STR = " WHERE "; public static final String AND_STR = " AND "; public static final String SET_STR = " SET "; public static final String VALUES_STR = " VALUES "; public static final String UPDATE_STR = "UPDATE "; public static final String INSERT_STR = "INSERT INTO "; public static final String DELETE_STR = "DELETE FROM "; public static final String EQUALS_BINDING1_STR = " = ?1"; public static final String EQUALS_BINDING_STR = " = ?"; public static final String QUESTION_STR = "?"; // SQL class names public static final String ARRAY_CLS_STR = "java.sql.Array"; public static final String NCLOB_CLS_STR = "java.sql.NClob"; public static final String OPAQUE_CLS_STR = "java.sql.Struct"; public static final String ROWID_CLS_STR = "java.sql.RowId"; public static final String STRUCT_CLS_STR = "oracle.sql.OPAQUE"; public static final String ORACLE_TIMESTAMP_CLS_STR = "oracle.sql.TIMESTAMP"; // JDK class names static final String ARRAYLIST_CLS_STR = "java.util.ArrayList"; // direction types static final String IN_STR = "IN"; static final String INOUT_STR = "IN_OUT"; static final String OUT_STR = "OUT"; static final String OUT_CURSOR_STR = "OUT_CURSOR"; static final String CURSOR_STR = "CURSOR"; static final String RESULT_STR = "RESULT"; // misc public static final String DOT = "."; public static final String PERCENT = "%"; public static final String UNDERSCORE = "_"; public static final String ITEMS_FLD_STR = "items"; public static final String ITEMS_COL_STR = "ITEMS"; public static final int OPAQUE = 2007; /** * Returns a unqualified entity class name based on a given table or type name. * The returned string will contain an upper case first char, with the * remaining chars in lower case format. */ public static String getUnqualifiedEntityName(String tableName) { String first = tableName.substring(0, 1).toUpperCase(); String rest = tableName.toLowerCase().substring(1); return first.concat(rest); } /** * Returns a entity class name based on a given table or type name. The returned * string will contain an upper case first char, with the remaining chars in * lower case format. The packageName will be prepended to the Entity name * if non-null. */ public static String getEntityName(String tableName, String packageName) { String entityName = getUnqualifiedEntityName(tableName); return packageName == null ? entityName : packageName + DOT + entityName; } /** * Return the JDBC type name for a given type String. */ public static String getJDBCTypeName(String typeName) { // this is inefficient and should be rewritten such that checking is done once only return getJDBCTypeNameFromType(getJDBCTypeFromTypeName(typeName)); } /** * Return the JDBC type (as int) for a given type name. */ public static int getJDBCTypeFromTypeName(String typeName) { if (typeName.equals(NUMERIC_STR)) { return Types.NUMERIC; } if (typeName.equals(VARCHAR_STR) || typeName.equals(VARCHAR2_STR)) { return Types.VARCHAR; } if (typeName.equals(NVARCHAR_STR) || typeName.equals(NVARCHAR2_STR)) { return Types.NVARCHAR; } if (typeName.equals(DATE_STR)) { return Types.DATE; } if (typeName.equals(TIME_STR)) { return Types.TIME; } if (typeName.equals(TIMESTAMP_STR)) { return Types.TIMESTAMP; } if (typeName.equals(DECIMAL_STR)) { return Types.DECIMAL; } if (typeName.equals(INTEGER_STR)) { return Types.INTEGER; } if (typeName.equals(CHAR_STR)) { return Types.CHAR; } if (typeName.equals(NCHAR_STR)) { return Types.NCHAR; } if (typeName.equals(FLOAT_STR)) { return Types.FLOAT; } if (typeName.equals(REAL_STR)) { return Types.REAL; } if (typeName.equals(DOUBLE_STR)) { return Types.DOUBLE; } if (typeName.equals(BINARY_STR)) { return Types.BINARY; } if (typeName.equals(BLOB_STR)) { return Types.BLOB; } if (typeName.equals(CLOB_STR) || typeName.equals(LONG_STR)) { return Types.CLOB; } if (typeName.equals(NCLOB_STR)) { return Types.NCLOB; } if (typeName.equals(RAW_STR) || typeName.equals(LONGRAW_STR)) { return Types.LONGVARBINARY; } if (typeName.equals(ROWID_STR)) { return Types.VARCHAR; } if (typeName.equals(UROWID_STR)) { return Types.VARCHAR; } if (typeName.equals(BIGINT_STR)) { return Types.BIGINT; } if (typeName.equals(STRUCT_STR)) { return Types.STRUCT; } if (typeName.equals(ARRAY_STR)) { return Types.ARRAY; } if (typeName.equals(ROWID_STR)) { return Types.ROWID; } if (typeName.equalsIgnoreCase(XMLTYPE_STR) || typeName.equalsIgnoreCase(SYS_XMLTYPE_STR)) { return Types.VARCHAR; } if (typeName.equals(BOOLEAN_STR) || typeName.equals(INTEGER_STR) || typeName.equals(SMALLINT_STR) || typeName.equals(TINYINT_STR)) { return Types.INTEGER; } return Types.OTHER; } /** * Return the JDBC type name for a given JDBC type code. */ public static String getJDBCTypeNameFromType(int jdbcType) { String typeName = null; switch (jdbcType) { case Types.NUMERIC: typeName = NUMERIC_STR; break; case Types.VARCHAR: typeName = VARCHAR_STR; break; case Types.NVARCHAR: typeName = NVARCHAR_STR; break; case Types.DECIMAL: typeName = DECIMAL_STR; break; case Types.CHAR: typeName = CHAR_STR; break; case Types.NCHAR: typeName = NCHAR_STR; break; case Types.FLOAT: typeName = FLOAT_STR; break; case Types.REAL: typeName = REAL_STR; break; case Types.DOUBLE: typeName = DOUBLE_STR; break; case Types.BINARY: typeName = BINARY_STR; break; case Types.BLOB: typeName = BLOB_STR; break; case Types.CLOB: typeName = CLOB_STR; break; case Types.NCLOB: typeName = NCLOB_STR; break; case Types.VARBINARY : typeName = LONGVARBINARY_STR; break; case Types.LONGVARBINARY: typeName = LONGVARBINARY_STR; break; case Types.DATE: typeName = DATE_STR; break; case Types.TIME: typeName = TIME_STR; break; case Types.TIMESTAMP: typeName = TIMESTAMP_STR; break; case Types.BIGINT: typeName = BIGINT_STR; break; case Types.ARRAY: typeName = ARRAY_STR; break; case Types.STRUCT: typeName = STRUCT_STR; break; case Types.ROWID: typeName = ROWID_STR; break; default: typeName = OTHER_STR; break; } return typeName; } /** * Return the Class name for a given type name using the provided DatabasePlatform. * Object.class.getName() will be returned if the DatabasePlatform returns null. */ public static String getClassNameFromJDBCTypeName(String typeName, DatabasePlatform databasePlatform) { return getClassFromJDBCTypeName(typeName, databasePlatform).getName(); } /** * Return the Class for a given type name using the provided DatabasePlatform. Object.class will * be returned if the DatabasePlatform returns null. */ public static Class<?> getClassFromJDBCTypeName(String typeName, DatabasePlatform databasePlatform) { Class<?> clz = databasePlatform.getClassTypes().get(typeName); if (clz == null) { return Object_Class; } return clz; } /** * Alter the given type name if required. */ protected static String processTypeName(String typeName) { if (!(getJDBCTypeFromTypeName(typeName) == Types.OTHER)) { if (typeName.equals(XMLTYPE_STR)) { typeName = XMLType.name(); } else { // OR Metadata doesn't handle VARCHAR2 if (typeName.equals(VARCHAR2_STR)) { typeName = VARCHAR_STR; } // for BOOLEAN we want to wrap the type in a PLSQLrecord (in ORMetadata.getDatabaseTypeEnum) to // force the appropriate conversion method in PLSQLStoredProcedureCall (declare block, etc) if (!typeName.equals(BOOLEAN_STR)) { typeName = typeName.concat(_TYPE_STR); } } } else { String oPLSQLTypeName = getOraclePLSQLTypeForName(typeName); if (oPLSQLTypeName != null) { typeName = oPLSQLTypeName; } } return typeName; } /** * Returns a Java class name based on a given qualified name. If the name has * a package prepended to it, the the returned string will be in the format * 'packagename.Name', otherwise the format will be 'Name'. For example, given * the name 'test.EMPLOYEE', the method would return the string 'test.Employee'. * */ public static String getGeneratedJavaClassName(String name) { int dotIdx = name.lastIndexOf(DOT); if (dotIdx == -1) { // no package return getGeneratedJavaClassName(name, null); } String packageName = name.substring(0, dotIdx).toLowerCase(); String typeName = name.substring(dotIdx + 1); String first = typeName.substring(0, 1).toUpperCase(); String rest = typeName.toLowerCase().substring(1); return packageName + DOT + first + rest; } /** * Returns a Java class name based on a given name and package. The returned * string will be in the format 'packagename.Name'. For example, given * the name 'EMPLOYEE' and packageName 'TEST', the method would return the * string 'test.Employee'. * */ public static String getGeneratedJavaClassName(String name, String packageName) { String first = name.substring(0, 1).toUpperCase(); String rest = name.toLowerCase().substring(1); return packageName == null || packageName.length() == 0 ? first + rest : packageName.toLowerCase() + DOT + first + rest; } /** * Return a qualified type name for the given DatabaseType. If the type is a * PLSQLType or a PLSQLCursor, and there is a package name available on the * type's parent, a string will be returned in the format 'package.typename'. * Otherwise the type name will be returned. */ public static String getQualifiedTypeName(DatabaseType dbType) { if (dbType.isPLSQLType()) { String packageName = ((PLSQLType) dbType).getParentType().getPackageName(); // for %ROWTYPE we build a record with no package name - need to check for null return packageName != null ? packageName + DOT + dbType.getTypeName() : dbType.getTypeName(); } if (dbType.isPLSQLCursorType()) { // handle cursor PLSQLCursorType cursor = ((PLSQLCursorType) dbType); return cursor.getParentType().getPackageName() + DOT + cursor.getCursorName(); } return dbType.getTypeName(); } /** * Return a qualified compatible type name for the given DatabaseType. If the type is a * PLSQLType, and there is a package name available on the type's parent, a string will * be returned in the format 'package_typename'. Otherwise the type name will be * returned. A compatible type is the JDBC type equivalent of a PL/SQL type. */ public static String getQualifiedCompatibleTypeName(DatabaseType dbType) { String packageName = null; if (dbType.isPLSQLType()) { PLSQLType plsqlType = (PLSQLType) dbType; packageName = plsqlType.getParentType().getPackageName(); } return packageName != null ? packageName + UNDERSCORE + dbType.getTypeName() : dbType.getTypeName(); } /** * Indicates if a given argument type name is considered a PL/SQL scalar * argument, i.e. is one of BOOLEAN, BINARY_INTEGER, PLS_INTEGER, etc. */ public static boolean isArgPLSQLScalar(String argTypeName) { return argTypeName.equals("BOOLEAN") || argTypeName.equals("PLS_INTEGER") || argTypeName.equals("BINARY_INTEGER") || argTypeName.equals("NATURAL") || argTypeName.equals("POSITIVE") || argTypeName.equals("SIGNTYPE"); } /** * Return the Oracle PL/SQL name for a given PL/SQL scalar type. */ public static String getOraclePLSQLTypeForName(String typeName) { if (typeName.equals(BINARY_INTEGER_STR)) return BINARY_INTEGER_TYPE_STR; if (typeName.equals(BOOLEAN_STR)) return PLS_BOOLEAN_TYPE_STR; if (typeName.equals(PLS_INTEGER_STR)) return PLS_INTEGER_TYPE_STR; if (typeName.equals(NATURAL_STR)) return NATURAL_TYPE_STR; if (typeName.equals(POSITIVE_STR)) return POSITIVE_TYPE_STR; if (typeName.equals(SIGNTYPE_STR)) return SIGNTYPE_TYPE_STR; return null; } /** * Return the attribute-type name for a given FieldType. * * For CHAR sized type, java.lang.Character will be returned. * For CHAR non-sized type, java.lang.String will be returned. * For non-CHAR type, the database platform will be used to get the type name. * If the type to be returned is oracle.sql.Timestamp, java.sql.Timestamp will be * returned instead (to handle conversion issue with Oracle11Platform) */ protected static String getAttributeTypeNameForFieldType(FieldType fldType, DatabasePlatform dbPlatform) { String typeName = getJDBCTypeName(fldType.getTypeName()); String attributeType; if (CHAR_STR.equalsIgnoreCase(typeName) && fldType.getEnclosedType().isSizedType()) { SizedType sizedType = (SizedType) fldType.getEnclosedType(); if (sizedType.getSize() == 1) { attributeType = ClassConstants.CHAR.getName(); } else { attributeType = ClassConstants.STRING.getName(); } } else { attributeType = getClassNameFromJDBCTypeName(typeName.toUpperCase(), dbPlatform); } // handle issue with java.sql.Timestamp conversion and Oracle11 platform if (attributeType.contains(ORACLE_TIMESTAMP_CLS_STR)) { attributeType = ClassConstants.TIMESTAMP.getName(); } else if (attributeType.contains(ClassConstants.ABYTE.getName())) { attributeType = ClassConstants.APBYTE.getName(); } return attributeType; } /** * Return the type name to be used for a given database type. The given * DatabaseType's typeName attribute is typically returned, however, in * some cases special handling may be required. For example, in the * case of a NumericType instance, "DECIMAL" should be used for the * type name. */ protected static String getTypeNameForDatabaseType(DatabaseType dataType) { String typeName = dataType.getTypeName(); if (dataType.isNumericType()) { NumericType numericDataType = (NumericType)dataType; if (numericDataType.getScale() > 0) { typeName = DECIMAL_STR; } } return typeName; } /** * Convenience method that detects multiple procedures with the same procedure * name, and updates the overload value on the relevant ProcedureTypes * accordingly. * * The first ProcedureType will have an overload value of 0, the second 1, * and so on. */ protected static void handleOverloading(List<ProcedureType> procedures) { Map<String, List<ProcedureType>> overloadMap = new HashMap<String, List<ProcedureType>>(); for (ProcedureType procedure : procedures) { String procedureName = procedure.getProcedureName(); List<ProcedureType> multipleProcedures = overloadMap.get(procedureName); if (multipleProcedures == null) { multipleProcedures = new ArrayList<ProcedureType>(); overloadMap.put(procedureName, multipleProcedures); } multipleProcedures.add(procedure); } for (List<ProcedureType> procs : overloadMap.values()) { if (procs.size() > 1) { for (int i = 0, len = procs.size(); i < len; i++) { procs.get(i).setOverload(i); } } } } }