blob: 3a648ceede479db54933a9f4ed109c92ac39309f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 1998, 2012 Oracle. 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:
* Oracle - initial API and implementation from Oracle TopLink
******************************************************************************/
package org.eclipse.persistence.tools.db.model.platformsmodel;
import java.lang.reflect.Field;
import java.sql.Types;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;
import java.util.Vector;
import org.eclipse.persistence.tools.utility.StringTools;
import org.eclipse.persistence.tools.utility.XMLTools;
import org.eclipse.persistence.tools.utility.collection.CollectionTools;
import org.eclipse.persistence.tools.utility.iterable.LiveCloneIterable;
import org.eclipse.persistence.tools.utility.iterator.IteratorTools;
import org.eclipse.persistence.tools.utility.iterator.TransformationIterator;
import org.eclipse.persistence.tools.utility.node.AbstractNode;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
/**
* This repository holds the JDBC Types that correspond to the data types
* defined in java.sql.Types. It also holds the mappings between JDBC
* types and Java type declarations, which are stored in an XML file.
*
* @see java.sql.Types
*
* For information on mapping JDBC and Java types,
* see "Getting Started with JDBC API"
* http://java.sun.com/j2se/1.4.2/docs/guide/jdbc/getstart/GettingStartedTOC.fm.html
*
* @version 2.6
*/
@SuppressWarnings("nls")
public final class JDBCTypeRepository extends AbstractNode {
/**
* All the data type constants in java.sql.Types (e.g. INTEGER: 4);
* by default, these are calculated via reflection on java.sql.Types.
* This will always contain at least one type, so we always have a default.
* @see java.sql.Types
*/
private Collection<JDBCType> jdbcTypes;
public static final String JDBC_TYPES_COLLECTION = "jdbcTypes";
// these aren't really JDBC types, although they are defined in java.sql.Types
private static final int[] DEFAULT_UNUSED_JDBC_TYPE_CODES = {Types.NULL};
/**
* The default JDBC type used to determine the default database type
* used when generating tables.
*/
private JDBCType defaultJDBCType;
public static final String DEFAULT_JDBC_TYPE_PROPERTY = "defaultJDBCType";
private static final int DEFAULT_DEFAULT_JDBC_TYPE_CODE = Types.VARCHAR;
/**
* Mappings of the JDBC types to Java type declarations
* (e.g. java.sql.Types.INTEGER maps to int).
* These are used to generate classes from tables.
* This collection is kept in synch with the jdbcTypes collection.
*/
private Collection<JDBCTypeToJavaTypeDeclarationMapping> jdbcTypeToJavaTypeDeclarationMappings;
public static final String JDBC_TYPE_TO_JAVA_TYPE_DECLARATION_MAPPINGS_COLLECTION = "jdbcTypeToJavaTypeDeclarationMappings";
/**
* Mappings of typical Java type declarations to the JDBC types
* (e.g. int and java.lang.Integer map to java.sql.Types.INTEGER).
* These are used to generate tables from classes.
*/
private Collection<JavaTypeDeclarationToJDBCTypeMapping> javaTypeDeclarationToJDBCTypeMappings;
public static final String JAVA_TYPE_DECLARATION_TO_JDBC_TYPE_MAPPINGS_COLLECTION = "javaTypeDeclarationToJDBCTypeMappings";
private static final boolean JDK16 = jdkIsVersion("1.6");
private static boolean jdkIsVersion(String version) {
return System.getProperty("java.version").indexOf(version) != -1;
}
// ********** constructors **********
/**
* this constructor is called when the repository is read from an XML file
*/
JDBCTypeRepository(DatabasePlatformRepository platformRepository, Node node) throws CorruptXMLException {
super(platformRepository);
this.read(node);
}
/**
* this constructor is called when the user (or a test case)
* creates a new repository (which shouldn't happen very often...)
*/
JDBCTypeRepository(DatabasePlatformRepository platformRepository) {
super(platformRepository);
this.initializeDefaults();
}
// ********** initialization **********
/**
* @see org.eclipse.persistence.tools.workbench.mappingsmodel.AbstractNodeModel#initialize()
*/
@Override
protected void initialize() {
super.initialize();
this.jdbcTypes = new Vector<JDBCType>();
this.jdbcTypeToJavaTypeDeclarationMappings = new Vector<JDBCTypeToJavaTypeDeclarationMapping>();
this.javaTypeDeclarationToJDBCTypeMappings = new Vector<JavaTypeDeclarationToJDBCTypeMapping>();
}
/**
* Initialize our attributes to some reasonable defaults.
*/
private void initializeDefaults() {
this.initializeDefaultJDBCTypes();
this.defaultJDBCType = this.jdbcTypeForCode(DEFAULT_DEFAULT_JDBC_TYPE_CODE);
this.initializeDefaultJDBCToJavaMappings();
// TODO change this conditional to
// if (JDK16) {
// and uncomment Types in #initializeDefaultJDBCToJavaMappings()
// when we start compiling with jdk1.6
if ( ! JDK16) {
this.checkJDBCTypeToJavaTypeDeclarationMappings();
}
this.initializeDefaultJavaToJDBCMappings();
}
private void checkJDBCTypeToJavaTypeDeclarationMappings() {
// make sure we have mapped ALL the JDBC types
if (this.jdbcTypeToJavaTypeDeclarationMappings.size() != this.jdbcTypes.size()) {
throw new IllegalStateException("the JDBC type mappings were not initialized properly");
}
}
/**
* build up the default JDBC types via reflection
* @see java.sql.Types
*/
private void initializeDefaultJDBCTypes() {
Field[] fields = Types.class.getDeclaredFields();
for (int i = fields.length; i-- > 0; ) {
String name = fields[i].getName();
int code;
try {
code = ((Integer) fields[i].get(null)).intValue();
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex); // shouldn't happen...
}
if (Arrays.binarySearch(DEFAULT_UNUSED_JDBC_TYPE_CODES, code) < 0) {
this.checkJDBCType(name, code); // check for duplicates
this.jdbcTypes.add(new JDBCType(this, name, code));
}
}
}
/**
* hard code the default mappings from the JDBC types to the
* appropriate Java type declarations
* @see java.sql.Types
* see "Getting Started with the JDBC API"
* http://java.sun.com/javase/6/docs/technotes/guides/jdbc/getstart/GettingStartedTOC.fm.html
* or
* http://java.sun.com/j2se/1.4.2/docs/guide/jdbc/getstart/GettingStartedTOC.fm.html
*/
private void initializeDefaultJDBCToJavaMappings() {
this.addJDBCToJavaMapping(Types.ARRAY, java.sql.Array.class);
this.addJDBCToJavaMapping(Types.BIGINT, long.class);
this.addJDBCToJavaMapping(Types.BINARY, byte.class, 1); // byte[]
this.addJDBCToJavaMapping(Types.BIT, boolean.class);
this.addJDBCToJavaMapping(Types.BLOB, java.sql.Blob.class);
this.addJDBCToJavaMapping(Types.BOOLEAN, boolean.class);
this.addJDBCToJavaMapping(Types.CHAR, java.lang.String.class);
this.addJDBCToJavaMapping(Types.CLOB, java.sql.Clob.class);
this.addJDBCToJavaMapping(Types.DATALINK, java.net.URL.class);
this.addJDBCToJavaMapping(Types.DATE, java.sql.Date.class);
this.addJDBCToJavaMapping(Types.DECIMAL, java.math.BigDecimal.class);
this.addJDBCToJavaMapping(Types.DISTINCT, java.lang.Object.class);
this.addJDBCToJavaMapping(Types.DOUBLE, double.class);
this.addJDBCToJavaMapping(Types.FLOAT, double.class);
this.addJDBCToJavaMapping(Types.INTEGER, int.class);
this.addJDBCToJavaMapping(Types.JAVA_OBJECT, java.lang.Object.class);
this.addJDBCToJavaMapping(Types.LONGNVARCHAR, java.lang.String.class); // JDK1.6
this.addJDBCToJavaMapping(Types.LONGVARBINARY, byte.class, 1); // byte[]
this.addJDBCToJavaMapping(Types.LONGVARCHAR, java.lang.String.class);
this.addJDBCToJavaMapping(Types.NCHAR, java.lang.String.class); // JDK1.6
this.addJDBCToJavaMapping(Types.NCLOB, java.sql.NClob.class); // JDK1.6
this.addJDBCToJavaMapping(Types.NVARCHAR, java.lang.String.class); // JDK1.6
// not sure why this is defined in java.sql.Types
this.addJDBCToJavaMapping(Types.NULL, java.lang.Object.class);
this.addJDBCToJavaMapping(Types.NUMERIC, java.math.BigDecimal.class);
this.addJDBCToJavaMapping(Types.OTHER, java.lang.Object.class); // ???
this.addJDBCToJavaMapping(Types.REAL, float.class);
this.addJDBCToJavaMapping(Types.REF, java.sql.Ref.class);
this.addJDBCToJavaMapping(Types.ROWID, java.sql.RowId.class); // JDK1.6
this.addJDBCToJavaMapping(Types.SMALLINT, short.class);
this.addJDBCToJavaMapping(Types.SQLXML, java.sql.SQLXML.class); // JDK1.6
this.addJDBCToJavaMapping(Types.STRUCT, java.sql.Struct.class);
this.addJDBCToJavaMapping(Types.TIME, java.sql.Time.class);
this.addJDBCToJavaMapping(Types.TIMESTAMP, java.sql.Timestamp.class);
this.addJDBCToJavaMapping(Types.TINYINT, byte.class);
this.addJDBCToJavaMapping(Types.VARBINARY, byte.class, 1); // byte[]
this.addJDBCToJavaMapping(Types.VARCHAR, java.lang.String.class);
}
private void addJDBCToJavaMapping(int jdbcTypeCode, Class<?> javaClass) {
this.addJDBCToJavaMapping(jdbcTypeCode, javaClass, 0);
}
private void addJDBCToJavaMapping(int jdbcTypeCode, Class<?> javaClass, int arrayDepth) {
JDBCType jdbcType = this.jdbcTypeForCode(jdbcTypeCode);
this.checkJDBCToJavaMapping(jdbcType); // check for duplicates
this.jdbcTypeToJavaTypeDeclarationMappings.add(new JDBCTypeToJavaTypeDeclarationMapping(this, jdbcType, javaClass.getName(), arrayDepth));
}
/**
* hard code the default mappings from the typical Java type declarations to the
* appropriate JDBC types
* @see java.sql.Types
* see "Getting Started with JDBC API"
* http://java.sun.com/j2se/1.4.2/docs/guide/jdbc/getstart/GettingStartedTOC.fm.html
*/
private void initializeDefaultJavaToJDBCMappings() {
this.addJavaToJDBCMapping(java.lang.String.class, Types.VARCHAR);
this.addJavaToJDBCMapping(boolean.class, Types.BOOLEAN);
this.addJavaToJDBCMapping(java.lang.Boolean.class, Types.BOOLEAN);
this.addJavaToJDBCMapping(char.class, Types.CHAR);
this.addJavaToJDBCMapping(java.lang.Character.class, Types.CHAR);
this.addJavaToJDBCMapping(byte.class, Types.TINYINT);
this.addJavaToJDBCMapping(java.lang.Byte.class, Types.TINYINT);
this.addJavaToJDBCMapping(short.class, Types.SMALLINT);
this.addJavaToJDBCMapping(java.lang.Short.class, Types.SMALLINT);
this.addJavaToJDBCMapping(int.class, Types.INTEGER);
this.addJavaToJDBCMapping(java.lang.Integer.class, Types.INTEGER);
this.addJavaToJDBCMapping(long.class, Types.BIGINT);
this.addJavaToJDBCMapping(java.lang.Long.class, Types.BIGINT);
this.addJavaToJDBCMapping(float.class, Types.REAL);
this.addJavaToJDBCMapping(java.lang.Float.class, Types.REAL);
this.addJavaToJDBCMapping(double.class, Types.DOUBLE);
this.addJavaToJDBCMapping(java.lang.Double.class, Types.DOUBLE);
this.addJavaToJDBCMapping(java.lang.Number.class, Types.NUMERIC);
this.addJavaToJDBCMapping(java.math.BigDecimal.class, Types.NUMERIC);
this.addJavaToJDBCMapping(java.math.BigInteger.class, Types.NUMERIC);
this.addJavaToJDBCMapping(java.util.Date.class, Types.DATE);
this.addJavaToJDBCMapping(java.sql.Date.class, Types.DATE);
this.addJavaToJDBCMapping(java.sql.Time.class, Types.TIME);
this.addJavaToJDBCMapping(java.sql.Timestamp.class, Types.TIMESTAMP);
this.addJavaToJDBCMapping(java.util.Calendar.class, Types.TIMESTAMP);
this.addJavaToJDBCMapping(java.sql.Blob.class, Types.BLOB);
this.addJavaToJDBCMapping(java.sql.Clob.class, Types.CLOB);
this.addJavaToJDBCMapping(java.sql.Array.class, Types.ARRAY);
this.addJavaToJDBCMapping(java.sql.Struct.class, Types.STRUCT);
this.addJavaToJDBCMapping(java.sql.Ref.class, Types.REF);
this.addJavaToJDBCMapping(java.net.URL.class, Types.DATALINK);
this.addJavaToJDBCMapping(byte.class, 1, Types.VARBINARY); // byte[]
this.addJavaToJDBCMapping(char.class, 1, Types.VARCHAR); // char[]
this.addJavaToJDBCMapping(java.lang.Class.class, Types.VARCHAR);
}
private void addJavaToJDBCMapping(Class<?> javaClass, int jdbcTypeCode) {
this.addJavaToJDBCMapping(javaClass, 0, jdbcTypeCode);
}
private void addJavaToJDBCMapping(Class<?> javaClass, int arrayDepth, int jdbcTypeCode) {
String javaClassName = javaClass.getName();
this.checkJavaToJDBCMapping(javaClassName, arrayDepth); // check for duplicates
this.javaTypeDeclarationToJDBCTypeMappings.add(new JavaTypeDeclarationToJDBCTypeMapping(this, javaClassName, arrayDepth, this.jdbcTypeForCode(jdbcTypeCode)));
}
// ********** accessors **********
private DatabasePlatformRepository getPlatformRepository() {
return (DatabasePlatformRepository) this.getParent();
}
public Iterable<JDBCType> jdbcTypes() {
return new LiveCloneIterable<JDBCType>(this.jdbcTypes) {
@Override
protected void remove(JDBCType current) {
JDBCTypeRepository.this.removeJDBCType(current);
}
};
}
public int jdbcTypesSize() {
return this.jdbcTypes.size();
}
public JDBCType addJDBCType(String name, int code) {
this.checkJDBCType(name, code);
return this.addJDBCType(new JDBCType(this, name, code));
}
private JDBCType addJDBCType(JDBCType jdbcType) {
this.addItemToCollection(jdbcType, this.jdbcTypes, JDBC_TYPES_COLLECTION);
this.jdbcTypeAdded(jdbcType);
return jdbcType;
}
private void jdbcTypeAdded(JDBCType addedJDBCType) {
// synchronize the mappings
this.addJDBCTypeToJavaTypeDeclarationMapping(addedJDBCType, java.lang.Object.class.getName(), 0); // hmmm...
// add the new JDBC type to all the platforms' mappings
this.getPlatformRepository().jdbcTypeAdded(addedJDBCType);
}
public void removeJDBCType(JDBCType jdbcType) {
if (jdbcType == this.defaultJDBCType) {
throw new IllegalArgumentException("the default JDBC type may not be removed: " + jdbcType);
}
this.removeItemFromCollection(jdbcType, this.jdbcTypes, JDBC_TYPES_COLLECTION);
this.jdbcTypeRemoved(jdbcType);
}
private void jdbcTypeRemoved(JDBCType removedJDBCType) {
for (Iterator<JDBCTypeToJavaTypeDeclarationMapping> stream = this.jdbcTypeToJavaTypeDeclarationMappings().iterator(); stream.hasNext(); ) {
if (stream.next().getJDBCType() == removedJDBCType) {
stream.remove();
break; // there should be only one...
}
}
for (Iterator<JavaTypeDeclarationToJDBCTypeMapping> stream = this.javaTypeDeclarationToJDBCTypeMappings().iterator(); stream.hasNext(); ) {
JavaTypeDeclarationToJDBCTypeMapping mapping = stream.next();
if (mapping.getJDBCType() == removedJDBCType) {
mapping.setJDBCType(this.defaultJDBCType); // hmmm...
}
}
this.getPlatformRepository().nodeRemoved(removedJDBCType);
}
public void removeJDBCTypes(Collection<JDBCType> types) {
if (types.contains(this.defaultJDBCType)) {
throw new IllegalArgumentException("the default JDBC type may not be removed: " + this.defaultJDBCType);
}
this.removeItemsFromCollection(types, this.jdbcTypes, JDBC_TYPES_COLLECTION);
this.jdbcTypesRemoved(types);
}
private void jdbcTypesRemoved(Collection<JDBCType> removedJDBCTypes) {
for (Iterator<JDBCType> stream = removedJDBCTypes.iterator(); stream.hasNext(); ) {
this.jdbcTypeRemoved(stream.next());
}
}
public void removeJDBCTypes(Iterator<JDBCType> types) {
this.removeJDBCTypes(CollectionTools.collection(types));
}
// default JDBC type
/**
* this will only be null when we have no JDBC types
*/
public JDBCType getDefaultJDBCType() {
return this.defaultJDBCType;
}
/**
* the default cannot be set to null
*/
public void setDefaultJDBCType(JDBCType defaultJDBCType) {
if (defaultJDBCType == null) {
throw new NullPointerException();
}
Object old = this.defaultJDBCType;
this.defaultJDBCType = defaultJDBCType;
this.firePropertyChanged(DEFAULT_JDBC_TYPE_PROPERTY, old, defaultJDBCType);
}
// JDBC => Java mappings
public Iterable<JDBCTypeToJavaTypeDeclarationMapping> jdbcTypeToJavaTypeDeclarationMappings() {
return new LiveCloneIterable<JDBCTypeToJavaTypeDeclarationMapping>(this.jdbcTypeToJavaTypeDeclarationMappings) {
@Override
protected void remove(JDBCTypeToJavaTypeDeclarationMapping current) {
JDBCTypeRepository.this.removeJDBCTypeToJavaTypeDeclarationMapping(current);
}
};
}
public int jdbcTypeToJavaTypeDeclarationMappingsSize() {
return this.jdbcTypeToJavaTypeDeclarationMappings.size();
}
public JDBCTypeToJavaTypeDeclarationMapping addJDBCTypeToJavaTypeDeclarationMapping(JDBCType jdbcType, String javaClassName, int arrayDepth) {
this.checkJDBCToJavaMapping(jdbcType);
return this.addJDBCTypeToJavaTypeDeclarationMapping(new JDBCTypeToJavaTypeDeclarationMapping(this, jdbcType, javaClassName, arrayDepth));
}
private JDBCTypeToJavaTypeDeclarationMapping addJDBCTypeToJavaTypeDeclarationMapping(JDBCTypeToJavaTypeDeclarationMapping mapping) {
this.addItemToCollection(mapping, this.jdbcTypeToJavaTypeDeclarationMappings, JDBC_TYPE_TO_JAVA_TYPE_DECLARATION_MAPPINGS_COLLECTION);
return mapping;
}
public void removeJDBCTypeToJavaTypeDeclarationMapping(JDBCTypeToJavaTypeDeclarationMapping mapping) {
this.removeItemFromCollection(mapping, this.jdbcTypeToJavaTypeDeclarationMappings, JDBC_TYPE_TO_JAVA_TYPE_DECLARATION_MAPPINGS_COLLECTION);
}
public void removeJDBCTypeToJavaTypeDeclarationMappings(Collection<JDBCTypeToJavaTypeDeclarationMapping> mappings) {
this.removeItemsFromCollection(mappings, this.jdbcTypeToJavaTypeDeclarationMappings, JDBC_TYPE_TO_JAVA_TYPE_DECLARATION_MAPPINGS_COLLECTION);
}
public void removeJDBCTypeToJavaTypeDeclarationMappings(Iterator<JDBCTypeToJavaTypeDeclarationMapping> mappings) {
this.removeItemsFromCollection(mappings, this.jdbcTypeToJavaTypeDeclarationMappings, JDBC_TYPE_TO_JAVA_TYPE_DECLARATION_MAPPINGS_COLLECTION);
}
// Java => JDBC mappings
public Iterable<JavaTypeDeclarationToJDBCTypeMapping> javaTypeDeclarationToJDBCTypeMappings() {
return new LiveCloneIterable<JavaTypeDeclarationToJDBCTypeMapping>(this.javaTypeDeclarationToJDBCTypeMappings) {
@Override
protected void remove(JavaTypeDeclarationToJDBCTypeMapping current) {
JDBCTypeRepository.this.removeJavaTypeDeclarationToJDBCTypeMapping(current);
}
};
}
public int javaTypeDeclarationToJDBCTypeMappingsSize() {
return this.javaTypeDeclarationToJDBCTypeMappings.size();
}
public JavaTypeDeclarationToJDBCTypeMapping addJavaTypeDeclarationToJDBCTypeMapping(String javaClassName, int arrayDepth, JDBCType jdbcType) {
this.checkJavaToJDBCMapping(javaClassName, arrayDepth);
return this.addJavaTypeDeclarationToJDBCTypeMapping(new JavaTypeDeclarationToJDBCTypeMapping(this, javaClassName, arrayDepth, jdbcType));
}
private JavaTypeDeclarationToJDBCTypeMapping addJavaTypeDeclarationToJDBCTypeMapping(JavaTypeDeclarationToJDBCTypeMapping mapping) {
this.addItemToCollection(mapping, this.javaTypeDeclarationToJDBCTypeMappings, JAVA_TYPE_DECLARATION_TO_JDBC_TYPE_MAPPINGS_COLLECTION);
return mapping;
}
public void removeJavaTypeDeclarationToJDBCTypeMapping(JavaTypeDeclarationToJDBCTypeMapping mapping) {
this.removeItemFromCollection(mapping, this.javaTypeDeclarationToJDBCTypeMappings, JAVA_TYPE_DECLARATION_TO_JDBC_TYPE_MAPPINGS_COLLECTION);
}
public void removeJavaTypeDeclarationToJDBCTypeMappings(Collection<JavaTypeDeclarationToJDBCTypeMapping> mappings) {
this.removeItemsFromCollection(mappings, this.javaTypeDeclarationToJDBCTypeMappings, JAVA_TYPE_DECLARATION_TO_JDBC_TYPE_MAPPINGS_COLLECTION);
}
public void removeJavaTypeDeclarationToJDBCTypeMappings(Iterator<JavaTypeDeclarationToJDBCTypeMapping> mappings) {
this.removeItemsFromCollection(mappings, this.javaTypeDeclarationToJDBCTypeMappings, JAVA_TYPE_DECLARATION_TO_JDBC_TYPE_MAPPINGS_COLLECTION);
}
// ********** queries **********
/**
* @see java.sql.Types
*/
public JDBCType jdbcTypeForCode(int jdbcTypeCode) {
synchronized (this.jdbcTypes) {
for (Iterator<JDBCType> stream = this.jdbcTypes.iterator(); stream.hasNext(); ) {
JDBCType jdbcType = stream.next();
if (jdbcType.getCode() == jdbcTypeCode) {
return jdbcType;
}
}
throw new IllegalArgumentException("missing JDBC type for code: " + jdbcTypeCode);
}
}
/**
* case sensitive
* @see java.sql.Types
*/
JDBCType jdbcTypeNamed(String jdbcTypeName) {
synchronized (this.jdbcTypes) {
for (Iterator<JDBCType> stream = this.jdbcTypes.iterator(); stream.hasNext(); ) {
JDBCType jdbcType = stream.next();
if (jdbcType.getName().equals(jdbcTypeName)) {
return jdbcType;
}
}
throw new IllegalArgumentException("missing JDBC type named: " + jdbcTypeName);
}
}
/**
* Returns whether the specified JDBC type has a corresponding
* Java type declaration; this is used for validation
*/
private boolean jdbcTypeCanBeMappedToJavaTypeDeclaration(JDBCType jdbcType) {
synchronized (this.jdbcTypeToJavaTypeDeclarationMappings) {
for (Iterator<JDBCTypeToJavaTypeDeclarationMapping> stream = this.jdbcTypeToJavaTypeDeclarationMappings.iterator(); stream.hasNext(); ) {
if (stream.next().maps(jdbcType)) {
return true;
}
}
return false;
}
}
/**
* Returns whether the specified Java type declaration has a corresponding
* JDBC datatype; this can be used to determine whether a
* Java class can be mapped with a direct-to-field mapping
* (or a direct collection mapping), as opposed to needing its
* own descriptor
*/
boolean javaTypeDeclarationCanBeMappedToJDBCType(String javaClassName, int arrayDepth) {
synchronized (this.javaTypeDeclarationToJDBCTypeMappings) {
for (Iterator<JavaTypeDeclarationToJDBCTypeMapping> stream = this.javaTypeDeclarationToJDBCTypeMappings.iterator(); stream.hasNext(); ) {
if (stream.next().maps(javaClassName, arrayDepth)) {
return true;
}
}
return false;
}
}
/**
* Returns the JDBC type for the specified Java type declaration;
* used to generate tables from classes
*/
JDBCType jdbcTypeForJavaTypeDeclaration(String javaClassName, int arrayDepth) {
synchronized (this.javaTypeDeclarationToJDBCTypeMappings) {
for (Iterator<JavaTypeDeclarationToJDBCTypeMapping> stream = this.javaTypeDeclarationToJDBCTypeMappings.iterator(); stream.hasNext(); ) {
JavaTypeDeclarationToJDBCTypeMapping mapping = stream.next();
if (mapping.maps(javaClassName, arrayDepth)) {
return mapping.getJDBCType();
}
}
throw new IllegalArgumentException("missing JDBC type mapping for Java type declaration: " + javaClassName + this.brackets(arrayDepth));
}
}
/**
* Returns the Java type declaration for the specified JDBC type;
* used to generate classes from tables
*/
JavaTypeDeclaration javaTypeDeclarationFor(JDBCType jdbcType) {
synchronized (this.jdbcTypeToJavaTypeDeclarationMappings) {
for (Iterator<JDBCTypeToJavaTypeDeclarationMapping> stream = this.jdbcTypeToJavaTypeDeclarationMappings.iterator(); stream.hasNext(); ) {
JDBCTypeToJavaTypeDeclarationMapping mapping = stream.next();
if (mapping.maps(jdbcType)) {
return mapping.getJavaTypeDeclaration();
}
}
throw new IllegalArgumentException("missing Java type declaration mapping for JDBC type: " + jdbcType);
}
}
/**
* Returns the Java type declaration for the specified JDBC type;
* used to generate classes from tables
*/
JavaTypeDeclaration javaTypeDeclarationForJDBCTypeCode(int jdbcTypeCode) {
return this.javaTypeDeclarationFor(this.jdbcTypeForCode(jdbcTypeCode));
}
private Iterator<String> jdbcTypeNames() {
return new TransformationIterator<JDBCType, String>(this.jdbcTypes()) {
@Override
protected String transform(JDBCType next) {
return next.getName();
}
};
}
// ********** behavior **********
/**
* @see org.eclipse.persistence.tools.workbench.mappingsmodel.AbstractNodeModel#addChildrenTo(java.util.List)
*/
@Override
protected void addChildrenTo(List<org.eclipse.persistence.tools.utility.node.Node> children) {
super.addChildrenTo(children);
synchronized (this.jdbcTypes) { children.addAll(this.jdbcTypes); }
synchronized (this.jdbcTypeToJavaTypeDeclarationMappings) { children.addAll(this.jdbcTypeToJavaTypeDeclarationMappings); }
synchronized (this.javaTypeDeclarationToJDBCTypeMappings) { children.addAll(this.javaTypeDeclarationToJDBCTypeMappings); }
}
/**
* disallow duplicate JDBC type names or codes
*/
private void checkJDBCType(JDBCType jdbcType) {
this.checkJDBCType(jdbcType.getName(), jdbcType.getCode());
}
/**
* disallow duplicate JDBC type names or codes
*/
private void checkJDBCType(String jdbcTypeName, int jdbcTypeCode) {
this.checkJDBCTypeName(jdbcTypeName);
this.checkJDBCTypeCode(jdbcTypeCode);
}
/**
* disallow duplicate JDBC type names
*/
void checkJDBCTypeName(String jdbcTypeName) {
if (jdbcTypeName == null) {
throw new NullPointerException();
}
if (IteratorTools.contains(this.jdbcTypeNames(), jdbcTypeName)) {
throw new IllegalArgumentException("duplicate JDBC type name: " + jdbcTypeName);
}
}
/**
* disallow duplicate JDBC type codes
*/
void checkJDBCTypeCode(int jdbcTypeCode) {
synchronized (this.jdbcTypes) {
for (Iterator<JDBCType> stream = this.jdbcTypes.iterator(); stream.hasNext(); ) {
if (stream.next().getCode() == jdbcTypeCode) {
throw new IllegalArgumentException("duplicate JDBC type code: " + jdbcTypeCode);
}
}
}
}
/**
* disallow duplicate JDBC type mappings
*/
private void checkJDBCToJavaMapping(JDBCType jdbcType) {
if (this.jdbcTypeCanBeMappedToJavaTypeDeclaration(jdbcType)) {
throw new IllegalArgumentException("duplicate mapping: " + jdbcType.getName());
}
}
/**
* disallow duplicate Java type declaration mappings
*/
private void checkJavaToJDBCMapping(String javaClassName, int arrayDepth) {
if (this.javaTypeDeclarationCanBeMappedToJDBCType(javaClassName, arrayDepth)) {
throw new IllegalArgumentException("duplicate mapping: " + javaClassName + this.brackets(arrayDepth));
}
}
/**
* disallow duplicate Java type declaration mappings
*/
private void checkJavaToJDBCMapping(JavaTypeDeclaration javaTypeDeclaration) {
this.checkJavaToJDBCMapping(javaTypeDeclaration.getJavaClassName(), javaTypeDeclaration.getArrayDepth());
}
// ********** i/o **********
private void read(Node node) throws CorruptXMLException {
if (node == null) {
throw new CorruptXMLException("missing node");
}
this.readJDBCTypeNodes(XMLTools.getChild(node, "jdbc-types"));
if (this.jdbcTypes.isEmpty()) {
throw new CorruptXMLException("the JDBC type repository is empty");
}
String defaultJDBCTypeName = XMLTools.getChildTextContent(node, "default-jdbc-type", null);
try {
this.defaultJDBCType = this.jdbcTypeNamed(defaultJDBCTypeName);
} catch (IllegalArgumentException ex) {
throw new CorruptXMLException("default JDBC type", ex);
}
this.readJDBCToJavaMappingNodes(XMLTools.getChild(node, "jdbc-type-to-java-type-declaration-mappings"));
// make sure we have mapped ALL the JDBC types (there are no duplicates at this point)
if (this.jdbcTypeToJavaTypeDeclarationMappings.size() != this.jdbcTypes.size()) {
throw new CorruptXMLException("all the JDBC types must be mapped to Java type declarations");
}
this.readJavaToJDBCMappingNodes(XMLTools.getChild(node, "java-type-declaration-to-jdbc-type-mappings"));
}
private void readJDBCTypeNodes(Node jdbcTypesNode) throws CorruptXMLException {
for (Node jdbcTypeNode : XMLTools.getChildren(jdbcTypesNode, "jdbc-type")) {
JDBCType jdbcType = new JDBCType(this, jdbcTypeNode);
try {
this.checkJDBCType(jdbcType); // check for duplicates
} catch (IllegalArgumentException ex) {
throw new CorruptXMLException(ex);
}
this.jdbcTypes.add(jdbcType);
}
}
private void readJDBCToJavaMappingNodes(Node mappingsNode) throws CorruptXMLException {
for (Node mappingNode : XMLTools.getChildren(mappingsNode, "jdbc-type-to-java-type-declaration-mapping")) {
JDBCTypeToJavaTypeDeclarationMapping mapping = new JDBCTypeToJavaTypeDeclarationMapping(this, mappingNode);
try {
this.checkJDBCToJavaMapping(mapping.getJDBCType()); // check for duplicates
} catch (IllegalArgumentException ex) {
throw new CorruptXMLException(ex);
}
this.jdbcTypeToJavaTypeDeclarationMappings.add(mapping);
}
}
private void readJavaToJDBCMappingNodes(Node mappingsNode) throws CorruptXMLException {
for (Node mappingNode : XMLTools.getChildren(mappingsNode, "java-type-declaration-to-jdbc-type-mapping")) {
JavaTypeDeclarationToJDBCTypeMapping mapping = new JavaTypeDeclarationToJDBCTypeMapping(this, mappingNode);
try {
this.checkJavaToJDBCMapping(mapping.getJavaTypeDeclaration()); // check for duplicates
} catch (IllegalArgumentException ex) {
throw new CorruptXMLException(ex);
}
this.javaTypeDeclarationToJDBCTypeMappings.add(mapping);
}
}
void write(Node node) {
Document document = node.getOwnerDocument();
XMLTools.addSimpleTextNode(node, "default-jdbc-type", this.defaultJDBCType.getName());
this.writeJDBCTypeNodes(node.appendChild(document.createElement("jdbc-types")));
this.writeJDBCToJavaMappingNodes(node.appendChild(document.createElement("jdbc-type-to-java-type-declaration-mappings")));
this.writeJavaToJDBCMappingNodes(node.appendChild(document.createElement("java-type-declaration-to-jdbc-type-mappings")));
}
private void writeJDBCTypeNodes(Node jdbcTypesNode) {
Document document = jdbcTypesNode.getOwnerDocument();
synchronized (this.jdbcTypes) {
for (Iterator<JDBCType> stream = new TreeSet<JDBCType>(this.jdbcTypes).iterator(); stream.hasNext(); ) {
Node jdbcTypeNode = document.createElement("jdbc-type");
jdbcTypesNode.appendChild(jdbcTypeNode);
stream.next().write(jdbcTypeNode);
}
}
}
private void writeJDBCToJavaMappingNodes(Node mappingsNode) {
Document document = mappingsNode.getOwnerDocument();
synchronized (this.jdbcTypeToJavaTypeDeclarationMappings) {
for (Iterator<JDBCTypeToJavaTypeDeclarationMapping> stream = new TreeSet<JDBCTypeToJavaTypeDeclarationMapping>(this.jdbcTypeToJavaTypeDeclarationMappings).iterator(); stream.hasNext(); ) {
Node mappingNode = document.createElement("jdbc-type-to-java-type-declaration-mapping");
mappingsNode.appendChild(mappingNode);
stream.next().write(mappingNode);
}
}
}
private void writeJavaToJDBCMappingNodes(Node mappingsNode) {
Document document = mappingsNode.getOwnerDocument();
synchronized (this.javaTypeDeclarationToJDBCTypeMappings) {
for (Iterator<JavaTypeDeclarationToJDBCTypeMapping> stream = new TreeSet<JavaTypeDeclarationToJDBCTypeMapping>(this.javaTypeDeclarationToJDBCTypeMappings).iterator(); stream.hasNext(); ) {
Node mappingNode = document.createElement("java-type-declaration-to-jdbc-type-mapping");
mappingsNode.appendChild(mappingNode);
stream.next().write(mappingNode);
}
}
}
// ********** printing and displaying **********
private String brackets(int arrayDepth) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < arrayDepth; i++) {
sb.append("[]");
}
return sb.toString();
}
@Override
public String displayString() {
return StringTools.EMPTY_STRING;
}
public void toString(StringBuffer sb) {
sb.append(this.jdbcTypes.size() + " JDBC types");
}
}