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
021
022import java.util.ArrayList;
023import java.util.HashMap;
024import java.util.Iterator;
025import java.util.Map;
026import java.util.Map.Entry;
027
028import lucee.loader.util.Util;
029import lucee.runtime.Component;
030import lucee.runtime.PageContext;
031import lucee.runtime.component.Property;
032import lucee.runtime.db.DatasourceConnection;
033import lucee.runtime.exp.PageException;
034import lucee.runtime.orm.ORMUtil;
035import lucee.runtime.type.Collection;
036import lucee.runtime.type.Collection.Key;
037import lucee.runtime.type.Struct;
038
039import org.w3c.dom.DOMException;
040import org.w3c.dom.Document;
041import org.w3c.dom.Element;
042import org.w3c.dom.NodeList;
043
044public class HBMCreator {
045        
046        
047        private static final Collection.Key PROPERTY = CommonUtil.createKey("property");
048        private static final Collection.Key LINK_TABLE = CommonUtil.createKey("linktable");
049        private static final Collection.Key CFC = CommonUtil.createKey("cfc");
050        private static final Collection.Key GENERATOR = CommonUtil.createKey("generator");
051        private static final Collection.Key PARAMS = CommonUtil.createKey("params");
052        private static final Collection.Key SEQUENCE = CommonUtil.createKey("sequence");
053        private static final Collection.Key UNIQUE_KEY_NAME = CommonUtil.createKey("uniqueKeyName");
054        private static final Collection.Key GENERATED = CommonUtil.createKey("generated");
055        private static final Collection.Key FIELDTYPE = CommonUtil.createKey("fieldtype");
056        private static final Collection.Key KEY = CommonUtil.createKey("key");
057        private static final Collection.Key TYPE = CommonUtil.createKey("type");
058   
059        public static void createXMLMapping(PageContext pc,DatasourceConnection dc, Component cfc,Element hibernateMapping,SessionFactoryData data) throws PageException {
060                
061                // MUST Support for embeded objects 
062                Struct meta = cfc.getMetaData(pc);
063                
064                String extend = cfc.getExtends();
065                boolean isClass=Util.isEmpty(extend);
066
067                // MZ: Fetches all inherited persistent properties
068                //Property[] _props=getAllPersistentProperties(pc,cfc,dc,meta,isClass);
069
070                Property[] _props=getProperties(pc,cfc,dc,meta,isClass, true,data);
071
072                
073
074                Map<String, PropertyCollection> joins=new HashMap<String, PropertyCollection>();
075                PropertyCollection propColl = splitJoins(cfc,joins, _props,data);
076                
077                
078                
079                // create class element and attach
080                Document doc = CommonUtil.getDocument(hibernateMapping);
081                
082                StringBuilder comment=new StringBuilder();
083                comment.append("\nsource:").append(cfc.getPageSource().getDisplayPath());
084                comment.append("\ncompilation-time:").append(CommonUtil.createDateTime(HibernateUtil.getCompileTime(pc,cfc.getPageSource())));
085                comment.append("\ndatasource:").append(dc.getDatasource().getName());
086                comment.append("\n");
087                
088                hibernateMapping.appendChild(doc.createComment(comment.toString()));
089                
090                //print.e(cfc.getAbsName()+";"+isClass+" -> "+cfci.getBaseAbsName()+":"+cfci.isBasePeristent());
091                if(!isClass && !cfc.isBasePeristent()) {
092                        isClass=true;
093                } 
094                
095                
096                Element join=null;
097                boolean doTable=true;
098                
099                Element clazz;
100                if(isClass)  {
101                        clazz = doc.createElement("class");
102                        hibernateMapping.appendChild(clazz);
103                }
104                // extended CFC
105                else{
106                        // MZ: Fetches one level deep
107                        _props=getProperties(pc,cfc,dc,meta,isClass, false,data);
108                        // MZ: Reinitiate the property collection
109                        propColl = splitJoins(cfc,joins, _props,data);
110
111                        String ext = CommonUtil.last(extend,'.').trim();
112                        try {
113                                Component base = data.getEntityByCFCName(ext, false);
114                                ext = HibernateCaster.getEntityName(base);
115                        }
116                        catch(Throwable t){
117                                lucee.commons.lang.ExceptionUtil.rethrowIfNecessary(t);
118                        }
119                        
120                        
121                        String discriminatorValue = toString(cfc,null,meta,"discriminatorValue",data);
122                        if(!Util.isEmpty(discriminatorValue,true)) {
123                                doTable=false;
124                                clazz = doc.createElement("subclass");
125                                hibernateMapping.appendChild(clazz);
126                        //addClassAttributes(classNode);
127                        clazz.setAttribute("extends", ext);
128                        clazz.setAttribute("discriminator-value", discriminatorValue);
129                        
130                        String joincolumn = toString(cfc,null,meta,"joincolumn",false,data);
131                        if(!Util.isEmpty(joincolumn)){
132                                join = doc.createElement("join");
133                                clazz.appendChild(join);
134                                doTable=true;
135                                Element key = doc.createElement("key");
136                                join.appendChild(key);
137                                key.setAttribute("column", formatColumn(joincolumn,data));
138                        }
139                        
140                        }
141                        else {
142                                // MZ: Match on joinColumn for a joined subclass, otherwise use a union subclass
143                                String joinColumn = toString(cfc,null,meta,"joincolumn",false,data);
144                                if (!Util.isEmpty(joinColumn,true)) {
145                                        clazz = doc.createElement("joined-subclass");
146                                        hibernateMapping.appendChild(   clazz);
147                                        clazz.setAttribute("extends",ext);
148                                        Element key = doc.createElement("key");
149                                        clazz.appendChild(key);
150                                        key.setAttribute("column", formatColumn(joinColumn,data));
151                                }
152                                else {
153                                        // MZ: When no joinColumn exists, default to an explicit table per class
154                                        clazz = doc.createElement("union-subclass");
155                                        clazz.setAttribute("extends",ext);
156                                        doTable = true;
157                                        hibernateMapping.appendChild(   clazz);
158                                }
159
160                        }
161                }
162
163                //createXMLMappingTuplizer(clazz,pc);
164
165                addGeneralClassAttributes(pc,cfc,meta,clazz,data);
166                String tableName=getTableName(pc,meta,cfc,data);
167                
168                if(join!=null) clazz=join;
169                if(doTable)addGeneralTableAttributes(pc,cfc,meta,clazz,data);
170                
171                
172        
173        
174        Struct columnsInfo=null;
175        if(data.getORMConfiguration().useDBForMapping()){
176                columnsInfo = data.getTableInfo(dc,getTableName(pc, meta, cfc,data));
177        }
178
179        if(isClass)setCacheStrategy(cfc,null,doc, meta, clazz,data);
180        
181                // id
182        if(isClass) addId(cfc,doc,clazz,meta,propColl,columnsInfo,tableName,data);
183              
184        // discriminator
185        if(isClass) addDiscriminator(cfc,doc,clazz,pc,meta,data);
186        
187                // version
188        if(isClass)addVersion(cfc,clazz,pc, propColl,columnsInfo,tableName,data);
189                
190                // property
191                addProperty(cfc,clazz,pc, propColl,columnsInfo,tableName,data);
192                
193                // relations
194                addRelation(cfc,clazz,pc, propColl,columnsInfo,tableName,dc,data);
195
196                // collection
197                addCollection(cfc,clazz,pc, propColl,columnsInfo,tableName,data);
198                
199                // join
200                addJoin(cfc,clazz,pc, joins,columnsInfo,tableName,dc,data);
201
202                
203        }
204        
205        private static Property[] getProperties(PageContext pc, Component cfc, DatasourceConnection dc, Struct meta, boolean isClass, boolean recursivePersistentMappedSuperclass,SessionFactoryData data) throws PageException, PageException {
206                Property[] _props;
207                if (recursivePersistentMappedSuperclass) {
208                        _props = CommonUtil.getProperties(cfc,true, true, true, true);
209                }
210                else {
211                        _props = cfc.getProperties(true);
212                }
213
214                if(isClass && _props.length==0 && data.getORMConfiguration().useDBForMapping()){
215                        if(meta==null)meta = cfc.getMetaData(pc);
216                _props=HibernateUtil.createPropertiesFromTable(dc,getTableName(pc, meta, cfc, data));
217        }
218                return _props;
219        }
220
221        private static void addId(Component cfc,Document doc, Element clazz, Struct meta, PropertyCollection propColl, Struct columnsInfo, String tableName, SessionFactoryData data) throws PageException {
222                Property[] _ids = getIds(cfc,propColl,data);
223                
224        //Property[] _ids = ids.toArray(new Property[ids.size()]);
225        
226        if(_ids.length==1) 
227                createXMLMappingId(cfc,clazz, _ids[0],columnsInfo,tableName,data);
228        else if(_ids.length>1) 
229                createXMLMappingCompositeId(cfc,clazz, _ids,columnsInfo,tableName,data);
230        else 
231                throw ExceptionUtil.createException(data,cfc,"missing id property for entity ["+HibernateCaster.getEntityName(cfc)+"]",null);
232        }
233        
234
235        private static PropertyCollection splitJoins(Component cfc,Map<String, PropertyCollection> joins,Property[] props,SessionFactoryData data) {
236                Struct sct=CommonUtil.createStruct();
237                ArrayList<Property> others = new ArrayList<Property>();
238                java.util.List<Property> list;
239                String table;
240                Property prop;
241                String fieldType;
242                boolean isJoin;
243                for(int i=0;i<props.length;i++){
244                        prop=props[i];
245                        table=getTable(cfc,prop,data);
246                        // joins
247                        if(!Util.isEmpty(table,true)){
248                                isJoin=true;
249                                // wrong field type
250                                try {
251                                        fieldType = toString(cfc, prop, sct, FIELDTYPE,false,data);
252                                        
253                                        if("collection".equalsIgnoreCase(fieldType)) isJoin=false;
254                                        else if("primary".equals(fieldType)) isJoin=false;
255                                        else if("version".equals(fieldType)) isJoin=false;
256                                        else if("timestamp".equals(fieldType)) isJoin=false;
257                                } 
258                                catch (PageException e) {}
259                                
260                                // missing column
261                                String columns=null;
262                                try {
263                                        if(ORMUtil.isRelated(props[i])){
264                                        columns=toString(cfc,props[i], prop.getDynamicAttributes(), "fkcolumn",data);
265                                }
266                                else {
267                                        columns=toString(cfc,props[i], prop.getDynamicAttributes(), "joincolumn",data);
268                                }
269                                }
270                                catch(PageException e){}
271                                if(Util.isEmpty(columns)) isJoin=false;
272                                
273                                if(isJoin){
274                                        table=table.trim();
275                                        list = (java.util.List<Property>) sct.get(table,null);
276                                        if(list==null){
277                                                list=new ArrayList<Property>();
278                                                sct.setEL(CommonUtil.createKey(table), list);
279                                        }
280                                        list.add(prop);
281                                        continue;
282                                }
283                        }
284                        others.add(prop);
285                }
286                
287                // fill to joins
288                Iterator<Entry<Key, Object>> it = sct.entryIterator();
289                Entry<Key, Object> e;
290                while(it.hasNext()){
291                        e = it.next();
292                        list=(java.util.List<Property>) e.getValue();
293                        joins.put(e.getKey().getString(), new PropertyCollection(e.getKey().getString(),list));
294                }
295                
296                
297                
298                return new PropertyCollection(null,others);
299        }
300        
301        
302        
303        
304
305        private static Property[] getIds(Component cfc,PropertyCollection pc,SessionFactoryData data) {
306                return getIds(cfc,pc.getProperties(), pc.getTableName(),false,data);
307        }
308        
309        private static Property[] getIds(Component cfc,Property[] props,String tableName, boolean ignoreTableName,SessionFactoryData data) {
310                ArrayList<Property> ids=new ArrayList<Property>();
311        for(int y=0;y<props.length;y++){
312                if(!ignoreTableName && !hasTable(cfc,props[y], tableName,data)) continue;
313                
314                
315                String fieldType = CommonUtil.toString(props[y].getDynamicAttributes().get(FIELDTYPE,null),null);
316                        if("id".equalsIgnoreCase(fieldType) || CommonUtil.listFindNoCaseIgnoreEmpty(fieldType,"id",',')!=-1)
317                                ids.add(props[y]);
318                }
319        
320        // no id field defined
321        if(ids.size()==0) {
322                String fieldType;
323                for(int y=0;y<props.length;y++){
324                        if(!ignoreTableName && !hasTable(cfc,props[y], tableName,data)) continue;
325                fieldType = CommonUtil.toString(props[y].getDynamicAttributes().get(FIELDTYPE,null),null);
326                        if(Util.isEmpty(fieldType,true) && props[y].getName().equalsIgnoreCase("id")){
327                                ids.add(props[y]);
328                                props[y].getDynamicAttributes().setEL(FIELDTYPE, "id");
329                        }
330                }
331        } 
332        
333        // still no id field defined
334        if(ids.size()==0 && props.length>0) {
335                String owner = props[0].getOwnerName();
336                        if(!Util.isEmpty(owner)) owner=CommonUtil.last(owner, '.').trim();
337                
338                String fieldType;
339                if(!Util.isEmpty(owner)){
340                        String id=owner+"id";
341                        for(int y=0;y<props.length;y++){
342                                if(!ignoreTableName && !hasTable(cfc,props[y], tableName,data)) continue;
343                        fieldType = CommonUtil.toString(props[y].getDynamicAttributes().get(FIELDTYPE,null),null);
344                                if(Util.isEmpty(fieldType,true) && props[y].getName().equalsIgnoreCase(id)){
345                                        ids.add(props[y]);
346                                        props[y].getDynamicAttributes().setEL(FIELDTYPE, "id");
347                                }
348                        }
349                }
350        } 
351        return ids.toArray(new Property[ids.size()]);
352        }
353
354
355
356
357
358
359        private static void addVersion(Component cfc,Element clazz, PageContext pc,PropertyCollection propColl, Struct columnsInfo, String tableName,SessionFactoryData data) throws PageException {
360        Property[] props = propColl.getProperties();
361                for(int y=0;y<props.length;y++){
362                        String fieldType = CommonUtil.toString(props[y].getDynamicAttributes().get(FIELDTYPE,null),null);
363                        if("version".equalsIgnoreCase(fieldType))
364                                createXMLMappingVersion(clazz,pc, cfc,props[y],data);
365                        else if("timestamp".equalsIgnoreCase(fieldType))
366                                createXMLMappingTimestamp(clazz,pc,cfc, props[y],data);
367                }
368        }
369
370
371
372        private static void addCollection(Component cfc,Element clazz, PageContext pc,PropertyCollection propColl, Struct columnsInfo, String tableName,SessionFactoryData data) throws PageException {
373                Property[] props = propColl.getProperties();
374                for(int y=0;y<props.length;y++){
375                        String fieldType = CommonUtil.toString(props[y].getDynamicAttributes().get(FIELDTYPE,"column"),"column");
376                        if("collection".equalsIgnoreCase(fieldType))
377                                createXMLMappingCollection(clazz,pc, cfc,props[y],data);
378                }
379        }
380        
381        
382        private static void addJoin(Component cfc,Element clazz, PageContext pc,Map<String, PropertyCollection> joins, Struct columnsInfo, String tableName,DatasourceConnection dc, SessionFactoryData data) throws PageException {
383        
384                Iterator<Entry<String, PropertyCollection>> it = joins.entrySet().iterator();
385                Entry<String, PropertyCollection> entry;
386                while(it.hasNext()){
387                        entry = it.next();
388                        addJoin(cfc,pc,columnsInfo,clazz,entry.getValue(),dc,data);
389                }
390                
391                
392                
393    }
394        
395        private static void addJoin(Component cfc,PageContext pc,Struct columnsInfo, Element clazz, PropertyCollection coll, DatasourceConnection dc, SessionFactoryData data) throws PageException {
396                String table = coll.getTableName();
397                Property[] properties = coll.getProperties();
398                if(properties.length==0) return;
399                
400                Document doc = CommonUtil.getDocument(clazz);
401                
402                Element join = doc.createElement("join");
403        clazz.appendChild(join);
404                
405        join.setAttribute("table", escape(HibernateUtil.convertTableName(data,coll.getTableName())));
406        //addTableInfo(joinNode, table, schema, catalog);
407        
408        Property first = properties[0];
409        String schema = null, catalog=null, mappedBy=null, columns=null;
410        if(ORMUtil.isRelated(first)){
411                catalog=toString(cfc,first, first.getDynamicAttributes(), "linkcatalog",data);
412                schema=toString(cfc,first, first.getDynamicAttributes(), "linkschema",data);
413                columns=toString(cfc,first, first.getDynamicAttributes(), "fkcolumn",data);
414                
415        }
416        else {
417                catalog=toString(cfc,first, first.getDynamicAttributes(), "catalog",data);
418                schema=toString(cfc,first, first.getDynamicAttributes(), "schema",data);
419                mappedBy=toString(cfc,first, first.getDynamicAttributes(), "mappedby",data);
420                columns=toString(cfc,first, first.getDynamicAttributes(), "joincolumn",data);
421        }
422
423        if(!Util.isEmpty(catalog)) join.setAttribute("catalog", catalog);
424        if(!Util.isEmpty(schema)) join.setAttribute("schema", schema);
425        
426        Element key = doc.createElement("key");
427        join.appendChild(key);
428        if(!Util.isEmpty(mappedBy)) key.setAttribute("property-ref", mappedBy);
429        setColumn(doc, key, columns,data);
430        
431        addProperty(cfc,join,pc, coll,columnsInfo,table,data);
432                int count=addRelation(cfc,join,pc, coll,columnsInfo,table,dc,data);
433        
434                if(count>0) join.setAttribute("inverse", "true");
435                        
436                
437        }
438
439
440
441
442
443
444
445        
446
447
448
449        private static int addRelation(Component cfc,Element clazz, PageContext pc,PropertyCollection propColl, Struct columnsInfo, String tableName, DatasourceConnection dc, SessionFactoryData data) throws PageException {
450        Property[] props = propColl.getProperties();
451                int count=0;
452        for(int y=0;y<props.length;y++){
453                        String fieldType = CommonUtil.toString(props[y].getDynamicAttributes().get(FIELDTYPE,"column"),"column");
454                        if("one-to-one".equalsIgnoreCase(fieldType)){
455                                createXMLMappingOneToOne(clazz,pc, cfc,props[y],data);
456                                count++;
457                        }
458                        else if("many-to-one".equalsIgnoreCase(fieldType)){
459                                createXMLMappingManyToOne(clazz,pc, cfc,props[y],propColl,data);
460                                count++;
461                        }
462                        else if("one-to-many".equalsIgnoreCase(fieldType)){
463                                createXMLMappingOneToMany(cfc,propColl,clazz,pc, props[y],data);
464                                count++;
465                        }
466                        else if("many-to-many".equalsIgnoreCase(fieldType)){
467                                createXMLMappingManyToMany(cfc,propColl,clazz,pc, props[y],dc,data);
468                                count++;
469                        }
470                }
471        return count;
472        }
473
474
475
476        private static void addProperty(Component cfc,Element clazz, PageContext pc, PropertyCollection propColl, Struct columnsInfo, String tableName, SessionFactoryData data) throws PageException {
477                Property[] props = propColl.getProperties();
478                for(int y=0;y<props.length;y++){
479                        String fieldType = CommonUtil.toString(props[y].getDynamicAttributes().get(FIELDTYPE,"column"),"column");
480                        if("column".equalsIgnoreCase(fieldType))
481                                createXMLMappingProperty(clazz,pc,cfc, props[y],columnsInfo,tableName,data);
482                }
483        }
484
485
486
487        private static void addDiscriminator(Component cfc,Document doc,Element clazz, PageContext pc,Struct meta,SessionFactoryData data) throws DOMException, PageException {
488                
489         String str = toString(cfc,null,meta,"discriminatorColumn",data);
490         if(!Util.isEmpty(str,true)){
491                 Element disc = doc.createElement("discriminator");
492                 clazz.appendChild(disc);
493                 disc.setAttribute("column",formatColumn(str,data));
494         }
495         
496
497        
498        
499        }
500
501
502
503        private static void addGeneralClassAttributes(PageContext pc, Component cfc, Struct meta, Element clazz, SessionFactoryData data) throws PageException {
504        
505        // name
506                clazz.setAttribute("node", HibernateCaster.toComponentName(cfc));
507                
508        // entity-name
509        String str=toString(cfc,null,meta,"entityname",data);
510                if(Util.isEmpty(str,true)) str=HibernateCaster.getEntityName(cfc);
511                clazz.setAttribute("entity-name",str);
512                
513
514        // batch-size
515        Integer i = toInteger(cfc,meta,"batchsize",data);
516        if(i!=null && i.intValue()>0)clazz.setAttribute("batch-size",CommonUtil.toString(i));
517                
518                // dynamic-insert
519        Boolean b = toBoolean(cfc,meta,"dynamicinsert",data);
520        if(b!=null && b.booleanValue())clazz.setAttribute("dynamic-insert","true");
521        
522        // dynamic-update
523        b=toBoolean(cfc,meta,"dynamicupdate",data);
524        if(b!=null && b.booleanValue())clazz.setAttribute("dynamic-update","true");
525        
526                // lazy (dtd defintion:<!ATTLIST class lazy (true|false) #IMPLIED>)
527        b=toBoolean(cfc,meta,"lazy",data);
528        if(b==null) b=Boolean.TRUE;
529        clazz.setAttribute("lazy",CommonUtil.toString(b.booleanValue()));
530                
531        // select-before-update
532        b=toBoolean(cfc,meta,"selectbeforeupdate",data);
533        if(b!=null && b.booleanValue())clazz.setAttribute("select-before-update","true");
534
535        // optimistic-lock
536        str=toString(cfc,null,meta,"optimisticLock",data);
537        if(!Util.isEmpty(str,true)) {
538                str=str.trim().toLowerCase();
539                if("all".equals(str) || "dirty".equals(str) || "none".equals(str) || "version".equals(str))
540                        clazz.setAttribute("optimistic-lock",str);
541                else
542                        throw ExceptionUtil.createException(data,cfc,"invalid value ["+str+"] for attribute [optimisticlock] of tag [component], valid values are [all,dirty,none,version]",null);
543        }
544        
545        // read-only
546        b=toBoolean(cfc,meta,"readOnly",data);
547        if(b!=null && b.booleanValue()) clazz.setAttribute("mutable", "false");
548        
549        // rowid
550        str=toString(cfc,null,meta,"rowid",data);
551        if(!Util.isEmpty(str,true)) clazz.setAttribute("rowid",str);
552        
553        // where
554        str=toString(cfc,null,meta,"where",data);
555        if(!Util.isEmpty(str,true)) clazz.setAttribute("where", str);
556
557       
558        }
559        private static void addGeneralTableAttributes(PageContext pc, Component cfc, Struct meta, Element clazz,SessionFactoryData data) throws PageException {
560                 // table
561        clazz.setAttribute("table",escape(getTableName(pc,meta,cfc,data)));
562        
563        // catalog
564        String str = toString(cfc,null,meta,"catalog",data);
565        if(str==null)// empty string is allowed as input
566                str=data.getORMConfiguration().getCatalog();
567        if(!Util.isEmpty(str,true)) clazz.setAttribute("catalog", str);
568        
569        // schema
570        str=toString(cfc,null,meta,"schema",data);
571        if(str==null)// empty string is allowed as input
572                str=data.getORMConfiguration().getSchema();
573        if(!Util.isEmpty(str,true)) clazz.setAttribute( "schema", str);
574        
575        }
576        private static String escape(String str) {
577                if(HibernateUtil.isKeyword(str)) return "`"+str+"`";
578                return str;
579        }
580
581
582
583
584
585
586        private static String getTableName(PageContext pc, Struct meta, Component cfc,SessionFactoryData data) throws PageException {
587                String tableName=toString(cfc,null,meta,"table",data);
588                if(Util.isEmpty(tableName,true)) 
589                        tableName=HibernateCaster.getEntityName(cfc);
590                return HibernateUtil.convertTableName(data,tableName);
591        }
592        
593        private static String getTable(Component cfc,Property prop,SessionFactoryData data) {
594                try {
595                        return HibernateUtil.convertTableName(data,toString(cfc,prop, prop.getDynamicAttributes(), "table",data));
596                } catch (PageException e) {
597                        return null;
598                }
599        }
600        
601        private static boolean hasTable(Component cfc,Property prop,String tableName,SessionFactoryData data) {
602                String t = getTable(cfc,prop,data);
603                boolean left=Util.isEmpty(t,true);
604                boolean right=Util.isEmpty(tableName,true);
605                if(left && right) return true;
606                if(left || right) return false;
607                return tableName.trim().equalsIgnoreCase(t.trim());
608        }
609
610
611
612
613
614
615        private static void createXMLMappingCompositeId(Component cfc,Element clazz, Property[] props,Struct columnsInfo,String tableName, SessionFactoryData data) throws PageException {
616                Struct meta;
617                
618                Document doc = CommonUtil.getDocument(clazz);
619                Element cid = doc.createElement("composite-id");
620                clazz.appendChild(cid);
621                
622                //cid.setAttribute("mapped","true");
623                
624                
625                Property prop;
626                String fieldType;
627                // ids
628                for(int y=0;y<props.length;y++){
629                        prop=props[y];
630                        
631                        
632                        // do not add "key-property" for many-to-one
633                        meta = prop.getDynamicAttributes();
634                        fieldType = toString(cfc,prop,meta,"fieldType",data);
635                        if(CommonUtil.listFindNoCaseIgnoreEmpty(fieldType,"many-to-one",',')!=-1)continue;
636                        
637                        
638                        Element key = doc.createElement("key-property");
639                        cid.appendChild(key);
640                        
641                        // name
642                        key.setAttribute("name",prop.getName());
643                        
644                        // column
645                        Element column = doc.createElement("column");
646                        key.appendChild(column);
647                        
648                        String str = toString(cfc,prop,meta,"column",data);
649                if(Util.isEmpty(str,true)) str=prop.getName();
650                column.setAttribute("name",formatColumn(str,data));
651                ColumnInfo info=getColumnInfo(columnsInfo,tableName,str,null);
652                
653            str = toString(cfc,prop,meta,"sqltype",data);
654                if(!Util.isEmpty(str,true)) column.setAttribute("sql-type",str);
655                str = toString(cfc,prop,meta,"length",data);
656                if(!Util.isEmpty(str,true)) column.setAttribute("length",str);
657            
658                        String generator=toString(cfc,prop,meta,"generator",data);
659                        String type = getType(info,cfc,prop,meta,getDefaultTypeForGenerator(generator,"string"),data);
660                        if(!Util.isEmpty(type))key.setAttribute("type", type);
661                }
662                
663                // many-to-one
664                for(int y=0;y<props.length;y++){
665                        prop=props[y];
666                        meta = prop.getDynamicAttributes();
667                        fieldType = toString(cfc,prop,meta,"fieldType",data);
668                        if(CommonUtil.listFindNoCaseIgnoreEmpty(fieldType,"many-to-one",',')==-1)continue;
669                        
670                        Element key = doc.createElement("key-many-to-one");
671                        cid.appendChild(key);
672                        
673                        // name
674                        key.setAttribute("name",prop.getName());
675                        
676                        // entity-name
677                        setForeignEntityName(cfc,prop, meta, key,false,data);
678                        
679                        // fkcolum
680                        String str=toString(cfc,prop,meta,"fkcolumn",data);
681                        setColumn(doc, key, str,data);
682                        
683                        // lazy
684                        setLazy(cfc,prop,meta,key,data);
685                }
686        }
687        
688        
689        private static void createXMLMappingId(Component cfc,Element clazz, Property prop,Struct columnsInfo,String tableName,SessionFactoryData data) throws PageException {
690                Struct meta = prop.getDynamicAttributes();
691                String str;
692                
693                Document doc = CommonUtil.getDocument(clazz);
694                Element id = doc.createElement("id");
695                clazz.appendChild(id);
696                        
697        // access
698        str=toString(cfc,prop,meta,"access",data);
699                if(!Util.isEmpty(str,true))id.setAttribute("access", str);
700        
701                // name
702                id.setAttribute("name",prop.getName());
703                
704                // column
705                Element column = doc.createElement("column");
706                id.appendChild(column);
707
708                str=toString(cfc,prop,meta,"column",data);
709        if(Util.isEmpty(str,true)) str=prop.getName();
710        column.setAttribute("name",formatColumn(str,data));
711        ColumnInfo info=getColumnInfo(columnsInfo,tableName,str,null);
712        StringBuilder foreignCFC=new StringBuilder();
713                String generator=createXMLMappingGenerator(id,cfc,prop,foreignCFC,data);
714
715                str = toString(cfc,prop,meta,"length",data);
716        if(!Util.isEmpty(str,true)) column.setAttribute("length",str);
717        
718                // type    
719                String type = getType(info,cfc,prop,meta,getDefaultTypeForGenerator(generator,foreignCFC,data),data);
720                //print.o(prop.getName()+":"+type+"::"+getDefaultTypeForGenerator(generator,foreignCFC));
721                if(!Util.isEmpty(type))id.setAttribute("type", type);
722                
723                // unsaved-value
724                str=toString(cfc,prop,meta,"unsavedValue",data);
725                if(str!=null)id.setAttribute("unsaved-value", str);
726                
727        }
728        
729        private static String getDefaultTypeForGenerator(String generator,StringBuilder foreignCFC, SessionFactoryData data) {
730                String value = getDefaultTypeForGenerator(generator, null);
731                if(value!=null) return value;
732                
733                if("foreign".equalsIgnoreCase(generator)) {
734                        if(!Util.isEmpty(foreignCFC.toString())) {
735                                try {
736                                        Component cfc = data.getEntityByCFCName(foreignCFC.toString(), false);
737                                        if(cfc!=null){
738                                                Property[] ids = getIds(cfc,cfc.getProperties(true),null,true,data);
739                                                if(ids!=null && ids.length>0){
740                                                        Property id = ids[0];
741                                                        id.getDynamicAttributes();
742                                                        Struct meta = id.getDynamicAttributes();
743                                                        if(meta!=null){
744                                                                String type=CommonUtil.toString(meta.get(TYPE,null));
745                                                                
746                                                                if(!Util.isEmpty(type) && (!type.equalsIgnoreCase("any") && !type.equalsIgnoreCase("object"))){
747                                                                        return type;
748                                                                }
749                                                                
750                                                                        String g=CommonUtil.toString(meta.get(GENERATOR,null));
751                                                                        if(!Util.isEmpty(g)){
752                                                                                return getDefaultTypeForGenerator(g,foreignCFC,data);
753                                                                        }
754                                                                
755                                                        }
756                                                }
757                                        }
758                                }
759                                catch(Throwable t){
760                                        lucee.commons.lang.ExceptionUtil.rethrowIfNecessary(t);
761                                }
762                        }
763                        return "string";
764                }
765                
766                return "string";
767        }
768        
769        public static String getDefaultTypeForGenerator(String generator,String defaultValue) {
770                if("increment".equalsIgnoreCase(generator)) return "integer";
771                if("identity".equalsIgnoreCase(generator)) return "integer";
772                if("native".equalsIgnoreCase(generator)) return "integer";
773                if("seqhilo".equalsIgnoreCase(generator)) return "string";
774                if("uuid".equalsIgnoreCase(generator)) return "string";
775                if("guid".equalsIgnoreCase(generator)) return "string";
776                if("select".equalsIgnoreCase(generator)) return "string";
777                return defaultValue;
778        }
779        
780        private static String getType(ColumnInfo info, Component cfc,Property prop,Struct meta,String defaultValue, SessionFactoryData data) throws PageException {
781                // ormType
782                String type = toString(cfc,prop,meta,"ormType",data);
783                //type=HibernateCaster.toHibernateType(info,type,null);
784                
785                // dataType
786                if(Util.isEmpty(type,true)){
787                        type=toString(cfc,prop,meta,"dataType",data);
788                        //type=HibernateCaster.toHibernateType(info,type,null);
789                }
790                
791                // type
792                if(Util.isEmpty(type,true)){
793                        type=prop.getType();
794                        //type=HibernateCaster.toHibernateType(info,type,null);
795                }
796                
797                // type from db info
798                if(Util.isEmpty(type,true)){
799                        if(info!=null){
800                                type=info.getTypeName();
801                                //type=HibernateCaster.toHibernateType(info,type,defaultValue);
802                        }
803                        else return defaultValue;
804                }
805                return HibernateCaster.toHibernateType(info,type,defaultValue);
806        }
807
808
809
810
811
812
813
814
815
816        private static ColumnInfo getColumnInfo(Struct columnsInfo,String tableName,String columnName,ColumnInfo defaultValue) {
817                if(columnsInfo!=null) {
818                ColumnInfo info = (ColumnInfo) columnsInfo.get(CommonUtil.createKey(columnName),null);
819                        if(info==null) return defaultValue;
820                        return info;
821        }
822                return defaultValue;
823        }
824
825        private static String createXMLMappingGenerator(Element id,Component cfc,Property prop,StringBuilder foreignCFC, SessionFactoryData data) throws PageException {
826                Struct meta = prop.getDynamicAttributes();
827                
828                // generator
829                String className=toString(cfc,prop,meta,"generator",data);
830                if(Util.isEmpty(className,true)) return null;
831                
832
833                Document doc = CommonUtil.getDocument(id);
834                Element generator = doc.createElement("generator");
835                id.appendChild(generator);
836                
837                generator.setAttribute("class", className);
838                
839                //print.e("generator:"+className);
840                
841                // params
842                Object obj=meta.get(PARAMS,null);
843                //if(obj!=null){
844                        Struct sct=null;
845                        if(obj==null) obj=CommonUtil.createStruct();
846                        else if(obj instanceof String) obj=ORMUtil.convertToSimpleMap((String)obj);
847                        
848                        if(CommonUtil.isStruct(obj)) sct=CommonUtil.toStruct(obj);
849                        else throw ExceptionUtil.createException(data,cfc,"invalid value for attribute [params] of tag [property]",null);
850                        className=className.trim().toLowerCase();
851                        
852                        // special classes
853                        if("foreign".equals(className)){
854                                if(!sct.containsKey(PROPERTY)) sct.setEL(PROPERTY, toString(cfc,prop,meta, PROPERTY,true,data));
855                                
856                                if(sct.containsKey(PROPERTY)){
857                                        String p = CommonUtil.toString(sct.get(PROPERTY),null);
858                                        if(!Util.isEmpty(p))foreignCFC.append(p);
859                                }
860                                
861                                
862                        }
863                        else if("select".equals(className)){
864                                //print.e("select:"+toString(meta, "selectKey",true));
865                                if(!sct.containsKey(KEY)) sct.setEL(KEY, toString(cfc,prop,meta, "selectKey",true,data));
866                        }
867                        else if("sequence".equals(className)){
868                                if(!sct.containsKey(SEQUENCE)) sct.setEL(SEQUENCE, toString(cfc,prop,meta, "sequence",true,data));
869                        }
870                        
871                        //Key[] keys = sct.keys();
872                        Iterator<Entry<Key, Object>> it = sct.entryIterator();
873                        Entry<Key, Object> e;
874                        Element param;
875                        while(it.hasNext()){
876                                e = it.next();
877                                param = doc.createElement("param");
878                                generator.appendChild(param);
879                                
880                                param.setAttribute( "name", e.getKey().getLowerString());
881                                param.appendChild(doc.createTextNode(CommonUtil.toString(e.getValue())));
882                                
883                        }
884                //}
885                return className;
886        }
887        
888        
889        
890        
891
892        
893
894        private static void createXMLMappingProperty(Element clazz, PageContext pc,Component cfc,Property prop,Struct columnsInfo,String tableName,SessionFactoryData data) throws PageException {
895                Struct meta = prop.getDynamicAttributes();
896                
897        
898                
899                // get table name
900                String columnName=toString(cfc,prop,meta,"column",data);
901        if(Util.isEmpty(columnName,true)) columnName=prop.getName();
902        
903        ColumnInfo info=getColumnInfo(columnsInfo,tableName,columnName,null);
904                
905                Document doc = CommonUtil.getDocument(clazz);
906                final Element property = doc.createElement("property");
907                clazz.appendChild(property);
908                
909                //name
910                property.setAttribute("name",prop.getName());
911                
912                // type
913                String str = getType(info, cfc,prop, meta, "string",data);
914                property.setAttribute("type",str);
915                
916                
917                
918                // formula or column
919                str=toString(cfc,prop,meta,"formula",data);
920        Boolean b;
921                if(!Util.isEmpty(str,true))     {
922                property.setAttribute("formula","("+str+")");
923        }
924        else {
925                //property.setAttribute("column",columnName);
926                
927                Element column = doc.createElement("column");
928                property.appendChild(column);
929                column.setAttribute("name", escape(HibernateUtil.convertColumnName(data,columnName)));
930
931            // check
932            str=toString(cfc,prop,meta,"check",data);
933            if(!Util.isEmpty(str,true)) column.setAttribute("check",str);
934            
935                // default
936                str=toString(cfc,prop,meta,"dbDefault",data);
937                if(!Util.isEmpty(str,true)) column.setAttribute("default",str);
938            
939            // index
940            str=toString(cfc,prop,meta,"index",data);
941            if(!Util.isEmpty(str,true)) column.setAttribute("index",str);
942                
943                // length
944            Integer i = toInteger(cfc,meta,"length",data);
945            if(i!=null && i>0) column.setAttribute("length",CommonUtil.toString(i.intValue()));
946            
947            // not-null
948            b=toBoolean(cfc,meta,"notnull",data);
949            if(b!=null && b.booleanValue())column.setAttribute("not-null","true");
950            
951            // precision
952            i=toInteger(cfc,meta,"precision",data);
953            if(i!=null && i>-1) column.setAttribute("precision",CommonUtil.toString(i.intValue()));
954            
955            // scale
956            i=toInteger(cfc,meta,"scale",data);
957            if(i!=null && i>-1) column.setAttribute("scale",CommonUtil.toString(i.intValue()));
958            
959            // sql-type
960            str=toString(cfc,prop,meta,"sqltype",data);
961            if(!Util.isEmpty(str,true)) column.setAttribute("sql-type",str);
962            
963        // unique
964            b=toBoolean(cfc,meta,"unique",data);
965                if(b!=null && b.booleanValue())column.setAttribute("unique","true");
966                
967                // unique-key
968                str=toString(cfc,prop,meta,"uniqueKey",data);
969                if(Util.isEmpty(str))str=CommonUtil.toString(meta.get(UNIQUE_KEY_NAME,null),null);
970                if(!Util.isEmpty(str,true)) column.setAttribute("unique-key",str);
971                
972                
973        }
974        
975        // generated
976        str=toString(cfc,prop,meta,"generated",data);
977        if(!Util.isEmpty(str,true)){
978                str=str.trim().toLowerCase();
979                
980                if("always".equals(str) || "insert".equals(str) || "never".equals(str))
981                        property.setAttribute("generated",str);
982                else
983                        throw invalidValue(cfc,prop,"generated",str,"always,insert,never",data);
984                                //throw new ORMException("invalid value ["+str+"] for attribute [generated] of column ["+columnName+"], valid values are [always,insert,never]");
985        }
986        
987        
988        // update
989        b=toBoolean(cfc,meta,"update",data);
990        if(b!=null && !b.booleanValue())property.setAttribute("update","false");
991        
992        // insert
993        b=toBoolean(cfc,meta,"insert",data);
994        if(b!=null && !b.booleanValue())property.setAttribute("insert","false");
995        
996        // lazy (dtd defintion:<!ATTLIST property lazy (true|false) "false">)
997        b=toBoolean(cfc,meta,"lazy",data);
998        if(b!=null && b.booleanValue())property.setAttribute("lazy","true");
999        
1000        // optimistic-lock
1001        b=toBoolean(cfc,meta,"optimisticlock",data);
1002        if(b!=null && !b.booleanValue())property.setAttribute("optimistic-lock","false");
1003        
1004        }
1005        
1006        
1007        /*
1008        MUST dies kommt aber nicht hier sondern in verarbeitung in component
1009        <cfproperty 
1010    persistent="true|false" 
1011   >
1012         * */
1013        private static void createXMLMappingOneToOne(Element clazz, PageContext pc,Component cfc,Property prop,SessionFactoryData data) throws PageException {
1014                Struct meta = prop.getDynamicAttributes();
1015                
1016                Boolean b;
1017                
1018                Document doc = CommonUtil.getDocument(clazz);
1019                Element x2o;
1020                
1021                // column
1022                String fkcolumn=toString(cfc,prop,meta,"fkcolumn",data);
1023                String linkTable=toString(cfc,prop,meta,"linkTable",data);
1024                
1025                
1026                if(!Util.isEmpty(linkTable,true) || !Util.isEmpty(fkcolumn,true)) {
1027                        clazz=getJoin(clazz);
1028                        
1029                        x2o= doc.createElement("many-to-one");
1030                        //x2o.setAttribute("column", fkcolumn);
1031                        x2o.setAttribute("unique", "true");
1032                        
1033                        if(!Util.isEmpty(linkTable,true)){
1034                                setColumn(doc, x2o, linkTable,data);
1035                        }
1036                        else {
1037                                setColumn(doc, x2o, fkcolumn,data);
1038                        }
1039
1040                        
1041                        
1042                        
1043                        // update
1044                b=toBoolean(cfc,meta,"update",data);
1045                if(b!=null)x2o.setAttribute("update",CommonUtil.toString(b.booleanValue()));
1046                
1047                // insert
1048                b=toBoolean(cfc,meta,"insert",data);
1049                if(b!=null)x2o.setAttribute("insert",CommonUtil.toString(b.booleanValue()));
1050                
1051                // not-null
1052                b=toBoolean(cfc,meta,"notNull",data);
1053                if(b!=null)x2o.setAttribute("not-null",CommonUtil.toString(b.booleanValue()));
1054                
1055                
1056                // optimistic-lock
1057                b=toBoolean(cfc,meta,"optimisticLock",data);
1058                if(b!=null)x2o.setAttribute("optimistic-lock",CommonUtil.toString(b.booleanValue()));
1059                
1060                // not-found
1061                        b=toBoolean(cfc,meta, "missingRowIgnored",data);
1062                if(b!=null && b.booleanValue()) x2o.setAttribute("not-found", "ignore");
1063
1064                        /* / index
1065                        str=toString(meta,"index");
1066                        if(!Util.isEmpty(str,true)) x2o.setAttribute("index", str); 
1067                        */
1068                        
1069                        
1070                }
1071                else {
1072                        x2o= doc.createElement("one-to-one");
1073                }
1074                clazz.appendChild(x2o);
1075                
1076                // access
1077                String str=toString(cfc,prop,meta,"access",data);
1078                if(!Util.isEmpty(str,true)) x2o.setAttribute("access", str);
1079                        
1080                // constrained
1081                b=toBoolean(cfc,meta, "constrained",data);
1082        if(b!=null && b.booleanValue()) x2o.setAttribute("constrained", "true");
1083                
1084                // formula
1085                str=toString(cfc,prop,meta,"formula",data);
1086                if(!Util.isEmpty(str,true)) x2o.setAttribute("formula", str);
1087                
1088                // embed-xml
1089                str=toString(cfc,prop,meta,"embedXml",data);
1090                if(!Util.isEmpty(str,true)) x2o.setAttribute("embed-xml", str);
1091                
1092                // property-ref
1093                str=toString(cfc,prop,meta,"mappedBy",data);
1094                if(!Util.isEmpty(str,true)) x2o.setAttribute("property-ref", str);
1095                
1096                // foreign-key
1097                str=toString(cfc,prop,meta,"foreignKeyName",data);
1098                if(Util.isEmpty(str,true)) str=toString(cfc,prop,meta,"foreignKey",data);
1099                if(!Util.isEmpty(str,true)) x2o.setAttribute("foreign-key", str);
1100        
1101                setForeignEntityName(cfc,prop,meta,x2o,true,data);
1102                
1103                createXMLMappingXToX(x2o, pc,cfc,prop,meta,data);
1104        }
1105        
1106        
1107        
1108        
1109        
1110        
1111        private static Component loadForeignCFC(PageContext pc,Component cfc,Property prop, Struct meta, SessionFactoryData data) throws PageException {
1112                // entity
1113                String str=toString(cfc,prop,meta,"entityName",data);
1114                Component fcfc=null;
1115                
1116                if(!Util.isEmpty(str,true)) {
1117                        fcfc = data.getEntityByEntityName(str, false);
1118                        if(fcfc!=null) return fcfc;
1119                }
1120                        
1121                str = toString(cfc,prop,meta,"cfc",false,data);
1122                if(!Util.isEmpty(str,true)){
1123                        return data.getEntityByCFCName(str, false);
1124                }
1125                return null;
1126        }
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136        private static void createXMLMappingCollection(Element clazz, PageContext pc,Component cfc,Property prop,SessionFactoryData data) throws PageException {
1137                Struct meta = prop.getDynamicAttributes();
1138                Document doc = CommonUtil.getDocument(clazz);
1139                Element el=null;
1140                        
1141                // collection type
1142                String str=prop.getType();
1143                if(Util.isEmpty(str,true) || "any".equalsIgnoreCase(str) || "object".equalsIgnoreCase(str))str="array";
1144                else str=str.trim().toLowerCase();
1145                
1146                
1147                
1148                // bag
1149                if("array".equals(str) || "bag".equals(str)){
1150                        el = doc.createElement("bag");
1151                }
1152                // map
1153                else if("struct".equals(str) || "map".equals(str)){
1154                        el = doc.createElement("map");
1155                        
1156                        
1157                        // map-key
1158                        str=toString(cfc,prop,meta,"structKeyColumn",true,data);
1159                        if(!Util.isEmpty(str,true)) {
1160                                Element mapKey=doc.createElement("map-key");
1161                                el.appendChild(mapKey);
1162                                mapKey.setAttribute("column", str);
1163                                
1164                                // type
1165                                str=toString(cfc,prop,meta,"structKeyType",data);
1166                                if(!Util.isEmpty(str,true))mapKey.setAttribute("type", str);
1167                                else mapKey.setAttribute("type", "string");
1168                        }
1169                }
1170                else throw invalidValue(cfc,prop,"collectiontype",str,"array,struct",data);
1171                //throw new ORMException("invalid value ["+str+"] for attribute [collectiontype], valid values are [array,struct]");
1172                
1173                setBeforeJoin(clazz,el);
1174                
1175                // name 
1176                el.setAttribute("name", prop.getName());
1177                
1178                // table
1179                str=toString(cfc,prop,meta, "table",true,data);
1180                el.setAttribute("table",escape(HibernateUtil.convertTableName(data,str)));
1181                
1182                // catalog
1183                str=toString(cfc,prop,meta, "catalog",data);
1184                if(!Util.isEmpty(str,true))el.setAttribute("catalog",str);
1185                
1186                // schema
1187                str=toString(cfc,prop,meta, "schema",data);
1188                if(!Util.isEmpty(str,true))el.setAttribute("schema",str);
1189                
1190                // mutable
1191                Boolean b=toBoolean(cfc,meta, "readonly",data);
1192        if(b!=null && b.booleanValue()) el.setAttribute("mutable", "false");
1193                
1194                // order-by
1195                str=toString(cfc,prop,meta, "orderby",data);
1196                if(!Util.isEmpty(str,true))el.setAttribute("order-by",str);
1197                
1198                // element-column
1199                str=toString(cfc,prop,meta,"elementcolumn",data);
1200                if(!Util.isEmpty(str,true)){
1201                        Element element = doc.createElement("element");
1202                        el.appendChild(element);
1203                        
1204                        // column
1205                        element.setAttribute("column", formatColumn(str,data));
1206                        
1207                        // type
1208                        str=toString(cfc,prop,meta,"elementtype",data);
1209                        if(!Util.isEmpty(str,true)) element.setAttribute("type", str);
1210                }
1211                
1212        // batch-size
1213        Integer i=toInteger(cfc,meta, "batchsize",data);
1214        if(i!=null && i.intValue()>1) el.setAttribute("batch-size", CommonUtil.toString(i.intValue()));
1215 
1216                // column
1217                str=toString(cfc,prop,meta,"fkcolumn",data);
1218                if(Util.isEmpty(str,true)) str=toString(cfc,prop,meta,"column",data);
1219                if(!Util.isEmpty(str,true)){
1220                        Element key = doc.createElement("key");
1221                        CommonUtil.setFirst(el,key);
1222                        //el.appendChild(key);
1223                        
1224                        // column
1225                        key.setAttribute("column", formatColumn(str,data));
1226                        
1227                        // property-ref
1228                        str=toString(cfc,prop,meta,"mappedBy",data);
1229                        if(!Util.isEmpty(str,true)) key.setAttribute("property-ref", str);
1230                }
1231                
1232                // cache
1233                setCacheStrategy(cfc,prop,doc, meta, el,data);
1234                
1235                // optimistic-lock
1236                b=toBoolean(cfc,meta, "optimisticlock",data);
1237        if(b!=null && !b.booleanValue()) el.setAttribute("optimistic-lock", "false");
1238        
1239       
1240        }
1241        
1242        
1243        private static void setBeforeJoin(Element clazz, Element el) {
1244                Element join;
1245                if(clazz.getNodeName().equals("join")) {
1246                        join=clazz;
1247                        clazz = getClazz(clazz);
1248                }
1249                else {
1250                        join = getJoin(clazz);
1251                }
1252                
1253                if(join==clazz) clazz.appendChild(el);
1254                else clazz.insertBefore(el, join);
1255                
1256                
1257        }
1258        
1259        private static Element getClazz(Element join) {
1260                if(join.getNodeName().equals("join")){
1261                        return (Element) join.getParentNode();
1262                }
1263                return join;
1264        }
1265
1266        private static Element getJoin(Element clazz) {
1267                if(clazz.getNodeName().equals("subclass")){
1268                        NodeList joins = clazz.getElementsByTagName("join");
1269                        if(joins!=null && joins.getLength()>0)
1270                                return (Element)joins.item(0);
1271                }
1272                return clazz;
1273        }
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286        private static void createXMLMappingManyToMany(Component cfc,PropertyCollection propColl,Element clazz, PageContext pc,Property prop,DatasourceConnection dc, SessionFactoryData data) throws PageException {
1287                Element el = createXMLMappingXToMany(propColl,clazz, pc, cfc,prop,data);
1288                Struct meta = prop.getDynamicAttributes();
1289                Document doc = CommonUtil.getDocument(clazz);
1290                Element m2m = doc.createElement("many-to-many");
1291                el.appendChild(m2m);
1292                
1293                // link
1294                setLink(cfc,prop,el,meta,true,data);
1295                
1296                setForeignEntityName(cfc,prop, meta, m2m,true,data);
1297
1298        // order-by
1299                String str = toString(cfc,prop,meta,"orderby",data);
1300                if(!Util.isEmpty(str,true))m2m.setAttribute("order-by", str);
1301                
1302                // column
1303                str=toString(cfc,prop,meta,"inversejoincolumn",data);
1304                
1305                // build fkcolumn name
1306                if(Util.isEmpty(str,true)) {
1307                        Component other = loadForeignCFC(pc, cfc, prop, meta,data);
1308                        if(other!=null){
1309                                boolean isClass=Util.isEmpty(other.getExtends());
1310                                // MZ: Recursive search for persistent mappedSuperclass properties
1311                                Property[] _props=getProperties(pc,other,dc,meta,isClass, true,data);
1312                                PropertyCollection _propColl = splitJoins(cfc,new HashMap<String, PropertyCollection>(), _props,data);
1313                                _props=_propColl.getProperties();
1314                                
1315                                Struct m;
1316                                Property _prop=null;
1317                                for(int i=0;i<_props.length;i++){
1318                                        m = _props[i].getDynamicAttributes();
1319                                        // fieldtype
1320                                        String fieldtype = CommonUtil.toString(m.get(FIELDTYPE,null),null);
1321                                        if("many-to-many".equalsIgnoreCase(fieldtype)) {
1322                                                // linktable
1323                                                String currLinkTable=CommonUtil.toString(meta.get(LINK_TABLE,null),null);
1324                                                String othLinkTable=CommonUtil.toString(m.get(LINK_TABLE,null),null);
1325                                                if(currLinkTable.equals(othLinkTable)) {
1326                                                        // cfc name
1327                                                        String cfcName=CommonUtil.toString(m.get(CFC,null),null);
1328                                                        if(cfc.equalTo(cfcName)){
1329                                                                _prop=_props[i];
1330                                                        }
1331                                                }
1332                                        }
1333                                }
1334                                str=createM2MFKColumnName( other, _prop, _propColl,data);
1335                        }
1336                }
1337                setColumn(doc, m2m, str,data);
1338                
1339                // not-found
1340                Boolean b=toBoolean(cfc,meta, "missingrowignored",data);
1341        if(b!=null && b.booleanValue()) m2m.setAttribute("not-found", "ignore");
1342        
1343        // property-ref
1344                str=toString(cfc,prop,meta,"mappedby",data);
1345                if(!Util.isEmpty(str,true)) m2m.setAttribute("property-ref", str);
1346                
1347                // foreign-key
1348                str=toString(cfc,prop,meta,"foreignKeyName",data);
1349                if(Util.isEmpty(str,true)) str=toString(cfc,prop,meta,"foreignKey",data);
1350                if(!Util.isEmpty(str,true)) m2m.setAttribute("foreign-key", str);
1351        }
1352        
1353        private static boolean setLink(Component cfc,Property prop,Element el, Struct meta, boolean linkTableRequired,SessionFactoryData data) throws PageException {
1354                String str=toString(cfc,prop,meta, "linktable",linkTableRequired,data);
1355                
1356                
1357                if(!Util.isEmpty(str,true)){
1358
1359                        el.setAttribute("table", escape(HibernateUtil.convertTableName(data,str)));
1360                
1361                        // schema
1362                        str=toString(cfc,prop,meta, "linkschema",data);
1363                        if(Util.isEmpty(str,true)) str=data.getORMConfiguration().getSchema();
1364                        if(!Util.isEmpty(str,true)) el.setAttribute("schema", str);
1365                        
1366                        // catalog
1367                        str=toString(cfc,prop,meta, "linkcatalog",data);
1368                        if(Util.isEmpty(str,true)) str=data.getORMConfiguration().getCatalog();
1369                        if(!Util.isEmpty(str,true)) el.setAttribute("catalog", str);
1370                        return true;
1371                }
1372                return false;
1373        }
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383        private static void createXMLMappingOneToMany(Component cfc,PropertyCollection propColl,Element clazz, PageContext pc,Property prop, SessionFactoryData data) throws PageException {
1384                Element el = createXMLMappingXToMany(propColl,clazz, pc, cfc,prop,data);
1385                Struct meta = prop.getDynamicAttributes();
1386                Document doc = CommonUtil.getDocument(clazz);
1387                Element x2m;
1388                
1389                
1390        // order-by
1391                String str = toString(cfc,prop,meta,"orderby",data);
1392                if(!Util.isEmpty(str,true))el.setAttribute("order-by", str);
1393                
1394                // link
1395                if(setLink(cfc,prop,el,meta,false,data)){
1396                        x2m = doc.createElement("many-to-many");
1397                        x2m.setAttribute("unique","true");
1398                        
1399                        str=toString(cfc,prop,meta,"inversejoincolumn",data);
1400                        setColumn(doc, x2m, str,data);
1401                }
1402                else {
1403                        x2m = doc.createElement("one-to-many");
1404                }
1405                el.appendChild(x2m);
1406                
1407
1408                // entity-name
1409                
1410                setForeignEntityName(cfc,prop,meta,x2m,true,data);
1411                
1412        }
1413
1414        
1415
1416        
1417        
1418        
1419        
1420        
1421        private static Element createXMLMappingXToMany(PropertyCollection propColl,Element clazz, PageContext pc,Component cfc,Property prop, SessionFactoryData data) throws PageException {
1422                final Struct meta = prop.getDynamicAttributes();
1423                Document doc = CommonUtil.getDocument(clazz);
1424                Element el=null;
1425                
1426                
1427                
1428        // collection type
1429                String str=prop.getType();
1430                if(Util.isEmpty(str,true) || "any".equalsIgnoreCase(str) || "object".equalsIgnoreCase(str))str="array";
1431                else str=str.trim().toLowerCase();
1432                
1433                Element mapKey=null;
1434                // bag
1435                if("array".equals(str) || "bag".equals(str)){
1436                        el = doc.createElement("bag");
1437                        
1438                }
1439                // map
1440                else if("struct".equals(str) || "map".equals(str)){
1441                        el = doc.createElement("map");
1442                        
1443                        // map-key
1444                        mapKey = doc.createElement("map-key");
1445                        //el.appendChild(mapKey);
1446                        
1447                        // column
1448                        str=toString(cfc,prop,meta,"structKeyColumn",true,data);
1449                        mapKey.setAttribute("column", formatColumn(str,data));
1450                        
1451                        // type
1452                        str=toString(cfc,prop,meta,"structKeyType",data);
1453                        if(!Util.isEmpty(str,true))mapKey.setAttribute("type", str);
1454                        else mapKey.setAttribute("type", "string");// MUST get type dynamicly
1455                }
1456                else throw invalidValue(cfc,prop,"collectiontype",str,"array,struct",data);
1457                //throw new ORMException("invalid value ["+str+"] for attribute [collectiontype], valid values are [array,struct]");
1458                
1459                setBeforeJoin(clazz,el);
1460                
1461
1462                
1463                // batch-size
1464        Integer i=toInteger(cfc,meta, "batchsize",data);
1465        if(i!=null){
1466                if(i.intValue()>1) el.setAttribute("batch-size", CommonUtil.toString(i.intValue()));
1467        }
1468 
1469                // cacheUse
1470        setCacheStrategy(cfc,prop,doc, meta, el,data);
1471        
1472        // column
1473        str=createFKColumnName(cfc,prop,propColl,data);
1474                
1475                if(!Util.isEmpty(str,true)){
1476                        Element key = doc.createElement("key");
1477                        el.appendChild(key);
1478                        
1479                        // column
1480                        setColumn(doc,key,str,data);
1481                        
1482                        // property-ref
1483                        str=toString(cfc,prop,meta,"mappedBy",data);
1484                        if(!Util.isEmpty(str,true)) key.setAttribute("property-ref", str);
1485                }
1486                
1487        // inverse
1488                Boolean b = toBoolean(cfc,meta, "inverse",data);
1489        if(b!=null && b.booleanValue()) el.setAttribute("inverse", "true");
1490        
1491                
1492                
1493                // mutable 
1494                b = toBoolean(cfc,meta, "readonly",data);
1495        if(b!=null && b.booleanValue()) el.setAttribute("mutable", "false");
1496                
1497                // optimistic-lock
1498                b=toBoolean(cfc,meta, "optimisticlock",data);
1499        if(b!=null && !b.booleanValue()) el.setAttribute("optimistic-lock", "false");
1500        
1501                // where
1502                str=toString(cfc,prop,meta,"where",data);
1503                if(!Util.isEmpty(str,true)) el.setAttribute("where", str);
1504        
1505                // add map key
1506        if(mapKey!=null)el.appendChild(mapKey);
1507        
1508        
1509                createXMLMappingXToX(el, pc,cfc,prop,meta,data);
1510                
1511                return el;
1512        }
1513        
1514        
1515        
1516        private static String createFKColumnName(Component cfc, Property prop, PropertyCollection propColl, SessionFactoryData data) throws PageException {
1517                
1518                
1519                // fk column from local defintion
1520                String str=prop==null?null:toString(cfc,prop,prop.getDynamicAttributes(),"fkcolumn",data);
1521                if(!Util.isEmpty(str))
1522                        return str;
1523                
1524                // no local defintion, get from Foreign enity
1525                Struct meta = prop.getDynamicAttributes();
1526                String type=toString(cfc,prop,meta,"fieldtype",false,data);
1527                String otherType;
1528                if("many-to-one".equalsIgnoreCase(type))                otherType="one-to-many";
1529                else if("one-to-many".equalsIgnoreCase(type))   otherType="many-to-one";
1530                else return createM2MFKColumnName( cfc, prop, propColl,data);
1531                
1532                String feName = toString(cfc,prop,meta,"cfc",true,data);
1533                Component feCFC=data.getEntityByCFCName(feName, false);
1534                Property[] feProps = feCFC.getProperties(true);
1535                
1536                Property p;
1537                Component _cfc;
1538                for(int i=0;i<feProps.length;i++){
1539                        p=feProps[i];
1540
1541                        // compare fieldType
1542                        str=toString(feCFC,p,p.getDynamicAttributes(),"fieldtype",false,data);
1543                        if(!otherType.equalsIgnoreCase(str)) continue;
1544                        
1545                        // compare cfc
1546                        str=toString(feCFC,p,p.getDynamicAttributes(),"cfc",false,data);
1547                        if(Util.isEmpty(str)) continue;
1548                        _cfc=data.getEntityByCFCName(str, false);
1549                        if(_cfc==null || !_cfc.equals(cfc))continue;
1550                        
1551                        // get fkcolumn
1552                        str=toString(_cfc,p,p.getDynamicAttributes(),"fkcolumn",data);
1553                        if(!Util.isEmpty(str)) return str;
1554                        
1555                        
1556                }
1557                throw ExceptionUtil.createException(data,null,"cannot terminate foreign key column name for component "+cfc.getName(),null);
1558        }
1559        
1560        
1561        private static String createM2MFKColumnName(Component cfc, Property prop, PropertyCollection propColl,SessionFactoryData data) throws PageException {
1562                
1563                String str=prop==null?null:toString(cfc,prop,prop.getDynamicAttributes(),"fkcolumn",data);
1564                if(Util.isEmpty(str)){
1565                        Property[] ids = getIds(cfc,propColl,data);
1566                        if(ids.length==1) {
1567                                str=toString(cfc,ids[0],ids[0].getDynamicAttributes(),"column",data);
1568                        if(Util.isEmpty(str,true)) str=ids[0].getName();
1569                        }
1570                        else if(prop!=null)str=toString(cfc,prop,prop.getDynamicAttributes(),"fkcolumn",true,data);
1571                        else
1572                                throw ExceptionUtil.createException(data,null,"cannot terminate foreign key column name for component "+cfc.getName(),null);
1573                        
1574                        str=HibernateCaster.getEntityName(cfc)+"_"+str;
1575                }
1576        return str;
1577        }
1578
1579        private static void setForeignEntityName(Component cfc,Property prop, Struct meta, Element el, boolean cfcRequired, SessionFactoryData data) throws PageException {
1580                // entity
1581                String str=cfcRequired?null:toString(cfc,prop,meta,"entityName",data);
1582                if(!Util.isEmpty(str,true)) {
1583                        el.setAttribute("entity-name", str);
1584                }
1585                else {
1586                        // cfc
1587                        //createFKColumnName( cfc, prop, propColl);
1588                        
1589                        str = toString(cfc,prop,meta,"cfc",cfcRequired,data);
1590                        if(!Util.isEmpty(str,true)){
1591                                Component _cfc=data.getEntityByCFCName(str, false);
1592                                str=HibernateCaster.getEntityName(_cfc);
1593                                el.setAttribute("entity-name", str);
1594                        }
1595                }
1596        }
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606        private static void setCacheStrategy(Component cfc,Property prop,Document doc,Struct meta, Element el, SessionFactoryData data) throws PageException {
1607                String strategy = toString(cfc,prop,meta,"cacheuse",data);
1608                
1609                if(!Util.isEmpty(strategy,true)){
1610                        strategy=strategy.trim().toLowerCase();
1611                        if("read-only".equals(strategy) || "nonstrict-read-write".equals(strategy) || "read-write".equals(strategy) || "transactional".equals(strategy)){
1612                                Element cache = doc.createElement("cache");
1613                                CommonUtil.setFirst(el, cache);
1614                                el.appendChild(cache);
1615                                cache.setAttribute("usage", strategy);
1616                                String name = toString(cfc,prop,meta,"cacheName",data);
1617                                if(!Util.isEmpty(name,true)){
1618                                        cache.setAttribute("region", name);
1619                                }
1620                        }       
1621                        else
1622                                throw ExceptionUtil.createException(data,cfc,"invalid value ["+strategy+"] for attribute [cacheuse], valid values are [read-only,nonstrict-read-write,read-write,transactional]",null);
1623                }
1624                
1625        }
1626
1627        
1628
1629
1630
1631
1632
1633        private static void setColumn(Document doc, Element el, String columnValue,SessionFactoryData data) throws PageException {
1634                if(Util.isEmpty(columnValue,true)) return;
1635                
1636                String[] arr = CommonUtil.toStringArray(columnValue, ',');
1637                if(arr.length==1){
1638                        el.setAttribute("column", formatColumn(arr[0],data));
1639                }
1640                else {
1641                        Element column;
1642                        for(int i=0;i<arr.length;i++){
1643                                column=doc.createElement("column");
1644                                el.appendChild(column);
1645                                column.setAttribute("name", formatColumn(arr[i],data));
1646                        }
1647                }
1648        }
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658        private static void createXMLMappingManyToOne(Element clazz, PageContext pc,Component cfc,Property prop, PropertyCollection propColl, SessionFactoryData data) throws PageException {
1659                Struct meta = prop.getDynamicAttributes();
1660                Boolean b;
1661                
1662                Document doc = CommonUtil.getDocument(clazz);
1663                clazz=getJoin(clazz);
1664                
1665                Element m2o = doc.createElement("many-to-one");
1666                clazz.appendChild(m2o);
1667                
1668                // columns
1669                String linktable = toString(cfc,prop,meta,"linktable",data);
1670                String _columns;
1671                if(!Util.isEmpty(linktable,true)) _columns=toString(cfc,prop,meta,"inversejoincolumn",data);
1672                else _columns=createFKColumnName(cfc, prop, propColl,data);//toString(cfc,prop,meta,"fkcolumn");
1673                setColumn(doc, m2o, _columns,data);
1674                
1675                // cfc
1676                setForeignEntityName(cfc,prop,meta,m2o,true,data);
1677                
1678                // column
1679                //String str=toString(prop,meta,"column",true);
1680                //m2o.setAttribute("column", str);
1681                
1682                // insert
1683                b=toBoolean(cfc,meta, "insert",data);
1684        if(b!=null && !b.booleanValue()) m2o.setAttribute("insert", "false");
1685                
1686        // update
1687                b=toBoolean(cfc,meta, "update",data);
1688        if(b!=null && !b.booleanValue()) m2o.setAttribute("update", "false");
1689                
1690        // property-ref
1691                String str=toString(cfc,prop,meta,"mappedBy",data);
1692                if(!Util.isEmpty(str,true)) m2o.setAttribute("property-ref", str);
1693
1694        // update
1695                b=toBoolean(cfc,meta, "unique",data);
1696        if(b!=null && b.booleanValue()) m2o.setAttribute("unique", "true");
1697
1698        // not-null
1699                b=toBoolean(cfc,meta, "notnull",data);
1700        if(b!=null && b.booleanValue()) m2o.setAttribute("not-null", "true");
1701                
1702        // optimistic-lock
1703                b=toBoolean(cfc,meta, "optimisticLock",data);
1704        if(b!=null && !b.booleanValue()) m2o.setAttribute("optimistic-lock", "false");
1705        
1706        // not-found
1707                b=toBoolean(cfc,meta, "missingRowIgnored",data);
1708        if(b!=null && b.booleanValue()) m2o.setAttribute("not-found", "ignore");
1709        
1710        // index
1711                str=toString(cfc,prop,meta,"index",data);
1712                if(!Util.isEmpty(str,true)) m2o.setAttribute("index", str);
1713        
1714        // unique-key
1715                str=toString(cfc,prop,meta,"uniqueKeyName",data);
1716                if(Util.isEmpty(str,true))str=toString(cfc,prop,meta,"uniqueKey",data);
1717                if(!Util.isEmpty(str,true)) m2o.setAttribute("unique-key", str);
1718                
1719                // foreign-key
1720                str=toString(cfc,prop,meta,"foreignKeyName",data);
1721                if(Util.isEmpty(str,true)) str=toString(cfc,prop,meta,"foreignKey",data);
1722                if(!Util.isEmpty(str,true)) m2o.setAttribute("foreign-key", str);
1723
1724                // access
1725                str=toString(cfc,prop,meta,"access",data);
1726                if(!Util.isEmpty(str,true)) m2o.setAttribute("access", str);
1727                
1728        createXMLMappingXToX(m2o, pc,cfc,prop,meta,data);
1729        
1730        }
1731        
1732        
1733        
1734        
1735        
1736        
1737        
1738        
1739        private static String formatColumn(String name,SessionFactoryData data) throws PageException {
1740        name=name.trim();
1741                return escape(HibernateUtil.convertColumnName(data,name));
1742    }
1743        
1744        /*
1745
1746 
1747        <cfproperty 
1748cfc="Referenced_CFC_Name" 
1749linktable="Link table name" 
1750linkcatalog="Catalog for the link table" 
1751linkschema="Schema for the link table" 
1752fkcolumn="Foreign Key column name" 
1753inversejoincolumn="Column name or comma-separated list of primary key columns" 
1754
1755
1756>
1757
1758        */
1759        private static void createXMLMappingXToX(Element x2x, PageContext pc, Component cfc,Property prop, Struct meta, SessionFactoryData data) throws PageException {
1760                x2x.setAttribute("name",prop.getName());
1761                
1762                // cascade
1763                String str=toString(cfc,prop,meta,"cascade",data);
1764                if(!Util.isEmpty(str,true)) x2x.setAttribute("cascade", str);
1765                
1766                // fetch
1767                str=toString(cfc,prop,meta,"fetch",data);
1768                if(!Util.isEmpty(str,true)) {
1769                        str=str.trim().toLowerCase();
1770                        if("join".equals(str) || "select".equals(str))
1771                                x2x.setAttribute("fetch", str);
1772                        else
1773                                throw invalidValue(cfc,prop,"fetch",str,"join,select",data);
1774                        //throw new ORMException("invalid value ["+str+"] for attribute [fetch], valid values are [join,select]");
1775                }
1776                
1777                // lazy
1778                setLazy(cfc,prop,meta,x2x,data);
1779                
1780        }
1781
1782
1783
1784        
1785
1786        private static void setLazy(Component cfc,Property prop, Struct meta, Element x2x, SessionFactoryData data) throws PageException {
1787                String str = toString(cfc,prop,meta, "lazy",data);
1788                if(!Util.isEmpty(str,true)){
1789                        str=str.trim();
1790                        String name=x2x.getNodeName();
1791                        Boolean b = CommonUtil.toBoolean(str,null);
1792                        
1793                        // <!ATTLIST many-to-one lazy (false|proxy|no-proxy) #IMPLIED>
1794                        // <!ATTLIST one-to-one lazy (false|proxy|no-proxy) #IMPLIED>
1795                        if("many-to-one".equals(name) || "one-to-one".equals(name)) {
1796                                if(b!=null) x2x.setAttribute("lazy", b.booleanValue()?"proxy":"false");
1797                                else if("proxy".equalsIgnoreCase(str)) x2x.setAttribute("lazy", "proxy");
1798                                else if("no-proxy".equalsIgnoreCase(str)) x2x.setAttribute("lazy", "no-proxy");
1799                                else throw invalidValue(cfc,prop,"lazy",str,"true,false,proxy,no-proxy",data);
1800                        }
1801                        
1802
1803                        // <!ATTLIST many-to-many lazy (false|proxy) #IMPLIED>
1804                        // <!ATTLIST key-many-to-one lazy (false|proxy) #IMPLIED>
1805                        else if("many-to-many".equals(name) || "key-many-to-one".equals(name)) {
1806                                if(b!=null) x2x.setAttribute("lazy", b.booleanValue()?"proxy":"false");
1807                                else if("proxy".equalsIgnoreCase(str)) x2x.setAttribute("lazy", "proxy");
1808                                throw invalidValue(cfc,prop,"lazy",str,"true,false,proxy",data);
1809                                
1810                        }
1811                        
1812                        else {
1813                                if(b!=null)     x2x.setAttribute("lazy", b.booleanValue()?"true":"false");
1814                                else if("extra".equalsIgnoreCase(str)) x2x.setAttribute("lazy", "extra");
1815                                else  throw invalidValue(cfc,prop,"lazy",str,"true,false,extra",data);
1816                        }
1817                }
1818        }
1819
1820        private static void createXMLMappingTimestamp(Element clazz, PageContext pc,Component cfc,Property prop,SessionFactoryData data) throws PageException {
1821                Struct meta = prop.getDynamicAttributes();
1822                String str;
1823                Boolean b;
1824                
1825
1826                Document doc = CommonUtil.getDocument(clazz);
1827                Element timestamp = doc.createElement("timestamp");
1828                clazz.appendChild(timestamp);
1829                
1830                timestamp.setAttribute("name",prop.getName());
1831                
1832                 // access
1833                str=toString(cfc,prop,meta,"access",data);
1834                if(!Util.isEmpty(str,true))timestamp.setAttribute("access", str);
1835                
1836                // column
1837                str=toString(cfc,prop,meta,"column",data);
1838                if(Util.isEmpty(str,true)) str=prop.getName();
1839                timestamp.setAttribute("column",formatColumn(str,data));
1840
1841                // generated
1842                b=toBoolean(cfc,meta, "generated",data);
1843        if(b!=null) timestamp.setAttribute("generated", b.booleanValue()?"always":"never");
1844        
1845        // source
1846        str=toString(cfc,prop,meta,"source",data);
1847                if(!Util.isEmpty(str,true)) {
1848                        str=str.trim().toLowerCase();
1849                        if("db".equals(str) || "vm".equals(str))
1850                                timestamp.setAttribute("source", str);
1851                        else 
1852                                throw invalidValue(cfc,prop,"source",str,"db,vm",data);
1853                }
1854                
1855                // unsavedValue
1856        str=toString(cfc,prop,meta,"unsavedValue",data);
1857                if(!Util.isEmpty(str,true)) {
1858                        str=str.trim().toLowerCase();
1859                        if("null".equals(str) || "undefined".equals(str))
1860                                timestamp.setAttribute("unsaved-value", str);
1861                        else 
1862                                throw invalidValue(cfc,prop,"unsavedValue",str,"null, undefined",data);
1863                                //throw new ORMException("invalid value ["+str+"] for attribute [unsavedValue], valid values are [null, undefined]");
1864                }
1865        }
1866
1867        
1868        private static PageException invalidValue(Component cfc,Property prop, String attrName, String invalid, String valid, SessionFactoryData data) {
1869                String owner = prop.getOwnerName();
1870                if(Util.isEmpty(owner))return ExceptionUtil.createException(data,cfc,"invalid value ["+invalid+"] for attribute ["+attrName+"] of property ["+prop.getName()+"], valid values are ["+valid+"]",null);
1871                return ExceptionUtil.createException(data,cfc,"invalid value ["+invalid+"] for attribute ["+attrName+"] of property ["+prop.getName()+"] of Component ["+CommonUtil.last(owner,'.')+"], valid values are ["+valid+"]",null);
1872        }
1873
1874
1875
1876
1877
1878
1879        private static void createXMLMappingVersion(Element clazz, PageContext pc,Component cfc,Property prop,SessionFactoryData data) throws PageException {
1880                Struct meta = prop.getDynamicAttributes();
1881                
1882                Document doc = CommonUtil.getDocument(clazz);
1883                Element version = doc.createElement("version");
1884                clazz.appendChild(version);
1885                
1886                
1887                version.setAttribute("name",prop.getName());
1888                
1889                // column
1890                String str = toString(cfc,prop,meta,"column",data);
1891                if(Util.isEmpty(str,true)) str=prop.getName();
1892                version.setAttribute("column",formatColumn(str,data));
1893                
1894                 // access
1895        str=toString(cfc,prop,meta,"access",data);
1896                if(!Util.isEmpty(str,true))version.setAttribute("access", str);
1897                
1898                // generated
1899                Object o=meta.get(GENERATED,null);
1900                if(o!=null){
1901                        Boolean b = CommonUtil.toBoolean(o,null); 
1902                        str=null;
1903                        if(b!=null) {
1904                                str=b.booleanValue()?"always":"never";
1905                        }
1906                        else {
1907                                str=CommonUtil.toString(o,null);
1908                                if("always".equalsIgnoreCase(str))str="always";
1909                                else if("never".equalsIgnoreCase(str))str="never";
1910                                else throw invalidValue(cfc,prop,"generated",o.toString(),"true,false,always,never",data);
1911                                //throw new ORMException("invalid value ["+o+"] for attribute [generated] of property ["+prop.getName()+"], valid values are [true,false,always,never]");
1912                        }
1913                        version.setAttribute( "generated", str);
1914                }
1915                
1916        // insert
1917        Boolean b = toBoolean(cfc,meta, "insert",data);
1918        if(b!=null && !b.booleanValue()) version.setAttribute("insert", "false");
1919        
1920        // type
1921        String typeName="dataType";
1922                str=toString(cfc,prop,meta,typeName,data);
1923                if(Util.isEmpty(str,true)){
1924                        typeName="ormType";
1925                        str=toString(cfc,prop,meta,typeName,data);
1926                }
1927                if(!Util.isEmpty(str,true)) {
1928                        str=str.trim().toLowerCase();
1929                        if("int".equals(str) || "integer".equals(str))
1930                                version.setAttribute("type", "integer");
1931                        else if("long".equals(str))
1932                                version.setAttribute("type", "long");
1933                        else if("short".equals(str))
1934                                version.setAttribute("type", "short");
1935                        else 
1936                                throw invalidValue(cfc,prop,typeName,str,"int,integer,long,short",data);
1937                        //throw new ORMException("invalid value ["+str+"] for attribute ["+typeName+"], valid values are [int,integer,long,short]");
1938                }
1939                else 
1940                        version.setAttribute("type", "integer");
1941                
1942                // unsavedValue
1943        str=toString(cfc,prop,meta,"unsavedValue",data);
1944                if(!Util.isEmpty(str,true)) {
1945                        str=str.trim().toLowerCase();
1946                        if("null".equals(str) || "negative".equals(str) || "undefined".equals(str))
1947                                version.setAttribute("unsaved-value", str);
1948                        else 
1949                                throw invalidValue(cfc,prop,"unsavedValue",str,"null, negative, undefined",data);
1950                        //throw new ORMException("invalid value ["+str+"] for attribute [unsavedValue], valid values are [null, negative, undefined]");
1951                }
1952        }   
1953        
1954        private static String toString(Component cfc,Property prop,Struct sct, String key, SessionFactoryData data) throws PageException {
1955                return toString(cfc,prop,sct, key, false,data);
1956        }
1957
1958        private static String toString(Component cfc,Property prop,Struct sct, String key, boolean throwErrorWhenNotExist, SessionFactoryData data) throws PageException {
1959                return toString(cfc,prop,sct, CommonUtil.createKey(key), throwErrorWhenNotExist,data);
1960        }
1961        
1962        private static String toString(Component cfc,Property prop,Struct sct, Collection.Key key, boolean throwErrorWhenNotExist, SessionFactoryData data) throws PageException {
1963                Object value = sct.get(key,null);
1964                if(value==null) {
1965                        if(throwErrorWhenNotExist){
1966                                if(prop==null)throw ExceptionUtil.createException(data,cfc,"attribute ["+key+"] is required",null);
1967                                throw ExceptionUtil.createException(data,cfc,"attribute ["+key+"] of property ["+prop.getName()+"] of Component ["+_getCFCName(prop)+"] is required",null);
1968                        }
1969                        return null;
1970                }
1971                
1972                String str=CommonUtil.toString(value,null);
1973                if(str==null) {
1974                        if(prop==null)
1975                                throw ExceptionUtil.createException(data,cfc,"invalid type ["+CommonUtil.toTypeName(value)+"] for attribute ["+key+"], value must be a string",null);
1976                        throw ExceptionUtil.createException(data,cfc,"invalid type ["+CommonUtil.toTypeName(value)+"] for attribute ["+key+"] of property ["+prop.getName()+"] of Component ["+_getCFCName(prop)+"], value must be a string",null);
1977                        }
1978                return str;
1979        }
1980        
1981        private static String _getCFCName(Property prop) {
1982                String owner = prop.getOwnerName();
1983                return CommonUtil.last(owner,'.');
1984        }
1985        
1986        
1987        
1988        
1989        private static Boolean toBoolean(Component cfc,Struct sct, String key, SessionFactoryData data) throws PageException {
1990                Object value = sct.get(CommonUtil.createKey(key),null);
1991                if(value==null) return null;
1992                
1993                Boolean b=CommonUtil.toBoolean(value,null);
1994                if(b==null) throw ExceptionUtil.createException(data,cfc,"invalid type ["+CommonUtil.toTypeName(value)+"] for attribute ["+key+"], value must be a boolean",null);
1995                return b;
1996        }
1997
1998        private static Integer toInteger(Component cfc,Struct sct, String key,SessionFactoryData data) throws PageException {
1999                Object value = sct.get(CommonUtil.createKey(key),null);
2000                if(value==null) return null;
2001                
2002                Integer i=CommonUtil.toInteger(value,null);
2003                if(i==null) throw ExceptionUtil.createException(data,cfc,"invalid type ["+CommonUtil.toTypeName(value)+"] for attribute ["+key+"], value must be a numeric value",null);
2004                return i;
2005        }
2006        
2007        
2008}
2009        
2010        class PropertyCollection {
2011                private Property[] properties;
2012                private String tableName;
2013                public PropertyCollection(String tableName,Property[] properties) {
2014                        this.tableName=tableName;
2015                        this.properties=properties;
2016                }
2017                public PropertyCollection(String tableName, java.util.List<Property> properties) {
2018                        this.tableName=tableName;
2019                        this.properties=properties.toArray(new Property[properties.size()]);
2020                }
2021                public Property[] getProperties() {
2022                        return properties;
2023                }
2024                public String getTableName() {
2025                        return tableName;
2026                }
2027
2028        }