/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.ejb.plugins.cmp.jdbc.metadata;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Collection;
import org.jboss.deployment.DeploymentException;
import org.jboss.metadata.MetaData;
import org.w3c.dom.Element;
/**
* Imutable class which holds a map between Java Classes and JDBCMappingMetaData.
*
* @author <a href="mailto:dain@daingroup.com">Dain Sundstrom</a>
* @author <a href="sebastien.alborini@m4x.org">Sebastien Alborini</a>
* @author <a href="mailto:loubyansky@ua.fm">Alex Loubyansky</a>
* @version $Revision: 81030 $
*/
public final class JDBCTypeMappingMetaData
{
private static final String[] PRIMITIVES = {
"boolean", "byte", "char", "short", "int", "long", "float", "double"
};
private static final String[] PRIMITIVE_CLASSES = {
"java.lang.Boolean", "java.lang.Byte", "java.lang.Character",
"java.lang.Short", "java.lang.Integer", "java.lang.Long",
"java.lang.Float", "java.lang.Double"
};
public static final String CONCAT = "concat";
public static final String SUBSTRING = "substring";
public static final String LCASE = "lcase";
public static final String UCASE = "ucase";
public static final String LENGTH = "length";
public static final String LOCATE = "locate";
public static final String ABS = "abs";
public static final String SQRT = "sqrt";
public static final String COUNT = "count";
public static final String MOD = "mod";
public static JDBCFunctionMappingMetaData COUNT_FUNC;
public static JDBCFunctionMappingMetaData MAX_FUNC;
public static JDBCFunctionMappingMetaData MIN_FUNC;
public static JDBCFunctionMappingMetaData AVG_FUNC;
public static JDBCFunctionMappingMetaData SUM_FUNC;
static
{
try
{
COUNT_FUNC = new JDBCFunctionMappingMetaData("count", "count(?1 ?2)");
MAX_FUNC = new JDBCFunctionMappingMetaData("max", "max(?1 ?2)");
MIN_FUNC = new JDBCFunctionMappingMetaData("min", "min(?1 ?2)");
AVG_FUNC = new JDBCFunctionMappingMetaData("avg", "avg(?1 ?2)");
SUM_FUNC = new JDBCFunctionMappingMetaData("sum", "sum(?1 ?2)");
}
catch(DeploymentException e)
{
throw new IllegalStateException(e.getMessage());
}
}
private final String name;
private final HashMap mappings = new HashMap();
private final HashMap functionMappings = new HashMap();
private final String aliasHeaderPrefix;
private final String aliasHeaderSuffix;
private final int aliasMaxLength;
private final boolean subquerySupported;
private final String trueMapping;
private final String falseMapping;
private final int maxKeysInDelete;
private JDBCFunctionMappingMetaData rowLocking = null;
private JDBCFunctionMappingMetaData fkConstraint = null;
private JDBCFunctionMappingMetaData pkConstraint = null;
private JDBCFunctionMappingMetaData autoIncrement = null;
private JDBCFunctionMappingMetaData addColumn = null;
private JDBCFunctionMappingMetaData dropColumn = null;
private JDBCFunctionMappingMetaData alterColumn = null;
/**
* Constructs a mapping with the data contained in the type-mapping xml
* element from a jbosscmp-jdbc xml file.
*
* @param element the xml Element which contains the metadata about
* this type mapping
* @throws DeploymentException if the xml element is not semantically correct
*/
public JDBCTypeMappingMetaData(Element element) throws DeploymentException
{
// get the name of this type-mapping
name = MetaData.getUniqueChildContent(element, "name");
// row-locking (i.e., select for update)
String rowLockingSQL = MetaData.getUniqueChildContent(element, "row-locking-template");
if(rowLockingSQL != null && !rowLockingSQL.trim().equals(""))
{
rowLocking = new JDBCFunctionMappingMetaData("row-locking", rowLockingSQL);
}
// pk constraint
String pkConstraintSQL = MetaData.getUniqueChildContent(element, "pk-constraint-template");
if(pkConstraintSQL != null && !pkConstraintSQL.trim().equals(""))
{
pkConstraint = new JDBCFunctionMappingMetaData("pk-constraint", pkConstraintSQL);
}
// fk constraint
String fkConstraintSQL = MetaData.getUniqueChildContent(element, "fk-constraint-template");
if(fkConstraintSQL != null && !fkConstraintSQL.trim().equals(""))
{
fkConstraint = new JDBCFunctionMappingMetaData("fk-constraint", fkConstraintSQL);
}
// alter table templates
String alterColumnSQL = MetaData.getOptionalChildContent(element, "add-column-template");
if(alterColumnSQL != null && !alterColumnSQL.trim().equals(""))
{
addColumn = new JDBCFunctionMappingMetaData("add-column-template", alterColumnSQL);
}
else
{
addColumn = new JDBCFunctionMappingMetaData("add-column-template", "ALTER TABLE ?1 ADD ?2 ?3");
}
alterColumnSQL = MetaData.getOptionalChildContent(element, "alter-column-template");
if(alterColumnSQL != null && !alterColumnSQL.trim().equals(""))
{
alterColumn = new JDBCFunctionMappingMetaData("alter-column-template", alterColumnSQL);
}
else
{
alterColumn = new JDBCFunctionMappingMetaData("alter-column-template", "ALTER TABLE ?1 ALTER ?2 TYPE ?3");
}
alterColumnSQL = MetaData.getOptionalChildContent(element, "drop-column-template");
if(alterColumnSQL != null && !alterColumnSQL.trim().equals(""))
{
dropColumn = new JDBCFunctionMappingMetaData("drop-column-template", alterColumnSQL);
}
else
{
dropColumn = new JDBCFunctionMappingMetaData("drop-column-template", "ALTER TABLE ?1 DROP ?2");
}
// auto increment
// WARN: it's optional
String autoIncrementSQL = MetaData.getOptionalChildContent(element, "auto-increment-template");
if(autoIncrementSQL != null && !autoIncrementSQL.trim().equals(""))
{
autoIncrement = new JDBCFunctionMappingMetaData("auto-increment", autoIncrementSQL);
}
// get the mappings
Iterator iterator = MetaData.getChildrenByTagName(element, "mapping");
while(iterator.hasNext())
{
Element mappingElement = (Element)iterator.next();
JDBCMappingMetaData mapping = new JDBCMappingMetaData(mappingElement);
mappings.put(mapping.getJavaType(), mapping);
}
addDefaultFunctionMapping();
// get the mappings
Iterator functions = MetaData.getChildrenByTagName(element, "function-mapping");
while(functions.hasNext())
{
Element mappingElement = (Element)functions.next();
JDBCFunctionMappingMetaData functionMapping = new JDBCFunctionMappingMetaData(mappingElement);
functionMappings.put(functionMapping.getFunctionName().toLowerCase(), functionMapping);
}
aliasHeaderPrefix = MetaData.getUniqueChildContent(element, "alias-header-prefix");
aliasHeaderSuffix = MetaData.getUniqueChildContent(element, "alias-header-suffix");
String aliasMaxLengthString = MetaData.getUniqueChildContent(element, "alias-max-length");
try
{
aliasMaxLength = Integer.parseInt(aliasMaxLengthString);
}
catch(NumberFormatException e)
{
throw new DeploymentException("Invalid number format in " +
"alias-max-length " + aliasMaxLengthString + "': " + e);
}
String subquerySupportedStr = MetaData.getUniqueChildContent(element, "subquery-supported");
subquerySupported = Boolean.valueOf(subquerySupportedStr).booleanValue();
trueMapping = MetaData.getUniqueChildContent(element, "true-mapping");
falseMapping = MetaData.getUniqueChildContent(element, "false-mapping");
String str = MetaData.getOptionalChildContent(element, "max-keys-in-delete");
if(str != null)
{
try
{
maxKeysInDelete = Integer.parseInt(str);
}
catch(NumberFormatException e)
{
throw new DeploymentException("Failed to parse int value '" + str + "' for max-keys-in-delete", e);
}
if(maxKeysInDelete < 0)
{
throw new DeploymentException("The value of max-keys-in-delete cannot be less than 0: " + maxKeysInDelete);
}
}
else
{
maxKeysInDelete = 0;
}
}
/**
* Gets the name of this mapping. The mapping name used to differentiate this
* mapping from other mappings and the mapping the application used is
* retrieved by name.
* @return the name of this mapping.
*/
public String getName()
{
return name;
}
/**
* Gets the prefix for that is used when generating an alias header. An
* alias header is prepended to a generated table alias to prevent name
* collisions. An alias header is constructed as folows:
* aliasHeaderPrefix + int_counter + aliasHeaderSuffix
*
* @return the prefix for alias headers
*/
public String getAliasHeaderPrefix()
{
return aliasHeaderPrefix;
}
/**
* Gets the suffix for that is used when generating an alias header. An
* alias header is prepended to a generated table alias to prevent name
* collisions. An alias header is constructed as folows:
* aliasHeaderPrefix + int_counter + aliasHeaderSuffix
*
* @return the suffix for alias headers
*/
public String getAliasHeaderSuffix()
{
return aliasHeaderSuffix;
}
/**
* Gets maximum length of a table alias.
* An alias is constructed as folows: aliasHeader + ejb_ql_identifier_path
* @return the maximum length that a table alias can be
*/
public int getAliasMaxLength()
{
return aliasMaxLength;
}
/**
* Does this type mapping support subqueries?
*/
public boolean isSubquerySupported()
{
return subquerySupported;
}
/**
* Gets the value to which the boolean true value in EJB-QL will be mapped.
*/
public String getTrueMapping()
{
return trueMapping;
}
/**
* Gets the value to which the boolean false value in EJB-QL will be mapped.
*/
public String getFalseMapping()
{
return falseMapping;
}
public int getMaxKeysInDelete()
{
return maxKeysInDelete;
}
public JDBCMappingMetaData getTypeMappingMetaData(Class type)
{
String javaType = type.getName();
// Check primitive first
for(int i = 0; i < PRIMITIVES.length; i++)
{
if(javaType.equals(PRIMITIVES[i]))
{
// Translate into class
javaType = PRIMITIVE_CLASSES[i];
break;
}
}
// Check other types
JDBCMappingMetaData mapping = (JDBCMappingMetaData)mappings.get(javaType);
// if not found, return mapping for java.lang.object
if(mapping == null)
{
mapping = (JDBCMappingMetaData)mappings.get("java.lang.Object");
}
return mapping;
}
public JDBCFunctionMappingMetaData getFunctionMapping(String name)
{
JDBCFunctionMappingMetaData funcMapping = (JDBCFunctionMappingMetaData)functionMappings.get(name.toLowerCase());
if(funcMapping == null)
throw new IllegalStateException("Function " + name + " is not defined for " + this.name);
return funcMapping;
}
/**
* Returns rowLocking SQL template.
*/
public JDBCFunctionMappingMetaData getRowLockingTemplate()
{
return rowLocking;
}
/**
* Returns pk constraint SQL template.
*/
public JDBCFunctionMappingMetaData getPkConstraintTemplate()
{
return pkConstraint;
}
/**
* Returns fk constraint SQL template.
*/
public JDBCFunctionMappingMetaData getFkConstraintTemplate()
{
return fkConstraint;
}
/**
* Returns auto increment SQL template.
*/
public JDBCFunctionMappingMetaData getAutoIncrementTemplate()
{
return autoIncrement;
}
/**
* Returns add column SQL template.
*/
public JDBCFunctionMappingMetaData getAddColumnTemplate()
{
return addColumn;
}
/**
* Returns auto increment SQL template.
*/
public JDBCFunctionMappingMetaData getDropColumnTemplate()
{
return dropColumn;
}
/**
* Returns auto increment SQL template.
*/
public JDBCFunctionMappingMetaData getAlterColumnTemplate()
{
return alterColumn;
}
public Collection getMappings()
{
return mappings.values();
}
private void addDefaultFunctionMapping() throws DeploymentException
{
JDBCFunctionMappingMetaData function;
// concat
function = new JDBCFunctionMappingMetaData("concat",
new String[]{
"{fn concat(",
", ",
")}"
},
new int[]{0, 1});
functionMappings.put(function.getFunctionName().toLowerCase(), function);
// substring
function = new JDBCFunctionMappingMetaData("substring",
new String[]{
"{fn substring(",
", ",
", ",
")}"
},
new int[]{0, 1, 2});
functionMappings.put(function.getFunctionName().toLowerCase(), function);
// lcase
function = new JDBCFunctionMappingMetaData("lcase",
new String[]{
"{fn lcase(",
")}"
},
new int[]{0});
functionMappings.put(function.getFunctionName().toLowerCase(), function);
// ucase
function = new JDBCFunctionMappingMetaData("ucase",
new String[]{
"{fn ucase(",
")}"
},
new int[]{0});
functionMappings.put(function.getFunctionName().toLowerCase(), function);
// length
function = new JDBCFunctionMappingMetaData("length",
new String[]{
"{fn length(",
")}"
},
new int[]{0});
functionMappings.put(function.getFunctionName().toLowerCase(), function);
// locate
function = new JDBCFunctionMappingMetaData("locate",
new String[]{
"{fn locate(",
", ",
", ",
")}"
},
new int[]{0, 1, 2});
functionMappings.put(function.getFunctionName().toLowerCase(), function);
// abs
function = new JDBCFunctionMappingMetaData("abs",
new String[]{
"{fn abs(",
")}"
},
new int[]{0});
functionMappings.put(function.getFunctionName().toLowerCase(), function);
// sqrt
function = new JDBCFunctionMappingMetaData("sqrt",
new String[]{
"{fn sqrt(",
")}"
},
new int[]{0});
functionMappings.put(function.getFunctionName().toLowerCase(), function);
// mod
function = new JDBCFunctionMappingMetaData("mod", "mod(?1, ?2)");
functionMappings.put(function.getFunctionName().toLowerCase(), function);
}
}