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