View Javadoc

1   /*
2    * Copyright 2007 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 1.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/ecl1.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.layout;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.rice.krad.uif.UifConstants.Orientation;
20  import org.kuali.rice.krad.uif.container.CollectionGroup;
21  import org.kuali.rice.krad.uif.container.Container;
22  import org.kuali.rice.krad.uif.container.Group;
23  import org.kuali.rice.krad.uif.container.View;
24  import org.kuali.rice.krad.uif.core.Component;
25  import org.kuali.rice.krad.uif.core.DataBinding;
26  import org.kuali.rice.krad.uif.field.ActionField;
27  import org.kuali.rice.krad.uif.field.Field;
28  import org.kuali.rice.krad.uif.field.GroupField;
29  import org.kuali.rice.krad.uif.util.ComponentUtils;
30  import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
31  
32  import java.util.ArrayList;
33  import java.util.List;
34  
35  /**
36   * Layout manager that works with <code>CollectionGroup</code> containers and
37   * renders the collection lines in a vertical row
38   * 
39   * <p>
40   * For each line of the collection, a <code>Group</code> instance is created.
41   * The group header contains a label for the line (summary information), the
42   * group fields are the collection line fields, and the group footer contains
43   * the line actions. All the groups are rendered using the
44   * <code>BoxLayoutManager</code> with vertical orientation.
45   * </p>
46   * 
47   * <p>
48   * Modify the lineGroupPrototype to change header/footer styles or any other
49   * customization for the line groups
50   * </p>
51   * 
52   * @author Kuali Rice Team (rice.collab@kuali.org)
53   */
54  public class StackedLayoutManager extends BoxLayoutManager implements CollectionLayoutManager {
55  	private static final long serialVersionUID = 4602368505430238846L;
56  
57  	private String summaryTitle;
58  	private List<String> summaryFields;
59  
60  	private Group addLineGroup;
61  	private Group lineGroupPrototype;
62  	private GroupField subCollectionGroupFieldPrototype;
63  
64  	private List<Group> stackedGroups;
65  
66  	public StackedLayoutManager() {
67  		super();
68  
69  		setOrientation(Orientation.VERTICAL);
70  
71  		summaryFields = new ArrayList<String>();
72  		stackedGroups = new ArrayList<Group>();
73  	}
74  
75  	/**
76  	 * The following actions are performed:
77  	 * 
78  	 * <ul>
79  	 * <li>Initializes the prototypes</li>
80  	 * </ul>
81  	 * 
82  	 * @see org.kuali.rice.krad.uif.layout.BoxLayoutManager#performInitialization(org.kuali.rice.krad.uif.container.View,
83  	 *      org.kuali.rice.krad.uif.container.Container)
84  	 */
85  	@Override
86  	public void performInitialization(View view, Container container) {
87  		super.performInitialization(view, container);
88  
89  		view.getViewHelperService().performComponentInitialization(view, lineGroupPrototype);
90  		view.getViewHelperService().performComponentInitialization(view, subCollectionGroupFieldPrototype);
91  	}
92  
93  	/**
94  	 * Builds a <code>Group</code> instance for a collection line. The group is
95  	 * built by first creating a copy of the configured prototype. Then the
96  	 * header for the group is created using the configured summary fields on
97  	 * the <code>CollectionGroup</code>. The line fields passed in are set as
98  	 * the items for the group, and finally the actions are placed into the
99  	 * group footer
100 	 * 
101 	 * @see org.kuali.rice.krad.uif.layout.CollectionLayoutManager#buildLine(org.kuali.rice.krad.uif.container.View,
102 	 *      java.lang.Object, org.kuali.rice.krad.uif.container.CollectionGroup,
103 	 *      java.util.List, java.util.List, java.lang.String, java.util.List,
104 	 *      java.lang.String, java.lang.Object, int)
105 	 */
106 	public void buildLine(View view, Object model, CollectionGroup collectionGroup, List<Field> lineFields,
107 			List<GroupField> subCollectionFields, String bindingPath, List<ActionField> actions, String idSuffix,
108 			Object currentLine, int lineIndex) {
109 		boolean isAddLine = lineIndex == -1;
110 
111 		// construct new group
112 		Group lineGroup = null;
113 		if (isAddLine) {
114 			stackedGroups = new ArrayList<Group>();
115 
116 			if (addLineGroup == null) {
117 				lineGroup = ComponentUtils.copy(lineGroupPrototype, idSuffix);
118 			}
119 			else {
120 				lineGroup = ComponentUtils.copy(getAddLineGroup(), idSuffix);
121 			}
122 		}
123 		else {
124 			lineGroup = ComponentUtils.copy(lineGroupPrototype, idSuffix);
125 		}
126 
127 		ComponentUtils.updateContextForLine(lineGroup, currentLine, lineIndex);
128 
129 		// build header text for group
130 		String headerText = "";
131 		if (isAddLine) {
132 			headerText = collectionGroup.getAddLineLabel();
133 		}
134 		else {
135 			// get the collection for this group from the model
136 			List<Object> modelCollection = ObjectPropertyUtils.getPropertyValue(model, ((DataBinding) collectionGroup)
137 					.getBindingInfo().getBindingPath());
138 
139 			headerText = buildLineHeaderText(modelCollection.get(lineIndex));
140 		}
141 		lineGroup.getHeader().setHeaderText(headerText);
142 
143 		// stack all fields (including sub-collections) for the group
144 		List<Field> groupFields = new ArrayList<Field>();
145 		groupFields.addAll(lineFields);
146 		groupFields.addAll(subCollectionFields);
147 
148 		lineGroup.setItems(groupFields);
149 
150 		// set line actions on group footer
151 		if (collectionGroup.isRenderLineActions() && !collectionGroup.isReadOnly()) {
152 			lineGroup.getFooter().setItems(actions);
153 		}
154 
155 		stackedGroups.add(lineGroup);
156 	}
157 
158 	/**
159 	 * Builds the header text for the collection line
160 	 * 
161 	 * <p>
162 	 * Header text is built up by first the collection label, either specified
163 	 * on the collection definition or retrieved from the dictionary. Then for
164 	 * each summary field defined, the value from the model is retrieved and
165 	 * added to the header.
166 	 * </p>
167 	 * 
168 	 * @param line
169 	 *            - Collection line containing data
170 	 * @return String header text for line
171 	 */
172 	protected String buildLineHeaderText(Object line) {
173 		String summaryFieldString = "";
174 		for (String summaryField : summaryFields) {
175 			Object summaryFieldValue = ObjectPropertyUtils.getPropertyValue(line, summaryField);
176 			if (StringUtils.isNotBlank(summaryFieldString)) {
177 				summaryFieldString += " - ";
178 			}
179 
180 			if (summaryFieldValue != null) {
181 				summaryFieldString += summaryFieldValue;
182 			}
183 			else {
184 				summaryFieldString += "Null";
185 			}
186 		}
187 
188 		String headerText = summaryTitle;
189 		if (StringUtils.isNotBlank(summaryFieldString)) {
190 			headerText += " ( " + summaryFieldString + " )";
191 		}
192 
193 		return headerText;
194 	}
195 
196 	/**
197 	 * @see org.kuali.rice.krad.uif.layout.ContainerAware#getSupportedContainer()
198 	 */
199 	@Override
200 	public Class<? extends Container> getSupportedContainer() {
201 		return CollectionGroup.class;
202 	}
203 
204 	/**
205 	 * @see org.kuali.rice.krad.uif.layout.LayoutManagerBase#getNestedComponents()
206 	 */
207 	@Override
208 	public List<Component> getNestedComponents() {
209 		List<Component> components = super.getNestedComponents();
210 
211 		components.addAll(stackedGroups);
212 
213 		return components;
214 	}
215 
216 	/**
217 	 * Text to appears in the header for each collection lines Group. Used in
218 	 * conjunction with {@link #getSummaryFields()} to build up the final header
219 	 * text
220 	 * 
221 	 * @return String summary title text
222 	 */
223 	public String getSummaryTitle() {
224 		return this.summaryTitle;
225 	}
226 
227 	/**
228 	 * Setter for the summary title text
229 	 * 
230 	 * @param summaryTitle
231 	 */
232 	public void setSummaryTitle(String summaryTitle) {
233 		this.summaryTitle = summaryTitle;
234 	}
235 
236 	/**
237 	 * List of attribute names from the collection line class that should be
238 	 * used to build the line summary. To build the summary the value for each
239 	 * attribute is retrieved from the line instance. All the values are then
240 	 * placed together with a separator.
241 	 * 
242 	 * @return List<String> summary field names
243 	 * @see #buildLineHeaderText(java.lang.Object)
244 	 */
245 	public List<String> getSummaryFields() {
246 		return this.summaryFields;
247 	}
248 
249 	/**
250 	 * Setter for the summary field name list
251 	 * 
252 	 * @param summaryFields
253 	 */
254 	public void setSummaryFields(List<String> summaryFields) {
255 		this.summaryFields = summaryFields;
256 	}
257 
258 	/**
259 	 * Group instance that will be used for the add line
260 	 * 
261 	 * <p>
262 	 * Add line fields and actions configured on the
263 	 * <code>CollectionGroup</code> will be set onto the add line group (if add
264 	 * line is enabled). If the add line group is not configured, a new instance
265 	 * of the line group prototype will be used for the add line.
266 	 * </p>
267 	 * 
268 	 * @return Group add line group instance
269 	 * @see #getAddLineGroup()
270 	 */
271 	public Group getAddLineGroup() {
272 		return this.addLineGroup;
273 	}
274 
275 	/**
276 	 * Setter for the add line group
277 	 * 
278 	 * @param addLineGroup
279 	 */
280 	public void setAddLineGroup(Group addLineGroup) {
281 		this.addLineGroup = addLineGroup;
282 	}
283 
284 	/**
285 	 * Group instance that is used as a prototype for creating the collection
286 	 * line groups. For each line a copy of the prototype is made and then
287 	 * adjusted as necessary
288 	 * 
289 	 * @return Group instance to use as prototype
290 	 */
291 	public Group getLineGroupPrototype() {
292 		return this.lineGroupPrototype;
293 	}
294 
295 	/**
296 	 * Setter for the line group prototype
297 	 * 
298 	 * @param lineGroupPrototype
299 	 */
300 	public void setLineGroupPrototype(Group lineGroupPrototype) {
301 		this.lineGroupPrototype = lineGroupPrototype;
302 	}
303 
304 	/**
305 	 * @see org.kuali.rice.krad.uif.layout.CollectionLayoutManager#getSubCollectionGroupFieldPrototype()
306 	 */
307 	public GroupField getSubCollectionGroupFieldPrototype() {
308 		return this.subCollectionGroupFieldPrototype;
309 	}
310 
311 	/**
312 	 * Setter for the sub-collection field group prototype
313 	 * 
314 	 * @param subCollectionGroupFieldPrototype
315 	 */
316 	public void setSubCollectionGroupFieldPrototype(GroupField subCollectionGroupFieldPrototype) {
317 		this.subCollectionGroupFieldPrototype = subCollectionGroupFieldPrototype;
318 	}
319 
320 	/**
321 	 * Final <code>List</code> of Groups to render for the collection
322 	 * 
323 	 * @return List<Group> collection groups
324 	 */
325 	public List<Group> getStackedGroups() {
326 		return this.stackedGroups;
327 	}
328 
329 	/**
330 	 * Setter for the collection groups
331 	 * 
332 	 * @param stackedGroups
333 	 */
334 	public void setStackedGroups(List<Group> stackedGroups) {
335 		this.stackedGroups = stackedGroups;
336 	}
337 
338 }