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; 020 021import java.util.ArrayList; 022import java.util.HashSet; 023import java.util.Iterator; 024import java.util.Map.Entry; 025 026import lucee.commons.io.SystemUtil; 027import lucee.commons.lang.StringUtil; 028import lucee.commons.lang.SystemOut; 029import lucee.runtime.Component; 030import lucee.runtime.PageContext; 031import lucee.runtime.PageContextImpl; 032import lucee.runtime.component.Property; 033import lucee.runtime.config.ConfigImpl; 034import lucee.runtime.config.Constants; 035import lucee.runtime.db.DataSource; 036import lucee.runtime.engine.ThreadLocalPageContext; 037import lucee.runtime.exp.PageException; 038import lucee.runtime.listener.ApplicationContextPro; 039import lucee.runtime.op.Caster; 040import lucee.runtime.op.Decision; 041import lucee.runtime.op.Operator; 042import lucee.runtime.orm.hibernate.ExceptionUtil; 043import lucee.runtime.type.Collection; 044import lucee.runtime.type.Collection.Key; 045import lucee.runtime.type.KeyImpl; 046import lucee.runtime.type.Struct; 047import lucee.runtime.type.StructImpl; 048import lucee.runtime.type.util.ComponentProUtil; 049import lucee.runtime.type.util.KeyConstants; 050import lucee.runtime.type.util.ListUtil; 051 052public class ORMUtil { 053 054 public static ORMSession getSession(PageContext pc) throws PageException { 055 return getSession(pc,true); 056 } 057 058 public static ORMSession getSession(PageContext pc, boolean create) throws PageException { 059 return ((PageContextImpl) pc).getORMSession(create); 060 } 061 062 public static ORMEngine getEngine(PageContext pc) throws PageException { 063 ConfigImpl config=(ConfigImpl) pc.getConfig(); 064 return config.getORMEngine(pc); 065 } 066 067 /** 068 * 069 * @param pc 070 * @param force if set to false the engine is on loaded when the configuration has changed 071 * @throws PageException 072 */ 073 public static void resetEngine(PageContext pc, boolean force) throws PageException { 074 ConfigImpl config=(ConfigImpl) pc.getConfig(); 075 config.resetORMEngine(pc,force); 076 } 077 078 public static void printError(Throwable t, ORMEngine engine) { 079 printError(t, engine, t.getMessage()); 080 } 081 082 public static void printError(String msg, ORMEngine engine) { 083 printError(null, engine, msg); 084 } 085 086 public static void printError(Throwable t) { 087 printError(t, null, t.getMessage()); 088 } 089 090 public static void printError(String msg) { 091 printError(null, null, msg); 092 } 093 094 private static void printError(Throwable t, ORMEngine engine,String msg) { 095 if(engine!=null)SystemOut.printDate("{"+engine.getLabel().toUpperCase()+"} - "+msg,SystemUtil.ERR); 096 else SystemOut.printDate(msg, SystemUtil.ERR); 097 if(t==null)t=new Throwable(); 098 t.printStackTrace(SystemOut.getPrinWriter(SystemUtil.ERR)); 099 } 100 101 public static boolean equals(Object left, Object right) { 102 HashSet<Object> done=new HashSet<Object>(); 103 return _equals(done, left, right); 104 } 105 106 private static boolean _equals(HashSet<Object> done,Object left, Object right) { 107 108 if(left==right) return true; 109 if(left==null || right==null) return false; 110 111 // components 112 if(left instanceof Component && right instanceof Component){ 113 return _equals(done,(Component)left, (Component)right); 114 } 115 116 // arrays 117 if(Decision.isArray(left) && Decision.isArray(right)){ 118 return _equals(done,Caster.toArray(left,null), Caster.toArray(right,null)); 119 } 120 121 // struct 122 if(Decision.isStruct(left) && Decision.isStruct(right)){ 123 return _equals(done,Caster.toStruct(left,null), Caster.toStruct(right,null)); 124 } 125 126 try { 127 return Operator.equals(left,right,false); 128 } catch (PageException e) { 129 return false; 130 } 131 } 132 133 private static boolean _equals(HashSet<Object> done,Collection left, Collection right) { 134 if(done.contains(left)) return done.contains(right); 135 done.add(left); 136 done.add(right); 137 138 if(left.size()!=right.size()) return false; 139 //Key[] keys = left.keys(); 140 Iterator<Entry<Key, Object>> it = left.entryIterator(); 141 Entry<Key, Object> e; 142 Object l,r; 143 while(it.hasNext()){ 144 e = it.next(); 145 l=e.getValue(); 146 r=right.get(e.getKey(),null); 147 if(r==null || !_equals(done,l, r)) return false; 148 } 149 return true; 150 } 151 152 private static boolean _equals(HashSet<Object> done,Component left, Component right) { 153 if(done.contains(left)) return done.contains(right); 154 done.add(left); 155 done.add(right); 156 157 if(left==null || right==null) return false; 158 if(!left.getPageSource().equals(right.getPageSource())) return false; 159 Property[] props = getProperties(left); 160 Object l,r; 161 props=getIds(props); 162 for(int i=0;i<props.length;i++){ 163 l=left.getComponentScope().get(KeyImpl.init(props[i].getName()),null); 164 r=right.getComponentScope().get(KeyImpl.init(props[i].getName()),null); 165 if(!_equals(done,l, r)) return false; 166 } 167 return true; 168 } 169 170 public static Property[] getIds(Property[] props) { 171 ArrayList<Property> ids=new ArrayList<Property>(); 172 for(int y=0;y<props.length;y++){ 173 String fieldType = Caster.toString(props[y].getDynamicAttributes().get(KeyConstants._fieldtype,null),null); 174 if("id".equalsIgnoreCase(fieldType) || ListUtil.listFindNoCaseIgnoreEmpty(fieldType,"id",',')!=-1) 175 ids.add(props[y]); 176 } 177 178 // no id field defined 179 if(ids.size()==0) { 180 String fieldType; 181 for(int y=0;y<props.length;y++){ 182 fieldType = Caster.toString(props[y].getDynamicAttributes().get(KeyConstants._fieldtype,null),null); 183 if(StringUtil.isEmpty(fieldType,true) && props[y].getName().equalsIgnoreCase("id")){ 184 ids.add(props[y]); 185 props[y].getDynamicAttributes().setEL(KeyConstants._fieldtype, "id"); 186 } 187 } 188 } 189 190 // still no id field defined 191 if(ids.size()==0 && props.length>0) { 192 String owner = props[0].getOwnerName(); 193 if(!StringUtil.isEmpty(owner)) owner=ListUtil.last(owner, '.').trim(); 194 195 String fieldType; 196 if(!StringUtil.isEmpty(owner)){ 197 String id=owner+"id"; 198 for(int y=0;y<props.length;y++){ 199 fieldType = Caster.toString(props[y].getDynamicAttributes().get(KeyConstants._fieldtype,null),null); 200 if(StringUtil.isEmpty(fieldType,true) && props[y].getName().equalsIgnoreCase(id)){ 201 ids.add(props[y]); 202 props[y].getDynamicAttributes().setEL(KeyConstants._fieldtype, "id"); 203 } 204 } 205 } 206 } 207 return ids.toArray(new Property[ids.size()]); 208 } 209 210 public static Object getPropertyValue(Component cfc, String name, Object defaultValue) { 211 Property[] props=getProperties(cfc); 212 213 for(int i=0;i<props.length;i++){ 214 if(!props[i].getName().equalsIgnoreCase(name)) continue; 215 return cfc.getComponentScope().get(KeyImpl.getInstance(name),null); 216 } 217 return defaultValue; 218 } 219 /* jira2049 220 public static Object getPropertyValue(ORMSession session,Component cfc, String name, Object defaultValue) { 221 Property[] props=getProperties(cfc); 222 Object raw=null; 223 SessionImpl sess=null; 224 if(session!=null){ 225 raw=session.getRawSession(); 226 if(raw instanceof SessionImpl) 227 sess=(SessionImpl) raw; 228 } 229 Object val; 230 for(int i=0;i<props.length;i++){ 231 if(!props[i].getName().equalsIgnoreCase(name)) continue; 232 val = cfc.getComponentScope().get(KeyImpl.getInstance(name),null); 233 if(sess!=null && !(val instanceof PersistentCollection)){ 234 if(val instanceof List) 235 return new PersistentList(sess,(List)val); 236 if(val instanceof Map && !(val instanceof Component)) 237 return new PersistentMap(sess,(Map)val); 238 if(val instanceof Set) 239 return new PersistentSet(sess,(Set)val); 240 if(val instanceof Array) 241 return new PersistentList(sess,Caster.toList(val,null)); 242 243 } 244 return val; 245 } 246 return defaultValue; 247 }*/ 248 249 private static Property[] getProperties(Component cfc) { 250 return ComponentProUtil.getProperties(cfc, true,true,false,false); 251 } 252 253 public static boolean isRelated(Property prop) { 254 String fieldType = Caster.toString(prop.getDynamicAttributes().get(KeyConstants._fieldtype,"column"),"column"); 255 if(StringUtil.isEmpty(fieldType,true)) return false; 256 fieldType=fieldType.toLowerCase().trim(); 257 258 if("one-to-one".equals(fieldType)) return true; 259 if("many-to-one".equals(fieldType)) return true; 260 if("one-to-many".equals(fieldType)) return true; 261 if("many-to-many".equals(fieldType)) return true; 262 return false; 263 } 264 265 public static Struct convertToSimpleMap(String paramsStr) { 266 paramsStr=paramsStr.trim(); 267 if(!StringUtil.startsWith(paramsStr, '{') || !StringUtil.endsWith(paramsStr, '}')) 268 return null; 269 270 paramsStr = paramsStr.substring(1, paramsStr.length() - 1); 271 String items[] = ListUtil.listToStringArray(paramsStr, ','); 272 273 Struct params=new StructImpl(); 274 String arr$[] = items; 275 int index; 276 for(int i = 0; i < arr$.length; i++) { 277 String pair = arr$[i]; 278 index = pair.indexOf('='); 279 if(index == -1) return null; 280 281 params.setEL( 282 KeyImpl.init(deleteQuotes(pair.substring(0, index).trim()).trim()), 283 deleteQuotes(pair.substring(index + 1).trim())); 284 } 285 286 return params; 287 } 288 289 private static String deleteQuotes(String str) { 290 if(StringUtil.isEmpty(str,true))return ""; 291 char first=str.charAt(0); 292 if((first=='\'' || first=='"') && StringUtil.endsWith(str, first)) 293 return str.substring(1, str.length() - 1); 294 return str; 295 } 296 297 public static DataSource getDefaultDataSource(PageContext pc) throws PageException{ 298 pc=ThreadLocalPageContext.get(pc); 299 Object o=((ApplicationContextPro)pc.getApplicationContext()).getORMDataSource(); 300 301 if(StringUtil.isEmpty(o)) 302 throw ExceptionUtil.createException((ORMSession)null/* no session here, otherwise we get a infiniti loop*/,null,"missing datasource defintion in "+Constants.APP_CFC+"/"+Constants.CFAPP_NAME,null); 303 return o instanceof DataSource?(DataSource)o:((PageContextImpl)pc).getDataSource(Caster.toString(o)); 304 } 305 306 public static DataSource getDefaultDataSource(PageContext pc, DataSource defaultValue) { 307 pc=ThreadLocalPageContext.get(pc); 308 Object o=((ApplicationContextPro)pc.getApplicationContext()).getORMDataSource(); 309 if(StringUtil.isEmpty(o)) 310 return defaultValue; 311 try { 312 return o instanceof DataSource?(DataSource)o:((PageContextImpl)pc).getDataSource(Caster.toString(o)); 313 } 314 catch (PageException e) { 315 return defaultValue; 316 } 317 } 318 319 public static DataSource getDataSource(PageContext pc, String dsn, DataSource defaultValue) { 320 if(StringUtil.isEmpty(dsn,true)) return ORMUtil.getDefaultDataSource(pc,defaultValue); 321 return ((PageContextImpl)pc).getDataSource(dsn.trim(),defaultValue); 322 } 323 public static DataSource getDataSource(PageContext pc, String dsn) throws PageException { 324 if(StringUtil.isEmpty(dsn,true)) return ORMUtil.getDefaultDataSource(pc); 325 return ((PageContextImpl)pc).getDataSource(dsn.trim()); 326 } 327 328 329 /** 330 * if the given component has defined a datasource in the meta data, lucee is returning this datasource, 331 * otherwise the default orm datasource is returned 332 * @param pc 333 * @param cfc 334 * @return 335 * @throws PageException 336 */ 337 public static DataSource getDataSource(PageContext pc, Component cfc, DataSource defaultValue) { 338 pc=ThreadLocalPageContext.get(pc); 339 340 // datasource defined with cfc 341 try{ 342 Struct meta = cfc.getMetaData(pc); 343 String datasourceName = Caster.toString(meta.get(KeyConstants._datasource,null),null); 344 if(!StringUtil.isEmpty(datasourceName,true)) { 345 DataSource ds = ((PageContextImpl)pc).getDataSource(datasourceName,null); 346 if(ds!=null) return ds; 347 } 348 } 349 catch(Throwable t){ 350 lucee.commons.lang.ExceptionUtil.rethrowIfNecessary(t); 351 } 352 return getDefaultDataSource(pc, defaultValue); 353 } 354 355 /** 356 * if the given component has defined a datasource in the meta data, lucee is returning this datasource, 357 * otherwise the default orm datasource is returned 358 * @param pc 359 * @param cfc 360 * @return 361 * @throws PageException 362 */ 363 public static DataSource getDataSource(PageContext pc, Component cfc) throws PageException { 364 pc=ThreadLocalPageContext.get(pc); 365 366 // datasource defined with cfc 367 Struct meta = cfc.getMetaData(pc); 368 String datasourceName = Caster.toString(meta.get(KeyConstants._datasource,null),null); 369 if(!StringUtil.isEmpty(datasourceName,true)) { 370 return ((PageContextImpl)pc).getDataSource(datasourceName); 371 } 372 373 return getDefaultDataSource(pc); 374 } 375 public static String getDataSourceName(PageContext pc, Component cfc) throws PageException { 376 pc=ThreadLocalPageContext.get(pc); 377 378 // datasource defined with cfc 379 Struct meta = cfc.getMetaData(pc); 380 String datasourceName = Caster.toString(meta.get(KeyConstants._datasource,null),null); 381 if(!StringUtil.isEmpty(datasourceName,true)) { 382 return datasourceName.trim(); 383 } 384 return getDefaultDataSource(pc).getName(); 385 } 386}