001    package net.sf.jolene.dom;
002    
003    import net.sf.jolene.constants.Elements;
004    import net.sf.jolene.constants.Tags;
005    import net.sf.jolene.html.Attributes;
006    import org.apache.commons.beanutils.BeanUtils;
007    
008    import java.util.*;
009    
010    /**
011     * A form in a html document. This acts as a container for other html objects.
012     *
013     * @author Dan Howard
014     * @since Oct 21, 2003
015     */
016    public final class Form extends HTMLElement {
017    
018        List<HTMLElement> elements;
019    
020        Form() {
021            tag = Tags.form;
022            elements = new ArrayList<HTMLElement>(16);
023        }
024    
025    
026        @Override
027        public Form clone() {
028            Form form = (Form) super.clone();
029            for (int j = 0; j < elements.size(); j++) {
030                HTMLElement element = elements(j).clone();
031                form.addElement(element);
032            }
033            return form;
034        }
035    
036    
037        /**
038         * This method returns the widget specified by the numeric index.
039         * The prefered way of retrieving an object from the form is to
040         * use it's name or id instead since the order of the objects can be
041         * changed easily by a page designer.
042         *
043         * @param element - The integer element number of the object desired.
044         * @return HTMLElement
045         */
046        public HTMLElement elements(int element) {
047            return elements.get(element);
048        }
049    
050        /**
051         * This method retrieves an object from the document by name or id.
052         * When the document is opened it creates objects by looking at the ID
053         * attribute first. If that attribute doesn't exist it uses the NAME
054         * attribute. If neither exist then the only way to address these
055         * objects is by number (not recommended).<br>
056         * This method returns the base HTMLElement object so if you want a Select or
057         * Grid object you can either cast it:
058         * <pre>
059         * Grid grid grid = (Grid)document.forms(0).elements("Grid1");
060         * </pre>
061         * or use the appropriate getter:
062         * <pre>
063         * Grid grid = document.forms(0).getGrid("Grid1");
064         * </pre>
065         *
066         * @param elementName - The string name or id of the object desired.
067         * @return HTMLElement or null if not found or elementName is null.
068         */
069        public HTMLElement elements(String elementName) {
070    
071            if (elementName == null) {
072                return null;
073            }
074    
075            // locate the object whose name == this string.
076            String name = "";
077            HTMLElement element;
078            int j;
079    
080            for (j = 0; j < elements.size(); j++) {
081    
082                if (elementName.equalsIgnoreCase(elements.get(j).getName())) {
083                    break;
084                }
085    /*
086                if (element.hasAttribute("id")) {
087                    name = element.getAttribute("id");
088                    if (name.equalsIgnoreCase(elementName)) {
089                        break; // found it.
090                    }
091                } else if (element.hasAttribute("name")) {
092                    name = (String) element.getAttribute("name");
093                    if (name.equalsIgnoreCase(elementName)) {
094                        break; // found it.
095                    }
096                } else {
097                    // We have to throw an exception here? We have an object with no NAME!!!!!
098    
099                }
100    */
101    
102            }
103    
104            if (j < elements.size()) {
105                return elements(j);
106            } else {
107                // THROW EXCEPTION
108                // Based on the HashMap, no exception is thrown but a null is returned which
109                // will cause a null pointer exception later.
110                return null;
111            }
112        }
113    
114    
115        /**
116         * @return Number of elements in the form.
117         */
118        public int getElementCount() {
119            return elements.size();
120        }
121    
122        /**
123         * Returns the specified element casted to a Grid object.
124         *
125         * @param name name of grid
126         * @return Grid
127         */
128        public Grid getGrid(String name) {
129            return (Grid) elements(name);
130        }
131    
132        /**
133         * Returns a Map of Radio objects based the specified radio group name.
134         * The map's order is insertion-order.
135         *
136         * @param groupName of radios
137         * @return Map<String, HTMLElement>
138         */
139        public Map<String, HTMLElement> getRadioGroup(String groupName) {
140            Map<String, HTMLElement> map = new LinkedHashMap<String, HTMLElement>(3);
141            // loop through the objects for radios with the specified name
142            // and add each to a hashmap based on their VALUE attribute.
143            // If no value exists, log a warn and add each with an integer as
144            // the key.
145            for (int j = 0; j < elements.size(); j++) {
146                HTMLElement element = elements.get(j);
147                if (element instanceof Radio) {
148                    if (groupName.equalsIgnoreCase(element.getName())) {
149                        log.debug("Radio " + groupName + " found.");
150                        String key;
151                        if (element.hasAttribute("value")) {
152                            key = element.getAttribute("value");
153                            log.debug("Radio " + groupName + " key " + key);
154                        } else {
155                            key = String.valueOf(j);
156                            log.debug("getRadioGroup: Radio has no value: " + element + " assigning " + key);
157                        }
158                        map.put(key, element);
159                    }
160                }
161            }
162            return map;
163        }
164    
165        /**
166         * Returns the specified element casted to a Select object.
167         *
168         * @param name name of select
169         * @return Select
170         */
171        public Select getSelect(String name) {
172            return (Select) elements(name);
173        }
174    
175        /**
176         * Checks if the specified object exists in the form.
177         *
178         * @param name name to check
179         * @return boolean
180         */
181        public boolean hasElement(String name) {
182            return elements(name) != null;
183        }
184    
185    /*
186        public Label getLabel(String name) {
187            return (Label) elements(name);
188        }
189    
190        public Button getButton(String name) {
191            return (Button) elements(name);
192        }
193    */
194    
195        /**
196         * Poplates the form objects based on the supplied java bean.
197         *
198         * @param bean any object which can be examined by BeanUtils.
199         * @throws FormPolulateException if BeanUtils fails.
200         */
201        public void populate(Object bean) throws FormPolulateException {
202    
203            Map<String, String> map = null;
204            //@todo should this support nested properties like the grid?
205            try {
206                map = BeanUtils.describe(bean);
207            } catch (Exception e) {
208                throw new FormPolulateException(e);
209            }
210            populate(map);
211        }
212    
213        /**
214         * Populates the form object from the supplied map object.
215         *
216         * @param map - name/values where names match element names
217         */
218        public void populate(Map<String, String> map) {
219            Map<String, String> radios = new HashMap<String, String>(3);
220    
221            Iterator it = map.keySet().iterator();
222            while (it.hasNext()) {
223                String key = (String) it.next();
224    
225                // Prevent null values in the bean from throwing null pointer
226                String value;
227                if (map.get(key) == null) {
228                    value = "";
229                } else {
230                    value = (String) map.get(key);
231                }
232    
233                if (hasElement(key.toString())) {
234                    // Do not auto populate Radios since we don't really
235                    // know which radio to assign the value to since usually
236                    // they would have the same name. See getRadioGroup() method.
237                    if (elements(key) instanceof Radio) {
238                        radios.put(key, "");
239                    } else {
240                        if (elements(key) instanceof CheckBox) {
241                            if (value.equalsIgnoreCase("true")) {
242                                elements(key).setChecked(true);
243                            } else {
244                                elements(key).setChecked(false);
245                            }
246                        } else {
247                            elements(key).setValue(value);
248                        }
249                    }
250                }
251            }
252    
253            /*
254            Check radios. If there are any radios that matched a field in the bean
255            then it means that maybe that fields value matches a radio value from the
256            group.  For each group check if the values match and set the CHECKED for
257            the ones that do.
258            */
259            it = radios.keySet().iterator();
260            while (it.hasNext()) {
261                String key;
262                key = it.next().toString();
263    
264                Iterator<HTMLElement> itGroup;
265                itGroup = getRadioGroup(key).values().iterator();
266                while (itGroup.hasNext()) {
267                    HTMLElement radio = itGroup.next();
268                    radio.setChecked(false);
269                    if (radio.getValue() != null && map.get(key) != null) {
270                        if (radio.getValue().equalsIgnoreCase(map.get(key).toString())) {
271                            radio.setChecked(true);
272                        }
273                    }
274                }
275            }
276    
277        }
278    
279        final HTMLElement addObject(Elements type, Attributes attributes, int start, int end) {
280    
281            HTMLElement element;
282    
283            log.debug("TYPE:" + type);
284    
285            switch (type) {
286    
287                case select:
288                    element = new Select();
289                    break;
290    
291                case textarea:
292                    element = new TextArea();
293                    break;
294    
295                case label:
296                    element = new Label();
297                    break;
298    
299                case button:
300                    element = new Button();
301                    break;
302    
303                case image:
304                    element = new Image();
305                    break;
306    
307                case grid:
308                    element = new Grid();
309                    break;
310    
311                case radio:
312                    element = new Radio();
313                    break;
314    
315                case checkbox:
316                    element = new CheckBox();
317                    break;
318    
319                case text:
320                    element = new Text();
321                    break;
322    
323                default:
324                    element = new Input();
325            }
326    
327            log.debug("CLASS:" + element.getClass().getName());
328    
329            Iterator i = attributes.keySet().iterator();
330    
331            while (i.hasNext()) {
332                String key, value;
333                key = (String) i.next();
334                value = attributes.getAttribute(key);
335                element.setAttribute(key, value);
336    
337                // Ensure to call setValue for values.
338                if ("value".equalsIgnoreCase(key)) {
339                    element.setValue(value);
340                }
341            }
342    
343            // Also assign the object's name if found.
344            if (attributes.hasAttribute("name")) {
345                element.setName(attributes.getAttribute("name"));
346            } else if (attributes.hasAttribute("id")) {
347                element.setName(attributes.getAttribute("id"));
348            }
349    
350            element.setStartPoint(start);
351            element.setEndPoint(end);
352    
353            elements.add(element);
354            return element;
355        }
356    
357        /**
358         * Used for cloning.
359         *
360         * @param element to add
361         * @return HTMLElement
362         */
363        HTMLElement addElement(HTMLElement element) {
364            elements.add(element);
365            return element;
366        }
367    
368    
369    }