/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
/*
* TypeSupport.java
*
* Created on November 22, 2001
*/
package com.sun.jdo.spi.persistence.support.ejb.ejbqlc;
import java.util.*;
import org.glassfish.persistence.common.I18NHelper;
import com.sun.jdo.api.persistence.model.Model;
import com.sun.jdo.api.persistence.model.jdo.*;
import com.sun.jdo.spi.persistence.support.ejb.model.util.NameMapper;
/**
* Helper class to support type info access.
* A type info is statically an object, internally the helper uses the type name
* as type info. The helper uses a model instance to access meta model info and
* uses a NameMapper to map EJB names to JDO names and vice versa.
*
* @author Michael Bouschen
* @author Shing Wai Chan
*/
public class TypeSupport
{
/** Represents the internal error type. */
public static final Object errorType = "error";
/** Represents the primitive type boolean. */
public static final Object booleanType = "boolean";
/** Represents the primitive type byte. */
public static final Object byteType = "byte";
/** Represents the primitive type short. */
public static final Object shortType = "short";
/** Represents the primitive type char. */
public static final Object charType = "char";
/** Represents the primitive type int. */
public static final Object intType = "int";
/** Represents the primitive type long. */
public static final Object longType = "long";
/** Represents the primitive type float. */
public static final Object floatType = "float";
/** Represents the primitive type double. */
public static final Object doubleType = "double";
/** Represents the wrapper class type boolean. */
public static final Object booleanClassType = "java.lang.Boolean";
/** Represents the wrapper class type byte. */
public static final Object byteClassType = "java.lang.Byte";
/** Represents the wrapper class type short. */
public static final Object shortClassType = "java.lang.Short";
/** Represents the wrapper class type char. */
public static final Object characterClassType = "java.lang.Character";
/** Represents the wrapper class type int. */
public static final Object integerClassType = "java.lang.Integer";
/** Represents the wrapper class type long. */
public static final Object longClassType = "java.lang.Long";
/** Represents the wrapper class type float. */
public static final Object floatClassType = "java.lang.Float";
/** Represents the wrapper class type double. */
public static final Object doubleClassType = "java.lang.Double";
/** Represents the type java.lang.String. */
public static final Object stringType = "java.lang.String";
/** Represents the type java.math.BigDecimal. */
public static final Object bigDecimalType = "java.math.BigDecimal";
/** Represents the type java.math.BigInteger. */
public static final Object bigIntegerType = "java.math.BigInteger";
/** Set of names of numeric types. */
protected static final Set numericTypes = new HashSet();
/** Set of names of numeric wrapper classes. */
protected static final Set numericWrapperTypes = new HashSet();
/** Set of names of date and time types. */
protected static final Set dateTimeTypes = new HashSet();
/** Meta data access. */
protected Model model;
/** Name mapping EJB <-> JDO. */
protected NameMapper nameMapper;
/** I18N support. */
protected final static ResourceBundle msgs = I18NHelper.loadBundle(
TypeSupport.class);
/** Inilialize static fields numericTypes numericWrapperTypes. */
static
{
numericTypes.add(byteType);
numericTypes.add(shortType);
numericTypes.add(charType);
numericTypes.add(intType);
numericTypes.add(longType);
numericTypes.add(floatType);
numericTypes.add(doubleType);
numericWrapperTypes.add(byteClassType);
numericWrapperTypes.add(shortClassType);
numericWrapperTypes.add(characterClassType);
numericWrapperTypes.add(integerClassType);
numericWrapperTypes.add(longClassType);
numericWrapperTypes.add(floatClassType);
numericWrapperTypes.add(doubleClassType);
dateTimeTypes.add("java.util.Date"); //NOI18N
dateTimeTypes.add("java.sql.Date"); //NOI18N
dateTimeTypes.add("java.sql.Time"); //NOI18N
dateTimeTypes.add("java.sql.Timestamp"); //NOI18N
}
/**
* Creates a new TypeSupport using the specified model instance to
* access meta data and the specified nameMapper for EJB <-> JDO
* name mapping.
*/
public TypeSupport(Model model, NameMapper nameMapper)
{
this.model = model;
this.nameMapper = nameMapper;
}
/**
* The method returns a type info by type name.
* If the type name denotes a class the name should be fully qualified.
* The method uses the type name as type info.
*/
public Object getTypeInfo(String name)
{
return name;
}
/**
* The method returns a type info by type name by class object.
*/
public Object getTypeInfo(Class clazz)
{
return getTypeInfo(clazz.getName());
}
/**
* Returns <code>true</code> if type denotes the error type.
*/
public static boolean isErrorType(Object type)
{
return type.equals(errorType);
}
/**
* Returns <code>true</code> if type is boolean or java.lang.Boolean
*/
public static boolean isBooleanType(Object type)
{
return type.equals(booleanType) ||
type.equals(booleanClassType);
}
/**
* Returns <code>true</code> if type is char or java.lang.Character
*/
public static boolean isCharType(Object type)
{
return type.equals(charType) ||
type.equals(characterClassType);
}
/**
* Returns <code>true</code> if type is int or java.lang.Integer
*/
public static boolean isIntType(Object type)
{
return type.equals(intType) ||
type.equals(integerClassType);
}
/**
* Returns <code>true</code> if type is double or java.lang.Double.
*/
public static boolean isDoubleType(Object type)
{
return type.equals(doubleType) ||
type.equals(doubleClassType);
}
/**
* Returns <code>true</code> if type is a primitive numeric type such as
* byte, int etc.
*/
public static boolean isNumericType(Object type)
{
return numericTypes.contains(type);
}
/**
* Returns <code>true</code> if type is a wrapper class of a primitive
* numeric type such as java.lang.Byte, java.lang.Integer etc.
*/
public static boolean isNumericWrapperType(Object type)
{
return numericWrapperTypes.contains(type);
}
/**
* Returns <code>true</code> if type is a NumerType, which means it is either
* a numeric primitive or a numeric wrapper class.
*/
public static boolean isNumberType(Object type)
{
return isNumericType(type) ||
isNumericWrapperType(type) ||
bigDecimalType.equals(type) ||
bigIntegerType.equals(type);
}
/**
* Returns <code>true</code> if type is a floating point type or
* wrapper class of a floating point type.
*/
public static boolean isFloatingPointType(Object type)
{
return doubleType.equals(type) ||
doubleClassType.equals(type) ||
floatType.equals(type) ||
floatClassType.equals(type);
}
/** Returns <code>true</code> if type denotes java.lang.String. */
public static boolean isStringType(Object type)
{
return type.equals(stringType);
}
/** Returns <code>true</code> if type is a collection type. */
public boolean isCollectionType(Object type)
{
return model.isCollection((String)type);
}
/** Returns <code>true</code> if type is a date or time type */
public boolean isDateTimeType(Object type)
{
return dateTimeTypes.contains(getTypeName(type));
}
/** Returns <code>true</code> if type is an orderable type */
public boolean isOrderableType(Object type)
{
return isNumberType(type) || isDateTimeType(type) || isStringType(type);
}
/**
* Returns the type info for a primitive type. The method returns
* {@link #errorType} if the specified type is not a primitive type.
*/
public static Object getPrimitiveType(Object type)
{
Object result = errorType;
if (type.equals(booleanClassType))
result = booleanType;
else if (type.equals(integerClassType))
result = intType;
else if (type.equals(longClassType))
result = longType;
else if (type.equals(floatClassType))
result = floatType;
else if (type.equals(doubleClassType))
result = doubleType;
else if (type.equals(byteClassType))
result = byteType;
else if (type.equals(shortClassType))
result = shortType;
else if (type.equals(characterClassType))
result = charType;
return result;
}
/**
* Returns the type info for a wrapper class type. The method returns
* {@link #errorType} if the specified type is not a wrapper class type.
*/
public static Object getWrapperType(Object type)
{
Object result = errorType;
if (type.equals(booleanType))
result = booleanClassType;
else if (type.equals(intType))
result = integerClassType;
else if (type.equals(longType))
result = longClassType;
else if (type.equals(floatType))
result = floatClassType;
else if (type.equals(doubleType))
result = doubleClassType;
else if (type.equals(byteType))
result = byteClassType;
else if (type.equals(shortType))
result = shortClassType;
else if (type.equals(charType))
result = characterClassType;
return result;
}
/**
* Implements binary numeric promotion as defined in the
* Java Language Specification section 5.6.2
*/
public static Object binaryNumericPromotion(Object left, Object right)
{
if (isNumericType(left) && isNumericType(right)) {
if (left.equals(doubleType) || right.equals(doubleType))
return doubleType;
else if (left.equals(floatType) || right.equals(floatType))
return floatType;
else if (left.equals(longType) || right.equals(longType))
return longType;
else
return intType;
}
return errorType;
}
/**
* Implements unray numeric promotion as defined in the
* Java Language Specification section 5.6.1
*/
public static Object unaryNumericPromotion(Object type)
{
if (isNumericType(type)) {
if (type.equals(byteType) || type.equals(shortType) ||
type.equals(charType)) {
return intType;
}
else {
return type;
}
}
return errorType;
}
/**
* Implements type compatibility. The method returns <code>true</code>
* if left is compatible with right. This is equivalent to
* rightClass.isAssignableFrom(leftClass).
* Note, the method does not support inheritance.
*/
public boolean isCompatibleWith(Object left, Object right)
{
String leftTypeName = getTypeName(left);
String rightTypeName = getTypeName(right);
if (nameMapper.isLocalInterface(leftTypeName) &&
nameMapper.isEjbName(rightTypeName))
rightTypeName = nameMapper.getLocalInterfaceForEjbName(rightTypeName);
else if (nameMapper.isRemoteInterface(leftTypeName) &&
nameMapper.isEjbName(rightTypeName))
rightTypeName = nameMapper.getRemoteInterfaceForEjbName(rightTypeName);
else if (nameMapper.isLocalInterface(rightTypeName) &&
nameMapper.isEjbName(leftTypeName))
leftTypeName = nameMapper.getLocalInterfaceForEjbName(leftTypeName);
else if (nameMapper.isRemoteInterface(rightTypeName) &&
nameMapper.isEjbName(leftTypeName))
leftTypeName = nameMapper.getRemoteInterfaceForEjbName(leftTypeName);
// does not handle inheritance!
return leftTypeName.equals(rightTypeName);
}
/** Returns the type name for a specified type info. */
public static String getTypeName(Object type)
{
return (String)type;
}
/** Returns the typeInfo (the ejb name) for the specified abstract schema. */
public Object getTypeInfoForAbstractSchema(String abstractSchema)
{
return nameMapper.getEjbNameForAbstractSchema(abstractSchema);
}
/** Returns the typeInfo (the ejb name) for the specified abstract schema. */
public String getAbstractSchemaForTypeInfo(Object typeInfo)
{
String typeName = getTypeName(typeInfo);
return nameMapper.isEjbName(typeName) ?
nameMapper.getAbstractSchemaForEjbName(typeName) :
typeName;
}
/** Returns the type info for the type of the given field. */
public Object getFieldType(Object typeInfo, String fieldName)
{
String typeName = getTypeName(typeInfo);
if (!nameMapper.isEjbName(typeName)) {
ErrorMsg.fatal(I18NHelper.getMessage(
msgs, "ERR_EjbNameExpected", //NOI18N
"TypeSupport.getFieldType", typeName)); //NOI18N
}
String fieldType = model.getFieldType(typeName, fieldName);
// check for local or remote interface, map to ejb name
if (nameMapper.isLocalInterface(fieldType)) {
fieldType = nameMapper.getEjbNameForLocalInterface(
typeName, fieldName, fieldType);
}
else if (nameMapper.isRemoteInterface(fieldType)) {
fieldType = nameMapper.getEjbNameForRemoteInterface(
typeName, fieldName, fieldType);
}
return getTypeInfo(fieldType);
}
/**
* Returns the field info for the specified field of the specified type.
* The field info is opaque for the caller. Methods {@link #isRelationship}
* and {@link #getElementType} allow to get details for a given field info.
*/
public Object getFieldInfo(Object typeInfo, String fieldName)
{
Object fieldInfo = null;
String typeName = getTypeName(typeInfo);
if (!nameMapper.isEjbName(typeName)) {
ErrorMsg.fatal(I18NHelper.getMessage(
msgs, "ERR__EjbNameExpected", //NOI18N
"TypeSupport.getFieldInfo", typeName)); //NOI18N
}
String pcClassName = nameMapper.getPersistenceClassForEjbName(typeName);
String pcFieldName = nameMapper.getPersistenceFieldForEjbField(
typeName, fieldName);
PersistenceClassElement pce = model.getPersistenceClass(pcClassName);
if (pce != null) {
fieldInfo = pce.getField(pcFieldName);
}
return fieldInfo;
}
/**
* Returns <code>true</code> if the specified field info denotes a
* relationship field.
*/
public boolean isRelationship(Object fieldInfo)
{
return (fieldInfo != null) && (fieldInfo instanceof RelationshipElement);
}
/**
* Returns the type info of the element type if the specified field info
* denotes a collection relationship. Otherwise it returns <code>null</code>.
*/
public Object getElementType(Object fieldInfo)
{
if ((fieldInfo != null) && (fieldInfo instanceof RelationshipElement)) {
String elementClass = ((RelationshipElement)fieldInfo).getElementClass();
return nameMapper.getEjbNameForPersistenceClass(elementClass);
}
else
return null;
}
/**
* Gets the name of the persistence-capable class which corresponds to
* the specified typeInfo (assuming an ejb name). The method returs the
* type name of the specified typeInfo, it the typeInfo does not denote
* an ejb-name (e.g. a local or remote interface).
*/
public String getPCForTypeInfo(Object typeInfo)
{
String typeName = getTypeName(typeInfo);
String pcClassName =
nameMapper.getPersistenceClassForEjbName(typeName);
return (pcClassName != null) ? pcClassName : typeName;
}
/**
* Returns <code>true</code> if the specified type info denotes an ejb name.
*/
public boolean isEjbName(Object typeInfo)
{
return nameMapper.isEjbName(getTypeName(typeInfo));
}
/**
* Returns <code>true</code> if the specified type info denotes an ejb name
* or the name of a local interface or the name of a remote interface.
*/
public boolean isEjbOrInterfaceName(Object typeInfo)
{
String typeName = getTypeName(typeInfo);
return nameMapper.isEjbName(typeName) ||
nameMapper.isLocalInterface(typeName) ||
nameMapper.isRemoteInterface(typeName);
}
/**
* Returns <code>true</code> if the specified type info denotes the
* remote interface of the bean with the specified ejb name.
*/
public boolean isRemoteInterfaceOfEjb(Object typeInfo, String ejbName)
{
String typeName = getTypeName(typeInfo);
String remoteInterface = nameMapper.getRemoteInterfaceForEjbName(ejbName);
return (remoteInterface != null) && remoteInterface.equals(typeName);
}
/**
* Returns <code>true</code> if the specified type info denotes the
* local interface of the bean with the specified ejb name.
*/
public boolean isLocalInterfaceOfEjb(Object typeInfo, String ejbName)
{
String typeName = getTypeName(typeInfo);
String localInterface = nameMapper.getLocalInterfaceForEjbName(ejbName);
return (localInterface != null) && localInterface.equals(typeName);
}
/**
* Returns <code>true</code> if the specified type info denotes
* a remote interface.
*/
public boolean isRemoteInterface(Object typeInfo)
{
return nameMapper.isRemoteInterface(getTypeName(typeInfo));
}
/**
* Returns <code>true</code> if the specified type info denotes
* a local interface.
*/
public boolean isLocalInterface(Object typeInfo)
{
return nameMapper.isLocalInterface(getTypeName(typeInfo));
}
/**
* Returns <code>true</code> if the bean with the specified ejb name
* has a remote interface.
*/
public boolean hasRemoteInterface(Object typeInfo)
{
return nameMapper.getRemoteInterfaceForEjbName(
getTypeName(typeInfo)) != null;
}
/**
* Returns <code>true</code> if the bean with the specified ejb name
* has a local interface.
*/
public boolean hasLocalInterface(Object typeInfo)
{
return nameMapper.getLocalInterfaceForEjbName(
getTypeName(typeInfo)) != null;
}
/**
* Return JDO QL return type for Sum function for a given type.
* @param type is a number data type
*/
public Object getSumReturnType(Object type) {
if (isFloatingPointType(type)) {
return doubleClassType;
} else if (isNumericType(type) || isNumericWrapperType(type)) {
return longClassType;
} else {
return type;
}
}
/**
* Return JDO QL return type for Avg function for a given type.
* @param type is a number data type
*/
public Object getAvgReturnType(Object type) {
if (isNumericType(type) || isNumericWrapperType(type)) {
return doubleClassType;
} else {
return type;
}
}
/**
* Return JDO QL return type for Min/Max function for a given type.
* @param type is an orderable data type
*/
public Object getMinMaxReturnType(Object type) {
if (isFloatingPointType(type)) {
return doubleClassType;
} else if (isCharType(type)) {
return characterClassType;
} else if (isNumericType(type) || isNumericWrapperType(type)) {
return longClassType;
} else {
return type;
}
}
}