001/**
002 * Copyright 2005-2015 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.rice.kns.datadictionary;
017
018import org.apache.commons.lang.StringUtils;
019import org.kuali.rice.core.api.CoreApiServiceLocator;
020import org.kuali.rice.core.api.config.property.ConfigurationService;
021import org.kuali.rice.krad.datadictionary.DataDictionaryDefinitionBase;
022import org.kuali.rice.krad.datadictionary.HelpDefinition;
023import org.kuali.rice.krad.datadictionary.SortDefinition;
024import org.kuali.rice.krad.datadictionary.exception.DuplicateEntryException;
025import org.kuali.rice.krad.util.KRADConstants;
026
027import java.util.ArrayList;
028import java.util.LinkedHashMap;
029import java.util.List;
030import java.util.Map;
031
032/**
033 * Contains lookup-related information relating to the parent BusinessObject.
034 * <p/>
035 * The lookup element is used to specify the rules for "looking up"
036 * a business object.  These specifications define the following:
037 * How to specify the search criteria used to locate a set of business objects
038 * How to display the search results
039 * <p/>
040 * DD: See LookupDefinition.java
041 * <p/>
042 * JSTL: The lookup element is a Map which is accessed using
043 * a key of "lookup".  This map contains the following keys:
044 * lookupableID (String, optional)
045 * title (String)
046 * menubar (String, optional)
047 * defaultSort (Map, optional)
048 * lookupFields (Map)
049 * resultFields (Map)
050 * resultSetLimit (String, optional)
051 * <p/>
052 * See LookupMapBuilder.java
053 * <p/>
054 * Note: the setters do copious amounts of validation, to facilitate generating errors during the parsing process.
055 *
056 * @deprecated Use {@link org.kuali.rice.krad.lookup.LookupView}.
057 */
058@Deprecated
059public class LookupDefinition extends DataDictionaryDefinitionBase {
060    private static final long serialVersionUID = 6733008572890721359L;
061
062    protected String lookupableID;
063    protected String title;
064    protected String menubar;
065    protected SortDefinition defaultSort;
066
067    protected List<FieldDefinition> lookupFields = new ArrayList<FieldDefinition>();
068    protected Map<String, FieldDefinition> lookupFieldMap = new LinkedHashMap<String, FieldDefinition>();
069    protected List<FieldDefinition> resultFields = new ArrayList<FieldDefinition>();
070    protected Map<String, FieldDefinition> resultFieldMap = new LinkedHashMap<String, FieldDefinition>();
071
072    protected Integer resultSetLimit = null;
073    protected Integer multipleValuesResultSetLimit = null;
074
075    protected String extraButtonSource;
076    protected String extraButtonParams;
077
078    protected String searchIconOverride;
079
080    protected int numOfColumns;
081
082    protected HelpDefinition helpDefinition;
083    protected String helpUrl;
084
085    protected boolean translateCodes = false;
086    protected boolean disableSearchButtons = false;
087
088    public LookupDefinition() {
089    }
090
091    /**
092     * The lookupableID element identifies the name of the Spring bean which
093     * will be used to obtain the lookupable helper service for the business object.
094     * For example, the Balance.xml file has a lookupableId = "glBalanceLookupable".
095     * The KualiSpringBeansGL.xml file determines that the helper service will be an
096     * instance of BalanceLookupableHelperServiceImpl.
097     * <p/>
098     * If this field is omitted, the default bean id used will be kualiLookupable which uses
099     * the KualiLookupableHelperServiceImpl helper service.
100     */
101    public void setLookupableID(String lookupableID) {
102        if (lookupableID == null) {
103            throw new IllegalArgumentException("invalid (null) lookupableID");
104        }
105
106        this.lookupableID = lookupableID;
107    }
108
109    /**
110     * @return custom lookupable id
111     */
112    public String getLookupableID() {
113        return this.lookupableID;
114    }
115
116    /**
117     * @return title
118     */
119    public String getTitle() {
120        return title;
121    }
122
123    /**
124     * Sets title to the given value.
125     *
126     * @param title
127     * @throws IllegalArgumentException if the given title is blank
128     */
129    public void setTitle(String title) {
130        if (StringUtils.isBlank(title)) {
131            throw new IllegalArgumentException("invalid (blank) title");
132        }
133        this.title = title;
134    }
135
136    /**
137     * @return true if this instance has a menubar
138     */
139    public boolean hasMenubar() {
140        return (menubar != null);
141    }
142
143    /**
144     * @return menubar
145     */
146    public String getMenubar() {
147        return menubar;
148    }
149
150    /**
151     * The menubar element is used to add additional html code
152     * to the header line on the lookup screen.
153     * <p/>
154     * For example, Account.xml uses this element to
155     * add the "create new global" button to the Account Lookup header.
156     *
157     * @throws IllegalArgumentException if the given menubar is blank
158     */
159    public void setMenubar(String menubar) {
160        if (StringUtils.isBlank(menubar)) {
161            throw new IllegalArgumentException("invalid (blank) menubar");
162        }
163        // TODO: catch exception if service locator call fails
164        ConfigurationService kualiConfigurationservice = CoreApiServiceLocator.getKualiConfigurationService();
165        this.menubar = menubar.replace("${kr.externalizable.images.url}",
166                kualiConfigurationservice.getPropertyValueAsString(KRADConstants.EXTERNALIZABLE_IMAGES_URL_KEY)).replace("${externalizable.images.url}",
167                kualiConfigurationservice.getPropertyValueAsString(
168                        KRADConstants.APPLICATION_EXTERNALIZABLE_IMAGES_URL_KEY));
169        this.menubar = this.menubar.replace("${application.url}", kualiConfigurationservice.getPropertyValueAsString(
170                KRADConstants.APPLICATION_URL_KEY));
171    }
172
173
174    /**
175     * @return true if this instance has a default sort defined
176     */
177    public boolean hasDefaultSort() {
178        return (defaultSort != null);
179    }
180
181    /**
182     * @return defaultSort
183     */
184    public SortDefinition getDefaultSort() {
185        return defaultSort;
186    }
187
188    /**
189     * The defaultSort element specifies the sequence in which the
190     * lookup search results should be displayed.  It contains an
191     * ascending/descending indicator and a list of attribute names.
192     * <p/>
193     * DD: See SortDefinition.java
194     * <p/>
195     * JSTL: defaultSort is a Map with the following keys:
196     * sortAscending (boolean String)
197     * sortAttributes (Map)
198     * <p/>
199     * By the time JSTL export occurs, the optional attributeName from the defaultSort
200     * tag will have been converted into the first contained sortAttribute
201     * <p/>
202     * See LookupMapBuilder.java
203     *
204     * @throws IllegalArgumentException if the given defaultSort is blank
205     */
206    public void setDefaultSort(SortDefinition defaultSort) {
207        if (defaultSort == null) {
208            throw new IllegalArgumentException("invalid (null) defaultSort");
209        }
210        this.defaultSort = defaultSort;
211    }
212
213    /**
214     * @return List of attributeNames of all lookupField FieldDefinitions associated with this LookupDefinition, in the order in
215     *         which they were added
216     */
217    public List getLookupFieldNames() {
218        List fieldNames = new ArrayList();
219        fieldNames.addAll(this.lookupFieldMap.keySet());
220
221        return fieldNames;
222    }
223
224    /**
225     * @return Collection of all lookupField FieldDefinitions associated with this LookupDefinition, in the order in which they were
226     *         added
227     */
228    public List<FieldDefinition> getLookupFields() {
229        return lookupFields;
230    }
231
232    /**
233     * @param fieldName
234     * @return FieldDefinition associated with the named lookup field, or null if there is none
235     */
236    public FieldDefinition getLookupField(String attributeName) {
237        return lookupFieldMap.get(attributeName);
238    }
239
240    /**
241     * @return List of attributeNames of all resultField FieldDefinitions associated with this LookupDefinition, in the order in
242     *         which they were added
243     */
244    public List<String> getResultFieldNames() {
245        List<String> fieldNames = new ArrayList<String>();
246        fieldNames.addAll(resultFieldMap.keySet());
247
248        return fieldNames;
249    }
250
251    /**
252     * @return Collection of all resultField FieldDefinitions associated with this LookupDefinition, in the order in which they were
253     *         added
254     */
255    public List<FieldDefinition> getResultFields() {
256        return resultFields;
257    }
258
259
260    /**
261     * @param fieldName
262     * @return FieldDefinition associated with the named result field, or null if there is none
263     */
264    public FieldDefinition getResultField(String attributeName) {
265        return resultFieldMap.get(attributeName);
266    }
267
268    /**
269     * The resultSetLimit element specifies the maximum number of records that will be listed
270     * as a result of the lookup search.
271     */
272    public void setResultSetLimit(Integer resultSetLimit) {
273        this.resultSetLimit = resultSetLimit;
274    }
275
276    /**
277     * @return true if this instance has a result set limit
278     */
279    public boolean hasResultSetLimit() {
280        return (resultSetLimit != null);
281    }
282
283
284    /**
285     * The resultSetLimit element specifies the maximum number of records that will be listed
286     * as a result of the lookup search.
287     */
288    public Integer getResultSetLimit() {
289        return resultSetLimit;
290    }
291  
292    /**
293     * The multipleValuesResultSetLimit element specifies the maximum number of records that will be listed
294     * as a result of a multiple values lookup search.
295     */
296    public void setMultipleValuesResultSetLimit(Integer multipleValuesResultSetLimit) {
297        this.multipleValuesResultSetLimit = multipleValuesResultSetLimit;
298        }
299
300    /**
301     * @return true if this instance has a multiple values result set limit
302     */
303    public boolean hasMultipleValuesResultSetLimit() {
304        return (multipleValuesResultSetLimit != null);
305    }
306
307
308    /**
309     * The multipleValuesResultSetLimit element specifies the maximum number of records that will be listed
310     * as a result of a multiple values lookup search.
311     */
312    public Integer getMultipleValuesResultSetLimit() {
313        return multipleValuesResultSetLimit;
314    }
315    
316    /**
317     * Directly validate simple fields, call completeValidation on Definition fields.
318     *
319     * @see org.kuali.rice.krad.datadictionary.DataDictionaryDefinition#completeValidation(java.lang.Class, java.lang.Object)
320     */
321    public void completeValidation(Class rootBusinessObjectClass, Class otherBusinessObjectClass) {
322        if (hasDefaultSort()) {
323            defaultSort.completeValidation(rootBusinessObjectClass, null);
324        }
325
326        for (FieldDefinition lookupField : lookupFields) {
327            lookupField.completeValidation(rootBusinessObjectClass, null);
328        }
329
330        for (FieldDefinition resultField : resultFields) {
331            resultField.completeValidation(rootBusinessObjectClass, null);
332        }
333    }
334
335    /**
336     * @return true if this instance has extraButtonSource
337     */
338    public boolean hasExtraButtonSource() {
339        return extraButtonSource != null;
340    }
341
342    /**
343     * @return extraButtonSource
344     */
345    public String getExtraButtonSource() {
346        return extraButtonSource;
347    }
348
349    /**
350     * The extraButton element is used to define additional buttons which will
351     * appear on the lookup screen next to the Search and Clear buttons.
352     * You can define the image source and additional html parameters for
353     * each button.
354     * <p/>
355     * The extraButtonSource element defines the location of an image file
356     * to use for the extra button.
357     *
358     * @throws IllegalArgumentException if the given source is blank
359     */
360    public void setExtraButtonSource(String extraButtonSource) {
361        if (StringUtils.isBlank(extraButtonSource)) {
362            throw new IllegalArgumentException("invalid (blank) button source");
363        }
364        this.extraButtonSource = extraButtonSource;
365    }
366
367    /**
368     * @return true if this instance has extraButtonParams
369     */
370    public boolean hasExtraButtonParams() {
371        return extraButtonParams != null;
372    }
373
374    /**
375     * @return extraButtonParams
376     */
377    public String getExtraButtonParams() {
378        return extraButtonParams;
379    }
380
381    /**
382     * The extraButton element is used to define additional buttons which will
383     * appear on the lookup screen next to the Search and Clear buttons.
384     * You can define the image source and additional html parameters for
385     * each button.
386     * <p/>
387     * The extraButtonParams contains extra HTML parameters that be associated
388     * with the button.
389     */
390    public void setExtraButtonParams(String extraButtonParams) {
391        this.extraButtonParams = extraButtonParams;
392    }
393
394
395    /**
396     * @return true if this instance has an alternate icon to use for lookup icon
397     */
398    public boolean hasSearchIconOverride() {
399        return searchIconOverride != null;
400    }
401
402    /**
403     * @return search icon override url
404     */
405    public String getSearchIconOverride() {
406        return searchIconOverride;
407    }
408
409    /**
410     * The searchIconOverride element is used to define alternative icons
411     * appear on the lookup screen next to the Search and Clear buttons.
412     * You can define the image source.
413     *
414     * @throws IllegalArgumentException if the given source is blank
415     */
416    public void setSearchIconOverride(String searchIconOverride) {
417        if (StringUtils.isBlank(searchIconOverride)) {
418            throw new IllegalArgumentException("invalid (blank) search icon override");
419        }
420        this.searchIconOverride = searchIconOverride;
421    }
422
423
424    public String toString() {
425        return "LookupDefinition '" + getTitle() + "'";
426    }
427
428    /**
429     * The lookupFields element defines the set of fields in which the user
430     * can enter values representing search selection criteria.  A search result
431     * record will be returned only if the criteria entered in all the
432     * lookup fields are met.
433     * <p/>
434     * DD:  See LookupDefinition.java
435     * <p/>
436     * JSTL: lookupFields is a Map which is accessed using a key of "lookupFields".
437     * This map contains the following keys:
438     * attributeName of first lookup field
439     * attributeName of second lookup field
440     * etc.
441     * The corresponding values are lookupField Export Maps.
442     * See LookupMapBuilder.java.
443     * <p/>
444     * The lookupField element defines one lookup search
445     * criterion field.
446     * DD: See LookupDefinition.java.
447     * <p/>
448     * JSTL: lookupField is a Map which is accessed by a key
449     * which is the attributeName of a lookup field.  This map contains
450     * entries with the following keys:
451     * "attributeName" (String)
452     * "required" (boolean String)
453     * <p/>
454     * lookupField attribute definitions:
455     * <p/>
456     * required = true means that the user must enter something
457     * into the search criterion lookup field
458     * forceLookup = this attribute is not used
459     * noLookup = true means that field should not include magnifying glass (i.e. quickfinder)
460     */
461    public void setLookupFields(List<FieldDefinition> lookupFields) {
462        lookupFieldMap.clear();
463        for (FieldDefinition lookupField : lookupFields) {
464            if (lookupField == null) {
465                throw new IllegalArgumentException("invalid (null) lookupField");
466            }
467            String keyName = lookupField.getAttributeName();
468            if (lookupFieldMap.containsKey(keyName)) {
469                throw new DuplicateEntryException("duplicate lookupField entry for attribute '" + keyName + "'");
470            }
471
472            lookupFieldMap.put(keyName, lookupField);
473        }
474        this.lookupFields = lookupFields;
475    }
476
477    /**
478     * The resultFields element specifies the list of fields that are shown as a result
479     * of the lookup search.
480     * <p/>
481     * JSTL: resultFields is a Map which is accesseed by a key of "resultFields".
482     * This map contains entries with the following keys:
483     * attributeName of first result field
484     * attributeName of second result field
485     * etc.
486     * The corresponding values are ExportMap's
487     * <p/>
488     * The ExportMaps are accessed using a key of attributeName.
489     * Each ExportMap contains a single entry as follows:
490     * "attributeName"
491     * The corresponding value is the attributeName of the field.
492     * <p/>
493     * See LookupMapBuilder.java.
494     */
495    public void setResultFields(List<FieldDefinition> resultFields) {
496        resultFieldMap.clear();
497        for (FieldDefinition resultField : resultFields) {
498            if (resultField == null) {
499                throw new IllegalArgumentException("invalid (null) resultField");
500            }
501
502            String keyName = resultField.getAttributeName();
503            if (resultFieldMap.containsKey(keyName)) {
504                throw new DuplicateEntryException("duplicate resultField entry for attribute '" + keyName + "'");
505            }
506
507            resultFieldMap.put(keyName, resultField);
508        }
509        this.resultFields = resultFields;
510    }
511
512    /**
513     * @return the numOfColumns
514     */
515    public int getNumOfColumns() {
516        return this.numOfColumns;
517    }
518
519    /**
520     * @param numOfColumns the numOfColumns to set
521     */
522    public void setNumOfColumns(int numOfColumns) {
523        this.numOfColumns = numOfColumns;
524    }
525
526    /**
527     * @return the helpDefinition
528     */
529    public HelpDefinition getHelpDefinition() {
530        return this.helpDefinition;
531    }
532
533    /**
534     * @param helpDefinition the helpDefinition to set
535     */
536    public void setHelpDefinition(HelpDefinition helpDefinition) {
537        this.helpDefinition = helpDefinition;
538    }
539
540    /**
541     * @return the helpUrl
542     */
543    public String getHelpUrl() {
544        return this.helpUrl;
545    }
546
547    /**
548     * @param helpUrl the helpUrl to set
549     */
550    public void setHelpUrl(String helpUrl) {
551        this.helpUrl = helpUrl;
552    }
553
554    public boolean isTranslateCodes() {
555        return this.translateCodes;
556    }
557
558    public void setTranslateCodes(boolean translateCodes) {
559        this.translateCodes = translateCodes;
560    }
561
562    public boolean isDisableSearchButtons() {
563        return this.disableSearchButtons;
564    }
565
566    public void setDisableSearchButtons(boolean disableSearchButtons) {
567        this.disableSearchButtons = disableSearchButtons;
568    }
569
570}