View Javadoc

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