001 package railo.runtime.orm.hibernate; 002 003 import java.sql.Types; 004 import java.util.ArrayList; 005 import java.util.Iterator; 006 import java.util.List; 007 008 import org.hibernate.metadata.ClassMetadata; 009 import org.hibernate.type.Type; 010 011 import railo.commons.lang.StringUtil; 012 import railo.commons.lang.types.RefBoolean; 013 import railo.runtime.Component; 014 import railo.runtime.ComponentScope; 015 import railo.runtime.PageContext; 016 import railo.runtime.component.Property; 017 import railo.runtime.db.SQLCaster; 018 import railo.runtime.db.SQLItemImpl; 019 import railo.runtime.engine.ThreadLocalPageContext; 020 import railo.runtime.exp.PageException; 021 import railo.runtime.op.Caster; 022 import railo.runtime.op.Decision; 023 import railo.runtime.orm.ORMEngine; 024 import railo.runtime.orm.ORMException; 025 import railo.runtime.type.Array; 026 import railo.runtime.type.ArrayImpl; 027 import railo.runtime.type.Collection; 028 import railo.runtime.type.Collection.Key; 029 import railo.runtime.type.KeyImpl; 030 import railo.runtime.type.Query; 031 import railo.runtime.type.QueryImpl; 032 import railo.runtime.type.Struct; 033 import railo.runtime.type.cfc.ComponentAccess; 034 import railo.runtime.type.util.ArrayUtil; 035 import railo.runtime.type.util.ComponentUtil; 036 import railo.runtime.type.util.KeyConstants; 037 import railo.runtime.type.util.QueryUtil; 038 039 public class HibernateCaster { 040 041 private static final int NULL = -178696; 042 private static final Key ENTITY_NAME = KeyImpl.intern("entityname"); 043 044 public static Object toCFML(Object src) { 045 if(src==null) return null; 046 if(src instanceof Collection) return src; 047 048 if(src instanceof List){ 049 return toCFML((List) src); 050 } 051 /*if(src instanceof Map){ 052 return toCFML(pc,(Map) src); 053 }*/ 054 return src; 055 } 056 057 public static Array toCFML(List src) { 058 int size=src.size(); 059 060 Array trg = new ArrayImpl(); 061 for(int i=0;i<size;i++) { 062 trg.setEL(i+1,toCFML(src.get(i))); 063 } 064 return trg; 065 } 066 067 /*public static Object toCFML(PageContext pc,Map src) throws PageException { 068 069 Object type =src.remove("$type$"); 070 if(type instanceof String){ 071 072 Component cfc = toComponent(pc, (String)type); 073 return toCFML(pc,src, cfc); 074 } 075 076 077 Iterator<Map.Entry<String, Object>> it = src.entrySet().iterator(); 078 Struct trg=new StructImpl(); 079 Map.Entry<String, Object> entry; 080 while(it.hasNext()){ 081 entry=it.next(); 082 trg.setEL(entry.getKey(),toCFML(pc,entry.getValue())); 083 } 084 return trg; 085 }*/ 086 087 088 089 public static String getEntityName(Component cfc) { 090 091 String name=null; 092 try { 093 ComponentAccess cfca = ComponentUtil.toComponentAccess(cfc); 094 name=Caster.toString(cfca.getMetaStructItem(ENTITY_NAME),null); 095 } 096 catch (Throwable t) { 097 try { 098 Struct md = cfc.getMetaData(ThreadLocalPageContext.get()); 099 name = Caster.toString(md.get(ENTITY_NAME),null); 100 101 }catch (PageException e) {} 102 } 103 104 if(!StringUtil.isEmpty(name)) { 105 return name; 106 } 107 return getName(cfc); 108 109 110 } 111 112 private static String getName(Component cfc) { 113 String name=null; 114 // MUSTMUST cfc.getName() should return the real case, this should not be needed 115 name = cfc.getPageSource().getDisplayPath(); 116 name=railo.runtime.type.util.ListUtil.last(name, "\\/",true); 117 int index=name.lastIndexOf('.'); 118 name= name.substring(0,index); 119 return name; 120 } 121 122 public static int cascade(HibernateORMEngine engine,String cascade) throws ORMException { 123 int c=cascade(cascade,-1); 124 if(c!=-1) return c; 125 throw new HibernateException(engine,"invalid cascade defintion ["+cascade+"], valid values are [all,all-delete-orphan,delete,delete-orphan,refresh,save-update]"); 126 } 127 128 public static int cascade(String cascade, int defaultValue) { 129 cascade=cascade.trim().toLowerCase(); 130 if("all".equals(cascade)) return HibernateConstants.CASCADE_ALL; 131 132 if("save-update".equals(cascade)) return HibernateConstants.CASCADE_SAVE_UPDATE; 133 if("save_update".equals(cascade)) return HibernateConstants.CASCADE_SAVE_UPDATE; 134 if("saveupdate".equals(cascade)) return HibernateConstants.CASCADE_SAVE_UPDATE; 135 136 if("delete".equals(cascade)) return HibernateConstants.CASCADE_DELETE; 137 138 if("delete-orphan".equals(cascade)) return HibernateConstants.CASCADE_DELETE_ORPHAN; 139 if("delete_orphan".equals(cascade)) return HibernateConstants.CASCADE_DELETE_ORPHAN; 140 if("deleteorphan".equals(cascade)) return HibernateConstants.CASCADE_DELETE_ORPHAN; 141 142 if("all-delete-orphan".equals(cascade)) return HibernateConstants.CASCADE_ALL_DELETE_ORPHAN; 143 if("all_delete_orphan".equals(cascade)) return HibernateConstants.CASCADE_ALL_DELETE_ORPHAN; 144 if("alldeleteorphan".equals(cascade)) return HibernateConstants.CASCADE_ALL_DELETE_ORPHAN; 145 146 if("refresh".equals(cascade)) return HibernateConstants.REFRESH; 147 148 return defaultValue; 149 } 150 151 public static int collectionType(HibernateORMEngine engine,String strCollectionType) throws ORMException { 152 int ct=collectionType(strCollectionType, -1); 153 if(ct!=-1) return ct; 154 throw new ORMException(engine,"invalid collectionType defintion ["+strCollectionType+"], valid values are [array,struct]"); 155 } 156 public static int collectionType(String strCollectionType, int defaultValue) { 157 strCollectionType=strCollectionType.trim().toLowerCase(); 158 if("struct".equals(strCollectionType)) return HibernateConstants.COLLECTION_TYPE_STRUCT; 159 if("array".equals(strCollectionType)) return HibernateConstants.COLLECTION_TYPE_ARRAY; 160 161 return defaultValue; 162 } 163 164 public static String toHibernateType(ColumnInfo info, String type, String defaultValue) { 165 166 // no type defined 167 if(StringUtil.isEmpty(type,true)) { 168 return HibernateCaster.toHibernateType(info,defaultValue); 169 } 170 171 // type defined 172 String tmp=HibernateCaster.toHibernateType(type,null); 173 if(tmp!=null) return tmp; 174 175 if(info!=null){ 176 tmp=HibernateCaster.toHibernateType(info,defaultValue); 177 //ORMUtil.printError("type ["+type+"] is not a valid Hibernate type. Use instead type ["+tmp+"]", engine); 178 return tmp; 179 } 180 //throw new ORMException("type ["+type+"] is not a valid Hibernate type."); 181 return defaultValue; 182 183 184 } 185 186 public static int toSQLType(String type,int defaultValue) { 187 type=type.trim().toLowerCase(); 188 type=toHibernateType(type,type); 189 if("long".equals(type)) return Types.BIGINT; 190 if("binary".equals(type)) return Types.BINARY; 191 if("boolean".equals(type)) return Types.BIT; 192 if("blob".equals(type)) return Types.BLOB; 193 if("boolean".equals(type)) return Types.BOOLEAN; 194 if("character".equals(type)) return Types.CHAR; 195 if("clob".equals(type)) return Types.CLOB; 196 if("date".equals(type)) return Types.DATE; 197 if("big_decimal".equals(type)) return Types.DECIMAL; 198 if("big_integer".equals(type)) return Types.NUMERIC; 199 if("double".equals(type)) return Types.DOUBLE; 200 if("float".equals(type)) return Types.FLOAT; 201 if("integer".equals(type)) return Types.INTEGER; 202 if("binary".equals(type)) return Types.VARBINARY; 203 if("string".equals(type)) return Types.VARCHAR; 204 if("short".equals(type)) return Types.SMALLINT; 205 if("time".equals(type)) return Types.TIME; 206 if("timestamp".equals(type)) return Types.TIMESTAMP; 207 if("byte".equals(type)) return Types.TINYINT; 208 209 return defaultValue; 210 } 211 212 public static String toHibernateType(ColumnInfo info, String defaultValue) { 213 if(info==null)return defaultValue; 214 215 String rtn = toHibernateType(info.getType(),info.getSize(), null); 216 if(rtn!=null) return rtn; 217 return toHibernateType(info.getTypeName(),defaultValue); 218 } 219 220 public static String toHibernateType(int type, int size, String defaultValue) { 221 // MUST do better 222 switch(type){ 223 case Types.ARRAY: return ""; 224 case Types.BIGINT: return "long"; 225 case Types.BINARY: return "binary"; 226 case Types.BIT: return "boolean"; 227 case Types.BLOB: return "blob"; 228 case Types.BOOLEAN: return "boolean"; 229 case Types.CHAR: 230 return "string"; 231 //if(size>1) return "string"; 232 //return "character"; 233 case Types.CLOB: return "clob"; 234 //case Types.DATALINK: return ""; 235 case Types.DATE: return "date"; 236 case Types.DECIMAL: return "big_decimal"; 237 //case Types.DISTINCT: return ""; 238 case Types.DOUBLE: return "double"; 239 case Types.FLOAT: return "float"; 240 case Types.INTEGER: return "integer"; 241 //case Types.JAVA_OBJECT: return ""; 242 case Types.LONGVARBINARY: return "binary"; 243 case Types.LONGVARCHAR: return "string"; 244 //case Types.NULL: return ""; 245 case Types.NUMERIC: return "big_decimal"; 246 //case Types.OTHER: return ""; 247 //case Types.REAL: return ""; 248 //case Types.REF: return ""; 249 case Types.SMALLINT: return "short"; 250 //case Types.STRUCT: return ""; 251 case Types.TIME: return "time"; 252 case Types.TIMESTAMP: return "timestamp"; 253 case Types.TINYINT: return "byte"; 254 case Types.VARBINARY: return "binary"; 255 case Types.NVARCHAR: return "string"; 256 case Types.VARCHAR: return "string"; 257 } 258 return defaultValue; 259 } 260 261 public static String toHibernateType(HibernateORMEngine engine,String type) throws ORMException { 262 String res=toHibernateType(type, null); 263 if(res==null) throw new ORMException(engine,"the type ["+type+"] is not supported"); 264 return res; 265 } 266 267 268 // calendar_date: A type mapping for a Calendar object that represents a date 269 //calendar: A type mapping for a Calendar object that represents a datetime. 270 public static String toHibernateType(String type, String defaultValue) { 271 type=type.trim().toLowerCase(); 272 type=StringUtil.replace(type, "java.lang.", "", true); 273 type=StringUtil.replace(type, "java.util.", "", true); 274 type=StringUtil.replace(type, "java.sql.", "", true); 275 276 // return same value 277 if("long".equals(type)) return type; 278 if("binary".equals(type)) return type; 279 if("boolean".equals(type)) return type; 280 if("blob".equals(type)) return "binary"; 281 if("boolean".equals(type)) return type; 282 if("character".equals(type)) return type; 283 if("clob".equals(type)) return "text"; 284 if("date".equals(type)) return type; 285 if("big_decimal".equals(type)) return type; 286 if("double".equals(type)) return type; 287 if("float".equals(type)) return type; 288 if("integer".equals(type)) return type; 289 if("binary".equals(type)) return type; 290 if("string".equals(type)) return type; 291 if("big_integer".equals(type)) return type; 292 if("short".equals(type)) return type; 293 if("time".equals(type)) return type; 294 if("timestamp".equals(type)) return type; 295 if("byte".equals(type)) return type; 296 if("binary".equals(type)) return type; 297 if("string".equals(type)) return type; 298 if("text".equals(type)) return type; 299 if("calendar".equals(type)) return type; 300 if("calendar_date".equals(type)) return type; 301 if("locale".equals(type)) return type; 302 if("timezone".equals(type)) return type; 303 if("currency".equals(type)) return type; 304 305 if("imm_date".equals(type)) return type; 306 if("imm_time".equals(type)) return type; 307 if("imm_timestamp".equals(type)) return type; 308 if("imm_calendar".equals(type)) return type; 309 if("imm_calendar_date".equals(type)) return type; 310 if("imm_serializable".equals(type)) return type; 311 if("imm_binary".equals(type)) return type; 312 313 // return different value 314 if("bigint".equals(type)) return "long"; 315 if("bit".equals(type)) return "boolean"; 316 317 if("int".equals(type)) return "integer"; 318 if("char".equals(type)) return "character"; 319 320 if("bool".equals(type)) return "boolean"; 321 if("yes-no".equals(type)) return "yes_no"; 322 if("yesno".equals(type)) return "yes_no"; 323 if("yes_no".equals(type)) return "yes_no"; 324 if("true-false".equals(type)) return "true_false"; 325 if("truefalse".equals(type)) return "true_false"; 326 if("true_false".equals(type)) return "true_false"; 327 if("varchar".equals(type)) return "string"; 328 if("big-decimal".equals(type)) return "big_decimal"; 329 if("bigdecimal".equals(type)) return "big_decimal"; 330 if("java.math.bigdecimal".equals(type)) return "big_decimal"; 331 if("big-integer".equals(type)) return "big_integer"; 332 if("biginteger".equals(type)) return "big_integer"; 333 if("bigint".equals(type)) return "big_integer"; 334 if("java.math.biginteger".equals(type)) return "big_integer"; 335 if("byte[]".equals(type)) return "binary"; 336 if("serializable".equals(type)) return "serializable"; 337 338 if("datetime".equals(type)) return "timestamp"; 339 if("numeric".equals(type)) return "double"; 340 if("number".equals(type)) return "double"; 341 if("numeric".equals(type)) return "double"; 342 if("char".equals(type)) return "character"; 343 if("nchar".equals(type)) return "character"; 344 if("decimal".equals(type)) return "double"; 345 if("eurodate".equals(type)) return "timestamp"; 346 if("usdate".equals(type)) return "timestamp"; 347 if("int".equals(type)) return "integer"; 348 if("varchar".equals(type)) return "string"; 349 if("nvarchar".equals(type)) return "string"; 350 351 return defaultValue; 352 353 // FUTURE 354 /* 355 356 add support for 357 - any, object,other 358 359 add support for custom types https://issues.jboss.org/browse/RAILO-1341 360 - array 361 - base64 362 - guid 363 - memory 364 - node, xml 365 - query 366 - struct 367 - uuid 368 - variablename, variable_name 369 - variablestring, variable_string 370 371 */ 372 373 } 374 375 /** 376 * translate CFMl specific types to Hibernate/SQL specific types 377 * @param engine 378 * @param ci 379 * @param value 380 * @return 381 * @throws PageException 382 */ 383 public static Object toSQL(HibernateORMEngine engine,ColumnInfo ci, Object value, RefBoolean isArray) throws PageException { 384 return toSQL(engine, ci.getType(), value,isArray); 385 } 386 387 /** 388 * translate CFMl specific types to Hibernate/SQL specific types 389 * @param engine 390 * @param type 391 * @param value 392 * @return 393 * @throws PageException 394 */ 395 public static Object toSQL(HibernateORMEngine engine,Type type, Object value, RefBoolean isArray) throws PageException { 396 int t = toSQLType(type.getName(), Types.OTHER); 397 if(t==Types.OTHER) return value; 398 return toSQL(engine, t, value,isArray); 399 } 400 401 /** 402 * translate CFMl specific type to SQL specific types 403 * @param engine 404 * @param sqlType 405 * @param value 406 * @return 407 * @throws PageException 408 */ 409 private static Object toSQL(HibernateORMEngine engine,int sqlType, Object value, RefBoolean isArray) throws PageException { 410 if(isArray!=null)isArray.setValue(false); 411 SQLItemImpl item = new SQLItemImpl(value,sqlType); 412 try{ 413 return SQLCaster.toSqlType(item); 414 } 415 catch(PageException pe){ 416 // pherhaps it is a array of this type 417 if(isArray!=null && Decision.isArray(value)) { 418 Object[] src = Caster.toNativeArray(value); 419 ArrayList<Object> trg = new ArrayList<Object>(); 420 for(int i=0;i<src.length;i++){ 421 try{ 422 trg.add(SQLCaster.toSqlType(new SQLItemImpl(src[i],sqlType))); 423 } 424 catch(PageException inner){ 425 throw pe; 426 } 427 } 428 isArray.setValue(true); 429 return ArrayUtil.toArray(trg); 430 431 } 432 throw pe; 433 } 434 435 } 436 437 438 public static railo.runtime.type.Query toQuery(PageContext pc,HibernateORMSession session, Object obj, String name) throws PageException { 439 Query qry=null; 440 // a single entity 441 if(!Decision.isArray(obj)){ 442 qry= toQuery(pc,session,HibernateCaster.toComponent(obj),name,null,1,1); 443 } 444 445 // a array of entities 446 else { 447 Array arr=Caster.toArray(obj); 448 int len=arr.size(); 449 if(len>0) { 450 Iterator<Object> it = arr.valueIterator(); 451 int row=1; 452 while(it.hasNext()){ 453 qry=toQuery(pc,session,HibernateCaster.toComponent(it.next()),name,qry,len,row++); 454 } 455 } 456 else 457 qry=new QueryImpl(new Collection.Key[0],0,"orm"); 458 } 459 460 if(qry==null) { 461 if(!StringUtil.isEmpty(name)) 462 throw new ORMException(session.getEngine(),"there is no entity inheritance that match the name ["+name+"]"); 463 throw new ORMException(session.getEngine(),"cannot create query"); 464 } 465 return qry; 466 } 467 468 private static Query toQuery(PageContext pc,HibernateORMSession session,Component cfc, String entityName,Query qry, int rowcount, int row) throws PageException { 469 // inheritance mapping 470 if(!StringUtil.isEmpty(entityName)){ 471 //String cfcName = toComponentName(HibernateCaster.toComponent(pc, entityName)); 472 return inheritance(pc,session,cfc,qry, entityName); 473 } 474 return populateQuery(pc,session,cfc,qry); 475 } 476 477 478 479 480 private static Query populateQuery(PageContext pc,HibernateORMSession session,Component cfc,Query qry) throws PageException { 481 Property[] properties = ComponentUtil.getProperties(cfc,true,true,false,false); 482 ComponentScope scope = cfc.getComponentScope(); 483 HibernateORMEngine engine=(HibernateORMEngine) session.getEngine(); 484 485 // init 486 if(qry==null){ 487 ClassMetadata md = ((HibernateORMEngine)session.getEngine()).getSessionFactory(pc).getClassMetadata(getEntityName(cfc)); 488 //Struct columnsInfo= engine.getTableInfo(session.getDatasourceConnection(),toEntityName(engine, cfc),session.getEngine()); 489 Array names=new ArrayImpl(); 490 Array types=new ArrayImpl(); 491 String name; 492 //ColumnInfo ci; 493 int t; 494 Object obj; 495 Struct sct; 496 String fieldType; 497 for(int i=0;i<properties.length;i++){ 498 obj = properties[i].getMetaData(); 499 if(obj instanceof Struct) { 500 sct=(Struct) obj; 501 fieldType = Caster.toString(sct.get(KeyConstants._fieldtype,null),null); 502 if("one-to-many".equalsIgnoreCase(fieldType) || "many-to-many".equalsIgnoreCase(fieldType) || "many-to-one".equalsIgnoreCase(fieldType) || "one-to-one".equalsIgnoreCase(fieldType)) 503 continue; 504 505 } 506 507 name=HibernateUtil.validateColumnName(md, properties[i].getName(),null); 508 //if(columnsInfo!=null)ci=(ColumnInfo) columnsInfo.get(name,null); 509 //else ci=null; 510 names.append(name); 511 if(name!=null){ 512 513 t=HibernateCaster.toSQLType(HibernateUtil.getPropertyType(md, name).getName(), NULL); 514 if(t==NULL) 515 types.append("object"); 516 else 517 types.append(SQLCaster.toStringType(t)); 518 } 519 else 520 types.append("object"); 521 } 522 523 qry=new QueryImpl(names,types,0,getEntityName(cfc)); 524 525 } 526 // check 527 else if(engine.getMode() == ORMEngine.MODE_STRICT){ 528 if(!qry.getName().equals(getEntityName(cfc))) 529 throw new ORMException(session.getEngine(),"can only merge entities of the same kind to a query"); 530 } 531 532 // populate 533 Key[] names=QueryUtil.getColumnNames(qry); 534 535 int row=qry.addRow(); 536 for(int i=0;i<names.length;i++){ 537 qry.setAtEL(names[i], row, scope.get(names[i],null)); 538 } 539 return qry; 540 } 541 542 543 544 545 private static Query inheritance(PageContext pc,HibernateORMSession session,Component cfc,Query qry, String entityName) throws PageException { 546 Property[] properties = cfc.getProperties(true); 547 ComponentScope scope = cfc.getComponentScope(); 548 String name; 549 Object value; 550 Array arr; 551 for(int i=0;i<properties.length;i++){ 552 name=properties[i].getName(); 553 value=scope.get(name,null); 554 if(value instanceof Component){ 555 qry=inheritance(pc,session,qry,cfc,(Component) value,entityName); 556 } 557 else if(Decision.isArray(value)){ 558 arr = Caster.toArray(value); 559 Iterator<Object> it = arr.valueIterator(); 560 while(it.hasNext()){ 561 value=it.next(); 562 if(value instanceof Component){ 563 qry=inheritance(pc,session,qry,cfc,(Component) value,entityName); 564 } 565 } 566 } 567 } 568 return qry; 569 } 570 571 572 573 574 private static Query inheritance(PageContext pc,HibernateORMSession session,Query qry,Component parent,Component child,String entityName) throws PageException { 575 if(getEntityName(child).equalsIgnoreCase(entityName)) 576 return populateQuery(pc,session,child,qry); 577 return inheritance(pc,session,child, qry, entityName);// MUST geh ACF auch so tief? 578 } 579 580 581 /** 582 * return the full name (package and name) of a component 583 * @param cfc 584 * @return 585 */ 586 public static String toComponentName(Component cfc) { 587 return cfc.getPageSource().getComponentName(); 588 } 589 590 public static Component toComponent(Object obj) throws PageException { 591 return Caster.toComponent(obj); 592 } 593 594 595 /*public static Component toComponent(PageContext pc, Object obj) throws PageException { 596 if(obj instanceof String) 597 return toComponent(pc, (String)obj); 598 return Caster.toComponent(obj); 599 }*/ 600 /*public static Component toComponent(PageContext pc, String name) throws PageException { 601 // MUST muss �ber cfcs kommen oder neues init machen 602 return CreateObject.doComponent(pc, name); 603 }*/ 604 }