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