001: /* ====================================================================
002: * The Jcorporate Apache Style Software License, Version 1.2 05-07-2002
003: *
004: * Copyright (c) 1995-2002 Jcorporate Ltd. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * 3. The end-user documentation included with the redistribution,
019: * if any, must include the following acknowledgment:
020: * "This product includes software developed by Jcorporate Ltd.
021: * (http://www.jcorporate.com/)."
022: * Alternately, this acknowledgment may appear in the software itself,
023: * if and wherever such third-party acknowledgments normally appear.
024: *
025: * 4. "Jcorporate" and product names such as "Expresso" must
026: * not be used to endorse or promote products derived from this
027: * software without prior written permission. For written permission,
028: * please contact info@jcorporate.com.
029: *
030: * 5. Products derived from this software may not be called "Expresso",
031: * or other Jcorporate product names; nor may "Expresso" or other
032: * Jcorporate product names appear in their name, without prior
033: * written permission of Jcorporate Ltd.
034: *
035: * 6. No product derived from this software may compete in the same
036: * market space, i.e. framework, without prior written permission
037: * of Jcorporate Ltd. For written permission, please contact
038: * partners@jcorporate.com.
039: *
040: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
041: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
042: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
043: * DISCLAIMED. IN NO EVENT SHALL JCORPORATE LTD OR ITS CONTRIBUTORS
044: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
045: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
046: * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
047: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
048: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
049: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
050: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
051: * SUCH DAMAGE.
052: * ====================================================================
053: *
054: * This software consists of voluntary contributions made by many
055: * individuals on behalf of the Jcorporate Ltd. Contributions back
056: * to the project(s) are encouraged when you make modifications.
057: * Please send them to support@jcorporate.com. For more information
058: * on Jcorporate Ltd. and its products, please see
059: * <http://www.jcorporate.com/>.
060: *
061: * Portions of this software are based upon other open source
062: * products and are subject to their respective licenses.
063: */
064:
065: /**
066: * Copyright 1999, 2000, 2001 Jcorporate Ltd.
067: */package com.jcorporate.expresso.core.controller;
068:
069: import com.jcorporate.expresso.core.cache.Cacheable;
070: import com.jcorporate.expresso.core.dbobj.ValidValue;
071: import com.jcorporate.expresso.core.misc.ConfigManager;
072: import com.jcorporate.expresso.core.misc.StringUtil;
073: import com.jcorporate.expresso.kernel.util.FastStringBuffer;
074: import org.w3c.dom.NamedNodeMap;
075: import org.w3c.dom.Node;
076: import org.w3c.dom.NodeList;
077:
078: import java.util.ArrayList;
079: import java.util.Enumeration;
080: import java.util.Iterator;
081: import java.util.List;
082: import java.util.Vector;
083:
084: /**
085: * An Input object is one of the three types of objects generated by a
086: * Controller when it transitions from one state to another. The other two
087: * are Actions and Outputs. <br>
088: * An Input is a request for information from the user. It may or may
089: * not have a default value, a list of valid values, and other
090: * attributes - any or all of which can be used by the user interface,
091: * which can be a JSP, Servlet, application, or even an applet.<br />
092: * <p/>
093: * <h4>Recognized Types:</h4>
094: * <p>The following types are recognized by the expresso framework and
095: * automatically rendered: You may add your own types or ignore them
096: * if you are doing your own page rendering.</p>
097: * <p/>
098: * <p>checkbox: Renders as a checkbox. If multivalued=true then will render
099: * the options as a list of checkboxes.</p>
100: * <p/>
101: * <p>radio: If multivalued, will render all options as a list of radio buttons
102: * </p>
103: * <p>checkbox-vertical: Only relevent for multivalues, renders the checkbox
104: * style as vertical</p>
105: */
106: public class Input extends ControllerElement implements Cloneable,
107: Cacheable {
108:
109: /**
110: * constant for JSTL attribute for 'selectness' of a given input
111: */
112: public static final String SELECTED = "selected";
113:
114: /**
115: * Default value(s) for the item (optional)
116: */
117: private ArrayList defaultValue = null;
118:
119: /**
120: * Vector of value/description pairs that are valid for this item
121: */
122: private Vector validValues = null;
123:
124: /**
125: * Object name that can be used to look up valid values for
126: * this item
127: */
128: private String lookup = null;
129:
130: /**
131: * Max length of the input field
132: */
133: private int maxLength = 80;
134:
135: /** @todo add this variable for multi valued selects to work *RD* Mon Jul 27 2004 */
136: /**
137: * Multiple select true or false
138: */
139: private String multiple = "false";
140:
141: /**
142: * Key used when this input is used as a Cacheable object
143: */
144: private String key = null;
145:
146: /**
147: * The Following are known Input attributes
148: */
149:
150: /**
151: * The Input is intended to be rendered as a List Box
152: */
153: public static final String ATTRIBUTE_LISTBOX = "listbox";
154:
155: /**
156: * The Input is intended to be rendered as a drop down box
157: */
158: public static final String ATTRIBUTE_DROPDOWN = "dropdown";
159:
160: /**
161: * The Input is intended to be rendered as a check box
162: */
163: public static final String ATTRIBUTE_CHECKBOX = "checkbox";
164:
165: /**
166: * The Input is intended to be rendered as a vertically aligned checkbox
167: */
168: public static final String ATTRIBUTE_CHECKBOX_VERTICAL = "checkbox-vertical";
169:
170: /**
171: * The Input is intended to be rendered as a Radio Button
172: */
173: public static final String ATTRIBUTE_RADIO = "radio";
174:
175: /**
176: * The Input is intended to be rendered as a Vertically Aligned Radio Button
177: */
178: public static final String ATTRIBUTE_RADIO_VERTICAL = "radio-vertical";
179:
180: /**
181: * The Input is intended to be rendered as a Text Area
182: */
183: public static final String ATTRIBUTE_TEXTAREA = "textarea";
184:
185: /**
186: * The Input is intended to be rendered as a Hidden Field
187: */
188: public static final String ATTRIBUTE_HIDDEN = "hidden";
189:
190: /**
191: * The Input is intended to be read only.
192: */
193: public static final String ATTRIBUTE_READONLY = "readOnly";
194:
195: /**
196: * The Input is indended to be a file upload box.
197: */
198: public static final String ATTRIBUTE_FILE = "fileBox";
199:
200: /**
201: * Attribute for password boxes.
202: */
203: public static final String ATTRIBUTE_PASSWORD = "password";
204:
205: /**
206: * Attribute that contains the original value of this input. Usually set
207: * for read only inputs.
208: */
209: public static final String ATTRIBUTE_ORIGINAL_VALUE = "originalValue";
210:
211: /**
212: * Defines the CSS style to use when rendering the input.
213: */
214: public static final String ATTRIBUTE_CSS_STYLE = "styleClass";
215:
216: /**
217: * Means that the Input will be some sort of multi-valued field and
218: * has valid values associated with it.
219: */
220: public static final String ATTRIBUTE_MULTIVALUED = "multiValued";
221:
222: /**
223: * text input, single line (as opposed to text area)
224: */
225: public static final String ATTRIBUTE_TEXTLINE = "text";
226:
227: /**
228: * Attribute defines the HTML Type that will be used with the Input
229: */
230: public static final String ATTRIBUTE_TYPE = "type";
231:
232: /**
233: * Default constructor
234: */
235: public Input() {
236: super ();
237: setType(null);
238: } /* Input() */
239:
240: /**
241: * Convenience constructor - to make a new Input object
242: * with a specific name
243: *
244: * @param newName the new input name
245: */
246: public Input(String newName) {
247: super ();
248: setName(newName);
249: setType(null);
250: } /* Input(String) */
251:
252: /**
253: * Constructor that sets the name and the label of the input
254: *
255: * @param newName the name of the Input
256: * @param newLabel the label of the input
257: */
258: public Input(String newName, String newLabel) {
259: super ();
260: setName(newName);
261: setLabel(newLabel);
262: setType(null);
263: }
264:
265: /**
266: * Clones the input as per standard java.lang.Object specifications
267: *
268: * @return java.lang.Object which is actually an Input.
269: */
270: public Object clone() throws CloneNotSupportedException {
271: Input i;
272:
273: synchronized (this ) { // this sync means nothing since setter methods are not sync'd, but inputs are generally not handled by more than one thread at a time
274: i = (Input) super .clone();
275: if (defaultValue != null) {
276: i.defaultValue = (ArrayList) defaultValue.clone();
277: }
278: i.maxLength = maxLength;
279: i.lookup = (lookup);
280: i.key = key;
281: if (validValues != null) {
282: i.validValues = (Vector) validValues.clone();
283: }
284: if (getAttributes().size() > 0) {
285: i.getAttributes().putAll(getAttributes());
286: }
287: }
288:
289: return i;
290: }
291:
292: /**
293: * Return a single default value supplied by the Controller for this
294: * Input object; returns the 0th item in list (we expect this function
295: * to be used only for single selection items)
296: *
297: * @return A String to be used as the default value for this Input; empty string if no default has been set
298: * @see #getDefaultValueList for multiple selections
299: */
300: public String getDefaultValue() {
301: String result = "";
302: if (defaultValue != null) {
303: result = (String) defaultValue.get(0);
304: }
305:
306: return result;
307: } /* getDefaultValue() */
308:
309: /**
310: * (Convenience for JSTL access, to be parallel with Output)
311: * Return a single default value supplied by the Controller for this
312: * Input object; returns the 0th item in list (we expect this function
313: * to be used only for single selection items)
314: *
315: * @return A String to be used as the default value for this Input; empty string if no default has been set
316: * @see #getDefaultValueList for multiple selections
317: */
318: public String getContent() {
319: return getDefaultValue();
320: }
321:
322: /**
323: * Return a list of default values supplied by the Controller for this
324: * Input object; appropriate for multiple selection items
325: *
326: * @return A list of strings to be used as the default values for this Input; never null, though list can be empty
327: * @see #getDefaultValue for single selections
328: */
329: public ArrayList getDefaultValueList() {
330: if (defaultValue == null) {
331: defaultValue = new ArrayList();
332: }
333:
334: return (ArrayList) defaultValue.clone();
335: }
336:
337: /**
338: * Returns the class name of a database object that can be used to
339: * look up valid values for this Input item, if there is one.
340: *
341: * @return java.lang.String the class name of the lookup database object
342: */
343: public String getLookup() {
344: return lookup;
345: } /* getLookup() */
346:
347: /**
348: * Return the maximum recommended length of the value for this
349: * Input object.
350: *
351: * @return The maximum number of characters that should be supplied
352: * * as a value to this Input object
353: */
354: public int getMaxLength() {
355: return maxLength;
356: } /* getMaxLength() */
357:
358: /**
359: * Concert the object to an xml fragment.
360: *
361: * @param stream A FastStringBuffer to append the data to.
362: * @return FastStringBuffer( usually the same one passed in)
363: * @todo adapt to support multiple default values
364: */
365: public FastStringBuffer toXML(FastStringBuffer stream) {
366: Vector vv = this .getValidValues();
367:
368: if (vv == null) {
369: vv = new Vector();
370: }
371:
372: stream.append("<input");
373:
374: if (this .getName() != null && this .getName().length() > 0) {
375: stream.append(" name=\""
376: + StringUtil.xmlEscape(this .getName()) + "\"");
377: }
378: if (vv.size() > 0) {
379: stream.append(" multivalue=\"y\"");
380: }
381: if (defaultValue != null && defaultValue.size() > 0) {
382: stream.append(" default-value=\""
383: + StringUtil.xmlEscape(this .getDefaultValue())
384: + "\"");
385: }
386: if (this .getMaxLength() > 0) {
387: stream
388: .append(" max-length=\"" + this .getMaxLength()
389: + "\"");
390: }
391: if (!StringUtil.notNull(this .getLookup()).equals("")) {
392: stream.append(" lookup-object=\""
393: + StringUtil.xmlEscape(this .getLookup()) + "\"");
394: }
395: if (this .getType() != null && this .getType().length() > 0) {
396: stream.append(" type=\""
397: + StringUtil.xmlEscape(this .getType()) + "\"");
398: }
399:
400: stream.append(">\n");
401:
402: if (vv.size() > 0) {
403: ValidValue oneValue = null;
404: stream.append(" <valid-values>\n");
405:
406: for (Enumeration e = vv.elements(); e.hasMoreElements();) {
407: oneValue = (ValidValue) e.nextElement();
408: stream.append(" <valid-value value=\""
409: + StringUtil.xmlEscape(oneValue.getValue())
410: + "\" description=\""
411: + StringUtil.xmlEscape(oneValue
412: .getDescription()) + "\" />\n");
413: }
414:
415: stream.append(" </valid-values>\n");
416: }
417:
418: //Super deals with nested ControllerElements
419: super .toXML(stream);
420: stream.append("</input>\n");
421:
422: return stream;
423: }
424:
425: /**
426: * Return a controller element based upon the xml fragment [Factory Method]
427: *
428: * @param n A DOM node to assemble this Input from.
429: * @return a constructed ControllerElement [Really an Input]
430: * @throws ControllerException if there was a parsing error. (ie malformed
431: * nodes)
432: * @throws ControllerException upon error
433: */
434: public static ControllerElement fromXML(Node n)
435: throws ControllerException {
436:
437: //If we're at the root node, then it'll be doc instead of input.
438: if (n.getNodeName().equals("#document")) {
439: return fromXML(n.getChildNodes().item(0));
440: }
441: if (!n.getNodeName().equals("input")) {
442: throw new ControllerException("Failed To Get DOM Node of "
443: + " type 'input' Got " + n.getNodeName()
444: + " instead.");
445: }
446:
447: Input i = new Input();
448: NamedNodeMap attributes = n.getAttributes();
449: Node oneAttribute = attributes.getNamedItem("name");
450:
451: if (oneAttribute != null) {
452: i.setName(oneAttribute.getNodeValue());
453: }
454:
455: oneAttribute = attributes.getNamedItem("default-value");
456:
457: if (oneAttribute != null) {
458: i.setDefaultValue(oneAttribute.getNodeValue());
459: }
460:
461: oneAttribute = attributes.getNamedItem("max-length");
462:
463: if (oneAttribute != null) {
464: try {
465: i.maxLength = Integer.parseInt(oneAttribute
466: .getNodeValue());
467: } catch (NumberFormatException nfe) {
468: }
469: }
470:
471: oneAttribute = attributes.getNamedItem("lookup-object");
472:
473: if (oneAttribute != null) {
474: i.lookup = oneAttribute.getNodeValue();
475: }
476:
477: oneAttribute = attributes.getNamedItem("type");
478:
479: if (oneAttribute != null) {
480: i.setType(oneAttribute.getNodeValue());
481: }
482:
483: NodeList children = n.getChildNodes();
484:
485: for (int index = 0; index < children.getLength(); index++) {
486: Node oneChild = children.item(index);
487: String nodeName = oneChild.getNodeName();
488:
489: if (nodeName != null) {
490: if (nodeName.equals("valid-values")) {
491: NodeList vv = oneChild.getChildNodes();
492:
493: for (int j = 0; j < vv.getLength(); j++) {
494: Node onevalue = vv.item(j);
495:
496: if (onevalue.getNodeName()
497: .equals("valid-value")) {
498: NamedNodeMap valueAttributes = onevalue
499: .getAttributes();
500:
501: if (valueAttributes != null) {
502: String value;
503: String description;
504: Node attribute = valueAttributes
505: .getNamedItem("value");
506:
507: if (attribute != null) {
508: value = StringUtil
509: .notNull(attribute
510: .getNodeValue());
511: attribute = valueAttributes
512: .getNamedItem("description");
513:
514: if (attribute == null) {
515: description = ("");
516: } else {
517: description = attribute
518: .getNodeValue();
519: }
520:
521: i.addValidValue(value, description);
522: }
523: }
524: }
525: }
526: } else if (nodeName.equals("controller-element")) {
527: i = (Input) ControllerElement.fromXML(oneChild, i);
528: }
529: }
530: }
531:
532: return i;
533: }
534:
535: /**
536: * Return the list of Valid Values that are allowed for this
537: * Input item.
538: *
539: * @return A Vector of value/description pairs that enumerate
540: * * the valid values for this Input item. The user is expected
541: * * to select one of these values, but the presentation of the
542: * * list is up to the client (e.g. could be a drop-down, radio
543: * * buttons, etc). Can be empty, but Never null.
544: */
545: public Vector getValidValues() {
546: if (validValues == null) {
547: return new Vector();
548: }
549: return validValues;
550: }
551:
552: /**
553: * supply a default value for this
554: * Input object; appropriate for single selection items; sets the 0th item in underlying list
555: *
556: * @param newValue The new default value for this Input item
557: * @see #addDefaultValue for multiple selection
558: */
559: public void setDefaultValue(String newValue) {
560: if (newValue == null) {
561: // is this trying to unset a value?
562: if (defaultValue != null) {
563: defaultValue.clear();
564: }
565:
566: return;
567: }
568:
569: if (defaultValue == null) {
570: defaultValue = new ArrayList();
571: }
572:
573: if (defaultValue.size() == 0) {
574: defaultValue.add(newValue);
575: } else {
576: defaultValue.set(0, newValue);
577: }
578: }
579:
580: /**
581: * supply a list of default values for this
582: * Input object; appropriate for multiple selection items
583: *
584: * @param list The new list of default values (strings) for this Input item
585: * @see #addDefaultValue for adding multiple selection defaults one at a time
586: */
587: public void setDefaultValue(List list) {
588: if (list == null) {
589: // is this trying to unset a value?
590: if (defaultValue != null) {
591: defaultValue.clear();
592: }
593:
594: return;
595: }
596:
597: if (defaultValue == null) {
598: defaultValue = new ArrayList(list);
599: } else {
600: defaultValue.clear();
601: defaultValue.addAll(list);
602: }
603: }
604:
605: /**
606: * supply another default value for this
607: * Input object; appropriate for multiple selection items
608: *
609: * @param newValue The new default value for this Input item
610: * @see #setDefaultValue(String) for single selection
611: */
612: public void addDefaultValue(String newValue) {
613: if (newValue == null) {
614: return; // could throw, but seems nicer to just discard
615: }
616:
617: if (defaultValue == null) {
618: defaultValue = new ArrayList();
619: }
620:
621: defaultValue.add(newValue);
622: }
623:
624: /**
625: * Convenience method to quickly set the default value from the form cache.
626: *
627: * @param response The controller response where the form cache may exist
628: * @throws ControllerException upon error
629: */
630: public void setDefaultValue(ControllerResponse response)
631: throws ControllerException {
632: setDefaultValue(response.getFormCache(this .getName()));
633: }
634:
635: /**
636: * Set the name of the "lookup object" - the database object that
637: * the client can use to look up valid values for this
638: * Input item.
639: *
640: * @param s the lookup class name to associate with the Input
641: */
642: public void setLookup(String s) {
643: lookup = s;
644: } /* setLookup(String) */
645:
646: /**
647: * Set the maximum recommended length of the Input object
648: *
649: * @param newMaxLength Specify the max length (in chars) for this
650: * * input item's value.
651: */
652: public void setMaxLength(int newMaxLength) {
653: maxLength = newMaxLength;
654: } /* setMaxLength(int) */
655:
656: /**
657: * Sets the inputs name. Also checks against reserved words
658: *
659: * @param newName the new name for the Input
660: * @throws IllegalArgumentException if the name is a 'reserved word' as
661: * defined by ConfigManager.
662: */
663: public void setName(String newName) {
664: if (ConfigManager.isParameterReservedWord(newName)) {
665: throw new IllegalArgumentException(
666: "You cannot have a input name of "
667: + newName
668: + ". It is a reserved word. Check "
669: + "com.jcorporate.expresso.core.misc.ConfigManager for a full list"
670: + " of reservered words");
671: }
672:
673: super .setName(newName);
674: }
675:
676: /**
677: * Add a valid value to the Input's dropdown
678: *
679: * @param value the 'key' part of the valid value
680: * @param descrip the description part of the valid value.
681: */
682: public synchronized void addValidValue(String value, String descrip) {
683: ValidValue v = new ValidValue(value, descrip);
684:
685: if (validValues == null) {
686: validValues = new Vector();
687: }
688:
689: validValues.addElement(v);
690: }
691:
692: /**
693: * Sets the input type
694: *
695: * @param newType the new type as appears in an Input tag
696: */
697: public void setType(String newType) {
698: if (newType == null) {
699: super .setType("C");
700: } else {
701: super .setType(newType);
702: }
703: }
704:
705: /**
706: * Method for the Controller to specify the valid values for this
707: * Input item
708: *
709: * @param v The value/description pairs that make up the
710: * * valid values for this Input object.
711: */
712: public void setValidValues(Vector v) {
713: validValues = v;
714: } /* setValidValues(Vector) */
715:
716: /**
717: * The Key of the Input
718: *
719: * @param newKey the new value
720: */
721: public synchronized void setKey(String newKey) {
722: key = newKey;
723: } /* setKey(String) */
724:
725: /**
726: * Retrieve the key for the input.
727: *
728: * @return java.lang.String
729: */
730: public String getKey() {
731: return key;
732: } /* getKey() */
733:
734: /** @todo add this two methods for multi-valued fields to work *RD* Mon Jul 27 2004 */
735: /**
736: * Set the the Input object allow multiple selections
737: *
738: * @param newMultiple Specify if true or false the input will accept multiple selects.
739: */
740: public void setMultiple(String newMultiple) {
741: multiple = newMultiple;
742: } /* setMultiple(String) */
743:
744: /**
745: * Retrieve the multi select status.
746: *
747: * @return java.lang.String
748: */
749: public String getMultiple() {
750: return multiple;
751: } /* getMultiple() */
752:
753: /**
754: * get the display string associated with the currently-selected (default).
755: * useful for a JSTL reuse of Input for output purposes, when
756: * the default value is an ID, while its partner ValidValue pairing is the display string.
757: *
758: * @return the display string associated with the currently-selected (default) value, or empty string if none found--never null
759: */
760: public String getSelectedDisplay() {
761: String result = "";
762: String currentIndex = getDefaultValue();
763: if (currentIndex != null && validValues != null) {
764: for (Iterator iterator = validValues.iterator(); iterator
765: .hasNext();) {
766: ValidValue vv = (ValidValue) iterator.next();
767: if (currentIndex.equals(vv.getValue())) {
768: result = vv.getDescription();
769: break;
770: }
771: }
772: }
773:
774: if (result == null) {
775: result = "";
776: }
777: return result;
778: }
779: } /* Input */
|