View Javadoc
1   /**
2    * Copyright 2010 The Kuali Foundation Licensed under the
3    * Educational Community License, Version 2.0 (the "License"); you may
4    * not use this file except in compliance with the License. You may
5    * obtain a copy of the License at
6    * <p/>
7    * http://www.osedu.org/licenses/ECL-2.0
8    * <p/>
9    * Unless required by applicable law or agreed to in writing,
10   * software distributed under the License is distributed on an "AS IS"                       
11   * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12   * or implied. See the License for the specific language governing
13   * permissions and limitations under the License.
14   * <p/>
15   */
16  package org.kuali.student.common.ui.client.configurable.mvc.multiplicity;
17  
18  import java.util.ArrayList;
19  import java.util.HashMap;
20  import java.util.List;
21  import java.util.Map;
22  
23  import org.kuali.student.common.ui.client.configurable.mvc.FieldDescriptor;
24  import org.kuali.student.common.ui.client.configurable.mvc.SectionTitle;
25  import org.kuali.student.common.ui.client.widgets.field.layout.element.MessageKeyInfo;
26  import org.kuali.student.r1.common.assembly.data.Metadata;
27  import org.kuali.student.r1.common.assembly.data.QueryPath;
28  
29  import com.google.gwt.core.client.GWT;
30  
31  /**
32    *
33    * MultiplicityConfiguration is passed into a MultiplicitySection to control the creation of the multiplicity.
34    *
35    * This class is used to control the Multiplicity, e.g. if its updateable, has headers, min number of items, labels etc.
36    * It also holds FieldDescriptors for the fields required to be included in the multiplicity. They are
37    * held in a HashMap keyed by rows, i.e. {@literal Map<Integer, List<FieldDescriptor>>} .
38    *
39    * The parent for the multiplicity refers to the path to the high level field that owns
40    * the repeating elements, e.g. course/versions is the parent field for course version fields versionCode and
41    * versionTitle
42    *
43    * For multiplicities nested inside another multiplicity, create a MultiplicityConfiguration for the nested
44    * multiplicity and call setNestedConfig to add the lower level multiplicity
45    *
46    *
47   */
48  public class MultiplicityConfiguration {
49  
50      private MultiplicityType multiplicityType;
51      
52      private StyleType styleType;
53      private boolean updateable = false;
54  
55      private String itemLabel;
56      private String addItemLabel;
57      
58      private Metadata metaData;
59      
60      private SectionTitle title;
61      private int defaultItemsCreated = 0;
62  
63      int row = 0;
64      
65      private FieldDescriptor parentFd;
66      private Map<Integer, List<MultiplicityFieldConfiguration>> fields = new HashMap<Integer, List<MultiplicityFieldConfiguration>>();
67      private Map<String, FieldDescriptor> fieldMap = new HashMap<String, FieldDescriptor>();
68      protected Map<String, String> concatenatedFields = new HashMap<String, String>();
69      
70      private MultiplicityConfiguration nestedConfig ;
71      private MultiplicityGroup customMultiplicityGroup;
72      private boolean showHeaders;
73  
74      public static enum MultiplicityType {GROUP, TABLE }
75      public static enum StyleType {
76          TOP_LEVEL_GROUP, SUB_LEVEL_GROUP, BORDERLESS_TABLE, BORDERED_TABLE
77      }
78  
79      public MultiplicityConfiguration() {}
80      
81       /**
82       *
83       * Creates a new MultiplicityConfiguration.
84       *
85       *
86       * @param multiplicityType      the type of multiplicity required, MultiplicityType
87       * @param styleType             the style type for this multiplicity, StyleType
88       * @param metaData              the metadata for the parentFd
89       */
90      public MultiplicityConfiguration(MultiplicityType multiplicityType, StyleType styleType, Metadata metaData) {
91          init(multiplicityType, styleType, metaData);
92      }
93  
94      public void init(MultiplicityType multiplicityType, StyleType styleType, Metadata metaData) {
95          this.multiplicityType = multiplicityType;
96          this.styleType = styleType;
97          this.metaData = metaData;
98      }
99  
100     /**
101      * Causes a line throw so that the subsequent fields are placed on the next line of the screen
102      *
103      */
104     public void nextLine() {
105     	row++;
106     }
107 
108     /**
109      * Includes this field (as defined by the FieldDescriptor) on the current line at the next horizontal position
110      *
111      *
112      * @param fieldDescriptor
113      */
114     public void addFieldConfiguration(MultiplicityFieldConfiguration fieldDescriptor) {
115     	
116         List<MultiplicityFieldConfiguration> fds;
117         if (fields.containsKey(row)) {
118             fds = fields.get(row);
119         }
120         else {
121             fds = new ArrayList<MultiplicityFieldConfiguration>();
122         }
123         fds.add(fieldDescriptor);
124         fields.put(row, fds);
125     }
126     
127     public void setFields(Map<Integer, List<MultiplicityFieldConfiguration>> fields) {
128         if (fields == null || fields.isEmpty()) {
129             row = 0;
130         } else {
131             row = fields.size() - 1;
132         }
133         this.fields = fields;
134     }
135 
136 	/**
137 	 * Concatenates multiple field values into a single table cell
138      *
139      *  !!!!!  Currently only implemented for MultiplicityTable !!!!!
140      *
141      * The parentField should define a field with a Data value. The fieldKey should resolve to a single value
142 	 * field in that Data value. The binding will iterate the values in the Data object
143 	 * and concatenate the named fieldKeys into the same cell in the current row of the table
144 	 * separated by commas.
145      *
146      * e.g. parentFD = "course/versions", fieldKey = "versionCode", version codes will be concatenated to
147      * display "code1, code2, code3" in a single table cell
148 	 *
149 	 * @param fieldKey
150 	 */
151 	public void addConcatenatedField(MultiplicityFieldConfiguration parentField, String fieldKey){
152 		addFieldConfiguration(parentField);
153 		concatenatedFields.put(parentField.getFieldPath(), fieldKey);
154 	}
155 
156     /**
157      * Creates a copy of this MultiplicityConfiguration. Used when creating a new MultiplicityItem
158      *
159      * @return  MultiplicityConfiguration
160      */
161     public MultiplicityConfiguration copy() {
162         MultiplicityConfiguration copy = GWT.create(MultiplicityConfiguration.class);
163         copy.init(getMultiplicityType(), getStyleType(), getMetaData());
164         copy.setAddItemLabel(getAddItemLabel());
165         copy.setItemLabel(getItemLabel());
166         copy.setMetaData(getMetaData());
167         copy.setUpdateable(isUpdateable());
168         copy.setDefaultItemsCreated(getDefaultItemsCreated());
169         if (getNestedConfig() != null) {
170             copy.setNestedConfig(getNestedConfig().copy());
171         }
172         FieldDescriptor parent = new FieldDescriptor(getParentFd().getFieldKey(), getParentFd().getMessageKey(), getParentFd().getMetadata());
173         if(!getParentFd().isLabelShown()){
174         	parent.hideLabel();
175         }
176         copy.setParent(parent);
177         for (Map.Entry<Integer, List<MultiplicityFieldConfiguration>> entry: getFields().entrySet()) {
178             List<MultiplicityFieldConfiguration> fields = entry.getValue();
179             for (MultiplicityFieldConfiguration fieldConfig : fields) {
180                 MultiplicityFieldConfiguration newfieldConfig = new MultiplicityFieldConfiguration(fieldConfig.getFieldPath(), fieldConfig.getMessageKeyInfo(), fieldConfig.getMetadata(), fieldConfig.getFieldWidgetInitializer());
181                 newfieldConfig.setOptional(fieldConfig.isOptional());
182                 newfieldConfig.setModelWidgetBinding(fieldConfig.getModelWidgetBinding());
183                 copy.addFieldConfiguration(newfieldConfig);
184             }
185             copy.nextLine();
186         }
187         return copy;
188     }
189 
190     public MultiplicityType getMultiplicityType() {
191         return multiplicityType;
192     }
193 
194      /**
195       * Sets the MultiplicityType required for this config
196       *
197       * Valid values are defined in {@link #MultiplicityConfiguration.MultiplicityType}
198       * @param multiplicityType
199       */
200      public void setMultiplicityType(MultiplicityType multiplicityType) {
201         this.multiplicityType = multiplicityType;
202     }
203 
204 	public FieldDescriptor getParentFd() {
205 		return parentFd;
206 	}
207 
208     /**
209      * The parent fd defines the high level parent field that contains the repeating elements
210      *
211      * @param parentFd
212      */
213     public void setParent(FieldDescriptor parentFd) {
214 		this.parentFd = parentFd;
215 	}
216 
217 //	public Map<Integer, List<FieldDescriptor>> getFields() {return null;}
218 
219 	public Map<Integer, List<MultiplicityFieldConfiguration>> getFields() {
220 		return fields;
221 	}
222 
223     public String getItemLabel() {
224         return itemLabel;
225     }
226 
227     /**
228      * Sets text to be used as the header for each multiplicity item
229      * @param itemLabel
230      */
231     public void setItemLabel(String itemLabel) {
232         this.itemLabel = itemLabel;
233     }
234 
235     public String getAddItemLabel() {
236         return addItemLabel;
237     }
238 
239     /**
240      * Sets text to be used as the button text to add a new empty item in the multiplicity
241      * @param addItemLabel
242      */
243     public void setAddItemLabel(String addItemLabel) {
244         this.addItemLabel = addItemLabel;
245     }
246 
247 	public StyleType getStyleType() {
248 		return styleType;
249 	}
250 
251 	public void setStyleType(StyleType styleType) {
252 		this.styleType = styleType;
253 	}
254 
255 	public MultiplicityConfiguration getNestedConfig() {
256 		return nestedConfig;
257 	}
258 
259     /**
260      * If this multiplicity is to contain other nested multiplicities, create a MultiplicityConfiguration for the child
261      * multiplicity and set as a nested config in the parent.
262      *
263      * @param config
264      */
265     public void setNestedConfig(MultiplicityConfiguration config) {
266 		this.nestedConfig = config;
267 	}
268 
269     public boolean isShowHeaders() {
270          return showHeaders;
271     }
272 
273     /**
274      *
275      * @param showHeaders
276      */
277     public void setShowHeaders(boolean showHeaders) {
278         this.showHeaders = showHeaders;
279     }
280 
281 	public Map<String, String> getConcatenatedFields() {
282 		return concatenatedFields;
283 	}
284 
285     public boolean isUpdateable() {
286         return updateable;
287     }
288 
289     /**
290      * Sets if this multiplicity will be updateable or display.
291      * Add delete buttons/links will not be shown for display multiplicities
292      *
293      * @param updateable
294      */
295     public void setUpdateable(boolean updateable) {
296         this.updateable = updateable;
297     }
298 
299     public MultiplicityType getLayoutType() {
300         return multiplicityType;
301     }
302 
303     public void setLayoutType(MultiplicityType multiplicityType) {
304         this.multiplicityType = multiplicityType;
305     }
306 
307 	public Metadata getMetaData() {
308 		return metaData;
309 	}
310 
311 	public void setMetaData(Metadata metaData) {
312 		this.metaData = metaData;
313 	}
314 
315 
316      public SectionTitle getTitle() {
317         return title;
318     }
319 
320     public void setTitle(SectionTitle title) {
321         this.title = title;
322     }
323 
324     /**
325      * Creates a field descriptor for the parent for this multiplicity
326      * This defines the high level parent field that contains the repeating elements
327      * Will use default widget and binding. For more complex fields create your own
328      * fieldDescriptor and use the other setParent method
329      *
330      * @param fieldKey
331      * @param messageKey
332      * @param parentPath
333      * @param meta
334      */
335     public void setParent(String fieldKey, String messageKey,  String parentPath, Metadata meta) {
336 
337         QueryPath path = QueryPath.concat(parentPath, fieldKey);
338         FieldDescriptor fd = new FieldDescriptor(path.toString(), new MessageKeyInfo(messageKey), meta);
339         fd.getFieldElement().setRequired(false);
340 
341         setParent(fd);
342 
343     }
344 
345     /**
346      * Includes this field on the current line at the next horizontal position
347      *
348      * Will use default widget and binding. For more complex fields create your own
349      * FieldDescriptor and pass it in using the other addField method
350      *
351      * @param fieldKey
352      * @param messageKey
353      * @param parentPath
354      * @param meta
355      */
356     public void addField(String fieldKey, String messageKey,  String parentPath, Metadata meta) {
357 
358         QueryPath path = QueryPath.concat(parentPath, QueryPath.getWildCard(), fieldKey);
359 
360         MultiplicityFieldConfiguration fieldConfig = new MultiplicityFieldConfiguration(path.toString(), new MessageKeyInfo(messageKey), meta, null);
361         fieldConfig.setRequired(false);
362         addFieldConfiguration(fieldConfig);
363     }
364 
365 //    private String translatePath(String path, Metadata metadata) {
366 //
367 //        String result = path;
368 //
369 //        QueryPath qPath = QueryPath.parse(path);
370 //
371 //        if(metadata!=null&&metadata.getInitialLookup()!=null){
372 //            QueryPath translationPath = qPath.subPath(0, qPath.size()-1);
373 //            if (metadata.getDataType().equals(Data.DataType.STRING)) {
374 //                translationPath.add(new Data.StringKey("_runtimeData"));
375 //                translationPath.add(new Data.StringKey((String)qPath.get(qPath.size() - 1).get()));
376 //                translationPath.add(new Data.StringKey("id-translation"));
377 //                result = translationPath.toString();
378 //            }
379 //        }
380 //        return result;
381 //    }
382 
383     public MultiplicityGroup getCustomMultiplicityGroup() {
384         return customMultiplicityGroup;
385     }
386 
387     public void setCustomMultiplicityGroup(MultiplicityGroup customMultiplicityGroup) {
388         this.customMultiplicityGroup = customMultiplicityGroup;
389     }
390 
391 	public void setDefaultItemsCreated(int defaultItemsCreated) {
392 		this.defaultItemsCreated = defaultItemsCreated;
393 	}
394 
395 	public int getDefaultItemsCreated() {
396 		return defaultItemsCreated;
397 	}
398     
399     
400 }
401