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