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