/** * Copyright (c) 2014, the Railo Company Ltd. * Copyright (c) 2015, Lucee Assosication Switzerland * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see <http://www.gnu.org/licenses/>. * */ package lucee.runtime.orm; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.Map.Entry; import lucee.commons.io.SystemUtil; import lucee.commons.lang.ExceptionUtil; import lucee.commons.lang.StringUtil; import lucee.commons.lang.SystemOut; import lucee.loader.engine.CFMLEngine; import lucee.runtime.Component; import lucee.runtime.PageContext; import lucee.runtime.PageContextImpl; import lucee.runtime.component.Property; import lucee.runtime.config.ConfigImpl; import lucee.runtime.config.Constants; import lucee.runtime.db.DataSource; import lucee.runtime.engine.ThreadLocalPageContext; import lucee.runtime.exp.PageException; import lucee.runtime.op.Caster; import lucee.runtime.op.Decision; import lucee.runtime.op.Operator; import lucee.runtime.type.Collection; import lucee.runtime.type.Collection.Key; import lucee.runtime.type.KeyImpl; import lucee.runtime.type.Struct; import lucee.runtime.type.StructImpl; import lucee.runtime.type.util.KeyConstants; import lucee.runtime.type.util.ListUtil; public class ORMUtil { public static ORMSession getSession(PageContext pc) throws PageException { return getSession(pc,true); } public static ORMSession getSession(PageContext pc, boolean create) throws PageException { return ((PageContextImpl) pc).getORMSession(create); } public static ORMEngine getEngine(PageContext pc) throws PageException { ConfigImpl config=(ConfigImpl) pc.getConfig(); return config.getORMEngine(pc); } /** * * @param pc * @param force if set to false the engine is on loaded when the configuration has changed * @throws PageException */ public static void resetEngine(PageContext pc, boolean force) throws PageException { ConfigImpl config=(ConfigImpl) pc.getConfig(); config.resetORMEngine(pc,force); } public static void printError(Throwable t, ORMEngine engine) { printError(t, engine, t.getMessage()); } public static void printError(String msg, ORMEngine engine) { printError(null, engine, msg); } public static void printError(Throwable t) { printError(t, null, t.getMessage()); } public static void printError(String msg) { printError(null, null, msg); } private static void printError(Throwable t, ORMEngine engine,String msg) { if(engine!=null)SystemOut.printDate("{"+engine.getLabel().toUpperCase()+"} - "+msg,SystemUtil.ERR); else SystemOut.printDate(msg, SystemUtil.ERR); if(t==null)t=new Throwable(); t.printStackTrace(SystemOut.getPrinWriter(SystemUtil.ERR)); } public static boolean equals(Object left, Object right) { HashSet<Object> done=new HashSet<Object>(); return _equals(done, left, right); } private static boolean _equals(HashSet<Object> done,Object left, Object right) { if(left==right) return true; if(left==null || right==null) return false; // components if(left instanceof Component && right instanceof Component){ return _equals(done,(Component)left, (Component)right); } // arrays if(Decision.isArray(left) && Decision.isArray(right)){ return _equals(done,Caster.toArray(left,null), Caster.toArray(right,null)); } // struct if(Decision.isStruct(left) && Decision.isStruct(right)){ return _equals(done,Caster.toStruct(left,null), Caster.toStruct(right,null)); } try { return Operator.equals(left,right,false); } catch (PageException e) { return false; } } private static boolean _equals(HashSet<Object> done,Collection left, Collection right) { if(done.contains(left)) return done.contains(right); done.add(left); done.add(right); if(left.size()!=right.size()) return false; //Key[] keys = left.keys(); Iterator<Entry<Key, Object>> it = left.entryIterator(); Entry<Key, Object> e; Object l,r; while(it.hasNext()){ e = it.next(); l=e.getValue(); r=right.get(e.getKey(),null); if(r==null || !_equals(done,l, r)) return false; } return true; } private static boolean _equals(HashSet<Object> done,Component left, Component right) { if(done.contains(left)) return done.contains(right); done.add(left); done.add(right); if(left==null || right==null) return false; if(!left.getPageSource().equals(right.getPageSource())) return false; Property[] props = getProperties(left); Object l,r; props=getIds(props); for(int i=0;i<props.length;i++){ l=left.getComponentScope().get(KeyImpl.init(props[i].getName()),null); r=right.getComponentScope().get(KeyImpl.init(props[i].getName()),null); if(!_equals(done,l, r)) return false; } return true; } public static Property[] getIds(Property[] props) { ArrayList<Property> ids=new ArrayList<Property>(); for(int y=0;y<props.length;y++){ String fieldType = Caster.toString(props[y].getDynamicAttributes().get(KeyConstants._fieldtype,null),null); if("id".equalsIgnoreCase(fieldType) || ListUtil.listFindNoCaseIgnoreEmpty(fieldType,"id",',')!=-1) ids.add(props[y]); } // no id field defined if(ids.size()==0) { String fieldType; for(int y=0;y<props.length;y++){ fieldType = Caster.toString(props[y].getDynamicAttributes().get(KeyConstants._fieldtype,null),null); if(StringUtil.isEmpty(fieldType,true) && props[y].getName().equalsIgnoreCase("id")){ ids.add(props[y]); props[y].getDynamicAttributes().setEL(KeyConstants._fieldtype, "id"); } } } // still no id field defined if(ids.size()==0 && props.length>0) { String owner = props[0].getOwnerName(); if(!StringUtil.isEmpty(owner)) owner=ListUtil.last(owner, '.').trim(); String fieldType; if(!StringUtil.isEmpty(owner)){ String id=owner+"id"; for(int y=0;y<props.length;y++){ fieldType = Caster.toString(props[y].getDynamicAttributes().get(KeyConstants._fieldtype,null),null); if(StringUtil.isEmpty(fieldType,true) && props[y].getName().equalsIgnoreCase(id)){ ids.add(props[y]); props[y].getDynamicAttributes().setEL(KeyConstants._fieldtype, "id"); } } } } return ids.toArray(new Property[ids.size()]); } public static Object getPropertyValue(Component cfc, String name, Object defaultValue) { Property[] props=getProperties(cfc); for(int i=0;i<props.length;i++){ if(!props[i].getName().equalsIgnoreCase(name)) continue; return cfc.getComponentScope().get(KeyImpl.getInstance(name),null); } return defaultValue; } /* jira2049 public static Object getPropertyValue(ORMSession session,Component cfc, String name, Object defaultValue) { Property[] props=getProperties(cfc); Object raw=null; SessionImpl sess=null; if(session!=null){ raw=session.getRawSession(); if(raw instanceof SessionImpl) sess=(SessionImpl) raw; } Object val; for(int i=0;i<props.length;i++){ if(!props[i].getName().equalsIgnoreCase(name)) continue; val = cfc.getComponentScope().get(KeyImpl.getInstance(name),null); if(sess!=null && !(val instanceof PersistentCollection)){ if(val instanceof List) return new PersistentList(sess,(List)val); if(val instanceof Map && !(val instanceof Component)) return new PersistentMap(sess,(Map)val); if(val instanceof Set) return new PersistentSet(sess,(Set)val); if(val instanceof Array) return new PersistentList(sess,Caster.toList(val,null)); } return val; } return defaultValue; }*/ private static Property[] getProperties(Component cfc) { return cfc.getProperties(true,true,false,false); } public static boolean isRelated(Property prop) { String fieldType = Caster.toString(prop.getDynamicAttributes().get(KeyConstants._fieldtype,"column"),"column"); if(StringUtil.isEmpty(fieldType,true)) return false; fieldType=fieldType.toLowerCase().trim(); if("one-to-one".equals(fieldType)) return true; if("many-to-one".equals(fieldType)) return true; if("one-to-many".equals(fieldType)) return true; if("many-to-many".equals(fieldType)) return true; return false; } public static Struct convertToSimpleMap(String paramsStr) { paramsStr=paramsStr.trim(); if(!StringUtil.startsWith(paramsStr, '{') || !StringUtil.endsWith(paramsStr, '}')) return null; paramsStr = paramsStr.substring(1, paramsStr.length() - 1); String items[] = ListUtil.listToStringArray(paramsStr, ','); Struct params=new StructImpl(); String arr$[] = items; int index; for(int i = 0; i < arr$.length; i++) { String pair = arr$[i]; index = pair.indexOf('='); if(index == -1) return null; params.setEL( KeyImpl.init(deleteQuotes(pair.substring(0, index).trim()).trim()), deleteQuotes(pair.substring(index + 1).trim())); } return params; } private static String deleteQuotes(String str) { if(StringUtil.isEmpty(str,true))return ""; char first=str.charAt(0); if((first=='\'' || first=='"') && StringUtil.endsWith(str, first)) return str.substring(1, str.length() - 1); return str; } public static DataSource getDefaultDataSource(PageContext pc) throws PageException{ pc=ThreadLocalPageContext.get(pc); Object o=pc.getApplicationContext().getORMDataSource(); if(StringUtil.isEmpty(o)) { boolean isCFML=pc.getRequestDialect()==CFMLEngine.DIALECT_CFML; throw ORMExceptionUtil.createException((ORMSession)null/* no session here, otherwise we get a infiniti loop*/,null, "missing datasource defintion in " +(isCFML?Constants.CFML_APPLICATION_EVENT_HANDLER:Constants.LUCEE_APPLICATION_EVENT_HANDLER) +"/" +(isCFML?Constants.CFML_APPLICATION_TAG_NAME:Constants.LUCEE_APPLICATION_TAG_NAME),null); } return o instanceof DataSource?(DataSource)o:pc.getDataSource(Caster.toString(o)); } public static DataSource getDefaultDataSource(PageContext pc, DataSource defaultValue) { pc=ThreadLocalPageContext.get(pc); Object o=pc.getApplicationContext().getORMDataSource(); if(StringUtil.isEmpty(o)) return defaultValue; try { return o instanceof DataSource?(DataSource)o:pc.getDataSource(Caster.toString(o)); } catch (PageException e) { return defaultValue; } } public static DataSource getDataSource(PageContext pc, String dsn, DataSource defaultValue) { if(StringUtil.isEmpty(dsn,true)) return ORMUtil.getDefaultDataSource(pc,defaultValue); return ((PageContextImpl)pc).getDataSource(dsn.trim(),defaultValue); } public static DataSource getDataSource(PageContext pc, String dsn) throws PageException { if(StringUtil.isEmpty(dsn,true)) return ORMUtil.getDefaultDataSource(pc); return ((PageContextImpl)pc).getDataSource(dsn.trim()); } /** * if the given component has defined a datasource in the meta data, lucee is returning this datasource, * otherwise the default orm datasource is returned * @param pc * @param cfc * @return * @throws PageException */ public static DataSource getDataSource(PageContext pc, Component cfc, DataSource defaultValue) { pc=ThreadLocalPageContext.get(pc); // datasource defined with cfc try{ Struct meta = cfc.getMetaData(pc); String datasourceName = Caster.toString(meta.get(KeyConstants._datasource,null),null); if(!StringUtil.isEmpty(datasourceName,true)) { DataSource ds = pc.getDataSource(datasourceName,null); if(ds!=null) return ds; } } catch(Throwable t) {ExceptionUtil.rethrowIfNecessary(t);} return getDefaultDataSource(pc, defaultValue); } /** * if the given component has defined a datasource in the meta data, lucee is returning this datasource, * otherwise the default orm datasource is returned * @param pc * @param cfc * @return * @throws PageException */ public static DataSource getDataSource(PageContext pc, Component cfc) throws PageException { pc=ThreadLocalPageContext.get(pc); // datasource defined with cfc Struct meta = cfc.getMetaData(pc); String datasourceName = Caster.toString(meta.get(KeyConstants._datasource,null),null); if(!StringUtil.isEmpty(datasourceName,true)) { return pc.getDataSource(datasourceName); } return getDefaultDataSource(pc); } public static String getDataSourceName(PageContext pc, Component cfc) throws PageException { pc=ThreadLocalPageContext.get(pc); // datasource defined with cfc Struct meta = cfc.getMetaData(pc); String datasourceName = Caster.toString(meta.get(KeyConstants._datasource,null),null); if(!StringUtil.isEmpty(datasourceName,true)) { return datasourceName.trim(); } return getDefaultDataSource(pc).getName(); } public static String getDataSourceName(PageContext pc, Component cfc, String defaultValue) { pc=ThreadLocalPageContext.get(pc); // datasource defined with cfc Struct meta=null; try { meta = cfc.getMetaData(pc); String datasourceName = Caster.toString(meta.get(KeyConstants._datasource,null),null); if(!StringUtil.isEmpty(datasourceName,true)) { return datasourceName.trim(); } } catch (PageException e) {} DataSource ds = getDefaultDataSource(pc,null); if(ds!=null)return ds.getName(); return defaultValue; } }