001    /**
002     * Copyright 2005-2013 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     */
016    package org.kuali.rice.krms.impl.type;
017    
018    import org.apache.commons.collections.CollectionUtils;
019    import org.apache.commons.lang.StringUtils;
020    import org.kuali.rice.core.api.exception.RiceIllegalArgumentException;
021    import org.kuali.rice.core.api.uif.RemotableAttributeError;
022    import org.kuali.rice.core.api.uif.RemotableAttributeField;
023    import org.kuali.rice.core.api.uif.RemotableTextInput;
024    import org.kuali.rice.core.api.util.jaxb.MapStringStringAdapter;
025    import org.kuali.rice.kns.datadictionary.validation.AttributeValidatingTypeServiceBase;
026    import org.kuali.rice.krad.service.DataDictionaryRemoteFieldService;
027    import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
028    import org.kuali.rice.krms.api.repository.type.KrmsAttributeDefinition;
029    import org.kuali.rice.krms.api.repository.type.KrmsTypeAttribute;
030    import org.kuali.rice.krms.api.repository.type.KrmsTypeDefinition;
031    import org.kuali.rice.krms.api.repository.type.KrmsTypeRepositoryService;
032    import org.kuali.rice.krms.framework.type.RemotableAttributeOwner;
033    import org.kuali.rice.krms.impl.repository.KrmsRepositoryServiceLocator;
034    
035    import javax.jws.WebParam;
036    import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
037    import java.util.ArrayList;
038    import java.util.Collections;
039    import java.util.Comparator;
040    import java.util.HashMap;
041    import java.util.List;
042    import java.util.Map;
043    
044    /**
045     * {@link KrmsTypeServiceBase} is an abstract class providing default implementation and hooks for
046     * provisioning and validating the custom attributes of a krms type.  Is should probably be mentioned that the
047     * default validation methods don't actually check anything, they just return empty error lists.
048     */
049    public abstract class KrmsTypeServiceBase extends AttributeValidatingTypeServiceBase implements RemotableAttributeOwner {
050    
051        /**
052         * <p>get the {@link RemotableAttributeField}s for the custom attributes of this krms type.  This implementation
053         * will (by default) return any attributes mapped to the type via
054         * {@link org.kuali.rice.krms.impl.repository.KrmsTypeAttributeBo}. If there is is a component name defined on the
055         * related {@link org.kuali.rice.krms.impl.repository.KrmsAttributeDefinitionBo} then that will be used to generate
056         * the {@link RemotableAttributeField}.  If not, then a simple text input will be produced.</p>
057         *
058         * <p>An extending class can also override the
059         * {@link #translateTypeAttribute(org.kuali.rice.krms.api.repository.type.KrmsTypeAttribute,
060         * org.kuali.rice.krms.api.repository.type.KrmsAttributeDefinition)}
061         * method which is called from here, and within it hand create the RemotableAttributeField for a certain attribute.
062         * </p>
063         *
064         * <p>Also handy for extenders to know, this method delegates to {@link #getTypeAttributeDefinitions(String)} and
065         * then pulls out the {@link RemotableAttributeField}s from the returned {@link TypeAttributeDefinition}s</p>
066         *
067         * @param krmsTypeId the people flow type identifier.  Must not be null or blank.
068         * @return
069         * @throws RiceIllegalArgumentException
070         */
071        @Override
072        public List<RemotableAttributeField> getAttributeFields(@WebParam(name = "krmsTypeId") String krmsTypeId) throws RiceIllegalArgumentException {
073            List<TypeAttributeDefinition> typeAttributeDefinitions = getTypeAttributeDefinitions(krmsTypeId);
074    
075            if (CollectionUtils.isEmpty(typeAttributeDefinitions)) {
076                return Collections.emptyList();
077            } else {
078                List<RemotableAttributeField> fields =
079                        new ArrayList<RemotableAttributeField>(typeAttributeDefinitions.size());
080    
081                for (TypeAttributeDefinition typeAttributeDefinition : typeAttributeDefinitions) {
082                    fields.add(typeAttributeDefinition.getField());
083                }
084                return fields;
085            }
086        }
087    
088        /**
089         * Gets an ordered List of {@link TypeAttributeDefinition}s for the attributes on the KRMS type specified by the
090         * given krmsTypeId.
091         * @param krmsTypeId the ID of the KRMS Type whose attributes we are getting.
092         * @return a List of type-agnostic {@link TypeAttributeDefinition}s
093         * @see AttributeValidatingTypeServiceBase
094         */
095        @Override
096        protected List<TypeAttributeDefinition> getTypeAttributeDefinitions(String krmsTypeId) {
097    
098            if (StringUtils.isBlank(krmsTypeId)) {
099                throw new RiceIllegalArgumentException("krmsTypeId must be non-null and non-blank");
100            }
101    
102            List<TypeAttributeDefinition> results = new ArrayList<TypeAttributeDefinition>();
103    
104            // keep track of how to sort these
105            final Map<String, Integer> sortCodeMap = new HashMap<String, Integer>();
106    
107            KrmsTypeDefinition krmsType =
108                    KrmsRepositoryServiceLocator.getKrmsTypeRepositoryService().getTypeById(krmsTypeId);
109    
110            if (krmsType == null) {
111                throw new RiceIllegalArgumentException("krmsTypeId must be a valid id of a KRMS type");
112            } else {
113                // translate attributes
114    
115                List<KrmsTypeAttribute> typeAttributes = krmsType.getAttributes();
116    
117                List<RemotableAttributeField> typeAttributeFields = new ArrayList<RemotableAttributeField>(10);
118                if (!CollectionUtils.isEmpty(typeAttributes)) {
119                    // translate the attribute and store the sort code in our map
120                    for (KrmsTypeAttribute typeAttribute : typeAttributes) {
121    
122                        KrmsTypeRepositoryService typeRepositoryService = KrmsRepositoryServiceLocator.getKrmsTypeRepositoryService();
123    
124                        KrmsAttributeDefinition attributeDefinition =
125                                typeRepositoryService.getAttributeDefinitionById(typeAttribute.getAttributeDefinitionId());
126    
127                        RemotableAttributeField attributeField = translateTypeAttribute(typeAttribute, attributeDefinition);
128    
129                        if (typeAttribute.getSequenceNumber() == null) {
130                            throw new IllegalStateException(typeAttribute.toString() + " has a null sequenceNumber");
131                        } else {
132                            sortCodeMap.put(attributeField.getName(), typeAttribute.getSequenceNumber());
133                        }
134    
135                        TypeAttributeDefinition typeAttributeDefinition =
136                                new TypeAttributeDefinition(attributeField, attributeDefinition.getName(), attributeDefinition.getComponentName(), attributeDefinition.getLabel(), null);
137    
138                        results.add(typeAttributeDefinition);
139                    }
140                }
141            }
142    
143            sortFields(results, sortCodeMap);
144    
145            return results;
146        }
147    
148    
149        protected void sortFields(List<TypeAttributeDefinition> results,
150                final Map<String, Integer> sortCodeMap) {// sort the results
151            Collections.sort(results, new Comparator<TypeAttributeDefinition>() {
152                @Override
153                public int compare(TypeAttributeDefinition o1, TypeAttributeDefinition o2) {
154                    if (o1 == o2 || o1.equals(o2))
155                        return 0;
156                    // we assume that each has a sort code based on our previous check
157                    Integer o1SortCode = sortCodeMap.get(o1.getName());
158                    Integer o2SortCode = sortCodeMap.get(o2.getName());
159    
160                    if (o1SortCode.compareTo(o2SortCode) != 0) {
161                        return o1SortCode.compareTo(o2SortCode);
162                    } else {
163                        // if sort codes are the same, we still would like a consistent order
164                        return o1.getName().compareTo(o2.getName());
165                    }
166                }
167            });
168        }
169    
170        @Override
171        public List<RemotableAttributeError> validateAttributes(@WebParam(name = "krmsTypeId") String krmsTypeId,
172                @WebParam(name = "attributes") @XmlJavaTypeAdapter(
173                        value = MapStringStringAdapter.class) Map<String, String> attributes) throws RiceIllegalArgumentException {
174            return super.validateAttributes(krmsTypeId, attributes);
175        }
176    
177        @Override
178        public List<RemotableAttributeError> validateAttributesAgainstExisting(
179                @WebParam(name = "krmsTypeId") String krmsTypeId, @WebParam(name = "newAttributes") @XmlJavaTypeAdapter(
180                value = MapStringStringAdapter.class) Map<String, String> newAttributes,
181                @WebParam(name = "oldAttributes") @XmlJavaTypeAdapter(
182                        value = MapStringStringAdapter.class) Map<String, String> oldAttributes) throws RiceIllegalArgumentException {
183            return validateAttributes(krmsTypeId, newAttributes);
184        }
185    
186        /**
187         * Translate a {@link org.kuali.rice.krms.api.repository.type.KrmsTypeAttribute} into a {@link org.kuali.rice.core.api.uif.RemotableAttributeField}.
188         * Override this method to provide custom translation of certain attributes.
189         * @param inputAttribute the {@link org.kuali.rice.krms.api.repository.type.KrmsTypeAttribute} to translate
190         * @param attributeDefinition the {@link KrmsAttributeDefinition} for the given inputAttribute
191         * @return a {@link org.kuali.rice.core.api.uif.RemotableAttributeField} for the given inputAttribute
192         */
193        public RemotableAttributeField translateTypeAttribute(KrmsTypeAttribute inputAttribute,
194                KrmsAttributeDefinition attributeDefinition) {
195    
196            if (StringUtils.isEmpty(attributeDefinition.getComponentName())) {
197                RemotableAttributeField.Builder builder = RemotableAttributeField.Builder.create(attributeDefinition.getName());
198    
199                RemotableTextInput.Builder controlBuilder = RemotableTextInput.Builder.create();
200                controlBuilder.setSize(80);
201    
202                controlBuilder.setWatermark(attributeDefinition.getDescription());
203    
204                builder.setShortLabel(attributeDefinition.getLabel());
205                builder.setLongLabel(attributeDefinition.getLabel());
206                builder.setName(attributeDefinition.getName());
207    
208    //            builder.setHelpSummary("helpSummary: " + attributeDefinition.getDescription());
209    //            builder.setHelpDescription("helpDescription: " + attributeDefinition.getDescription());
210    
211                builder.setControl(controlBuilder);
212                builder.setMaxLength(400);
213    
214                return builder.build();
215            } else {
216                return getDataDictionaryRemoteFieldService().buildRemotableFieldFromAttributeDefinition(
217                        attributeDefinition.getComponentName(),
218                        attributeDefinition.getName());
219            }
220        }
221    
222        public DataDictionaryRemoteFieldService getDataDictionaryRemoteFieldService() {
223            return KRADServiceLocatorWeb.getDataDictionaryRemoteFieldService();
224        }
225    
226        @Override
227        protected List<RemotableAttributeError> validateNonDataDictionaryAttribute(RemotableAttributeField attr, String key,
228                String value) {
229            return Collections.emptyList();
230        }
231    }