001/** 002 * Copyright 2005-2017 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.krms.impl.type; 017 018import org.apache.commons.collections.CollectionUtils; 019import org.apache.commons.lang.StringUtils; 020import org.kuali.rice.core.api.exception.RiceIllegalArgumentException; 021import org.kuali.rice.core.api.uif.RemotableAttributeError; 022import org.kuali.rice.core.api.uif.RemotableAttributeField; 023import org.kuali.rice.core.api.uif.RemotableTextInput; 024import org.kuali.rice.core.api.util.jaxb.MapStringStringAdapter; 025import org.kuali.rice.krad.datadictionary.validation.AttributeValidatingTypeServiceBase; 026import org.kuali.rice.krad.service.DataDictionaryRemoteFieldService; 027import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 028import org.kuali.rice.krms.api.repository.type.KrmsAttributeDefinition; 029import org.kuali.rice.krms.api.repository.type.KrmsTypeAttribute; 030import org.kuali.rice.krms.api.repository.type.KrmsTypeDefinition; 031import org.kuali.rice.krms.api.repository.type.KrmsTypeRepositoryService; 032import org.kuali.rice.krms.framework.type.RemotableAttributeOwner; 033import org.kuali.rice.krms.impl.repository.KrmsRepositoryServiceLocator; 034 035import javax.jws.WebParam; 036import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; 037import java.util.ArrayList; 038import java.util.Collections; 039import java.util.Comparator; 040import java.util.HashMap; 041import java.util.List; 042import 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 */ 049public 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}