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    }