001/** 002 * Copyright 2005-2016 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.kim.lookup; 017 018import org.apache.commons.collections.CollectionUtils; 019import org.apache.commons.lang.StringUtils; 020import org.kuali.rice.core.api.criteria.Predicate; 021import org.kuali.rice.core.api.criteria.PredicateUtils; 022import org.kuali.rice.core.api.criteria.QueryByCriteria; 023import org.kuali.rice.core.api.util.ClassLoaderUtils; 024import org.kuali.rice.core.api.util.ConcreteKeyValue; 025import org.kuali.rice.core.api.util.KeyValue; 026import org.kuali.rice.core.web.format.BooleanFormatter; 027import org.kuali.rice.core.web.format.CollectionFormatter; 028import org.kuali.rice.core.web.format.DateFormatter; 029import org.kuali.rice.core.web.format.Formatter; 030import org.kuali.rice.kew.api.KewApiConstants; 031import org.kuali.rice.kim.api.KimConstants; 032import org.kuali.rice.kim.api.group.Group; 033import org.kuali.rice.kim.api.group.GroupQueryResults; 034import org.kuali.rice.kim.api.identity.Person; 035import org.kuali.rice.kim.api.identity.principal.Principal; 036import org.kuali.rice.kim.api.identity.principal.PrincipalQueryResults; 037import org.kuali.rice.kim.api.services.KimApiServiceLocator; 038import org.kuali.rice.kim.api.type.KimAttributeField; 039import org.kuali.rice.kim.api.type.KimType; 040import org.kuali.rice.kim.framework.services.KimFrameworkServiceLocator; 041import org.kuali.rice.kim.framework.type.KimTypeService; 042import org.kuali.rice.kim.impl.KIMPropertyConstants; 043import org.kuali.rice.kim.impl.group.GroupBo; 044import org.kuali.rice.kim.impl.type.KimTypeLookupableHelperServiceImpl; 045import org.kuali.rice.kim.util.KimCommonUtilsInternal; 046import org.kuali.rice.kns.document.authorization.BusinessObjectRestrictions; 047import org.kuali.rice.kns.kim.type.DataDictionaryTypeServiceHelper; 048import org.kuali.rice.kns.lookup.HtmlData; 049import org.kuali.rice.kns.web.comparator.CellComparatorHelper; 050import org.kuali.rice.kns.web.struts.form.LookupForm; 051import org.kuali.rice.kns.web.ui.Column; 052import org.kuali.rice.kns.web.ui.Field; 053import org.kuali.rice.kns.web.ui.ResultRow; 054import org.kuali.rice.kns.web.ui.Row; 055import org.kuali.rice.krad.bo.BusinessObject; 056import org.kuali.rice.krad.bo.PersistableBusinessObject; 057import org.kuali.rice.krad.datadictionary.AttributeDefinition; 058import org.kuali.rice.krad.keyvalues.IndicatorValuesFinder; 059import org.kuali.rice.krad.keyvalues.KeyValuesFinder; 060import org.kuali.rice.krad.keyvalues.KimAttributeValuesFinder; 061import org.kuali.rice.krad.util.GlobalVariables; 062import org.kuali.rice.krad.util.KRADConstants; 063import org.kuali.rice.krad.util.ObjectUtils; 064import org.kuali.rice.krad.util.UrlFactory; 065 066import java.sql.Date; 067import java.sql.Timestamp; 068import java.util.ArrayList; 069import java.util.Calendar; 070import java.util.Collection; 071import java.util.Collections; 072import java.util.Comparator; 073import java.util.HashMap; 074import java.util.Iterator; 075import java.util.List; 076import java.util.Map; 077import java.util.Properties; 078 079import static org.kuali.rice.core.api.criteria.PredicateFactory.*; 080 081public class GroupLookupableHelperServiceImpl extends KimLookupableHelperServiceImpl { 082 083 // need this so kimtypeId value can be retained in 'rows' 084 // 1st pass populate the grprows 085 // 2nd pass for jsp, no populate, so return the existing one. 086 private static final String KIM_TYPE_ID_PROPERTY_NAME = "kimTypeId"; 087 private List<Row> grpRows = new ArrayList<Row>(); 088 private List<Row> attrRows = new ArrayList<Row>(); 089 private String typeId = ""; 090 private List<KimAttributeField> attrDefinitions; 091 private final Map<String, String> groupTypeValuesCache = new HashMap<String, String>(); 092 093 @Override 094 public List<HtmlData> getCustomActionUrls(BusinessObject bo, List pkNames) { 095 GroupBo groupImpl = (GroupBo) bo; 096 List<HtmlData> anchorHtmlDataList = new ArrayList<HtmlData>(); 097 if(allowsNewOrCopyAction(KimConstants.KimUIConstants.KIM_GROUP_DOCUMENT_TYPE_NAME)){ 098 anchorHtmlDataList.add(getEditGroupUrl(groupImpl)); 099 } 100 return anchorHtmlDataList; 101 } 102 103 protected HtmlData getEditGroupUrl(GroupBo groupBo) { 104 String href = ""; 105 106 Properties parameters = new Properties(); 107 parameters.put(KRADConstants.DISPATCH_REQUEST_PARAMETER, KRADConstants.DOC_HANDLER_METHOD); 108 parameters.put(KRADConstants.PARAMETER_COMMAND, KewApiConstants.INITIATE_COMMAND); 109 parameters.put(KRADConstants.DOCUMENT_TYPE_NAME, KimConstants.KimUIConstants.KIM_GROUP_DOCUMENT_TYPE_NAME); 110 parameters.put(KimConstants.PrimaryKeyConstants.GROUP_ID, groupBo.getId()); 111 if (StringUtils.isNotBlank(getReturnLocation())) { 112 parameters.put(KRADConstants.RETURN_LOCATION_PARAMETER, getReturnLocation()); 113 } 114 href = UrlFactory.parameterizeUrl(KimCommonUtilsInternal.getKimBasePath()+KimConstants.KimUIConstants.KIM_GROUP_DOCUMENT_ACTION, parameters); 115 116 HtmlData.AnchorHtmlData anchorHtmlData = new HtmlData.AnchorHtmlData(href, 117 KRADConstants.DOC_HANDLER_METHOD, KRADConstants.MAINTENANCE_EDIT_METHOD_TO_CALL); 118 return anchorHtmlData; 119 } 120 121 /** 122 * Converts GroupInfo objects to GroupBo objects. 123 * 124 * @param fieldValues names and values returned by the Group Lookup screen 125 * @return groupImplList a list of GroupImpl objects 126 */ 127 @Override 128 public List<GroupBo> getSearchResults(java.util.Map<String,String> fieldValues) { 129 Map<String, String> criteriaMap = new HashMap<String, String>(fieldValues); 130 QueryByCriteria.Builder criteria = QueryByCriteria.Builder.create(); 131 criteriaMap.remove(KRADConstants.DOC_FORM_KEY); 132 criteriaMap.remove(KRADConstants.BACK_LOCATION); 133 criteriaMap.remove(KRADConstants.DOC_NUM); 134 String refToRef = criteriaMap.get(KRADConstants.REFERENCES_TO_REFRESH); 135 if (StringUtils.isNotBlank(refToRef) && refToRef.equalsIgnoreCase("GroupBo")) { 136 criteriaMap.remove(KRADConstants.REFERENCES_TO_REFRESH); 137 } 138 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 } 166 167 } 168 criteriaMap.remove(KimConstants.UniqueKeyConstants.PRINCIPAL_NAME); 169 170 predicates.add(PredicateUtils.convertMapToPredicate(criteriaMap)); 171 criteria.setPredicates(and(predicates.toArray(new Predicate[predicates.size()]))); 172 } 173 GroupQueryResults groupResults = KimApiServiceLocator.getGroupService().findGroups(criteria.build()); 174 List<Group> groups = groupResults.getResults(); 175 176 //have to convert back to Bos :( 177 Map<String, GroupBo> groupBos = new HashMap<String, GroupBo>(groups.size()); 178 for (Group group : groups) { 179 if (groupBos.get(group.getId()) == null) { 180 groupBos.put(group.getId(), GroupBo.from(group)); 181 } 182 } 183 184 185 return new ArrayList<GroupBo>(groupBos.values()); 186 } 187 188 @Override 189 public boolean checkForAdditionalFields(Map<String, String> fieldValues) { 190 List<Row> attributeRows = setupAttributeRows(fieldValues); 191 if (attributeRows.isEmpty()) { 192 setAttrRows(attributeRows); 193 } else if (CollectionUtils.isEmpty(getAttrRows())) { 194 setAttrRows(attributeRows); 195 } 196 if (getAttrRows().size() > 0) { 197 return true; 198 } 199 return false; 200 } 201 202 203 @Override 204 public List<Row> getRows() { 205 if (getGrpRows().isEmpty()) { 206 List<Row> rows = super.getRows(); 207 List<Row> returnRows = new ArrayList<Row>(); 208 for (Row row : rows) { 209 for (int i = row.getFields().size() - 1; i >= 0; i--) { 210 Field field = row.getFields().get(i); 211 if (field.getPropertyName().equals(KIM_TYPE_ID_PROPERTY_NAME)) { 212 Field typeField = new Field(); 213 typeField.setFieldLabel("Type"); 214 typeField.setPropertyName(KIM_TYPE_ID_PROPERTY_NAME); 215 typeField.setFieldValidValues(getGroupTypeOptions()); 216 typeField.setFieldType(Field.DROPDOWN); 217 row.getFields().set(i, typeField); 218 } 219 } 220 returnRows.add(row); 221 } 222 // principalName 223 Field typeField = new Field(); 224 typeField.setFieldLabel("Principal Name"); 225 typeField.setPropertyName(KIMPropertyConstants.Person.PRINCIPAL_NAME); 226 typeField.setFieldType(Field.TEXT); 227 typeField.setMaxLength(40); 228 typeField.setSize(20); 229 typeField.setQuickFinderClassNameImpl("org.kuali.rice.kim.api.identity.Person"); 230 typeField.setFieldConversions( "principalName:principalName" ); 231 typeField.setLookupParameters( "principalName:principalName" ); 232 // Identify the best spot to insert the "Principal Name" search field. Note that the code below assumes that the final row of the 233 // group search fields is not a row with hidden fields; if this ever becomes the case in the future, this fix may need to 234 // be modified accordingly. 235 List<Field> fields = (returnRows.isEmpty()) ? new ArrayList<Field>() : returnRows.get(returnRows.size() - 1).getFields(); 236 if (!fields.isEmpty() && fields.get(fields.size() - 1).getFieldType().equals(Field.BLANK_SPACE)) { 237 // 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. 238 int insertLoc = fields.size() - 1; 239 while (insertLoc >= 0 && fields.get(insertLoc).getFieldType().equals(Field.BLANK_SPACE)) { 240 insertLoc--; 241 } 242 fields.set(insertLoc + 1, typeField); 243 returnRows.get(returnRows.size() - 1).setFields(fields); 244 } else { 245 // Otherwise, add a new row containing that field. 246 int fieldLen = fields.size(); 247 fields = new ArrayList<Field>(); 248 fields.add(typeField); 249 for (int i = 1; i < fieldLen; i++) { 250 Field blankSpace = new Field(); 251 blankSpace.setFieldType(Field.BLANK_SPACE); 252 blankSpace.setPropertyName(Field.BLANK_SPACE); 253 fields.add(blankSpace); 254 } 255 returnRows.add(new Row(fields)); 256 } 257 258 setGrpRows(returnRows); 259 } 260 if (getAttrRows().isEmpty()) { 261 setAttrDefinitions(Collections.<KimAttributeField>emptyList()); 262 return getGrpRows(); 263 } 264 List<Row> fullRows = new ArrayList<Row>(); 265 fullRows.addAll(getGrpRows()); 266 fullRows.addAll(getAttrRows()); 267 return fullRows; 268 } 269 270 271 @Override 272 public List<Column> getColumns() { 273 List<Column> columns = super.getColumns(); 274 for (Row row : attrRows) { 275 for (Field field : row.getFields()) { 276 Column newColumn = new Column(); 277 newColumn.setColumnTitle(field.getFieldLabel()); 278 newColumn.setMaxLength(getColumnMaxLength(field.getPropertyName())); 279 newColumn.setPropertyName(field.getPropertyName()); 280 newColumn.setFormatter(field.getFormatter()); 281 columns.add(newColumn); 282 } 283 } 284 return columns; 285 } 286 287 @Override 288 public Collection<GroupBo> performLookup(LookupForm lookupForm, Collection resultTable, boolean bounded) { 289 setBackLocation((String) lookupForm.getFieldsForLookup().get(KRADConstants.BACK_LOCATION)); 290 setDocFormKey((String) lookupForm.getFieldsForLookup().get(KRADConstants.DOC_FORM_KEY)); 291 List<GroupBo> displayList; 292 293 // call search method to get results 294 if (bounded) { 295 displayList = getSearchResults(lookupForm.getFieldsForLookup()); 296 } 297 else { 298 displayList = (List<GroupBo>)getSearchResultsUnbounded(lookupForm.getFieldsForLookup()); 299 } 300 301 HashMap<String,Class> propertyTypes = new HashMap<String, Class>(); 302 303 boolean hasReturnableRow = false; 304 305 List returnKeys = getReturnKeys(); 306 List pkNames = getBusinessObjectMetaDataService().listPrimaryKeyFieldNames(getBusinessObjectClass()); 307 Person user = GlobalVariables.getUserSession().getPerson(); 308 309 // iterate through result list and wrap rows with return url and action urls 310 for (Iterator iter = displayList.iterator(); iter.hasNext();) { 311 BusinessObject element = (BusinessObject) iter.next(); 312 if(element instanceof PersistableBusinessObject){ 313 lookupForm.setLookupObjectId(((PersistableBusinessObject)element).getObjectId()); 314 } 315 316 BusinessObjectRestrictions businessObjectRestrictions = getBusinessObjectAuthorizationService().getLookupResultRestrictions(element, user); 317 318 HtmlData returnUrl = getReturnUrl(element, lookupForm, returnKeys, businessObjectRestrictions); 319 320 String actionUrls = getActionUrls(element, pkNames, businessObjectRestrictions); 321 //Fix for JIRA - KFSMI-2417 322 if("".equals(actionUrls)){ 323 actionUrls = ACTION_URLS_EMPTY; 324 } 325 326 List<Column> columns = getColumns(); 327 for (Object element2 : columns) { 328 329 Column col = (Column) element2; 330 Formatter formatter = col.getFormatter(); 331 332 // pick off result column from result list, do formatting 333 String propValue = KRADConstants.EMPTY_STRING; 334 Object prop = null; 335 if (col.getPropertyName().matches("\\w+\\.\\d+$")) { 336 String id = col.getPropertyName().substring(col.getPropertyName().lastIndexOf('.') + 1); //.split("\\d+$"))[1]; 337 prop = ((GroupBo)element).getGroupAttributeValueById(id); 338 } 339 if (prop == null) { 340 prop = ObjectUtils.getPropertyValue(element, col.getPropertyName()); 341 } else { 342 } 343 344 // set comparator and formatter based on property type 345 Class propClass = propertyTypes.get(col.getPropertyName()); 346 if ( propClass == null /*&& !skipPropTypeCheck*/) { 347 try { 348 propClass = ObjectUtils.getPropertyType( element, col.getPropertyName(), getPersistenceStructureService() ); 349 propertyTypes.put( col.getPropertyName(), propClass ); 350 } catch (Exception e) { 351 throw new RuntimeException("Cannot access PropertyType for property " + "'" + col.getPropertyName() + "' " + " on an instance of '" + element.getClass().getName() + "'.", e); 352 } 353 } 354 355 // formatters 356 if (prop != null) { 357 // for Booleans, always use BooleanFormatter 358 if (prop instanceof Boolean) { 359 formatter = new BooleanFormatter(); 360 } 361 362 // for Dates, always use DateFormatter 363 if (prop instanceof Date) { 364 formatter = new DateFormatter(); 365 } 366 367 // for collection, use the list formatter if a formatter hasn't been defined yet 368 if (prop instanceof Collection && formatter == null) { 369 formatter = new CollectionFormatter(); 370 } 371 372 if (formatter != null) { 373 propValue = (String) formatter.format(prop); 374 } 375 else { 376 propValue = prop.toString(); 377 if (col.getPropertyName().equals(KIM_TYPE_ID_PROPERTY_NAME)) { 378 propValue = groupTypeValuesCache.get(prop.toString()); 379 } 380 } 381 } 382 383 // comparator 384 col.setComparator(CellComparatorHelper.getAppropriateComparatorForPropertyClass(propClass)); 385 col.setValueComparator(CellComparatorHelper.getAppropriateValueComparatorForPropertyClass(propClass)); 386 387 propValue = maskValueIfNecessary(element.getClass(), col.getPropertyName(), propValue, businessObjectRestrictions); 388 389 col.setPropertyValue(propValue); 390 391 if (StringUtils.isNotBlank(propValue)) { 392 col.setColumnAnchor(getInquiryUrl(element, col.getPropertyName())); 393 394 } 395 } 396 397 ResultRow row = new ResultRow(columns, returnUrl.constructCompleteHtmlTag(), actionUrls); 398 row.setRowId(returnUrl.getName()); 399 row.setReturnUrlHtmlData(returnUrl); 400 // because of concerns of the BO being cached in session on the ResultRow, 401 // let's only attach it when needed (currently in the case of export) 402 if (getBusinessObjectDictionaryService().isExportable(getBusinessObjectClass())) { 403 row.setBusinessObject(element); 404 } 405 if(element instanceof PersistableBusinessObject){ 406 row.setObjectId((((PersistableBusinessObject)element).getObjectId())); 407 } 408 409 410 boolean rowReturnable = isResultReturnable(element); 411 row.setRowReturnable(rowReturnable); 412 if (rowReturnable) { 413 hasReturnableRow = true; 414 } 415 resultTable.add(row); 416 } 417 418 lookupForm.setHasReturnableRow(hasReturnableRow); 419 420 return displayList; 421 } 422 423 424 private List<KeyValue> getGroupTypeOptions() { 425 List<KeyValue> options = new ArrayList<KeyValue>(); 426 options.add(new ConcreteKeyValue("", "")); 427 428 Collection<KimType> kimGroupTypes = KimApiServiceLocator.getKimTypeInfoService().findAllKimTypes(); 429 // get the distinct list of type IDs from all groups in the system 430 for (KimType kimType : kimGroupTypes) { 431 if (KimTypeLookupableHelperServiceImpl.hasGroupTypeService(kimType) && groupTypeValuesCache.get(kimType.getId()) == null) { 432 String value = kimType.getNamespaceCode().trim() + KRADConstants.FIELD_CONVERSION_PAIR_SEPARATOR + kimType.getName().trim(); 433 options.add(new ConcreteKeyValue(kimType.getId(), value)); 434 } 435 } 436 Collections.sort(options, new Comparator<KeyValue>() { 437 @Override 438 public int compare(KeyValue k1, KeyValue k2) { 439 return k1.getValue().compareTo(k2.getValue()); 440 } 441 }); 442 return options; 443 } 444 445 private List<Row> setupAttributeRows(Map fieldValues) { 446 List<Row> returnRows = new ArrayList<Row>(); 447 for (Row row : getGrpRows()) { 448 Field field = row.getFields().get(0); 449 if (field.getPropertyName().equals(KIM_TYPE_ID_PROPERTY_NAME) && StringUtils.isNotBlank(field.getPropertyValue())) { 450 if (!StringUtils.isBlank(getTypeId()) || !getTypeId().equals(field.getPropertyValue())) { 451 setTypeId(field.getPropertyValue()); 452 setAttrRows(new ArrayList<Row>()); 453 KimType kimType = getTypeInfoService().getKimType( field.getPropertyValue() ); 454 KimTypeService kimTypeService = KimFrameworkServiceLocator.getKimTypeService(kimType); 455 List<KimAttributeField> definitions = kimTypeService.getAttributeDefinitions(kimType.getId()); 456 setAttrDefinitions(definitions); 457 for (KimAttributeField d : definitions) { 458 final AttributeDefinition definition = DataDictionaryTypeServiceHelper 459 .toKimAttributeDefinition(d); 460 List<Field> fields = new ArrayList<Field>(); 461 Field typeField = new Field(); 462 463 String attrDefnId = d.getId(); 464 typeField.setFieldLabel(definition.getLabel()); 465 typeField.setPropertyName("attributes(" + definition.getName()+")"); 466 typeField.setPropertyValue(fieldValues.get(typeField.getPropertyName())); 467 if (definition.getControl().isSelect()) { 468 typeField.setFieldValidValues(definition.getOptionsFinder().getKeyValues()); 469 typeField.setFieldType(Field.DROPDOWN); 470 } else if (definition.getControl().isText()){ 471 typeField.setMaxLength(definition.getMaxLength()); 472 if (definition.getControl().getSize() != null) { 473 typeField.setSize(definition.getControl().getSize()); 474 } 475 typeField.setFieldType(Field.TEXT); 476 } else if (definition.getControl().isRadio()) { 477 typeField.setFieldValidValues(definition.getOptionsFinder().getKeyValues()); 478 typeField.setFieldType(Field.RADIO); 479 } else if (definition.getControl().isCheckbox()) { 480 KeyValuesFinder finder = new IndicatorValuesFinder(); 481 typeField.setFieldValidValues(finder.getKeyValues()); 482 typeField.setFieldType(Field.RADIO); 483 //typeField.setFieldType(Field.CHECKBOX); 484 } else if (definition.getControl().isHidden()) { 485 typeField.setFieldType(Field.HIDDEN); 486 } else if (definition.getControl().isLookupReadonly()) { 487 typeField.setFieldType(Field.LOOKUP_READONLY); 488 } else if (definition.getControl().isTextarea()) { 489 typeField.setMaxLength(definition.getMaxLength()); 490 if (definition.getControl().getSize() != null) { 491 typeField.setSize(definition.getControl().getSize()); 492 } 493 typeField.setFieldType(Field.TEXT_AREA); 494 } 495 fields.add(typeField); 496 returnRows.add(new Row(fields)); 497 } 498 } else { 499 return getAttrRows(); 500 } 501 } else if (field.getPropertyName().equals(KIM_TYPE_ID_PROPERTY_NAME) && StringUtils.isBlank(field.getPropertyValue())) { 502 setTypeId(""); 503 } 504 } 505 return returnRows; 506 } 507 508 public List<Row> getGrpRows() { 509 return this.grpRows; 510 } 511 512 public void setGrpRows(List<Row> grpRows) { 513 this.grpRows = grpRows; 514 } 515 516 public List<KimAttributeField> getAttrDefinitions() { 517 return this.attrDefinitions; 518 } 519 520 public void setAttrDefinitions(List<KimAttributeField> attrDefinitions) { 521 this.attrDefinitions = attrDefinitions; 522 } 523 524 public List<Row> getAttrRows() { 525 return this.attrRows; 526 } 527 528 public void setAttrRows(List<Row> attrRows) { 529 this.attrRows = attrRows; 530 } 531 532 public String getTypeId() { 533 return this.typeId; 534 } 535 536 public void setTypeId(String typeId) { 537 this.typeId = typeId; 538 } 539 540 @Override 541 public void performClear(LookupForm lookupForm) { 542 super.performClear(lookupForm); 543 this.attrRows = new ArrayList<Row>(); 544 } 545 546}