View Javadoc
1   /**
2    * Copyright 2005-2015 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.kim.lookup;
17  
18  import org.apache.commons.collections.CollectionUtils;
19  import org.apache.commons.lang.StringUtils;
20  import org.kuali.rice.core.api.criteria.Predicate;
21  import org.kuali.rice.core.api.criteria.PredicateUtils;
22  import org.kuali.rice.core.api.criteria.QueryByCriteria;
23  import org.kuali.rice.core.api.util.ConcreteKeyValue;
24  import org.kuali.rice.core.api.util.KeyValue;
25  import org.kuali.rice.core.web.format.BooleanFormatter;
26  import org.kuali.rice.core.web.format.CollectionFormatter;
27  import org.kuali.rice.core.web.format.DateFormatter;
28  import org.kuali.rice.core.web.format.Formatter;
29  import org.kuali.rice.kew.api.KewApiConstants;
30  import org.kuali.rice.kim.api.KimConstants;
31  import org.kuali.rice.kim.api.group.Group;
32  import org.kuali.rice.kim.api.group.GroupQueryResults;
33  import org.kuali.rice.kim.api.identity.Person;
34  import org.kuali.rice.kim.api.identity.principal.Principal;
35  import org.kuali.rice.kim.api.identity.principal.PrincipalQueryResults;
36  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
37  import org.kuali.rice.kim.api.type.KimAttributeField;
38  import org.kuali.rice.kim.api.type.KimType;
39  import org.kuali.rice.kim.framework.services.KimFrameworkServiceLocator;
40  import org.kuali.rice.kim.framework.type.KimTypeService;
41  import org.kuali.rice.kim.impl.KIMPropertyConstants;
42  import org.kuali.rice.kim.impl.group.GroupBo;
43  import org.kuali.rice.kim.impl.type.KimTypeLookupableHelperServiceImpl;
44  import org.kuali.rice.kim.util.KimCommonUtilsInternal;
45  import org.kuali.rice.kns.document.authorization.BusinessObjectRestrictions;
46  import org.kuali.rice.kns.kim.type.DataDictionaryTypeServiceHelper;
47  import org.kuali.rice.kns.lookup.HtmlData;
48  import org.kuali.rice.kns.web.comparator.CellComparatorHelper;
49  import org.kuali.rice.kns.web.struts.form.LookupForm;
50  import org.kuali.rice.kns.web.ui.Column;
51  import org.kuali.rice.kns.web.ui.Field;
52  import org.kuali.rice.kns.web.ui.ResultRow;
53  import org.kuali.rice.kns.web.ui.Row;
54  import org.kuali.rice.krad.bo.BusinessObject;
55  import org.kuali.rice.krad.bo.PersistableBusinessObject;
56  import org.kuali.rice.krad.data.KradDataServiceLocator;
57  import org.kuali.rice.krad.datadictionary.AttributeDefinition;
58  import org.kuali.rice.krad.keyvalues.IndicatorValuesFinder;
59  import org.kuali.rice.krad.keyvalues.KeyValuesFinder;
60  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
61  import org.kuali.rice.krad.util.GlobalVariables;
62  import org.kuali.rice.krad.util.KRADConstants;
63  import org.kuali.rice.krad.util.UrlFactory;
64  
65  import java.sql.Date;
66  import java.sql.Timestamp;
67  import java.util.ArrayList;
68  import java.util.Calendar;
69  import java.util.Collection;
70  import java.util.Collections;
71  import java.util.Comparator;
72  import java.util.HashMap;
73  import java.util.Iterator;
74  import java.util.List;
75  import java.util.Map;
76  import java.util.Properties;
77  
78  import static org.kuali.rice.core.api.criteria.PredicateFactory.*;
79  
80  public class GroupLookupableHelperServiceImpl  extends KimLookupableHelperServiceImpl {
81  
82  	// need this so kimtypeId value can be retained in 'rows'
83  	// 1st pass populate the grprows
84  	// 2nd pass for jsp, no populate, so return the existing one.
85      private static final String KIM_TYPE_ID_PROPERTY_NAME = "kimTypeId";
86  	private List<Row> grpRows = new ArrayList<Row>();
87  	private List<Row> attrRows = new ArrayList<Row>();
88  	private String typeId = "";
89  	private List<KimAttributeField> attrDefinitions;
90  	private final Map<String, String> groupTypeValuesCache = new HashMap<String, String>();
91  
92      @Override
93      public List<HtmlData> getCustomActionUrls(BusinessObject bo, List pkNames) {
94      	GroupBo groupImpl = (GroupBo) bo;
95          List<HtmlData> anchorHtmlDataList = new ArrayList<HtmlData>();
96          if(allowsNewOrCopyAction(KimConstants.KimUIConstants.KIM_GROUP_DOCUMENT_TYPE_NAME)){
97          	anchorHtmlDataList.add(getEditGroupUrl(groupImpl));	
98          }
99      	return anchorHtmlDataList;
100     }
101     
102     protected HtmlData getEditGroupUrl(GroupBo groupBo) {
103     	String href = "";
104 
105         Properties parameters = new Properties();
106         parameters.put(KRADConstants.DISPATCH_REQUEST_PARAMETER, KRADConstants.DOC_HANDLER_METHOD);
107         parameters.put(KRADConstants.PARAMETER_COMMAND, KewApiConstants.INITIATE_COMMAND);
108         parameters.put(KRADConstants.DOCUMENT_TYPE_NAME, KimConstants.KimUIConstants.KIM_GROUP_DOCUMENT_TYPE_NAME);
109         parameters.put(KimConstants.PrimaryKeyConstants.GROUP_ID, groupBo.getId());
110         if (StringUtils.isNotBlank(getReturnLocation())) {
111         	parameters.put(KRADConstants.RETURN_LOCATION_PARAMETER, getReturnLocation());
112 		}
113         href = UrlFactory.parameterizeUrl(KimCommonUtilsInternal.getKimBasePath()+KimConstants.KimUIConstants.KIM_GROUP_DOCUMENT_ACTION, parameters);
114         
115         HtmlData.AnchorHtmlData anchorHtmlData = new HtmlData.AnchorHtmlData(href,
116         		KRADConstants.DOC_HANDLER_METHOD, KRADConstants.MAINTENANCE_EDIT_METHOD_TO_CALL);
117         return anchorHtmlData;
118     }
119 
120     /**
121      * Converts GroupInfo objects to GroupBo objects.
122      * 
123      * @param  fieldValues  names and values returned by the Group Lookup screen
124      * @return  groupImplList  a list of GroupImpl objects
125      */
126     @Override
127     public List<GroupBo> getSearchResults(java.util.Map<String,String> fieldValues)  {
128         Map<String, String> criteriaMap = new HashMap<String, String>(fieldValues);
129         QueryByCriteria.Builder criteria = QueryByCriteria.Builder.create();
130         criteriaMap.remove(KRADConstants.DOC_FORM_KEY);
131         criteriaMap.remove(KRADConstants.BACK_LOCATION);
132         criteriaMap.remove(KRADConstants.DOC_NUM);
133         String refToRef = criteriaMap.get(KRADConstants.REFERENCES_TO_REFRESH);
134         if (StringUtils.isNotBlank(refToRef) && refToRef.equalsIgnoreCase("GroupBo")) {
135             criteriaMap.remove(KRADConstants.REFERENCES_TO_REFRESH);
136         }
137         boolean validPrncplFoundIfPrncplCritPresent = true;
138         Map<String, String> attribsMap = new HashMap<String, String>();
139         if (!criteriaMap.isEmpty()) {
140             List<Predicate> predicates = new ArrayList<Predicate>();
141             //principalId doesn't exist on 'Group'.  Lets do this predicate conversion separately
142             if (StringUtils.isNotBlank(criteriaMap.get(KimConstants.UniqueKeyConstants.PRINCIPAL_NAME))) {
143                 QueryByCriteria.Builder principalCriteria = QueryByCriteria.Builder.create();
144                 Predicate principalPred = like("principalName", criteriaMap.get(KimConstants.UniqueKeyConstants.PRINCIPAL_NAME));
145                 principalCriteria.setPredicates(principalPred);
146                 //String principalId = KimApiServiceLocator.getIdentityService()
147                 //        .getPrincipalByPrincipalName(criteriaMap.get(KimConstants.UniqueKeyConstants.PRINCIPAL_NAME)).getPrincipalId();
148                 PrincipalQueryResults principals = KimApiServiceLocator.getIdentityService()
149                         .findPrincipals(principalCriteria.build());
150                 List<String> principalIds = new ArrayList<String>();
151                 for (Principal principal : principals.getResults()) {
152                     principalIds.add(principal.getPrincipalId());
153                 }
154                 if (CollectionUtils.isNotEmpty(principalIds)) {
155                     Timestamp currentTime = new Timestamp(Calendar.getInstance().getTimeInMillis());
156                     predicates.add( and(
157                                     in("members.memberId", principalIds.toArray(
158                                             new String[principalIds.size()])),
159                                     equal("members.typeCode", KimConstants.KimGroupMemberTypes.PRINCIPAL_MEMBER_TYPE.getCode()),
160                                     and(
161                                             or(isNull("members.activeFromDateValue"), lessThanOrEqual("members.activeFromDateValue", currentTime)),
162                                             or(isNull("members.activeToDateValue"), greaterThan("members.activeToDateValue", currentTime))
163                                     )
164                                 ));
165                 }else {
166                     validPrncplFoundIfPrncplCritPresent = false;
167                 }
168 
169             }
170             criteriaMap.remove(KimConstants.UniqueKeyConstants.PRINCIPAL_NAME);
171             Map<String, String> criteriaCopy = new HashMap<String, String>();
172             // copy the criteria map so we can modify it
173             criteriaCopy.putAll(criteriaMap);
174             // check the attribute definitions for attribute names
175             for(String key : criteriaCopy.keySet()) {
176                 if (isParamAttribute(key)) {
177                     if (StringUtils.isNotBlank(criteriaMap.get(key))) {
178                         String attributeName = StringUtils.substringBetween(key, "attributes(", ")");
179                         attribsMap.put(attributeName, criteriaMap.get(key));
180                     }
181 
182                     // valid attribute name so remove from criteria map
183                     criteriaMap.remove(key);
184                 }
185             }
186             predicates.add(PredicateUtils.convertMapToPredicate(criteriaMap));
187             criteria.setPredicates(and(predicates.toArray(new Predicate[predicates.size()])));
188         }
189         List<Group> groups = new ArrayList<Group>();
190         if (validPrncplFoundIfPrncplCritPresent) {
191             GroupQueryResults groupResults = KimApiServiceLocator.getGroupService().findGroups(criteria.build());
192             groups = groupResults.getResults();
193         }
194 
195         //have to convert back to Bos
196         Map<String, GroupBo> groupBos = new HashMap<String, GroupBo>(groups.size());
197         for (Group group : groups) {
198             // filter by any attributes
199             if (attribsMap.isEmpty()) {
200                 if (groupBos.get(group.getId()) == null) {
201                     groupBos.put(group.getId(), GroupBo.from(group));
202                 }
203             } else {
204                 boolean containsAllAttribs = true;
205                 for (String attribute : attribsMap.keySet()) {
206                     containsAllAttribs &= group.getAttributes().containsKey(attribute) &&
207                             group.getAttributes().get(attribute).equalsIgnoreCase(attribsMap.get(attribute));
208                 }
209                 if (containsAllAttribs) {
210                     if (groupBos.get(group.getId()) == null) {
211                         groupBos.put(group.getId(), GroupBo.from(group));
212                     }
213                 }
214             }
215         }
216 
217     	return new ArrayList<GroupBo>(groupBos.values());
218     }
219 
220     /**
221      * Determines if the given parameter is wrapped with attributes() and the wrapped value is a non-empty
222      * <code>String</code>.
223      * @param param The string to test.
224      * @return <code>TRUE</code> if the parameter passed in is wrapped with attributes() and the wrapped value is
225      * non-empty, <code>FALSE</code> otherwise.
226      */
227     private boolean isParamAttribute(String param) {
228         return param.matches("attributes\\((.*?)\\)") &&
229                 StringUtils.isNotBlank(StringUtils.substringBetween(param, "attributes(",")")) &&
230                 StringUtils.substringBetween(param, "attributes(",")") != "null";
231     }
232 
233     @Override
234     public boolean checkForAdditionalFields(Map<String, String> fieldValues) {
235         List<Row> attributeRows = setupAttributeRows(fieldValues);
236         if (attributeRows.isEmpty()) {
237             setAttrRows(attributeRows);
238         } else if (CollectionUtils.isEmpty(getAttrRows())) {
239             setAttrRows(attributeRows);
240         }
241         if (getAttrRows().size() > 0) {
242             return true;
243         }
244         return false;
245     }
246 
247 
248 	@Override
249 	public List<Row> getRows() {
250 		if (getGrpRows().isEmpty()) {
251 			List<Row> rows = super.getRows();
252 			List<Row> returnRows = new ArrayList<Row>();
253 			for (Row row : rows) {
254 				for (int i = row.getFields().size() - 1; i >= 0; i--) {
255 					Field field = row.getFields().get(i);
256 					if (field.getPropertyName().equals(KIM_TYPE_ID_PROPERTY_NAME)) {
257 						Field typeField = new Field();
258 						typeField.setFieldLabel("Type");
259 						typeField.setPropertyName(KIM_TYPE_ID_PROPERTY_NAME);
260 						typeField.setFieldValidValues(getGroupTypeOptions());
261 						typeField.setFieldType(Field.DROPDOWN);
262 						row.getFields().set(i, typeField);
263 					}
264 				}
265 				returnRows.add(row);
266 			}
267 			// principalName
268 			Field typeField = new Field();
269 			typeField.setFieldLabel("Principal Name");
270 			typeField.setPropertyName(KIMPropertyConstants.Person.PRINCIPAL_NAME);
271 			typeField.setFieldType(Field.TEXT);
272 			typeField.setMaxLength(40);
273 			typeField.setSize(20);
274 			typeField.setQuickFinderClassNameImpl("org.kuali.rice.kim.api.identity.Person");
275 			typeField.setFieldConversions( "principalName:principalName" );
276 			typeField.setLookupParameters( "principalName:principalName" );
277 			// Identify the best spot to insert the "Principal Name" search field. Note that the code below assumes that the final row of the
278 			// group search fields is not a row with hidden fields; if this ever becomes the case in the future, this fix may need to
279 			// be modified accordingly.
280 			List<Field> fields = (returnRows.isEmpty()) ? new ArrayList<Field>() : returnRows.get(returnRows.size() - 1).getFields();
281 			if (!fields.isEmpty() && fields.get(fields.size() - 1).getFieldType().equals(Field.BLANK_SPACE)) {
282 				// If the last row in the list has a BLANK_SPACE field coming after any non-BLANK_SPACE fields, add the new field to that row.
283 				int insertLoc = fields.size() - 1;
284 				while (insertLoc >= 0 && fields.get(insertLoc).getFieldType().equals(Field.BLANK_SPACE)) {
285 					insertLoc--;
286 				}
287 				fields.set(insertLoc + 1, typeField);
288 				returnRows.get(returnRows.size() - 1).setFields(fields);
289 			} else {
290 				// Otherwise, add a new row containing that field.
291 				int fieldLen = fields.size();
292 				fields = new ArrayList<Field>();
293 				fields.add(typeField);
294 				for (int i = 1; i < fieldLen; i++) {
295 					Field blankSpace = new Field();
296 					blankSpace.setFieldType(Field.BLANK_SPACE);
297 					blankSpace.setPropertyName(Field.BLANK_SPACE);
298 					fields.add(blankSpace);
299 				}
300 				returnRows.add(new Row(fields));
301 			}
302 			
303 			setGrpRows(returnRows);
304 		}
305 		if (getAttrRows().isEmpty()) {
306 			setAttrDefinitions(Collections.<KimAttributeField>emptyList());
307 			return getGrpRows();
308 		} 
309 		List<Row> fullRows = new ArrayList<Row>();
310 		fullRows.addAll(getGrpRows());
311 		fullRows.addAll(getAttrRows());
312 		return fullRows;
313 	}
314 
315 
316 	@Override
317 	public List<Column> getColumns() {
318 		List<Column> columns =  super.getColumns();
319         for (Row row : attrRows) {
320             for (Field field : row.getFields()) {
321                 Column newColumn = new Column();
322                 newColumn.setColumnTitle(field.getFieldLabel());
323                 newColumn.setMaxLength(getColumnMaxLength(field.getPropertyName()));
324                 newColumn.setPropertyName(field.getPropertyName());
325                 newColumn.setFormatter(field.getFormatter());
326                 columns.add(newColumn);
327             }
328         }
329         return columns;
330 	}
331 
332     @Override
333 	public Collection<GroupBo> performLookup(LookupForm lookupForm, Collection resultTable, boolean bounded) {
334         setBackLocation((String) lookupForm.getFieldsForLookup().get(KRADConstants.BACK_LOCATION));
335         setDocFormKey((String) lookupForm.getFieldsForLookup().get(KRADConstants.DOC_FORM_KEY));
336         List<GroupBo> displayList;
337 
338         // call search method to get results
339         if (bounded) {
340             displayList = getSearchResults(lookupForm.getFieldsForLookup());
341         }
342         else {
343             displayList = (List<GroupBo>)getSearchResultsUnbounded(lookupForm.getFieldsForLookup());
344         }
345 
346         HashMap<String,Class> propertyTypes = new HashMap<String, Class>();
347 
348         boolean hasReturnableRow = false;
349 
350         List returnKeys = getReturnKeys();
351         List pkNames = KRADServiceLocatorWeb.getLegacyDataAdapter().listPrimaryKeyFieldNames(getBusinessObjectClass());
352         Person user = GlobalVariables.getUserSession().getPerson();
353 
354         // iterate through result list and wrap rows with return url and action urls
355         for (Iterator iter = displayList.iterator(); iter.hasNext();) {
356             BusinessObject element = (BusinessObject) iter.next();
357             if(element instanceof PersistableBusinessObject){
358                 lookupForm.setLookupObjectId(((PersistableBusinessObject)element).getObjectId());
359             }
360 
361             BusinessObjectRestrictions businessObjectRestrictions = getBusinessObjectAuthorizationService().getLookupResultRestrictions(element, user);
362 
363             HtmlData returnUrl = getReturnUrl(element, lookupForm, returnKeys, businessObjectRestrictions);
364 
365             String actionUrls = getActionUrls(element, pkNames, businessObjectRestrictions);
366             //Fix for JIRA - KFSMI-2417
367             if("".equals(actionUrls)){
368                 actionUrls = ACTION_URLS_EMPTY;
369             }
370 
371             List<Column> columns = getColumns();
372             for (Object element2 : columns) {
373 
374                 Column col = (Column) element2;
375                 Formatter formatter = col.getFormatter();
376 
377                 // pick off result column from result list, do formatting
378                 String propValue = KRADConstants.EMPTY_STRING;
379                 Object prop = null;
380                 if (col.getPropertyName().matches("\\w+\\.\\d+$")) {
381                     String id = col.getPropertyName().substring(col.getPropertyName().lastIndexOf('.') + 1); //.split("\\d+$"))[1];
382                     prop = ((GroupBo)element).getGroupAttributeValueById(id);
383                 }
384                 if (prop == null) {
385                     prop = (String) KradDataServiceLocator.getDataObjectService().wrap(element)
386                             .getPropertyValueNullSafe(findAndConvertAttributeNamesToMappableProperty(col.getPropertyName()));
387                 } else {
388                 }
389 
390                 // set comparator and formatter based on property type
391                 Class propClass = propertyTypes.get(col.getPropertyName());
392                 if ( propClass == null /*&& !skipPropTypeCheck*/) {
393                     try {
394                         propClass = KRADServiceLocatorWeb.getLegacyDataAdapter().getPropertyType(element,
395                                 col.getPropertyName());
396                         propertyTypes.put( col.getPropertyName(), propClass );
397                     } catch (Exception e) {
398                         throw new RuntimeException("Cannot access PropertyType for property " + "'" + col.getPropertyName() + "' " + " on an instance of '" + element.getClass().getName() + "'.", e);
399                     }
400                 }
401 
402                 // formatters
403                 if (prop != null) {
404                     // for Booleans, always use BooleanFormatter
405                     if (prop instanceof Boolean) {
406                         formatter = new BooleanFormatter();
407                     }
408 
409                     // for Dates, always use DateFormatter
410                     if (prop instanceof Date) {
411                         formatter = new DateFormatter();
412                     }
413 
414                     // for collection, use the list formatter if a formatter hasn't been defined yet
415                     if (prop instanceof Collection && formatter == null) {
416                     formatter = new CollectionFormatter();
417                     }
418 
419                     if (formatter != null) {
420                         propValue = (String) formatter.format(prop);
421                     }
422                     else {
423                         propValue = prop.toString();
424                         if (col.getPropertyName().equals(KIM_TYPE_ID_PROPERTY_NAME)) {
425                             propValue = groupTypeValuesCache.get(prop.toString());
426                         }
427                     }
428                 }
429 
430                 // comparator
431                 col.setComparator(CellComparatorHelper.getAppropriateComparatorForPropertyClass(propClass));
432                 col.setValueComparator(CellComparatorHelper.getAppropriateValueComparatorForPropertyClass(propClass));
433 
434                 propValue = maskValueIfNecessary(element.getClass(), col.getPropertyName(), propValue, businessObjectRestrictions);
435 
436                 col.setPropertyValue(propValue);
437 
438                 if (StringUtils.isNotBlank(propValue)) {
439                     col.setColumnAnchor(getInquiryUrl(element, col.getPropertyName()));
440 
441                 }
442             }
443 
444             ResultRow row = new ResultRow(columns, returnUrl.constructCompleteHtmlTag(), actionUrls);
445             row.setRowId(returnUrl.getName());
446             row.setReturnUrlHtmlData(returnUrl);
447             // because of concerns of the BO being cached in session on the ResultRow,
448             // let's only attach it when needed (currently in the case of export)
449             if (getBusinessObjectDictionaryService().isExportable(getBusinessObjectClass())) {
450                 row.setBusinessObject(element);
451             }
452             if(element instanceof PersistableBusinessObject){
453                 row.setObjectId((((PersistableBusinessObject)element).getObjectId()));
454             }
455 
456 
457             boolean rowReturnable = isResultReturnable(element);
458             row.setRowReturnable(rowReturnable);
459             if (rowReturnable) {
460                 hasReturnableRow = true;
461             }
462             resultTable.add(row);
463         }
464 
465         lookupForm.setHasReturnableRow(hasReturnableRow);
466 
467         return displayList;
468     }
469 
470 
471 	private List<KeyValue> getGroupTypeOptions() {
472 		List<KeyValue> options = new ArrayList<KeyValue>();
473 		options.add(new ConcreteKeyValue("", ""));
474 
475 		Collection<KimType> kimGroupTypes = KimApiServiceLocator.getKimTypeInfoService().findAllKimTypes();
476 		// get the distinct list of type IDs from all groups in the system
477         for (KimType kimType : kimGroupTypes) {
478             if (KimTypeLookupableHelperServiceImpl.hasGroupTypeService(kimType) && groupTypeValuesCache.get(kimType.getId()) == null) {
479                 String value = kimType.getNamespaceCode().trim() + KRADConstants.FIELD_CONVERSION_PAIR_SEPARATOR + kimType.getName().trim();
480                 options.add(new ConcreteKeyValue(kimType.getId(), value));
481             }
482         }
483         Collections.sort(options, new Comparator<KeyValue>() {
484            @Override
485            public int compare(KeyValue k1, KeyValue k2) {
486                return k1.getValue().compareTo(k2.getValue());
487            }
488         });
489 		return options;
490 	}
491 
492 	private List<Row> setupAttributeRows(Map fieldValues) {
493 		List<Row> returnRows = new ArrayList<Row>();
494 		for (Row row : getGrpRows()) {
495 			Field field = row.getFields().get(0);
496 			if (field.getPropertyName().equals(KIM_TYPE_ID_PROPERTY_NAME) && StringUtils.isNotBlank(field.getPropertyValue())) {
497 				if (!StringUtils.isBlank(getTypeId()) || !getTypeId().equals(field.getPropertyValue())) {
498 					setTypeId(field.getPropertyValue());
499 					setAttrRows(new ArrayList<Row>());
500 					KimType kimType = getTypeInfoService().getKimType( field.getPropertyValue() );
501 					KimTypeService kimTypeService = KimFrameworkServiceLocator.getKimTypeService(kimType);
502 			        List<KimAttributeField> definitions = kimTypeService.getAttributeDefinitions(kimType.getId());
503 			        setAttrDefinitions(definitions);
504 		            for (KimAttributeField d  : definitions) {
505 				        final AttributeDefinition definition  = DataDictionaryTypeServiceHelper
506                                 .toKimAttributeDefinition(d);
507                         List<Field> fields = new ArrayList<Field>();
508 						Field typeField = new Field();
509 
510 						String attrDefnId = d.getId();
511 						typeField.setFieldLabel(definition.getLabel());
512 						typeField.setPropertyName("attributes(" + definition.getName()+")");
513 						typeField.setPropertyValue(fieldValues.get(typeField.getPropertyName()));
514 						if (definition.getControl().isSelect()) {
515                             typeField.setFieldValidValues(definition.getOptionsFinder().getKeyValues());
516                             typeField.setFieldType(Field.DROPDOWN);
517 						} else if (definition.getControl().isText()){
518 							typeField.setMaxLength(definition.getMaxLength());
519 							if (definition.getControl().getSize() != null) {
520 							    typeField.setSize(definition.getControl().getSize());
521 							}
522 						    typeField.setFieldType(Field.TEXT);
523 						} else if (definition.getControl().isRadio()) {
524                             typeField.setFieldValidValues(definition.getOptionsFinder().getKeyValues());
525                             typeField.setFieldType(Field.RADIO);
526 						} else if (definition.getControl().isCheckbox()) {
527 						    KeyValuesFinder finder = new IndicatorValuesFinder();
528                             typeField.setFieldValidValues(finder.getKeyValues());
529                             typeField.setFieldType(Field.RADIO);
530 						    //typeField.setFieldType(Field.CHECKBOX);
531 						} else if (definition.getControl().isHidden()) {
532 						    typeField.setFieldType(Field.HIDDEN);
533 						} else if (definition.getControl().isLookupReadonly()) {
534 						    typeField.setFieldType(Field.LOOKUP_READONLY);
535 						} else if (definition.getControl().isTextarea()) {
536 						    typeField.setMaxLength(definition.getMaxLength());
537                             if (definition.getControl().getSize() != null) {
538                                 typeField.setSize(definition.getControl().getSize());
539                             }
540                             typeField.setFieldType(Field.TEXT_AREA);
541 						}
542 						fields.add(typeField);
543 						returnRows.add(new Row(fields));
544 		            }
545 				} else {
546 					return getAttrRows();
547 				}
548 			} else if (field.getPropertyName().equals(KIM_TYPE_ID_PROPERTY_NAME) && StringUtils.isBlank(field.getPropertyValue())) {
549 				setTypeId("");
550 			}
551 		}
552 		return returnRows;
553 	}
554 
555 	public List<Row> getGrpRows() {
556 		return this.grpRows;
557 	}
558 
559 	public void setGrpRows(List<Row> grpRows) {
560 		this.grpRows = grpRows;
561 	}
562 
563 	public List<KimAttributeField> getAttrDefinitions() {
564 		return this.attrDefinitions;
565 	}
566 
567 	public void setAttrDefinitions(List<KimAttributeField> attrDefinitions) {
568 		this.attrDefinitions = attrDefinitions;
569 	}
570 
571 	public List<Row> getAttrRows() {
572 		return this.attrRows;
573 	}
574 
575 	public void setAttrRows(List<Row> attrRows) {
576 		this.attrRows = attrRows;
577 	}
578 
579 	public String getTypeId() {
580 		return this.typeId;
581 	}
582 
583 	public void setTypeId(String typeId) {
584 		this.typeId = typeId;
585 	}
586 
587     /**
588      * This method determines if a property name refers to an attribute. If so, we have to adjust the formatting so the
589      * wrapper recognizes it as being mappable.
590      * @param propertyName is any property name that will be wrapped as part of a lookup result
591      * @return either the unaltered property name or a modified version of the property name if it is an attribute
592      */
593     private String findAndConvertAttributeNamesToMappableProperty(String propertyName) {
594         if (isParamAttribute(propertyName)) {
595             return propertyName.replaceAll("attributes\\((.*?)\\)", "attributes\\[$1\\]");
596         } else {
597             return propertyName;
598         }
599     }
600 
601     @Override
602     public void performClear(LookupForm lookupForm) {
603         super.performClear(lookupForm);
604         this.attrRows = new ArrayList<Row>();
605     }
606 
607 }