001 /**
002 * Copyright 2005-2012 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 */
016 package org.kuali.rice.krad.uif.container;
017
018 import org.apache.commons.lang.StringUtils;
019 import org.kuali.rice.krad.datadictionary.validator.ErrorReport;
020 import org.kuali.rice.krad.datadictionary.validator.RDValidator;
021 import org.kuali.rice.krad.datadictionary.validator.TracerToken;
022 import org.kuali.rice.krad.uif.component.Component;
023 import org.kuali.rice.krad.uif.component.DataBinding;
024 import org.kuali.rice.krad.uif.field.Field;
025 import org.kuali.rice.krad.uif.field.FieldGroup;
026 import org.kuali.rice.krad.uif.view.View;
027 import org.kuali.rice.krad.uif.widget.Scrollpane;
028 import org.kuali.rice.krad.uif.widget.Disclosure;
029
030 import java.util.ArrayList;
031 import java.util.HashSet;
032 import java.util.List;
033 import java.util.Set;
034
035 /**
036 * Container that holds a list of <code>Field</code> or other <code>Group</code>
037 * instances
038 *
039 * <p>
040 * Groups can exist at different levels of the <code>View</code>, providing
041 * conceptual groupings such as the page, section, and group. In addition, other
042 * group types can be created to add behavior like collection support
043 * </p>
044 *
045 * <p>
046 * <code>Group</code> implementation has properties for defaulting the binding
047 * information (such as the parent object path and a binding prefix) for the
048 * fields it contains. During the phase these properties (if given) are set on
049 * the fields contained in the <code>Group</code> that implement
050 * <code>DataBinding</code>, unless they have already been set on the field.
051 * </p>
052 *
053 * @author Kuali Rice Team (rice.collab@kuali.org)
054 */
055 public class Group extends ContainerBase {
056 private static final long serialVersionUID = 7953641325356535509L;
057
058 private String fieldBindByNamePrefix;
059 private String fieldBindingObjectPath;
060
061 private Disclosure disclosure;
062 private Scrollpane scrollpane;
063
064 private List<? extends Component> items;
065
066 /**
067 * Default Constructor
068 */
069 public Group() {
070 items = new ArrayList<Component>();
071 }
072
073 /**
074 * The following actions are performed:
075 *
076 * <ul>
077 * <li>Sets the bindByNamePrefix if blank on any InputField and
078 * FieldGroup instances within the items List</li>
079 * </ul>
080 *
081 * @see org.kuali.rice.krad.uif.component.ComponentBase#performInitialization(org.kuali.rice.krad.uif.view.View, java.lang.Object)
082 */
083 @Override
084 public void performInitialization(View view, Object model) {
085 super.performInitialization(view, model);
086
087 for (Component component : getItems()) {
088 // append group's field bind by name prefix (if set) to each
089 // attribute field's binding prefix
090 if (component instanceof DataBinding) {
091 DataBinding dataBinding = (DataBinding) component;
092
093 if (StringUtils.isNotBlank(getFieldBindByNamePrefix())) {
094 String bindByNamePrefixToSet = getFieldBindByNamePrefix();
095
096 if (StringUtils.isNotBlank(dataBinding.getBindingInfo().getBindByNamePrefix())) {
097 bindByNamePrefixToSet += "." + dataBinding.getBindingInfo().getBindByNamePrefix();
098 }
099 dataBinding.getBindingInfo().setBindByNamePrefix(bindByNamePrefixToSet);
100 }
101
102 if (StringUtils.isNotBlank(fieldBindingObjectPath) &&
103 StringUtils.isBlank(dataBinding.getBindingInfo().getBindingObjectPath())) {
104 dataBinding.getBindingInfo().setBindingObjectPath(fieldBindingObjectPath);
105 }
106 }
107 // set on FieldGroup's group to recursively set AttributeFields
108 else if (component instanceof FieldGroup) {
109 FieldGroup fieldGroup = (FieldGroup) component;
110
111 if (fieldGroup.getGroup() != null) {
112 if (StringUtils.isBlank(fieldGroup.getGroup().getFieldBindByNamePrefix())) {
113 fieldGroup.getGroup().setFieldBindByNamePrefix(fieldBindByNamePrefix);
114 }
115 if (StringUtils.isBlank(fieldGroup.getGroup().getFieldBindingObjectPath())) {
116 fieldGroup.getGroup().setFieldBindingObjectPath(fieldBindingObjectPath);
117 }
118 }
119 } else if (component instanceof Group) {
120 Group subGroup = (Group) component;
121 if (StringUtils.isNotBlank(getFieldBindByNamePrefix())) {
122 if (StringUtils.isNotBlank(subGroup.getFieldBindByNamePrefix())) {
123 subGroup.setFieldBindByNamePrefix(
124 getFieldBindByNamePrefix() + "." + subGroup.getFieldBindByNamePrefix());
125 } else {
126 subGroup.setFieldBindByNamePrefix(getFieldBindByNamePrefix());
127 }
128 }
129 if (StringUtils.isNotBlank(getFieldBindingObjectPath())) {
130 if (StringUtils.isNotBlank(subGroup.getFieldBindingObjectPath())) {
131 subGroup.setFieldBindingObjectPath(
132 getFieldBindingObjectPath() + "." + subGroup.getFieldBindingObjectPath());
133 } else {
134 subGroup.setFieldBindingObjectPath(getFieldBindingObjectPath());
135 }
136 }
137 }
138 }
139 }
140
141 /**
142 * @see org.kuali.rice.krad.uif.component.ComponentBase#getComponentsForLifecycle()
143 */
144 @Override
145 public List<Component> getComponentsForLifecycle() {
146 List<Component> components = super.getComponentsForLifecycle();
147
148 components.add(disclosure);
149 components.add(scrollpane);
150
151 return components;
152 }
153
154 /**
155 * @see org.kuali.rice.krad.uif.container.Container#getSupportedComponents()
156 */
157 @Override
158 public Set<Class<? extends Component>> getSupportedComponents() {
159 Set<Class<? extends Component>> supportedComponents = new HashSet<Class<? extends Component>>();
160 supportedComponents.add(Field.class);
161 supportedComponents.add(Group.class);
162
163 return supportedComponents;
164 }
165
166 /**
167 * @see org.kuali.rice.krad.uif.component.Component#getComponentTypeName()
168 */
169 @Override
170 public String getComponentTypeName() {
171 return "group";
172 }
173
174 /**
175 * Binding prefix string to set on each of the groups <code>DataField</code> instances
176 *
177 * <p>
178 * As opposed to setting the bindingPrefix on each attribute field instance,
179 * it can be set here for the group. During initialize the string will then
180 * be set on each attribute field instance if the bindingPrefix is blank and
181 * not a form field
182 * </p>
183 *
184 * @return String binding prefix to set
185 */
186 public String getFieldBindByNamePrefix() {
187 return this.fieldBindByNamePrefix;
188 }
189
190 /**
191 * Setter for the field binding prefix
192 *
193 * @param fieldBindByNamePrefix
194 */
195 public void setFieldBindByNamePrefix(String fieldBindByNamePrefix) {
196 this.fieldBindByNamePrefix = fieldBindByNamePrefix;
197 }
198
199 /**
200 * Object binding path to set on each of the group's
201 * <code>InputField</code> instances
202 *
203 * <p>
204 * When the attributes of the group belong to a object whose path is
205 * different from the default then this property can be given to set each of
206 * the attributes instead of setting the model path on each one. The object
207 * path can be overridden at the attribute level. The object path is set to
208 * the fieldBindingObjectPath during the initialize phase.
209 * </p>
210 *
211 * @return String model path to set
212 * @see org.kuali.rice.krad.uif.component.BindingInfo#getBindingObjectPath()
213 */
214 public String getFieldBindingObjectPath() {
215 return this.fieldBindingObjectPath;
216 }
217
218 /**
219 * Setter for the field object binding path
220 *
221 * @param fieldBindingObjectPath
222 */
223 public void setFieldBindingObjectPath(String fieldBindingObjectPath) {
224 this.fieldBindingObjectPath = fieldBindingObjectPath;
225 }
226
227 /**
228 * Disclosure widget that provides collapse/expand functionality for the
229 * group
230 *
231 * @return Disclosure instance
232 */
233 public Disclosure getDisclosure() {
234 return this.disclosure;
235 }
236
237 /**
238 * Setter for the group's disclosure instance
239 *
240 * @param disclosure
241 */
242 public void setDisclosure(Disclosure disclosure) {
243 this.disclosure = disclosure;
244 }
245
246 /**
247 * Scrollpane widget that provides scrolling functionality for the
248 * group
249 *
250 * @return Scrollpane instance
251 */
252 public Scrollpane getScrollpane() {
253 return this.scrollpane;
254 }
255
256 /**
257 * Setter for the group's scrollpane instance
258 *
259 * @param scrollpane
260 */
261 public void setScrollpane(Scrollpane scrollpane) {
262 this.scrollpane = scrollpane;
263 }
264
265 /**
266 * @see org.kuali.rice.krad.uif.container.ContainerBase#getItems()
267 */
268 @Override
269 public List<? extends Component> getItems() {
270 return this.items;
271 }
272
273 /**
274 * Setter for the Group's list of components
275 *
276 * @param items
277 */
278 @Override
279 public void setItems(List<? extends Component> items) {
280 this.items = items;
281 }
282
283 /**
284 * @see org.kuali.rice.krad.uif.component.Component#completeValidation
285 */
286 @Override
287 public ArrayList<ErrorReport> completeValidation(TracerToken tracer){
288 ArrayList<ErrorReport> reports=new ArrayList<ErrorReport>();
289 tracer.addBean(this);
290
291 // Checks that no invalid items are present
292 for(int i=0;i<getItems().size();i++){
293 if(getItems().get(i).getClass()==PageGroup.class || getItems().get(i).getClass()==NavigationGroup.class){
294 ErrorReport error = ErrorReport.createError("Items in Group cannot be PageGroup or NaviagtionGroup",tracer);
295 error.addCurrentValue("item("+i+").class ="+getItems().get(i).getClass());
296 reports.add(error);
297 }
298 }
299
300 // Checks that the layout manager is set
301 if(getLayoutManager()==null){
302 if(RDValidator.checkExpressions(this,"layoutManager")){
303 ErrorReport error = ErrorReport.createError("LayoutManager must be set",tracer);
304 error.addCurrentValue("layoutManager = "+getLayoutManager());
305 reports.add(error);
306 }
307 }
308
309 reports.addAll(super.completeValidation(tracer.getCopy()));
310
311 return reports;
312 }
313 }