package railo.runtime.orm.hibernate;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.type.Type;
import railo.commons.lang.types.RefBoolean;
import railo.loader.util.Util;
import railo.runtime.Component;
import railo.runtime.ComponentScope;
import railo.runtime.PageContext;
import railo.runtime.component.Property;
import railo.runtime.db.SQLCaster;
import railo.runtime.db.SQLItem;
import railo.runtime.exp.PageException;
import railo.runtime.orm.ORMEngine;
import railo.runtime.orm.ORMSession;
import railo.runtime.type.Array;
import railo.runtime.type.Collection;
import railo.runtime.type.Collection.Key;
import railo.runtime.type.Query;
import railo.runtime.type.Struct;
import railo.runtime.type.util.ArrayUtil;
import railo.runtime.type.util.QueryUtil;
public class HibernateCaster {
private static final int NULL = -178696;
public static Object toCFML(Object src) {
if(src==null) return null;
if(src instanceof Collection) return src;
if(src instanceof List){
return toCFML((List) src);
}
/*if(src instanceof Map){
return toCFML(pc,(Map) src);
}*/
return src;
}
public static Array toCFML(List src) {
int size=src.size();
Array trg = CommonUtil.createArray();
for(int i=0;i<size;i++) {
trg.setEL(i+1,toCFML(src.get(i)));
}
return trg;
}
/*public static Object toCFML(PageContext pc,Map src) throws PageException {
Object type =src.remove("$type$");
if(type instanceof String){
Component cfc = toComponent(pc, (String)type);
return toCFML(pc,src, cfc);
}
Iterator<Map.Entry<String, Object>> it = src.entrySet().iterator();
Struct trg=CommonUtil.createStruct();
Map.Entry<String, Object> entry;
while(it.hasNext()){
entry=it.next();
trg.setEL(entry.getKey(),toCFML(pc,entry.getValue()));
}
return trg;
}*/
public static String getEntityName(Component cfc) {
String name=null;
try {
name=CommonUtil.toString(HibernateUtil.getMetaStructItem(cfc,CommonUtil.ENTITY_NAME),null);
}
catch (Throwable t) {
try {
Struct md = cfc.getMetaData(CommonUtil.pc());
name = CommonUtil.toString(md.get(CommonUtil.ENTITY_NAME),null);
}catch (PageException e) {}
}
if(!Util.isEmpty(name)) {
return name;
}
return getName(cfc);
}
private static String getName(Component cfc) {
String name=null;
// MUSTMUST cfc.getName() should return the real case, this should not be needed
name = cfc.getPageSource().getDisplayPath();
name=CommonUtil.last(name, "\\/");
int index=name.lastIndexOf('.');
name= name.substring(0,index);
return name;
}
public static int cascade(ORMSession session,String cascade) throws PageException {
int c=cascade(cascade,-1);
if(c!=-1) return c;
throw ExceptionUtil.createException(session,null,"invalid cascade defintion ["+cascade+"], valid values are [all,all-delete-orphan,delete,delete-orphan,refresh,save-update]",null);
}
public static int cascade(String cascade, int defaultValue) {
cascade=cascade.trim().toLowerCase();
if("all".equals(cascade)) return HibernateConstants.CASCADE_ALL;
if("save-update".equals(cascade)) return HibernateConstants.CASCADE_SAVE_UPDATE;
if("save_update".equals(cascade)) return HibernateConstants.CASCADE_SAVE_UPDATE;
if("saveupdate".equals(cascade)) return HibernateConstants.CASCADE_SAVE_UPDATE;
if("delete".equals(cascade)) return HibernateConstants.CASCADE_DELETE;
if("delete-orphan".equals(cascade)) return HibernateConstants.CASCADE_DELETE_ORPHAN;
if("delete_orphan".equals(cascade)) return HibernateConstants.CASCADE_DELETE_ORPHAN;
if("deleteorphan".equals(cascade)) return HibernateConstants.CASCADE_DELETE_ORPHAN;
if("all-delete-orphan".equals(cascade)) return HibernateConstants.CASCADE_ALL_DELETE_ORPHAN;
if("all_delete_orphan".equals(cascade)) return HibernateConstants.CASCADE_ALL_DELETE_ORPHAN;
if("alldeleteorphan".equals(cascade)) return HibernateConstants.CASCADE_ALL_DELETE_ORPHAN;
if("refresh".equals(cascade)) return HibernateConstants.REFRESH;
return defaultValue;
}
public static int collectionType(ORMSession session,String strCollectionType) throws PageException {
int ct=collectionType(strCollectionType, -1);
if(ct!=-1) return ct;
throw ExceptionUtil.createException(session,null,"invalid collectionType defintion ["+strCollectionType+"], valid values are [array,struct]",null);
}
public static int collectionType(String strCollectionType, int defaultValue) {
strCollectionType=strCollectionType.trim().toLowerCase();
if("struct".equals(strCollectionType)) return HibernateConstants.COLLECTION_TYPE_STRUCT;
if("array".equals(strCollectionType)) return HibernateConstants.COLLECTION_TYPE_ARRAY;
return defaultValue;
}
public static String toHibernateType(ColumnInfo info, String type, String defaultValue) {
// no type defined
if(Util.isEmpty(type,true)) {
return HibernateCaster.toHibernateType(info,defaultValue);
}
// type defined
String tmp=HibernateCaster.toHibernateType(type,null);
if(tmp!=null) return tmp;
if(info!=null){
tmp=HibernateCaster.toHibernateType(info,defaultValue);
//ORMUtil.printError("type ["+type+"] is not a valid Hibernate type. Use instead type ["+tmp+"]", engine);
return tmp;
}
//throw new ORMException("type ["+type+"] is not a valid Hibernate type.");
return defaultValue;
}
public static int toSQLType(String type,int defaultValue) {
type=type.trim().toLowerCase();
type=toHibernateType(type,type);
if("long".equals(type)) return Types.BIGINT;
if("binary".equals(type)) return Types.BINARY;
if("boolean".equals(type)) return Types.BIT;
if("blob".equals(type)) return Types.BLOB;
if("boolean".equals(type)) return Types.BOOLEAN;
if("character".equals(type)) return Types.CHAR;
if("clob".equals(type)) return Types.CLOB;
if("date".equals(type)) return Types.DATE;
if("big_decimal".equals(type)) return Types.DECIMAL;
if("big_integer".equals(type)) return Types.NUMERIC;
if("double".equals(type)) return Types.DOUBLE;
if("float".equals(type)) return Types.FLOAT;
if("integer".equals(type)) return Types.INTEGER;
if("binary".equals(type)) return Types.VARBINARY;
if("string".equals(type)) return Types.VARCHAR;
if("short".equals(type)) return Types.SMALLINT;
if("time".equals(type)) return Types.TIME;
if("timestamp".equals(type)) return Types.TIMESTAMP;
if("byte".equals(type)) return Types.TINYINT;
return defaultValue;
}
public static String toHibernateType(ColumnInfo info, String defaultValue) {
if(info==null)return defaultValue;
String rtn = toHibernateType(info.getType(),info.getSize(), null);
if(rtn!=null) return rtn;
return toHibernateType(info.getTypeName(),defaultValue);
}
public static String toHibernateType(int type, int size, String defaultValue) {
// MUST do better
switch(type){
case Types.ARRAY: return "";
case Types.BIGINT: return "long";
case Types.BINARY: return "binary";
case Types.BIT: return "boolean";
case Types.BLOB: return "blob";
case Types.BOOLEAN: return "boolean";
case Types.CHAR:
return "string";
//if(size>1) return "string";
//return "character";
case Types.CLOB: return "clob";
//case Types.DATALINK: return "";
case Types.DATE: return "date";
case Types.DECIMAL: return "big_decimal";
//case Types.DISTINCT: return "";
case Types.DOUBLE: return "double";
case Types.FLOAT: return "float";
case Types.INTEGER: return "integer";
//case Types.JAVA_OBJECT: return "";
case Types.LONGVARBINARY: return "binary";
case Types.LONGVARCHAR: return "string";
//case Types.NULL: return "";
case Types.NUMERIC: return "big_decimal";
//case Types.OTHER: return "";
//case Types.REAL: return "";
//case Types.REF: return "";
case Types.SMALLINT: return "short";
//case Types.STRUCT: return "";
case Types.TIME: return "time";
case Types.TIMESTAMP: return "timestamp";
case Types.TINYINT: return "byte";
case Types.VARBINARY: return "binary";
case Types.NVARCHAR: return "string";
case Types.VARCHAR: return "string";
}
return defaultValue;
}
public static String toHibernateType(ORMSession session,String type) throws PageException {
String res=toHibernateType(type, null);
if(res==null) throw ExceptionUtil.createException(session,null,"the type ["+type+"] is not supported",null);
return res;
}
// calendar_date: A type mapping for a Calendar object that represents a date
//calendar: A type mapping for a Calendar object that represents a datetime.
public static String toHibernateType(String type, String defaultValue) {
type=type.trim().toLowerCase();
type=Util.replace(type, "java.lang.", "", true);
type=Util.replace(type, "java.util.", "", true);
type=Util.replace(type, "java.sql.", "", true);
// return same value
if("long".equals(type)) return type;
if("binary".equals(type)) return type;
if("boolean".equals(type)) return type;
if("blob".equals(type)) return "binary";
if("boolean".equals(type)) return type;
if("character".equals(type)) return type;
if("clob".equals(type)) return "text";
if("date".equals(type)) return type;
if("big_decimal".equals(type)) return type;
if("double".equals(type)) return type;
if("float".equals(type)) return type;
if("integer".equals(type)) return type;
if("binary".equals(type)) return type;
if("string".equals(type)) return type;
if("big_integer".equals(type)) return type;
if("short".equals(type)) return type;
if("time".equals(type)) return type;
if("timestamp".equals(type)) return type;
if("byte".equals(type)) return type;
if("binary".equals(type)) return type;
if("string".equals(type)) return type;
if("text".equals(type)) return type;
if("calendar".equals(type)) return type;
if("calendar_date".equals(type)) return type;
if("locale".equals(type)) return type;
if("timezone".equals(type)) return type;
if("currency".equals(type)) return type;
if("imm_date".equals(type)) return type;
if("imm_time".equals(type)) return type;
if("imm_timestamp".equals(type)) return type;
if("imm_calendar".equals(type)) return type;
if("imm_calendar_date".equals(type)) return type;
if("imm_serializable".equals(type)) return type;
if("imm_binary".equals(type)) return type;
// return different value
if("bigint".equals(type)) return "long";
if("bit".equals(type)) return "boolean";
if("int".equals(type)) return "integer";
if("char".equals(type)) return "character";
if("bool".equals(type)) return "boolean";
if("yes-no".equals(type)) return "yes_no";
if("yesno".equals(type)) return "yes_no";
if("yes_no".equals(type)) return "yes_no";
if("true-false".equals(type)) return "true_false";
if("truefalse".equals(type)) return "true_false";
if("true_false".equals(type)) return "true_false";
if("varchar".equals(type)) return "string";
if("big-decimal".equals(type)) return "big_decimal";
if("bigdecimal".equals(type)) return "big_decimal";
if("java.math.bigdecimal".equals(type)) return "big_decimal";
if("big-integer".equals(type)) return "big_integer";
if("biginteger".equals(type)) return "big_integer";
if("bigint".equals(type)) return "big_integer";
if("java.math.biginteger".equals(type)) return "big_integer";
if("byte[]".equals(type)) return "binary";
if("serializable".equals(type)) return "serializable";
if("datetime".equals(type)) return "timestamp";
if("numeric".equals(type)) return "double";
if("number".equals(type)) return "double";
if("numeric".equals(type)) return "double";
if("char".equals(type)) return "character";
if("nchar".equals(type)) return "character";
if("decimal".equals(type)) return "double";
if("eurodate".equals(type)) return "timestamp";
if("usdate".equals(type)) return "timestamp";
if("int".equals(type)) return "integer";
if("varchar".equals(type)) return "string";
if("nvarchar".equals(type)) return "string";
return defaultValue;
// FUTURE
/*
add support for
- any, object,other
add support for custom types https://issues.jboss.org/browse/RAILO-1341
- array
- base64
- guid
- memory
- node, xml
- query
- struct
- uuid
- variablename, variable_name
- variablestring, variable_string
*/
}
/**
* translate CFMl specific types to Hibernate/SQL specific types
* @param engine
* @param ci
* @param value
* @return
* @throws PageException
*/
public static Object toSQL(ColumnInfo ci, Object value, RefBoolean isArray) throws PageException {
return toSQL(ci.getType(), value,isArray);
}
/**
* translate CFMl specific types to Hibernate/SQL specific types
* @param engine
* @param type
* @param value
* @return
* @throws PageException
*/
public static Object toSQL(Type type, Object value, RefBoolean isArray) throws PageException {
int t = toSQLType(type.getName(), Types.OTHER);
if(t==Types.OTHER) return value;
return toSQL(t, value,isArray);
}
/**
* translate CFMl specific type to SQL specific types
* @param engine
* @param sqlType
* @param value
* @return
* @throws PageException
*/
private static Object toSQL(int sqlType, Object value, RefBoolean isArray) throws PageException {
if(isArray!=null)isArray.setValue(false);
SQLItem item = CommonUtil.toSQLItem(value,sqlType);
try{
return SQLCaster.toSqlType(item);
}
catch(PageException pe){
// pherhaps it is a array of this type
if(isArray!=null && CommonUtil.isArray(value)) {
Object[] src = CommonUtil.toNativeArray(value);
ArrayList<Object> trg = new ArrayList<Object>();
for(int i=0;i<src.length;i++){
try{
trg.add(SQLCaster.toSqlType(CommonUtil.toSQLItem(src[i],sqlType)));
}
catch(PageException inner){
throw pe;
}
}
isArray.setValue(true);
return ArrayUtil.toArray(trg);
}
throw pe;
}
}
public static railo.runtime.type.Query toQuery(PageContext pc,HibernateORMSession session, Object obj, String name) throws PageException {
Query qry=null;
// a single entity
if(!CommonUtil.isArray(obj)){
qry= toQuery(pc,session,HibernateCaster.toComponent(obj),name,null,1,1);
}
// a array of entities
else {
Array arr=CommonUtil.toArray(obj);
int len=arr.size();
if(len>0) {
Iterator<Object> it = arr.valueIterator();
int row=1;
while(it.hasNext()){
qry=toQuery(pc,session,HibernateCaster.toComponent(it.next()),name,qry,len,row++);
}
}
else
qry=CommonUtil.createQuery(new Collection.Key[0],0,"orm");
}
if(qry==null) {
if(!Util.isEmpty(name))
throw ExceptionUtil.createException(session,null,"there is no entity inheritance that match the name ["+name+"]",null);
throw ExceptionUtil.createException(session,null,"cannot create query",null);
}
return qry;
}
private static Query toQuery(PageContext pc,HibernateORMSession session,Component cfc, String entityName,Query qry, int rowcount, int row) throws PageException {
// inheritance mapping
if(!Util.isEmpty(entityName)){
//String cfcName = toComponentName(HibernateCaster.toComponent(pc, entityName));
return inheritance(pc,session,cfc,qry, entityName);
}
return populateQuery(pc,session,cfc,qry);
}
private static Query populateQuery(PageContext pc,HibernateORMSession session,Component cfc,Query qry) throws PageException {
Property[] properties = CommonUtil.getProperties(cfc,true,true,false,false);
ComponentScope scope = cfc.getComponentScope();
HibernateORMEngine engine=(HibernateORMEngine) session.getEngine();
// init
if(qry==null){
ClassMetadata md = ((HibernateORMEngine)session.getEngine()).getSessionFactory(pc).getClassMetadata(getEntityName(cfc));
//Struct columnsInfo= engine.getTableInfo(session.getDatasourceConnection(),toEntityName(engine, cfc),session.getEngine());
Array names=CommonUtil.createArray();
Array types=CommonUtil.createArray();
String name;
//ColumnInfo ci;
int t;
Object obj;
Struct sct;
String fieldType;
for(int i=0;i<properties.length;i++){
obj = properties[i].getMetaData();
if(obj instanceof Struct) {
sct=(Struct) obj;
fieldType = CommonUtil.toString(sct.get(CommonUtil.FIELDTYPE,null),null);
if("one-to-many".equalsIgnoreCase(fieldType) || "many-to-many".equalsIgnoreCase(fieldType) || "many-to-one".equalsIgnoreCase(fieldType) || "one-to-one".equalsIgnoreCase(fieldType))
continue;
}
name=HibernateUtil.validateColumnName(md, properties[i].getName(),null);
//if(columnsInfo!=null)ci=(ColumnInfo) columnsInfo.get(name,null);
//else ci=null;
names.append(name);
if(name!=null){
t=HibernateCaster.toSQLType(HibernateUtil.getPropertyType(md, name).getName(), NULL);
if(t==NULL)
types.append("object");
else
types.append(SQLCaster.toStringType(t));
}
else
types.append("object");
}
qry=CommonUtil.createQuery(names,types,0,getEntityName(cfc));
}
// check
else if(engine.getMode() == ORMEngine.MODE_STRICT){
if(!qry.getName().equals(getEntityName(cfc)))
throw ExceptionUtil.createException(session,null,"can only merge entities of the same kind to a query",null);
}
// populate
Key[] names=QueryUtil.getColumnNames(qry);
int row=qry.addRow();
for(int i=0;i<names.length;i++){
qry.setAtEL(names[i], row, scope.get(names[i],null));
}
return qry;
}
private static Query inheritance(PageContext pc,HibernateORMSession session,Component cfc,Query qry, String entityName) throws PageException {
Property[] properties = cfc.getProperties(true);
ComponentScope scope = cfc.getComponentScope();
Object value;
Array arr;
for(int i=0;i<properties.length;i++){
value=scope.get(CommonUtil.createKey(properties[i].getName()),null);
if(value instanceof Component){
qry=inheritance(pc,session,qry,cfc,(Component) value,entityName);
}
else if(CommonUtil.isArray(value)){
arr = CommonUtil.toArray(value);
Iterator<Object> it = arr.valueIterator();
while(it.hasNext()){
value=it.next();
if(value instanceof Component){
qry=inheritance(pc,session,qry,cfc,(Component) value,entityName);
}
}
}
}
return qry;
}
private static Query inheritance(PageContext pc,HibernateORMSession session,Query qry,Component parent,Component child,String entityName) throws PageException {
if(getEntityName(child).equalsIgnoreCase(entityName))
return populateQuery(pc,session,child,qry);
return inheritance(pc,session,child, qry, entityName);// MUST geh ACF auch so tief?
}
/**
* return the full name (package and name) of a component
* @param cfc
* @return
*/
public static String toComponentName(Component cfc) {
return cfc.getPageSource().getComponentName();
}
public static Component toComponent(Object obj) throws PageException {
return CommonUtil.toComponent(obj);
}
}