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