View Javadoc

1   /**
2    * Copyright 2005-2012 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.uif.modifier;
17  
18  import org.kuali.rice.krad.datadictionary.parse.BeanTag;
19  import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
20  import org.kuali.rice.krad.uif.view.View;
21  import org.kuali.rice.krad.uif.component.Component;
22  import org.kuali.rice.krad.uif.util.ComponentUtils;
23  import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
24  
25  import java.util.ArrayList;
26  import java.util.HashSet;
27  import java.util.List;
28  import java.util.Set;
29  
30  /**
31   * For a given <code>Component</code> instance converts all component properties
32   * of a certain type to instances of another configured <code>Component</code>.
33   * The conversion is performed recursively down all the component children
34   *
35   * <p>
36   * Some example uses of this are converting all checkbox controls to radio group
37   * controls within a group and replacement of a widget with another
38   * </p>
39   *
40   * @author Kuali Rice Team (rice.collab@kuali.org)
41   */
42  @BeanTag(name = "componentConvertModifier")
43  public class ComponentConvertModifier extends ComponentModifierBase {
44  	private static final long serialVersionUID = -7566547737669924605L;
45  
46  	private Class<? extends Component> componentTypeToReplace;
47  
48  	private Component componentReplacementPrototype;
49  
50  	public ComponentConvertModifier() {
51  		super();
52  	}
53  
54  	/**
55  	 * @see org.kuali.rice.krad.uif.modifier.ComponentModifier#performModification(org.kuali.rice.krad.uif.view.View,
56  	 *      java.lang.Object, org.kuali.rice.krad.uif.component.Component)
57  	 */
58  	@Override
59  	public void performModification(View view, Object model, Component component) {
60  		if (component == null) {
61  			return;
62  		}
63  
64  		int idSuffix = 0;
65  		convertToReplacement(component, idSuffix);
66  	}
67  
68  	/**
69  	 * Reads the component properties and looks for types that match the
70  	 * configured type to replace. If a match is found, a new instance of the
71  	 * replacement component prototype is created and set as the property value.
72  	 * The method is then called for each of the component's children
73  	 *
74  	 * @param component
75  	 *            - component instance to inspect properties for
76  	 * @param idSuffix
77  	 *            - suffix string to use for any generated component
78  	 *            replacements
79  	 */
80  	protected void convertToReplacement(Component component, int idSuffix) {
81  		if (component == null) {
82  			return;
83  		}
84  
85  		// check all component properties for the type to replace
86  		List<String> componentProperties = ComponentUtils.getComponentPropertyNames(component.getClass());
87  		for (String propertyPath : componentProperties) {
88  			Object propValue = ObjectPropertyUtils.getPropertyValue(component, propertyPath);
89  
90  			if (propValue != null) {
91  				if (getComponentTypeToReplace().isAssignableFrom(propValue.getClass())) {
92  					// types match, convert the component
93  					performConversion(component, propertyPath, idSuffix++);
94  				}
95  			}
96  		}
97  
98  		// recursively update components
99  		for (Component nestedComponent : component.getComponentsForLifecycle()) {
100 			convertToReplacement(nestedComponent, idSuffix);
101 		}
102 	}
103 
104 	/**
105 	 * Creates a new instance of the replacement component prototype and sets a
106 	 * the property value for the given property name and component instance
107 	 *
108 	 * @param component
109 	 *            - component instance to set property on
110 	 * @param componentProperty
111 	 *            - property name to set
112 	 * @param idSuffix
113 	 *            - suffix string to use for the generated component
114 	 */
115 	protected void performConversion(Component component, String componentProperty, int idSuffix) {
116 		// create new instance of replacement component
117 		Component componentReplacement = ComponentUtils.copy(getComponentReplacementPrototype(), Integer.toString(idSuffix));
118 
119 		ObjectPropertyUtils.setPropertyValue(component, componentProperty, componentReplacement);
120 	}
121 
122 	/**
123 	 * @see org.kuali.rice.krad.uif.modifier.ComponentModifier#getSupportedComponents()
124 	 */
125 	@Override
126 	public Set<Class<? extends Component>> getSupportedComponents() {
127 		Set<Class<? extends Component>> components = new HashSet<Class<? extends Component>>();
128 		components.add(Component.class);
129 
130 		return components;
131 	}
132 
133     /**
134      * @see org.kuali.rice.krad.uif.modifier.ComponentModifierBase#getComponentPrototypes()
135      */
136     public List<Component> getComponentPrototypes() {
137         List<Component> components = new ArrayList<Component>();
138 
139         components.add(componentReplacementPrototype);
140 
141         return components;
142     }
143 
144 	/**
145 	 * Type of component that should be replaced with an instance of the
146 	 * component prototype
147 	 *
148 	 * @return Class<? extends Component> component type to replace
149 	 */
150     @BeanTagAttribute(name="componentTypeToReplace")
151 	public Class<? extends Component> getComponentTypeToReplace() {
152 		return this.componentTypeToReplace;
153 	}
154 
155 	/**
156 	 * Setter for the component type to replace
157 	 *
158 	 * @param componentTypeToReplace
159 	 */
160 	public void setComponentTypeToReplace(Class<? extends Component> componentTypeToReplace) {
161 		this.componentTypeToReplace = componentTypeToReplace;
162 	}
163 
164 	/**
165 	 * Prototype for the component replacement
166 	 *
167 	 * <p>
168 	 * Each time the type to replace if found a new instance of the component
169 	 * prototype will be created and set as the new property value
170 	 * </p>
171 	 *
172 	 * @return
173 	 */
174     @BeanTagAttribute(name="componentReplacementPrototype", type= BeanTagAttribute.AttributeType.SINGLEBEAN)
175 	public Component getComponentReplacementPrototype() {
176 		return this.componentReplacementPrototype;
177 	}
178 
179 	/**
180 	 * Setter for the replacement component prototype
181 	 *
182 	 * @param componentReplacementPrototype
183 	 */
184 	public void setComponentReplacementPrototype(Component componentReplacementPrototype) {
185 		this.componentReplacementPrototype = componentReplacementPrototype;
186 	}
187 
188 }