View Javadoc

1   /**
2    * Copyright 2005-2013 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.service.impl;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.rice.core.api.CoreApiServiceLocator;
20  import org.kuali.rice.core.api.config.property.ConfigurationService;
21  import org.kuali.rice.core.api.exception.RiceRuntimeException;
22  import org.kuali.rice.core.api.uif.DataType;
23  import org.kuali.rice.core.api.uif.RemotableAbstractControl;
24  import org.kuali.rice.core.api.uif.RemotableAbstractWidget;
25  import org.kuali.rice.core.api.uif.RemotableAttributeField;
26  import org.kuali.rice.core.api.uif.RemotableCheckbox;
27  import org.kuali.rice.core.api.uif.RemotableCheckboxGroup;
28  import org.kuali.rice.core.api.uif.RemotableHiddenInput;
29  import org.kuali.rice.core.api.uif.RemotableQuickFinder;
30  import org.kuali.rice.core.api.uif.RemotableRadioButtonGroup;
31  import org.kuali.rice.core.api.uif.RemotableSelect;
32  import org.kuali.rice.core.api.uif.RemotableTextInput;
33  import org.kuali.rice.core.api.uif.RemotableTextarea;
34  import org.kuali.rice.core.api.util.KeyValue;
35  import org.kuali.rice.krad.bo.BusinessObject;
36  import org.kuali.rice.krad.bo.DataObjectRelationship;
37  import org.kuali.rice.krad.datadictionary.AttributeDefinition;
38  import org.kuali.rice.krad.service.DataDictionaryRemoteFieldService;
39  import org.kuali.rice.krad.service.DataDictionaryService;
40  import org.kuali.rice.krad.service.DataObjectMetaDataService;
41  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
42  import org.kuali.rice.krad.uif.control.CheckboxControl;
43  import org.kuali.rice.krad.uif.control.CheckboxGroupControl;
44  import org.kuali.rice.krad.uif.control.Control;
45  import org.kuali.rice.krad.uif.control.GroupControl;
46  import org.kuali.rice.krad.uif.control.HiddenControl;
47  import org.kuali.rice.krad.uif.control.MultiValueControl;
48  import org.kuali.rice.krad.uif.control.RadioGroupControl;
49  import org.kuali.rice.krad.uif.control.SelectControl;
50  import org.kuali.rice.krad.uif.control.TextAreaControl;
51  import org.kuali.rice.krad.uif.control.TextControl;
52  import org.kuali.rice.krad.uif.control.UserControl;
53  import org.kuali.rice.krad.util.DataTypeUtil;
54  import org.kuali.rice.krad.util.KRADConstants;
55  
56  import java.util.Collections;
57  import java.util.HashMap;
58  import java.util.List;
59  import java.util.Map;
60  
61  /**
62   * Implementation of the {@link DataDictionaryRemoteFieldService} service
63   *
64   * @author Kuali Rice Team (rice.collab@kuali.org)
65   */
66  public class DataDictionaryRemoteFieldServiceImpl implements DataDictionaryRemoteFieldService {
67  
68      /**
69       * @see org.kuali.rice.krad.service.DataDictionaryRemoteFieldService#buildRemotableFieldFromAttributeDefinition(java.lang.String,
70       *      java.lang.String)
71       */
72      public RemotableAttributeField buildRemotableFieldFromAttributeDefinition(String componentClassName,
73              String attributeName) {
74          AttributeDefinition baseDefinition;
75          Class<?> componentClass;
76          // try to resolve the component name - if not possible - try to pull the definition from the app mediation service
77          try {
78              componentClass = (Class<? extends BusinessObject>) Class.forName(componentClassName);
79              baseDefinition = getDataDictionaryService().getDataDictionary().getDictionaryObjectEntry(componentClassName)
80                      .getAttributeDefinition(attributeName);
81          } catch (ClassNotFoundException ex) {
82              throw new RiceRuntimeException("Unable to find attribute definition for attribute : " + attributeName);
83          }
84  
85          RemotableAttributeField.Builder definition = RemotableAttributeField.Builder.create(baseDefinition.getName());
86  
87          definition.setLongLabel(baseDefinition.getLabel());
88          definition.setShortLabel(baseDefinition.getShortLabel());
89          definition.setMaxLength(baseDefinition.getMaxLength());
90  
91          if (baseDefinition.isRequired() != null) {
92              definition.setRequired(baseDefinition.isRequired());
93          }
94  
95          definition.setForceUpperCase(baseDefinition.getForceUppercase());
96  
97          //set the datatype - needed for successful custom doc searches
98          String dataType = DataTypeUtil.determineFieldDataType((Class<? extends BusinessObject>) componentClass,
99                  attributeName);
100         definition.setDataType(DataType.valueOf(dataType.toUpperCase()));
101 
102         RemotableAbstractControl.Builder control = createControl(baseDefinition);
103         if (control != null) {
104             definition.setControl(control);
105         }
106 
107         RemotableQuickFinder.Builder qf = createQuickFinder(componentClass, attributeName);
108         if (qf != null) {
109             definition.setWidgets(Collections.<RemotableAbstractWidget.Builder>singletonList(qf));
110         }
111 
112         return definition.build();
113     }
114 
115     /**
116      * Creates a {@link RemotableAbstractControl} instance based on the control definition within the given
117      * attribute definition
118      *
119      * @param attr - attribute definition instance to pull control from
120      * @return RemotableAbstractControl instance or null if one could not be built
121      */
122     protected RemotableAbstractControl.Builder createControl(AttributeDefinition attr) {
123         Control control = attr.getControlField();
124 
125         if (control != null) {
126             if (control instanceof CheckboxControl) {
127                 return RemotableCheckbox.Builder.create();
128             } else if (control instanceof CheckboxGroupControl) {
129                 return RemotableCheckboxGroup.Builder.create(getValues(attr));
130             } else if (control instanceof HiddenControl) {
131                 return RemotableHiddenInput.Builder.create();
132             } else if (control instanceof SelectControl) {
133                 RemotableSelect.Builder b = RemotableSelect.Builder.create(getValues(attr));
134                 b.setMultiple(((SelectControl) control).isMultiple());
135                 b.setSize(((SelectControl) control).getSize());
136             } else if (control instanceof RadioGroupControl) {
137                 return RemotableRadioButtonGroup.Builder.create(getValues(attr));
138             } else if (control instanceof TextControl) {
139                 final RemotableTextInput.Builder b = RemotableTextInput.Builder.create();
140                 b.setSize(((TextControl) control).getSize());
141                 return b;
142             } else if (control instanceof UserControl) {
143                 final RemotableTextInput.Builder b = RemotableTextInput.Builder.create();
144                 b.setSize(((UserControl) control).getSize());
145                 return b;
146             } else if (control instanceof GroupControl) {
147                 final RemotableTextInput.Builder b = RemotableTextInput.Builder.create();
148                 b.setSize(((GroupControl) control).getSize());
149                 return b;
150             } else if (control instanceof TextAreaControl) {
151                 final RemotableTextarea.Builder b = RemotableTextarea.Builder.create();
152                 b.setCols(((TextAreaControl) control).getCols());
153                 b.setRows(((TextAreaControl) control).getRows());
154                 return b;
155             }
156         }
157         
158         return null;
159     }
160 
161     /**
162      * Will first try to retrieve options configured on the control.  If that doesn't return any values then will
163      * try to use the optionfinder on the AttributeDefinition.
164      *
165      * @param attr - AttributeDefinition
166      * @return Map of key value pairs
167      */
168     protected Map<String, String> getValues(AttributeDefinition attr) {
169         Control control = attr.getControlField();
170 
171         if ((control instanceof MultiValueControl)
172                 && (((MultiValueControl) control).getOptions() != null)
173                 && !((MultiValueControl) control).getOptions().isEmpty()) {
174             List<KeyValue> keyValues = ((MultiValueControl) control).getOptions();
175                     Map<String, String> options = new HashMap<String, String> ();
176                     for (KeyValue keyValue : keyValues) {
177                         options.put(keyValue.getKey(), keyValue.getValue());
178                     }
179                     return options;
180         } else if (attr.getOptionsFinder() != null) {
181             return attr.getOptionsFinder().getKeyLabelMap();
182         }
183 
184         return Collections.emptyMap();
185     }
186 
187     /**
188      * Builds a {@link RemotableQuickFinder} instance for the given attribute based on determined relationships
189      *
190      * <p>
191      * Uses the {@link DataObjectMetaDataService} to find relationships the given attribute participates in within the
192      * given class. If a relationship is not found, the title attribute is also checked to determine if a lookup should
193      * be rendered back to the component class itself. If a relationship suitable for lookup is found, the associated
194      * field conversions and lookup parameters are built
195      * </p>
196      *
197      * @param componentClass - class that attribute belongs to and should be checked for relationships
198      * @param attributeName - name of the attribute to determine quickfinder for
199      * @return RemotableQuickFinder.Builder instance for the configured lookup, or null if one could not be found
200      */
201     protected RemotableQuickFinder.Builder createQuickFinder(Class<?> componentClass, String attributeName) {
202         Object sampleComponent;
203         try {
204             sampleComponent = componentClass.newInstance();
205         } catch (InstantiationException e) {
206             throw new RiceRuntimeException(e);
207         } catch (IllegalAccessException e) {
208             throw new RiceRuntimeException(e);
209         }
210 
211         String lookupClassName = null;
212         Map<String, String> fieldConversions = new HashMap<String, String>();
213         Map<String, String> lookupParameters = new HashMap<String, String>();
214 
215         DataObjectRelationship relationship = getDataObjectMetaDataService().getDataObjectRelationship(sampleComponent,
216                 componentClass, attributeName, "", true, true, false);
217         if (relationship != null) {
218             lookupClassName = relationship.getRelatedClass().getName();
219 
220             for (Map.Entry<String, String> entry : relationship.getParentToChildReferences().entrySet()) {
221                 String fromField = entry.getValue();
222                 String toField = entry.getKey();
223                 fieldConversions.put(fromField, toField);
224             }
225 
226             for (Map.Entry<String, String> entry : relationship.getParentToChildReferences().entrySet()) {
227                 String fromField = entry.getKey();
228                 String toField = entry.getValue();
229 
230                 if (relationship.getUserVisibleIdentifierKey() == null || relationship.getUserVisibleIdentifierKey()
231                         .equals(fromField)) {
232                     lookupParameters.put(fromField, toField);
233                 }
234             }
235         } else {
236             // check for title attribute and if match build lookup to component class using pk fields
237             String titleAttribute = getDataObjectMetaDataService().getTitleAttribute(componentClass);
238             if (StringUtils.equals(titleAttribute, attributeName)) {
239                 lookupClassName = componentClass.getName();
240 
241                 List<String> pkAttributes = getDataObjectMetaDataService().listPrimaryKeyFieldNames(componentClass);
242                 for (String pkAttribute : pkAttributes) {
243                     fieldConversions.put(pkAttribute, pkAttribute);
244                     if (!StringUtils.equals(pkAttribute, attributeName)) {
245                         lookupParameters.put(pkAttribute, pkAttribute);
246                     }
247                 }
248             }
249         }
250         
251         if (StringUtils.isNotBlank(lookupClassName)) {
252             String baseUrl = getKualiConfigurationService().getPropertyValueAsString(KRADConstants.KRAD_LOOKUP_URL_KEY);
253             RemotableQuickFinder.Builder builder = RemotableQuickFinder.Builder.create(baseUrl, lookupClassName);
254             builder.setLookupParameters(lookupParameters);
255             builder.setFieldConversions(fieldConversions);
256 
257             return builder;
258         }
259 
260         return null;
261     }
262 
263     protected DataDictionaryService getDataDictionaryService() {
264         return KRADServiceLocatorWeb.getDataDictionaryService();
265     }
266 
267     protected DataObjectMetaDataService getDataObjectMetaDataService() {
268         return KRADServiceLocatorWeb.getDataObjectMetaDataService();
269     }
270 
271     protected ConfigurationService getKualiConfigurationService() {
272         return CoreApiServiceLocator.getKualiConfigurationService();
273     }
274 }