001/** 002 * Copyright 2005-2018 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.apache.commons.lang.ObjectUtils; 019import org.kuali.rice.core.api.util.AbstractKeyValue; 020import org.kuali.rice.core.api.util.KeyValue; 021import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute; 022import org.kuali.rice.krad.uif.UifConstants; 023import org.kuali.rice.krad.uif.component.Component; 024import org.kuali.rice.krad.uif.container.Container; 025import org.kuali.rice.krad.uif.element.Message; 026import org.kuali.rice.krad.uif.field.InputField; 027import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle; 028import org.kuali.rice.krad.uif.util.ComponentFactory; 029import org.kuali.rice.krad.uif.util.ComponentUtils; 030import org.kuali.rice.krad.uif.util.KeyMessage; 031import org.kuali.rice.krad.uif.util.LifecycleElement; 032import org.kuali.rice.krad.uif.util.UifKeyValueLocation; 033import org.kuali.rice.krad.uif.util.UifOptionGroupLabel; 034import org.kuali.rice.krad.uif.util.UrlInfo; 035import org.kuali.rice.krad.uif.view.ExpressionEvaluator; 036import org.kuali.rice.krad.uif.view.View; 037 038import java.util.ArrayList; 039import java.util.List; 040 041/** 042 * Base class for controls that accept/display multiple values 043 * 044 * @author Kuali Rice Team (rice.collab@kuali.org) 045 */ 046public abstract class MultiValueControlBase extends ControlBase implements MultiValueControl { 047 private static final long serialVersionUID = -8691367056245775455L; 048 049 private List<KeyValue> options; 050 private List<KeyMessage> richOptions; 051 private List<Component> inlineComponents; 052 053 private List<Message> internalMessageComponents; 054 055 private boolean locationSelect = false; 056 057 public MultiValueControlBase() { 058 super(); 059 } 060 061 /** 062 * Process rich message content that may be in the options, by creating and initializing the richOptions 063 * 064 * {@inheritDoc} 065 */ 066 @Override 067 public void performApplyModel(Object model, LifecycleElement parent) { 068 super.performApplyModel(model, parent); 069 getStyleClassesAsString(); 070 if (options != null && richOptions == null) { 071 richOptions = new ArrayList<KeyMessage>(); 072 internalMessageComponents = new ArrayList<Message>(); 073 074 for (KeyValue option : options) { 075 076 // do this?? 077 if (option instanceof UifOptionGroupLabel) { 078 continue; 079 } 080 081 Message message = ComponentFactory.getMessage(); 082 083 String key = option.getKey(); 084 if (key.contains(UifConstants.EL_PLACEHOLDER_PREFIX)) { 085 key = ViewLifecycle.getExpressionEvaluator().evaluateExpression(this.getContext(), key).toString(); 086 } 087 088 String value = option.getValue(); 089 if (value.contains(UifConstants.EL_PLACEHOLDER_PREFIX)) { 090 value = ViewLifecycle.getExpressionEvaluator().evaluateExpression(this.getContext(), value).toString(); 091 } 092 093 message.setMessageText(value); 094 message.setInlineComponents(inlineComponents); 095 message.setRenderWrapperTag(false); 096 097 // if the option is a sub-class of AbstractKeyValue class, then we also include the disabled attribute 098 if(AbstractKeyValue.class.isAssignableFrom(option.getClass()) && ((AbstractKeyValue)option).isDisabled()) { 099 richOptions.add(new KeyMessage(key, value, message, ((AbstractKeyValue)option).isDisabled())); 100 } else { 101 richOptions.add(new KeyMessage(key, value, message)); 102 } 103 104 internalMessageComponents.add(message); 105 } 106 } 107 } 108 109 /** 110 * Adds appropriate parent data to inputs internal to the controls that may be in rich content of options 111 * 112 * {@inheritDoc} 113 */ 114 @Override 115 public void performFinalize(Object model, LifecycleElement parent) { 116 super.performFinalize(model, parent); 117 118 View view = ViewLifecycle.getView(); 119 ExpressionEvaluator expressionEvaluator = ViewLifecycle.getExpressionEvaluator(); 120 121 if (options != null && !options.isEmpty()) { 122 for (KeyValue option : options) { 123 if (option instanceof UifKeyValueLocation) { 124 locationSelect = true; 125 126 UrlInfo url = ((UifKeyValueLocation) option).getLocation(); 127 128 ViewLifecycle.getExpressionEvaluator().populatePropertyExpressionsFromGraph(url, false); 129 expressionEvaluator.evaluateExpressionsOnConfigurable(view, url, view.getContext()); 130 } 131 } 132 } 133 134 if (richOptions == null || richOptions.isEmpty()) { 135 return; 136 } 137 138 //Messages included in options which have have rich message content need to be aware of their parent for 139 //validation purposes 140 for (KeyMessage richOption : richOptions) { 141 List<Component> components = richOption.getMessage().getMessageComponentStructure(); 142 143 if (components != null && !components.isEmpty()) { 144 for (Component c : components) { 145 if (c instanceof Container || c instanceof InputField) { 146 c.addDataAttribute(UifConstants.DataAttributes.PARENT, parent.getId()); 147 } 148 } 149 } 150 } 151 152 } 153 154 /** 155 * @see MultiValueControl#getOptions() 156 */ 157 @BeanTagAttribute 158 public List<KeyValue> getOptions() { 159 return this.options; 160 } 161 162 /** 163 * {@inheritDoc} 164 */ 165 public void setOptions(List<KeyValue> options) { 166 this.options = options; 167 } 168 169 /** 170 * Gets the inlineComponents which represent components that can be referenced in an option's value 171 * by index 172 * 173 * @return the components that can be used in rich values of options 174 */ 175 @BeanTagAttribute 176 public List<Component> getInlineComponents() { 177 return inlineComponents; 178 } 179 180 /** 181 * Sets the inlineComponents which represent components that can be referenced in an option's value 182 * by index 183 * 184 * @param inlineComponents 185 */ 186 public void setInlineComponents(List<Component> inlineComponents) { 187 this.inlineComponents = inlineComponents; 188 } 189 190 /** 191 * @see MultiValueControl#getRichOptions() 192 */ 193 public List<KeyMessage> getRichOptions() { 194 return richOptions; 195 } 196 197 /** 198 * Sets the richOptions. This will always override/ignore options if set. 199 * 200 * <p><b>Messages MUST be defined</b> when using this setter, do not use this setter for most cases 201 * as setting options through setOptions, with a richMessage value, is appropriate in MOST cases. This 202 * setter is only available for full control.</p> 203 * 204 * @param richOptions with their messages predefined 205 */ 206 public void setRichOptions(List<KeyMessage> richOptions) { 207 this.richOptions = richOptions; 208 } 209 210 /** 211 * Used by reflection during the lifecycle to get internal message components that may be contained in options 212 * 213 * <p>There are no references to this method in the code, this is intentional. DO NOT REMOVE.</p> 214 * 215 * @return the internal message components, if any 216 */ 217 public List<Message> getInternalMessageComponents() { 218 return internalMessageComponents; 219 } 220 221 /** 222 * If true, this select represents a location select (navigate on select of option) 223 * 224 * @return true if this is a location select 225 */ 226 public boolean isLocationSelect() { 227 return locationSelect; 228 } 229 230 /** 231 * Sets the location select (navigate on select of option) 232 * 233 * @param locationSelect 234 */ 235 protected void setLocationSelect(boolean locationSelect) { 236 this.locationSelect = locationSelect; 237 } 238}