001/*
002 * Copyright 2011 The Kuali Foundation.
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.ole.select.lookup;
017
018import org.apache.commons.lang.StringUtils;
019import org.kuali.ole.sys.context.SpringContext;
020import org.kuali.ole.sys.service.NonTransactional;
021import org.kuali.rice.core.api.config.property.ConfigurationService;
022import org.kuali.rice.core.api.search.SearchOperator;
023import org.kuali.rice.kns.service.DataDictionaryService;
024import org.kuali.rice.kns.service.KNSServiceLocator;
025import org.kuali.rice.krad.bo.ExternalizableBusinessObject;
026import org.kuali.rice.krad.bo.PersistableBusinessObject;
027import org.kuali.rice.krad.dao.LookupDao;
028import org.kuali.rice.krad.datadictionary.BusinessObjectEntry;
029import org.kuali.rice.krad.datadictionary.DataDictionaryEntry;
030import org.kuali.rice.krad.datadictionary.PrimitiveAttributeDefinition;
031import org.kuali.rice.krad.datadictionary.RelationshipDefinition;
032import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
033import org.kuali.rice.krad.service.LookupService;
034import org.kuali.rice.krad.service.ModuleService;
035import org.kuali.rice.krad.service.PersistenceStructureService;
036
037import java.lang.reflect.Method;
038import java.util.*;
039
040@NonTransactional
041public class DocLookupServiceImpl implements LookupService {
042    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DocLookupServiceImpl.class);
043
044    //TODO:This is just a draft version .The whole class needs to be rewritten once we have Document store
045    private IDocLookupSearch lookupDao;
046    private LookupDao lookupDaoOjb;
047    private ConfigurationService kualiConfigurationService;
048    private DataDictionaryService dataDictionaryService;
049    private PersistenceStructureService persistenceStructureService;
050
051
052    @Override
053    public Collection findCollectionBySearchUnbounded(Class example, Map formProps) {
054        return findCollectionBySearchHelper(example, formProps, true);
055    }
056
057    /**
058     * Returns a collection of objects based on the given search parameters.
059     *
060     * @return Collection returned from the search
061     */
062
063    @Override
064    public Collection findCollectionBySearch(Class example, Map formProps) {
065        try {
066            return getResult(example, formProps, true);
067        } catch (Exception e) {
068            throw new RuntimeException(e);
069        }
070    }
071
072
073    @Override
074    public Collection findCollectionBySearchHelper(Class example, Map formProps, boolean unbounded) {
075        try {
076            return getResult(example, formProps, true);
077        } catch (Exception e) {
078            throw new RuntimeException(e);
079        }
080    }
081
082    /**
083     * Retrieves a Object based on the search criteria, which should uniquely identify a record.
084     *
085     * @return Object returned from the search
086     */
087
088    @Override
089    public Object findObjectBySearch(Class example, Map formProps) {
090        if (example == null || formProps == null) {
091            throw new IllegalArgumentException("Object and Map must not be null");
092        }
093
094        PersistableBusinessObject obj = null;
095        try {
096            obj = (PersistableBusinessObject) example.newInstance();
097        } catch (IllegalAccessException e) {
098            throw new RuntimeException("Cannot get new instance of " + example.getName(), e);
099        } catch (InstantiationException e) {
100            throw new RuntimeException("Cannot instantiate " + example.getName(), e);
101        }
102
103        return lookupDaoOjb.findObjectByMap(obj, formProps);
104    }
105
106
107    @Override
108    public boolean allPrimaryKeyValuesPresentAndNotWildcard(Class boClass, Map formProps) {
109        List pkFields = KNSServiceLocator.getBusinessObjectMetaDataService().listPrimaryKeyFieldNames(boClass);
110        Iterator pkIter = pkFields.iterator();
111        boolean returnVal = true;
112        while (returnVal && pkIter.hasNext()) {
113            String pkName = (String) pkIter.next();
114            String pkValue = (String) formProps.get(pkName);
115
116            if (StringUtils.isBlank(pkValue)) {
117                returnVal = false;
118            } else if (StringUtils.indexOfAny(pkValue, SearchOperator.QUERY_CHARACTERS.toArray().toString()) != -1) {
119                returnVal = false;
120            }
121        }
122        return returnVal;
123    }
124
125    /**
126     * @return Returns the lookupDao.
127     */
128
129    public IDocLookupSearch getLookupDao() {
130        return lookupDao;
131    }
132
133    /**
134     * @param lookupDao The lookupDao to set.
135     */
136
137    public void setLookupDao(IDocLookupSearch lookupDao) {
138        this.lookupDao = lookupDao;
139    }
140
141
142    public ConfigurationService getConfigurationService() {
143        return kualiConfigurationService;
144    }
145
146
147    public void setConfigurationService(ConfigurationService kualiConfigurationService) {
148        this.kualiConfigurationService = kualiConfigurationService;
149    }
150
151    public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
152        this.dataDictionaryService = dataDictionaryService;
153    }
154
155    /**
156     * @return Returns the lookupDao.
157     */
158
159    public LookupDao getLookupDaoOjb() {
160        return lookupDaoOjb;
161    }
162
163
164    /**
165     * @param lookupDao The lookupDao to set.
166     */
167
168    public void setLookupDaoOjb(LookupDao lookupDaoOjb) {
169        this.lookupDaoOjb = lookupDaoOjb;
170    }
171
172
173    public void setPersistenceStructureService(PersistenceStructureService persistenceStructureService) {
174        this.persistenceStructureService = persistenceStructureService;
175    }
176
177
178    private List getResult(Class businessObjectClass, Map criterValues, boolean unbounded) throws Exception {
179        List result = new ArrayList();
180        Map dbCrit = new HashMap();
181        Map docCrit = new HashMap();
182        Map<String, List<String>> docData = getDDRelationship(businessObjectClass);
183        if (docData != null && docData.size() > 0) {
184            for (String key : (java.util.Set<String>) criterValues.keySet()) {
185                boolean found = false;
186                for (String key1 : docData.keySet()) {
187                    {
188                        if (key.contains(key1) && key.contains(".")) {
189                            found = true;
190                            String val = key.split("\\.")[key.split("\\.").length - 1];
191                            docCrit.put(val, criterValues.get(key));//dbCrit = null;
192                            break;
193                        }
194                    }
195                    if (!found) {
196                        dbCrit.put(key, criterValues.get(key));//dbCrit = null;
197                    }
198                }
199            }
200        }
201        if (ExternalizableBusinessObject.class.isAssignableFrom(businessObjectClass)) {
202
203            ModuleService eboModuleService = KRADServiceLocatorWeb.getKualiModuleService().getResponsibleModuleService(businessObjectClass);
204            BusinessObjectEntry ddEntry = eboModuleService.getExternalizableBusinessObjectDictionaryEntry(businessObjectClass);
205            Map<String, String> filteredFieldValues = new HashMap<String, String>();
206            for (String fieldName : (java.util.Set<String>) dbCrit.keySet()) {
207                if (ddEntry.getAttributeNames().contains(fieldName)) {
208                    filteredFieldValues.put(fieldName, (String) dbCrit.get(fieldName));
209                }
210            }
211
212            result = eboModuleService.getExternalizableBusinessObjectsListForLookup(businessObjectClass,
213                    (Map) filteredFieldValues, unbounded);
214
215        } else if (!org.kuali.ole.select.lookup.DocStoreData.class.isAssignableFrom(businessObjectClass)) {
216            result = (List) this.getLookupDaoOjb().findCollectionBySearchHelper(businessObjectClass, dbCrit, unbounded,
217                    allPrimaryKeyValuesPresentAndNotWildcard(businessObjectClass, dbCrit));
218        }
219        Class cla = null;
220        for (String key : docData.keySet()) {
221            List<String> data = docData.get(key);
222
223            try {
224                cla = Class.forName(data.get(0));
225            } catch (Exception e) {
226                throw new RuntimeException(e);
227            }
228            String attrs = data.get(1);
229            String[] ff = attrs.split(":")[0].split(",");
230            String sourAtt = null, targetAtt = null;
231            if (ff.length == 2) {
232                sourAtt = ff[0];
233                targetAtt = ff[1];
234            }
235            boolean isDBCrit = dbCrit == null || dbCrit.size() < 3;
236            boolean isDocCrit = docCrit == null || docCrit.size() < 1;
237            //retrieves the list of source/database attribute values
238            List dbSourceAttrib = getSourceData(result, sourAtt, businessObjectClass);
239            //retrieves the list of document store data for the corresponding source/database attribute values
240            List docStoreResult = this.getLookupDao().getResult(cla, targetAtt, dbSourceAttrib, docCrit);//docStoreResult.remove(2)
241            //merges both docstore and database data
242            result = mergeResult(result, sourAtt, businessObjectClass, docStoreResult, targetAtt, cla, docData.keySet().iterator().next(), isDBCrit, isDocCrit);
243        }
244        if (org.kuali.ole.select.lookup.DocStoreData.class.isAssignableFrom(businessObjectClass)) {
245            result = (List) this.getLookupDaoOjb().findCollectionBySearchHelper(businessObjectClass, criterValues, true, true);
246        }
247        return result;
248    }
249
250    private Method getMethod(Class c, String attr, Class[] objectAttributes) throws Exception {
251        Method met = c.getMethod("get" + StringUtils.capitalize(attr), objectAttributes);
252        return met;
253    }
254
255    private Method getSetMethod(Class c, String attr, Class[] objectAttributes) throws Exception {
256        attr = "docData";
257        Method met = c.getMethod("set" + StringUtils.capitalize(attr), objectAttributes);
258        return met;
259    }
260
261    private List mergeResult(List result, String sourAtt, Class sourClass, List dres, String targetAtt, Class targeClass,
262                             String attt, boolean isDBCrit, boolean isDocCrit)
263            throws Exception {
264        List resul = new ArrayList();
265        List resut = new ArrayList();
266        Class[] ptyeps = {targeClass};
267        Method srcm = this.getMethod(sourClass, sourAtt, null);
268        Method tcm = this.getMethod(targeClass, targetAtt, null);
269        Method sem = this.getSetMethod(sourClass, attt, ptyeps);
270        for (Object val : result) {
271            for (Object dval : dres) {
272                Object sval = srcm.invoke(val, (Object[]) null);
273                Object dvall = tcm.invoke(dval, (Object[]) null);
274                if ((!isDocCrit && sval != null && sval.equals(dvall)) || (isDocCrit && dvall != null && dvall.equals(sval))) {
275                    Object[] arr = {dval};
276                    sem.invoke(val, arr);
277                    resul.add(val);
278                }
279            }
280        }
281        return resul;
282
283    }
284
285    private List<Object> getSourceData(List result, String objectAttribute, Class clas) throws Exception {
286        List<Object> resul = new ArrayList<Object>(0);
287        Method met = clas.getMethod("get" + StringUtils.capitalize(objectAttribute), (Class[]) null);
288        for (Object data : result) {
289            Object res = met.invoke(data, (Object[]) null);
290            if (res != null) {
291                resul.add(res);
292            }
293        }
294        return resul;
295    }
296
297    public Class getDocClass(Class clas, String objectAttribute) {
298        boolean result = false;
299        Method met = null;
300        try {
301            met = clas.getMethod("get" + StringUtils.capitalize(objectAttribute), (Class[]) null);
302        } catch (Exception e) {
303            return null;
304        }
305        return org.kuali.ole.select.lookup.DocStoreData.class.isAssignableFrom(met.getReturnType()) ?
306                met.getReturnType() : null;
307    }
308
309
310    public Map<String, List<String>> getDDRelationship(Class c) {
311        Map<String, List<String>> result = new HashMap<String, List<String>>(0);
312        DataDictionaryEntry entryBase = SpringContext.getBean(DataDictionaryService.class)
313                .getDataDictionary().getDictionaryObjectEntry(c.getName());
314        if (entryBase == null) {
315            return null;
316        }
317
318        List<RelationshipDefinition> ddRelationships = entryBase
319                .getRelationships();
320        RelationshipDefinition relationship = null;
321        int minKeys = Integer.MAX_VALUE;
322        for (RelationshipDefinition def : ddRelationships) {
323            // favor key sizes of 1 first
324            if (def.getPrimitiveAttributes().size() == 1) {
325                for (PrimitiveAttributeDefinition primitive : def
326                        .getPrimitiveAttributes()) {
327                    if (def.getObjectAttributeName() != null) {
328                        List<String> data = new ArrayList<String>(0);
329                        Class cc = getDocClass(c, def.getObjectAttributeName());//cc= null;data.remove("java.lang.String");
330                        if (cc != null) {
331                            data.add(cc.getName());
332                            StringBuffer sb = new StringBuffer();
333                            List<PrimitiveAttributeDefinition> res = def.getPrimitiveAttributes();
334                            for (PrimitiveAttributeDefinition pdef : res) {
335                                sb.append(pdef.getSourceName() + "," + pdef.getTargetName() + ":");
336                            }
337                            sb.deleteCharAt(sb.length() - 1);
338                            data.add(sb.toString());
339                            result.put(def.getObjectAttributeName(), data);
340                        }
341                    }
342                }
343            }
344        }
345        return result;//result.remove("oleRequestor");
346    }
347
348    @Override
349    public <T> Collection<T> findCollectionBySearchHelper(Class<T> example, Map<String, String> formProperties, boolean unbounded, Integer searchResultsLimit) {
350        return null;  //To change body of implemented methods use File | Settings | File Templates.
351    }
352}
353