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}