001/** 002 * Copyright 2005-2015 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.krad.uif.control; 017 018import org.kuali.rice.core.api.util.ConcreteKeyValue; 019import org.kuali.rice.core.api.util.KeyValue; 020import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute; 021import org.kuali.rice.krad.uif.UifConstants; 022import org.kuali.rice.krad.uif.component.Component; 023import org.kuali.rice.krad.uif.container.Container; 024import org.kuali.rice.krad.uif.element.Message; 025import org.kuali.rice.krad.uif.field.InputField; 026import org.kuali.rice.krad.uif.util.ComponentUtils; 027import org.kuali.rice.krad.uif.view.ExpressionEvaluator; 028import org.kuali.rice.krad.uif.util.ComponentFactory; 029import org.kuali.rice.krad.uif.util.ExpressionUtils; 030import org.kuali.rice.krad.uif.util.KeyMessage; 031import org.kuali.rice.krad.uif.util.UrlInfo; 032import org.kuali.rice.krad.uif.util.UifKeyValueLocation; 033import org.kuali.rice.krad.uif.view.View; 034 035import java.util.ArrayList; 036import java.util.List; 037 038/** 039 * Base class for controls that accept/display multiple values 040 * 041 * @author Kuali Rice Team (rice.collab@kuali.org) 042 */ 043public abstract class MultiValueControlBase extends ControlBase implements MultiValueControl { 044 private static final long serialVersionUID = -8691367056245775455L; 045 046 private List<KeyValue> options; 047 private List<KeyMessage> richOptions; 048 private List<Component> inlineComponents; 049 050 private boolean locationSelect = false; 051 052 public MultiValueControlBase() { 053 super(); 054 } 055 056 /** 057 * Process rich message content that may be in the options, by creating and initializing the richOptions 058 * 059 * @see org.kuali.rice.krad.uif.component.ComponentBase#performApplyModel(org.kuali.rice.krad.uif.view.View, 060 * Object, org.kuali.rice.krad.uif.component.Component) 061 */ 062 @Override 063 public void performApplyModel(View view, Object model, Component parent) { 064 super.performApplyModel(view, model, parent); 065 066 if (options != null && richOptions == null) { 067 richOptions = new ArrayList<KeyMessage>(); 068 069 for (KeyValue option : options) { 070 Message message = ComponentFactory.getMessage(); 071 view.assignComponentIds(message); 072 message.setMessageText(option.getValue()); 073 message.setInlineComponents(inlineComponents); 074 message.setGenerateSpan(false); 075 076 view.getViewHelperService().performComponentInitialization(view, model, message); 077 richOptions.add(new KeyMessage(option.getKey(), option.getValue(), message)); 078 } 079 } 080 } 081 082 /** 083 * Adds appropriate parent data to inputs internal to the controls that may be in rich content of options 084 * 085 * @see org.kuali.rice.krad.uif.component.Component#performFinalize(org.kuali.rice.krad.uif.view.View, Object, 086 * org.kuali.rice.krad.uif.component.Component) 087 */ 088 @Override 089 public void performFinalize(View view, Object model, Component parent) { 090 super.performFinalize(view, model, parent); 091 092 ExpressionEvaluator expressionEvaluator = view.getViewHelperService().getExpressionEvaluator(); 093 094 if (options != null && !options.isEmpty()) { 095 for (KeyValue option : options) { 096 if (option instanceof UifKeyValueLocation) { 097 locationSelect = true; 098 099 UrlInfo url = ((UifKeyValueLocation) option).getLocation(); 100 101 ExpressionUtils.populatePropertyExpressionsFromGraph(url, false); 102 expressionEvaluator.evaluateExpressionsOnConfigurable(view, url, view.getContext()); 103 } 104 } 105 } 106 107 if (richOptions == null || richOptions.isEmpty()) { 108 return; 109 } 110 111 //Messages included in options which have have rich message content need to be aware of their parent for 112 //validation purposes 113 for (KeyMessage richOption : richOptions) { 114 List<Component> components = richOption.getMessage().getMessageComponentStructure(); 115 116 if (components != null && !components.isEmpty()) { 117 for (Component c : components) { 118 if (c instanceof Container || c instanceof InputField) { 119 c.addDataAttribute(UifConstants.DataAttributes.PARENT, parent.getId()); 120 } 121 } 122 } 123 } 124 125 } 126 127 /** 128 * @see org.kuali.rice.krad.uif.component.ComponentBase#getComponentsForLifecycle() 129 */ 130 @Override 131 public List<Component> getComponentsForLifecycle() { 132 List<Component> components = super.getComponentsForLifecycle(); 133 134 if (richOptions != null) { 135 for (KeyMessage richOption : richOptions) { 136 components.add(richOption.getMessage()); 137 } 138 } 139 140 return components; 141 } 142 143 /** 144 * @see MultiValueControl#getOptions() 145 */ 146 @BeanTagAttribute(name = "options", type = BeanTagAttribute.AttributeType.LISTBEAN) 147 public List<KeyValue> getOptions() { 148 return this.options; 149 } 150 151 /** 152 * @see MultiValueControl#setOptions(java.util.List<org.kuali.rice.core.api.util.KeyValue>) 153 */ 154 public void setOptions(List<KeyValue> options) { 155 this.options = options; 156 } 157 158 /** 159 * Gets the inlineComponents which represent components that can be referenced in an option's value 160 * by index 161 * 162 * @return the components that can be used in rich values of options 163 */ 164 @BeanTagAttribute(name = "inlineComponents", type = BeanTagAttribute.AttributeType.LISTBEAN) 165 public List<Component> getInlineComponents() { 166 return inlineComponents; 167 } 168 169 /** 170 * Sets the inlineComponents which represent components that can be referenced in an option's value 171 * by index 172 * 173 * @param inlineComponents 174 */ 175 public void setInlineComponents(List<Component> inlineComponents) { 176 this.inlineComponents = inlineComponents; 177 } 178 179 /** 180 * @see MultiValueControl#getRichOptions() 181 */ 182 public List<KeyMessage> getRichOptions() { 183 return richOptions; 184 } 185 186 /** 187 * Sets the richOptions. This will always override/ignore options if set. 188 * 189 * <p><b>Messages MUST be defined</b> when using this setter, do not use this setter for most cases 190 * as setting options through setOptions, with a richMessage value, is appropriate in MOST cases. This 191 * setter is only available for full control.</p> 192 * 193 * @param richOptions with their messages predefined 194 */ 195 public void setRichOptions(List<KeyMessage> richOptions) { 196 this.richOptions = richOptions; 197 } 198 199 /** 200 * If true, this select represents a location select (navigate on select of option) 201 * 202 * @return true if this is a location select 203 */ 204 public boolean isLocationSelect() { 205 return locationSelect; 206 } 207 208 /** 209 * Sets the location select (navigate on select of option) 210 * 211 * @param locationSelect 212 */ 213 protected void setLocationSelect(boolean locationSelect) { 214 this.locationSelect = locationSelect; 215 } 216 217 /** 218 * @see org.kuali.rice.krad.uif.component.ComponentBase#copy() 219 */ 220 @Override 221 protected <T> void copyProperties(T component) { 222 super.copyProperties(component); 223 MultiValueControlBase multiValueControlBaseCopy = (MultiValueControlBase) component; 224 225 try { 226 if (options != null) { 227 List<KeyValue> optionsCopy = new ArrayList<KeyValue>(); 228 for (KeyValue option : options) { 229 230 KeyValue keyValue = null; 231 if (option != null) { 232 Class<? extends KeyValue> optionClass = option.getClass(); 233 keyValue = optionClass.getDeclaredConstructor(String.class, String.class).newInstance( 234 option.getKey(), option.getValue()); 235 if (keyValue instanceof UifKeyValueLocation) { 236 ((UifKeyValueLocation) keyValue).setLocation( 237 (UrlInfo) ((UifKeyValueLocation) option).getLocation().copy()); 238 } 239 } 240 241 optionsCopy.add(keyValue); 242 } 243 multiValueControlBaseCopy.setOptions(optionsCopy); 244 } 245 } catch (Exception e) { 246 throw new RuntimeException("Failed to copy options in MultiValueControlBase", e); 247 } 248 249 if (richOptions != null) { 250 List<KeyMessage> richOptionsCopy = new ArrayList<KeyMessage>(); 251 for (KeyMessage richOption : richOptions) { 252 KeyMessage keyMessage = new KeyMessage(richOption.getKey(), richOption.getValue(), 253 richOption.getMessage()); 254 richOptionsCopy.add(keyMessage); 255 } 256 multiValueControlBaseCopy.setRichOptions(richOptionsCopy); 257 } 258 259 if (inlineComponents != null) { 260 List<Component> inlineComponentsCopy = new ArrayList<Component>(); 261 for (Component inlineComponent : inlineComponents) { 262 inlineComponentsCopy.add((Component) inlineComponent.copy()); 263 } 264 multiValueControlBaseCopy.setInlineComponents(inlineComponentsCopy); 265 } 266 267 multiValueControlBaseCopy.setLocationSelect(this.locationSelect); 268 } 269}