001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    
019    package org.apache.commons.beanutils.converters;
020    
021    
022    import java.io.IOException;
023    import java.io.StreamTokenizer;
024    import java.io.StringReader;
025    import java.util.ArrayList;
026    import java.util.List;
027    import org.apache.commons.beanutils.ConversionException;
028    import org.apache.commons.beanutils.Converter;
029    
030    
031    
032    /**
033     * <p>Convenience base class for converters that translate the String
034     * representation of an array into a corresponding array of primitives
035     * object.  This class encapsulates the functionality required to parse
036     * the String into a list of String elements that can later be
037     * individually converted to the appropriate primitive type.</p>
038     *
039     * <p>The input syntax accepted by the <code>parseElements()</code> method
040     * is designed to be compatible with the syntax used to initialize arrays
041     * in a Java source program, except that only String literal values are
042     * supported.  For maximum flexibility, the surrounding '{' and '}'
043     * characters are optional, and individual elements may be separated by
044     * any combination of whitespace and comma characters.</p>
045     *
046     * @author Craig R. McClanahan
047     * @version $Revision: 557808 $ $Date: 2007-07-20 00:05:03 +0100 (Fri, 20 Jul 2007) $
048     * @since 1.4
049     * @deprecated Replaced by the new {@link ArrayConverter} implementation
050     */
051    
052    public abstract class AbstractArrayConverter implements Converter {
053    
054    
055        // ----------------------------------------------------------- Constructors
056    
057    
058        /**
059         * Create a {@link Converter} that will throw a {@link ConversionException}
060         * if a conversion error occurs.
061         */
062        public AbstractArrayConverter() {
063    
064            this.defaultValue = null;
065            this.useDefault = false;
066    
067        }
068    
069        /**
070         * Create a {@link Converter} that will return the specified default value
071         * if a conversion error occurs.
072         *
073         * @param defaultValue The default value to be returned
074         */
075        public AbstractArrayConverter(Object defaultValue) {
076    
077            if (defaultValue == NO_DEFAULT) {
078                this.useDefault = false;
079            } else {
080                this.defaultValue = defaultValue;
081                this.useDefault = true;
082            }
083    
084        }
085    
086        // ------------------------------------------------------- Static Variables
087    
088        /**
089         * This is a special reference that can be passed as the "default object"
090         * to the constructor to indicate that no default is desired. Note that
091         * the value 'null' cannot be used for this purpose, as the caller may
092         * want a null to be returned as the default.
093         */
094        public static final Object NO_DEFAULT = new Object();
095    
096        // ----------------------------------------------------- Instance Variables
097    
098    
099        /**
100         * <p>Model object for string arrays.</p>
101         */
102        protected static String[] strings = new String[0];
103    
104    
105        /**
106         * The default value specified to our Constructor, if any.
107         */
108        protected Object defaultValue = null;
109    
110    
111        /**
112         * Should we return the default value on conversion errors?
113         */
114        protected boolean useDefault = true;
115    
116    
117        // --------------------------------------------------------- Public Methods
118    
119    
120        /**
121         * Convert the specified input object into an output object of the
122         * specified type.  This method must be implemented by a concrete
123         * subclass.
124         *
125         * @param type Data type to which this value should be converted
126         * @param value The input value to be converted
127         * @return The converted value
128         *
129         * @exception ConversionException if conversion cannot be performed
130         *  successfully
131         */
132        public abstract Object convert(Class type, Object value);
133    
134    
135        // ------------------------------------------------------ Protected Methods
136    
137    
138        /**
139         * <p>Parse an incoming String of the form similar to an array initializer
140         * in the Java language into a <code>List</code> individual Strings
141         * for each element, according to the following rules.</p>
142         * <ul>
143         * <li>The string is expected to be a comma-separated list of values.</li>
144         * <li>The string may optionally have matching '{' and '}' delimiters
145         *   around the list.</li>
146         * <li>Whitespace before and after each element is stripped.</li>
147         * <li>Elements in the list may be delimited by single or double quotes.
148         *  Within a quoted elements, the normal Java escape sequences are valid.</li>
149         * </ul>
150         *
151         * @param svalue String value to be parsed
152         * @return The parsed list of String values
153         *
154         * @exception ConversionException if the syntax of <code>svalue</code>
155         *  is not syntactically valid
156         * @exception NullPointerException if <code>svalue</code>
157         *  is <code>null</code>
158         */
159        protected List parseElements(String svalue) {
160    
161            // Validate the passed argument
162            if (svalue == null) {
163                throw new NullPointerException();
164            }
165    
166            // Trim any matching '{' and '}' delimiters
167            svalue = svalue.trim();
168            if (svalue.startsWith("{") && svalue.endsWith("}")) {
169                svalue = svalue.substring(1, svalue.length() - 1);
170            }
171    
172            try {
173    
174                // Set up a StreamTokenizer on the characters in this String
175                StreamTokenizer st =
176                    new StreamTokenizer(new StringReader(svalue));
177                st.whitespaceChars(',',','); // Commas are delimiters
178                st.ordinaryChars('0', '9');  // Needed to turn off numeric flag
179                st.ordinaryChars('.', '.');
180                st.ordinaryChars('-', '-');
181                st.wordChars('0', '9');      // Needed to make part of tokens
182                st.wordChars('.', '.');
183                st.wordChars('-', '-');
184    
185                // Split comma-delimited tokens into a List
186                ArrayList list = new ArrayList();
187                while (true) {
188                    int ttype = st.nextToken();
189                    if ((ttype == StreamTokenizer.TT_WORD) ||
190                        (ttype > 0)) {
191                        list.add(st.sval);
192                    } else if (ttype == StreamTokenizer.TT_EOF) {
193                        break;
194                    } else {
195                        throw new ConversionException
196                            ("Encountered token of type " + ttype);
197                    }
198                }
199    
200                // Return the completed list
201                return (list);
202    
203            } catch (IOException e) {
204    
205                throw new ConversionException(e);
206    
207            }
208    
209    
210    
211        }
212    
213    
214    }