KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > faces > samples > carstore > CarBean


1 /*
2  * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or
5  * without modification, are permitted provided that the following
6  * conditions are met:
7  *
8  * - Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *
11  * - Redistribution in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following
13  * disclaimer in the documentation and/or other materials
14  * provided with the distribution.
15  *
16  * Neither the name of Sun Microsystems, Inc. or the names of
17  * contributors may be used to endorse or promote products derived
18  * from this software without specific prior written permission.
19  *
20  * This software is provided "AS IS," without a warranty of any
21  * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
22  * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
24  * EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY
25  * DAMAGES OR LIABILITIES SUFFERED BY LICENSEE AS A RESULT OF OR
26  * RELATING TO USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE OR
27  * ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE
28  * FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT,
29  * SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
30  * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF
31  * THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS
32  * BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
33  *
34  * You acknowledge that this software is not designed, licensed or
35  * intended for use in the design, construction, operation or
36  * maintenance of any nuclear facility.
37  */

38
39
40 package org.apache.cocoon.faces.samples.carstore;
41
42 import org.apache.commons.logging.Log;
43 import org.apache.commons.logging.LogFactory;
44
45 import javax.faces.application.Application;
46 import javax.faces.application.FacesMessage;
47 import javax.faces.component.UIComponent;
48 import javax.faces.component.UIInput;
49 import javax.faces.component.UISelectItems;
50 import javax.faces.component.ValueHolder;
51 import javax.faces.context.FacesContext;
52 import javax.faces.convert.Converter;
53 import javax.faces.model.SelectItem;
54
55 import java.util.ArrayList JavaDoc;
56 import java.util.Enumeration JavaDoc;
57 import java.util.HashMap JavaDoc;
58 import java.util.Iterator JavaDoc;
59 import java.util.Map JavaDoc;
60 import java.util.MissingResourceException JavaDoc;
61 import java.util.ResourceBundle JavaDoc;
62 import java.util.StringTokenizer JavaDoc;
63
64 /**
65  * <p>This bean encapsulates a car model, including pricing and package
66  * choices. The system allows the user to customize the properties of
67  * this bean with the help of the {@link CarCustomizer}.</p>
68  *
69  * <h3>Data Access</h3>
70  *
71  * <p>This is the only bean in the system that has complicated access to
72  * the persistent store of data. In the present implementation, this
73  * persistent store is in <code>ResourceBundle</code> instances.</p>
74  *
75  * <p>There are three data source <code>ResourceBundle</code> files
76  * used:</p>
77  *
78  * <ol>
79  *
80  * <li><p><code>&lt;ModelName&gt;</code></p>
81  *
82  * <p>This contains the localized content for this model. There
83  * is a variant of this file for each supported locale, for
84  * example, <code>Jalopy_de.properties</code></p>
85  *
86  * </li>
87  *
88  * <li><p><code>&lt;Common_properties&gt;</code></p>
89  *
90  * <p>This contains the localized content common to all
91  * models.</p>
92  *
93  * </li>
94  *
95  * <li><p><code>&lt;ModelName_options&gt;</code></p>
96  *
97  * <p>This contains the non-localized content for this model,
98  * including the non-localized options. There is only one
99  * variant of this file for all locales for example,
100  * <code>Jalopy_options.properties</code></p>
101  *
102  * </li>
103  *
104  * </ol>
105  *
106  * <p>All files conform to the following convention:</p>
107  *
108  * <code><pre>
109  * key
110  * key_componentType
111  * key_valueType
112  * </pre></code>
113  *
114  * <p>Where <code>key</code> is the name of an attribute of this car.
115  * For example, <code>basePrice</code>, or <code>description</code>.
116  * <code>key_componentType</code> is the component type of the
117  * <code>UIComponent</code> subclass to be used to represent this
118  * attribute in the page, for example <code>SelectManyMenu</code>.
119  * <code>key_valueType</code> is the data type of the value of the
120  * <code>UIComponent</code>, for example <code>java.lang.Integer</code>.
121  * For all non-String valueTypes.</p>
122  *
123  * <p>When the bean is instantiated, we load both of the above
124  * properties files and iterate over the keys in each one. For each
125  * key, we look at the <code>componentType</code> and ask the
126  * <code>Application</code> to create a component of that type. We
127  * store that <code>UIComponent</code> instance in our
128  * <code>components</code> <code>Map</code> under the name
129  * <code>key</code>. We look at the <code>valueType</code> for the
130  * <code>key</code>. For non <code>java.lang.String</code> types, we
131  * ask the <code>Application</code> for a <code>Converter</code>
132  * instance for that class. If found, we use it to convert the value
133  * for the <code>key</code> to the appropriate type and store that as
134  * the <code>value</code> of the <code>UIComponent</code> instance.</p>
135  */

136
137 public class CarBean extends Object JavaDoc {
138
139     protected static final Log log = LogFactory.getLog(CarBean.class);
140
141     /**
142      * <p>The message identifier of the Message to be created if
143      * the conversion fails. The message format string for this
144      * message may optionally include a <code>{0}</code>
145      * placeholder, which will be replaced by the object and value.</p>
146      */

147     public static final String JavaDoc CONVERTER_ERROR_MESSAGE_ID =
148         "carstore.Converter_Error";
149
150
151     //
152
// Relationship Instance Variables
153
//
154

155     /**
156      * Localized labels
157      */

158
159     private ResourceBundle JavaDoc resources = null;
160
161     /**
162      * Price data
163      */

164     private ResourceBundle JavaDoc priceData = null;
165
166     /**
167      * Keys: String attribute name, such as engine. Values: UIComponent
168      * for the attribute
169      */

170
171     private Map JavaDoc components = null;
172
173     /**
174      * Keys: String attribute name, such as engine. Values: String value
175      * of the component named by key in our components Map.
176      */

177
178     private Map JavaDoc attributes = null;
179
180     //
181
// Constructors
182
//
183

184     public CarBean() {
185         this.init(CarStore.DEFAULT_MODEL_PROPERTIES);
186     }
187
188
189     public CarBean(String JavaDoc bundleName) {
190         this.init(bundleName);
191     }
192
193
194     /**
195      * <p>Initialize our components <code>Map</code> as described in the
196      * class documentation.</p>
197      *
198      * <p>Create a wrapper <code>Map</code> around the components
199      * <code>Map</code> that exposes the String converted value of each
200      * component.</p>
201      */

202
203     private void init(String JavaDoc bundleName) {
204         FacesContext context = FacesContext.getCurrentInstance();
205         ResourceBundle JavaDoc data = null;
206         Enumeration JavaDoc keys = null;
207         components = new HashMap JavaDoc();
208
209         // load the labels
210
resources =
211             ResourceBundle.getBundle(CarStore.CARSTORE_PREFIX +
212                                      ".bundles.Resources",
213                                      context.getViewRoot().getLocale());
214
215         // load the prices
216
priceData = ResourceBundle.getBundle(CarStore.CARSTORE_PREFIX +
217                                              ".bundles.OptionPrices");
218
219         // populate the locale-specific information
220
if (log.isDebugEnabled()) {
221             log.debug("Loading bundle: " + bundleName + ".");
222         }
223         data = ResourceBundle.getBundle(bundleName,
224                                         context.getViewRoot().getLocale());
225         if (log.isDebugEnabled()) {
226             log.debug("Bundle " + bundleName +
227                       " loaded. Reading properties...");
228         }
229         initComponentsFromProperties(context, data);
230         if (log.isDebugEnabled()) {
231             log.debug("done.");
232         }
233
234         // populate the non-locale-specific information common to all cars
235
if (log.isDebugEnabled()) {
236             log.debug("Loading bundle: Common_options.");
237         }
238         data = ResourceBundle.getBundle(CarStore.CARSTORE_PREFIX +
239                                         ".bundles.Common_options");
240         if (log.isDebugEnabled()) {
241             log.debug("Bundle Common_options loaded. Reading properties...");
242         }
243         initComponentsFromProperties(context, data);
244         if (log.isDebugEnabled()) {
245             log.debug("done.");
246         }
247
248         // populate the non-locale-specific information specific to each car
249
if (log.isDebugEnabled()) {
250             log.debug("Loading bundle: " + bundleName + "_options.");
251         }
252         data = ResourceBundle.getBundle(bundleName + "_options");
253         if (log.isDebugEnabled()) {
254             log.debug("Bundle " + bundleName +
255                       "_options loaded. Reading properties...");
256         }
257         initComponentsFromProperties(context, data);
258         if (log.isDebugEnabled()) {
259             log.debug("done.");
260         }
261
262         // create a read-only Map exposing the values of all of our
263
// components.
264
attributes =
265             new Map JavaDoc() {
266                 public void clear() {
267                     CarBean.this.components.clear();
268                 }
269
270
271                 public boolean containsKey(Object JavaDoc key) {
272                     return CarBean.this.components.containsKey(key);
273                 }
274
275
276                 public boolean containsValue(Object JavaDoc value) {
277                     throw new UnsupportedOperationException JavaDoc();
278                 }
279
280
281                 public java.util.Set JavaDoc entrySet() {
282                     throw new UnsupportedOperationException JavaDoc();
283                 }
284
285
286                 public boolean equals(Object JavaDoc o) {
287                     throw new UnsupportedOperationException JavaDoc();
288                 }
289
290
291                 public Object JavaDoc get(Object JavaDoc key) {
292                     UIComponent component = null;
293                     Converter converter = null;
294                     Object JavaDoc result = null;
295                     if (null == key) {
296                         return null;
297                     }
298                     if (null != (component = (UIComponent)
299                         CarBean.this.components.get(key))) {
300                         // if this component can have a Converter
301
if (component instanceof ValueHolder) {
302                             // try to get it
303
converter = ((ValueHolder) component).
304                                 getConverter();
305                             result = ((ValueHolder) component).getValue();
306                         }
307
308                         // if we do have a value
309
if (null != result) {
310                             // and we do have a converter
311
if (null != converter) {
312                                 // convert the value to String
313
result = converter.
314                                     getAsString(FacesContext.
315                                                 getCurrentInstance(),
316                                                 component, result);
317                             }
318                         }
319                     }
320                     return result;
321                 }
322
323
324                 public int hashCode() {
325                     return CarBean.this.components.hashCode();
326                 }
327
328
329                 public boolean isEmpty() {
330                     return CarBean.this.components.isEmpty();
331                 }
332
333
334                 public java.util.Set JavaDoc keySet() {
335                     return CarBean.this.components.keySet();
336                 }
337
338
339                 public Object JavaDoc put(Object JavaDoc k, Object JavaDoc v) {
340                     throw new UnsupportedOperationException JavaDoc();
341                 }
342
343
344                 public void putAll(Map JavaDoc t) {
345                     throw new UnsupportedOperationException JavaDoc();
346                 }
347
348
349                 public Object JavaDoc remove(Object JavaDoc k) {
350                     throw new UnsupportedOperationException JavaDoc();
351                 }
352
353
354                 public int size() {
355                     return CarBean.this.components.size();
356                 }
357
358
359                 public java.util.Collection JavaDoc values() {
360                     ArrayList JavaDoc result = new ArrayList JavaDoc();
361                     Iterator JavaDoc keys = keySet().iterator();
362                     while (keys.hasNext()) {
363                         result.add(get(keys.next()));
364                     }
365                     return result;
366                 }
367             };
368
369     }
370
371
372     /**
373      * <p>For each entry in data, create component and cause it to be
374      * populated with values.</p>
375      */

376
377     private void initComponentsFromProperties(FacesContext context,
378                                               ResourceBundle JavaDoc data) {
379         Application application = context.getApplication();
380         Enumeration JavaDoc keys = data.getKeys();
381         String JavaDoc
382             key = null,
383             value = null,
384             componentType = null,
385             valueType = null;
386         UIComponent component = null;
387
388         // populate the map
389
while (keys.hasMoreElements()) {
390             key = (String JavaDoc) keys.nextElement();
391             if (key == null) {
392                 continue;
393             }
394             // skip secondary keys.
395
if (-1 != key.indexOf("_")) {
396                 continue;
397             }
398             value = data.getString(key);
399             componentType = data.getString(key + "_componentType");
400             valueType = data.getString(key + "_valueType");
401             if (log.isDebugEnabled()) {
402                 log.debug("populating map for " + key + "\n" +
403                           "\n\tvalue: " + value +
404                           "\n\tcomponentType: " + componentType +
405                           "\n\tvalueType: " + valueType);
406             }
407             // create the component for this componentType
408
component = application.createComponent(componentType);
409             populateComponentWithValue(context, component, componentType,
410                                        value, valueType);
411             components.put(key, component);
412         }
413     }
414
415
416     /**
417      * <p>populate the argument component with values, being sensitive
418      * to the possible multi-nature of the values, and to the type of
419      * the values.</p>
420      */

421
422     private void populateComponentWithValue(FacesContext context,
423                                             UIComponent component,
424                                             String JavaDoc componentType,
425                                             String JavaDoc value, String JavaDoc valueType) {
426         Application application = context.getApplication();
427         Converter converter = null;
428         UISelectItems items = null;
429
430         // if we need a converter, and can have a converter
431
if (!valueType.equals("java.lang.String") &&
432             component instanceof ValueHolder) {
433             // if so create it,
434
try {
435                 converter =
436                     application.createConverter(CarStore.loadClass(valueType,
437                                                                    this));
438             } catch (ClassNotFoundException JavaDoc cne) {
439                 FacesMessage errMsg = MessageFactory.getMessage(
440                     CONVERTER_ERROR_MESSAGE_ID,
441                     (new Object JavaDoc[]{valueType}));
442                 throw new IllegalStateException JavaDoc(errMsg.getSummary());
443             }
444             // add it to our component,
445
((ValueHolder) component).setConverter(converter);
446         }
447
448         // if this component is a SelectOne or SelectMany, take special action
449
if (isMultiValue(componentType)) {
450             // create a UISelectItems instance
451
items = new UISelectItems();
452             items.setValue(parseStringIntoArrayList(context, component,
453                                                     value, valueType,
454                                                     converter));
455             // add it to the component
456
component.getChildren().add(items);
457         } else {
458             // we have a single value
459
if (null != converter) {
460                 component.getAttributes().put("value",
461                                               converter.getAsObject(context,
462                                                                     component,
463                                                                     value));
464             } else {
465                 component.getAttributes().put("value", value);
466             }
467         }
468     }
469
470
471     /**
472      * @return true if componentType starts with SelectMany or SelectOne
473      */

474     private boolean isMultiValue(String JavaDoc componentType) {
475         if (null == componentType) {
476             return false;
477         }
478         return (componentType.startsWith("javax.faces.SelectMany") ||
479             componentType.startsWith("javax.faces.SelectOne"));
480     }
481
482
483     /**
484      * Tokenizes the passed in String which is a comma separated string of
485      * option values that serve as keys into the main resources file.
486      * For example, optionStr could be "Disc,Drum", which corresponds to
487      * brake options available for the chosen car. This String is tokenized
488      * and used as key into the main resource file to get the localized option
489      * values and stored in the passed in ArrayList.
490      */

491     public ArrayList JavaDoc parseStringIntoArrayList(FacesContext context,
492                                               UIComponent component,
493                                               String JavaDoc value,
494                                               String JavaDoc valueType,
495                                               Converter converter) {
496         ArrayList JavaDoc optionsList = null;
497         int i = 0;
498         Object JavaDoc optionValue = null;
499         String JavaDoc
500             optionKey = null,
501             optionLabel = null;
502         Map JavaDoc nonLocalizedOptionValues = null;
503
504         if (value == null) {
505             return null;
506         }
507         StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(value, ",");
508         optionsList = new ArrayList JavaDoc((st.countTokens()) + 1);
509         while (st.hasMoreTokens()) {
510             optionKey = st.nextToken();
511             try {
512                 optionLabel = resources.getString(optionKey);
513             } catch (MissingResourceException JavaDoc e) {
514                 // if we can't find a hit, the key is the label
515
optionLabel = optionKey;
516             }
517
518             if (null != converter) {
519                 // PENDING deal with the converter case
520
} else {
521                 optionsList.add(new SelectItem(optionKey, optionLabel));
522             }
523         }
524         return optionsList;
525     }
526
527
528     public String JavaDoc updatePricing() {
529         getCurrentPrice();
530         return null;
531     }
532
533
534     public Integer JavaDoc getCurrentPrice() {
535         // go through our options and try to get the prices
536
int sum = ((Integer JavaDoc) ((ValueHolder) getComponents().get("basePrice")).
537             getValue()).intValue();
538         Iterator JavaDoc iter = getComponents().keySet().iterator();
539         String JavaDoc key = null;
540         Object JavaDoc value = null;
541         UIComponent component = null;
542         while (iter.hasNext()) {
543             key = (String JavaDoc) iter.next();
544             component = (UIComponent) getComponents().get(key);
545             value = component.getAttributes().get("value");
546             if (null == value || (!(component instanceof UIInput))) {
547                 continue;
548             }
549
550             // if the value is a String, see if we have priceData for it
551
if (value instanceof String JavaDoc) {
552                 try {
553                     sum +=
554                         Integer.valueOf(priceData.getString((String JavaDoc) value))
555                         .intValue();
556                 } catch (NumberFormatException JavaDoc e) {
557                 }
558             }
559             // if the value is a Boolean, look up the price by name
560
else if (value instanceof Boolean JavaDoc &&
561                 ((Boolean JavaDoc) value).booleanValue()) {
562                 try {
563                     sum +=
564                         Integer.valueOf(priceData.getString(key)).intValue();
565                 } catch (NumberFormatException JavaDoc e) {
566                 }
567             } else if (value instanceof Number JavaDoc) {
568                 sum += ((Number JavaDoc) value).intValue();
569             }
570         }
571         Integer JavaDoc result = new Integer JavaDoc(sum);
572         // store the new price into the component for currentPrice
573
((ValueHolder) getComponents().get("currentPrice")).
574             setValue(result);
575         return result;
576     }
577
578
579     public Map JavaDoc getComponents() {
580         return components;
581     }
582
583
584     public Map JavaDoc getAttributes() {
585         return attributes;
586     }
587
588
589 }
590
Popular Tags