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