View Javadoc
1   /*
2    * Copyright 2012 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.ole.sys.document.workflow;
17  
18  import java.util.Collections;
19  import java.util.HashMap;
20  import java.util.List;
21  import java.util.Map;
22  
23  import org.apache.commons.lang.StringUtils;
24  import org.kuali.ole.sys.context.SpringContext;
25  import org.kuali.rice.core.api.config.property.ConfigurationService;
26  import org.kuali.rice.core.api.exception.RiceRuntimeException;
27  import org.kuali.rice.core.api.uif.DataType;
28  import org.kuali.rice.core.api.uif.RemotableAbstractControl;
29  import org.kuali.rice.core.api.uif.RemotableAbstractWidget;
30  import org.kuali.rice.core.api.uif.RemotableAttributeField;
31  import org.kuali.rice.core.api.uif.RemotableCheckbox;
32  import org.kuali.rice.core.api.uif.RemotableHiddenInput;
33  import org.kuali.rice.core.api.uif.RemotableQuickFinder;
34  import org.kuali.rice.core.api.uif.RemotableRadioButtonGroup;
35  import org.kuali.rice.core.api.uif.RemotableSelect;
36  import org.kuali.rice.core.api.uif.RemotableTextInput;
37  import org.kuali.rice.core.api.uif.RemotableTextarea;
38  import org.kuali.rice.kns.datadictionary.control.CheckboxControlDefinition;
39  import org.kuali.rice.kns.datadictionary.control.HiddenControlDefinition;
40  import org.kuali.rice.kns.datadictionary.control.MultivalueControlDefinitionBase;
41  import org.kuali.rice.kns.datadictionary.control.RadioControlDefinition;
42  import org.kuali.rice.kns.datadictionary.control.SelectControlDefinition;
43  import org.kuali.rice.kns.datadictionary.control.TextControlDefinition;
44  import org.kuali.rice.kns.datadictionary.control.TextareaControlDefinition;
45  import org.kuali.rice.kns.service.WorkflowAttributePropertyResolutionService;
46  import org.kuali.rice.krad.bo.BusinessObject;
47  import org.kuali.rice.krad.bo.DataObjectRelationship;
48  import org.kuali.rice.krad.datadictionary.AttributeDefinition;
49  import org.kuali.rice.krad.datadictionary.control.ControlDefinition;
50  import org.kuali.rice.krad.keyvalues.KeyValuesFinder;
51  import org.kuali.rice.krad.service.DataDictionaryService;
52  import org.kuali.rice.krad.service.DataObjectMetaDataService;
53  import org.kuali.rice.krad.service.KRADServiceLocator;
54  import org.kuali.rice.krad.service.KRADServiceLocatorInternal;
55  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
56  import org.kuali.rice.krad.util.KRADConstants;
57  
58  //RICE20 This class is a temporary fix to support KNS attribute definitions. Should be deleted when rice2.0 adds support.
59  public class DataDictionaryRemoteFieldBuilder {
60      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DataDictionaryRemoteFieldBuilder.class);
61  
62      /**
63       * @see org.kuali.rice.krad.service.DataDictionaryRemoteFieldService#buildRemotableFieldFromAttributeDefinition(java.lang.String,
64       *      java.lang.String)
65       */
66      public RemotableAttributeField buildRemotableFieldFromAttributeDefinition(String componentClassName, String attributeName) {
67          AttributeDefinition baseDefinition;
68          Class<?> componentClass;
69          // try to resolve the component name - if not possible - try to pull the definition from the app mediation service
70          try {
71              componentClass = (Class<? extends BusinessObject>) Class.forName(componentClassName);
72              baseDefinition = getDataDictionaryService().getDataDictionary().getDictionaryObjectEntry(componentClassName).getAttributeDefinition(attributeName);
73          }
74          catch (ClassNotFoundException ex) {
75              throw new RiceRuntimeException("Unable to find attribute definition for attribute : " + attributeName);
76          }
77  
78          RemotableAttributeField.Builder definition = RemotableAttributeField.Builder.create(baseDefinition.getName());
79  
80          definition.setLongLabel(baseDefinition.getLabel());
81          definition.setShortLabel(baseDefinition.getShortLabel());
82          definition.setMaxLength(baseDefinition.getMaxLength());
83          definition.setRequired(baseDefinition.isRequired());
84          definition.setForceUpperCase(baseDefinition.getForceUppercase());
85          // set the datatype - needed for successful custom doc searches
86          WorkflowAttributePropertyResolutionService propertyResolutionService = (WorkflowAttributePropertyResolutionService)SpringContext.getBean("workflowAttributesPropertyResolutionService");
87          String dataType = propertyResolutionService.determineFieldDataType((Class<? extends BusinessObject>) componentClass, attributeName);
88          definition.setDataType(DataType.valueOf(dataType.toUpperCase()));
89          RemotableAbstractControl.Builder control = createControl(baseDefinition);
90          if (control != null) {
91              definition.setControl(control);
92          }
93  
94          try {
95              RemotableQuickFinder.Builder qf = createQuickFinder(componentClass, attributeName);
96              if (qf != null) {
97                  definition.setWidgets(Collections.<RemotableAbstractWidget.Builder> singletonList(qf));
98              }
99          } catch ( Exception ex ) {
100             LOG.warn(ex);
101         }
102 
103         return definition.build();
104     }
105 
106     /**
107      * Creates a {@link RemotableAbstractControl} instance based on the control definition within the given attribute definition
108      *
109      * @param attr - attribute definition instance to pull control from
110      * @return RemotableAbstractControl instance or null if one could not be built
111      */
112     protected RemotableAbstractControl.Builder createControl(AttributeDefinition attr) {
113         ControlDefinition control = attr.getControl();
114 
115         if (control != null) {
116             if (control instanceof CheckboxControlDefinition) {
117                 return RemotableCheckbox.Builder.create();
118             }
119             // else if (control instanceof CheckboxGroupControl) {
120             // return RemotableCheckboxGroup.Builder.create(getValues(attr));
121             // }
122             else if (control instanceof HiddenControlDefinition) {
123                 return RemotableHiddenInput.Builder.create();
124             }
125             else if (control instanceof SelectControlDefinition) {
126                 RemotableSelect.Builder b = RemotableSelect.Builder.create(getValues(attr));
127                 b.setMultiple(((SelectControlDefinition) control).isMultiselect());
128                 b.setSize(((SelectControlDefinition) control).getSize());
129             }
130             else if (control instanceof RadioControlDefinition) {
131                 return RemotableRadioButtonGroup.Builder.create(getValues(attr));
132             }
133             else if (control instanceof TextControlDefinition) {
134                 final RemotableTextInput.Builder b = RemotableTextInput.Builder.create();
135                 b.setSize(((TextControlDefinition) control).getSize());
136                 return b;
137             }
138 
139             // else if (control instanceof UserControl) {
140             // final RemotableTextInput.Builder b = RemotableTextInput.Builder.create();
141             // b.setSize(((UserControl) control).getSize());
142             // return b;
143             // }
144             // else if (control instanceof GroupControl) {
145             // final RemotableTextInput.Builder b = RemotableTextInput.Builder.create();
146             // b.setSize(((GroupControl) control).getSize());
147             // return b;
148             // }
149 
150             else if (control instanceof TextareaControlDefinition) {
151                 final RemotableTextarea.Builder b = RemotableTextarea.Builder.create();
152                 b.setCols(((TextareaControlDefinition) control).getCols());
153                 b.setRows(((TextareaControlDefinition) 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 try to use the
163      * 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         ControlDefinition control = attr.getControl();
170 
171         if (MultivalueControlDefinitionBase.class.isAssignableFrom(control.getClass())) {
172             String valuesFinderClass = ((MultivalueControlDefinitionBase) control).getValuesFinderClass();
173             KeyValuesFinder finder;
174             try {
175                 finder = (KeyValuesFinder) Class.forName(valuesFinderClass).newInstance();
176             }
177             catch (Exception ex) {
178                 throw new RuntimeException("", ex);
179             }
180             Map<String, String> options = finder.getKeyLabelMap();
181             return options;
182         }
183         else if (attr.getOptionsFinder() != null) {
184             return attr.getOptionsFinder().getKeyLabelMap();
185         }
186 
187         return Collections.emptyMap();
188     }
189 
190     /**
191      * Builds a {@link RemotableQuickFinder} instance for the given attribute based on determined relationships
192      * <p>
193      * Uses the {@link DataObjectMetaDataService} to find relationships the given attribute participates in within the given class.
194      * If a relationship is not found, the title attribute is also checked to determine if a lookup should be rendered back to the
195      * component class itself. If a relationship suitable for lookup is found, the associated field conversions and lookup
196      * parameters are built
197      * </p>
198      *
199      * @param componentClass - class that attribute belongs to and should be checked for relationships
200      * @param attributeName - name of the attribute to determine quickfinder for
201      * @return RemotableQuickFinder.Builder instance for the configured lookup, or null if one could not be found
202      */
203     protected RemotableQuickFinder.Builder createQuickFinder(Class<?> componentClass, String attributeName) {
204         Object sampleComponent;
205         try {
206             sampleComponent = componentClass.newInstance();
207         }
208         catch (InstantiationException e) {
209             throw new RiceRuntimeException(e);
210         }
211         catch (IllegalAccessException e) {
212             throw new RiceRuntimeException(e);
213         }
214 
215         String lookupClassName = null;
216         Map<String, String> fieldConversions = new HashMap<String, String>();
217         Map<String, String> lookupParameters = new HashMap<String, String>();
218 
219         DataObjectRelationship relationship = getDataObjectMetaDataService().getDataObjectRelationship(sampleComponent, componentClass, attributeName, "", true, true, false);
220         if (relationship != null) {
221             lookupClassName = relationship.getRelatedClass().getName();
222 
223             for (Map.Entry<String, String> entry : relationship.getParentToChildReferences().entrySet()) {
224                 String fromField = entry.getValue();
225                 String toField = entry.getKey();
226                 fieldConversions.put(fromField, toField);
227             }
228 
229             for (Map.Entry<String, String> entry : relationship.getParentToChildReferences().entrySet()) {
230                 String fromField = entry.getKey();
231                 String toField = entry.getValue();
232 
233                 if (relationship.getUserVisibleIdentifierKey() == null || relationship.getUserVisibleIdentifierKey().equals(fromField)) {
234                     lookupParameters.put(fromField, toField);
235                 }
236             }
237         }
238         else {
239             Map foreignKeysForReference = KRADServiceLocator.getPersistenceStructureService().getForeignKeysForReference(componentClass, attributeName);
240             // check for title attribute and if match build lookup to component class using pk fields
241             String titleAttribute = getDataObjectMetaDataService().getTitleAttribute(componentClass);
242             if (StringUtils.equals(titleAttribute, attributeName)) {
243                 lookupClassName = componentClass.getName();
244 
245                 List<String> pkAttributes = getDataObjectMetaDataService().listPrimaryKeyFieldNames(componentClass);
246                 for (String pkAttribute : pkAttributes) {
247                     fieldConversions.put(pkAttribute, pkAttribute);
248                     if (!StringUtils.equals(pkAttribute, attributeName)) {
249                         lookupParameters.put(pkAttribute, pkAttribute);
250                     }
251                 }
252             }
253         }
254 
255         if (StringUtils.isNotBlank(lookupClassName)) {
256             String baseUrl = getKualiConfigurationService().getPropertyValueAsString(KRADConstants.KRAD_LOOKUP_URL_KEY);
257             RemotableQuickFinder.Builder builder = RemotableQuickFinder.Builder.create(baseUrl, lookupClassName);
258             builder.setLookupParameters(lookupParameters);
259             builder.setFieldConversions(fieldConversions);
260 
261             return builder;
262         }
263 
264         return null;
265     }
266 
267     protected DataDictionaryService getDataDictionaryService() {
268         return KRADServiceLocatorWeb.getDataDictionaryService();
269     }
270 
271     protected DataObjectMetaDataService getDataObjectMetaDataService() {
272         return KRADServiceLocatorWeb.getDataObjectMetaDataService();
273     }
274 
275     protected ConfigurationService getKualiConfigurationService() {
276         return SpringContext.getBean(ConfigurationService.class);
277     }
278 
279 
280 }