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: 690380 $ $Date: 2008-08-29 21:04:38 +0100 (Fri, 29 Aug 2008) $
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         * @since 1.8.0
075         */
076        public AbstractArrayConverter(Object defaultValue) {
077    
078            if (defaultValue == NO_DEFAULT) {
079                this.useDefault = false;
080            } else {
081                this.defaultValue = defaultValue;
082                this.useDefault = true;
083            }
084    
085        }
086    
087        // ------------------------------------------------------- Static Variables
088    
089        /**
090         * This is a special reference that can be passed as the "default object"
091         * to the constructor to indicate that no default is desired. Note that
092         * the value 'null' cannot be used for this purpose, as the caller may
093         * want a null to be returned as the default.
094         * @since 1.8.0
095         */
096        public static final Object NO_DEFAULT = new Object();
097    
098        // ----------------------------------------------------- Instance Variables
099    
100    
101        /**
102         * <p>Model object for string arrays.</p>
103         */
104        protected static String[] strings = new String[0];
105    
106    
107        /**
108         * The default value specified to our Constructor, if any.
109         */
110        protected Object defaultValue = null;
111    
112    
113        /**
114         * Should we return the default value on conversion errors?
115         */
116        protected boolean useDefault = true;
117    
118    
119        // --------------------------------------------------------- Public Methods
120    
121    
122        /**
123         * Convert the specified input object into an output object of the
124         * specified type.  This method must be implemented by a concrete
125         * subclass.
126         *
127         * @param type Data type to which this value should be converted
128         * @param value The input value to be converted
129         * @return The converted value
130         *
131         * @exception ConversionException if conversion cannot be performed
132         *  successfully
133         */
134        public abstract Object convert(Class type, Object value);
135    
136    
137        // ------------------------------------------------------ Protected Methods
138    
139    
140        /**
141         * <p>Parse an incoming String of the form similar to an array initializer
142         * in the Java language into a <code>List</code> individual Strings
143         * for each element, according to the following rules.</p>
144         * <ul>
145         * <li>The string is expected to be a comma-separated list of values.</li>
146         * <li>The string may optionally have matching '{' and '}' delimiters
147         *   around the list.</li>
148         * <li>Whitespace before and after each element is stripped.</li>
149         * <li>Elements in the list may be delimited by single or double quotes.
150         *  Within a quoted elements, the normal Java escape sequences are valid.</li>
151         * </ul>
152         *
153         * @param svalue String value to be parsed
154         * @return The parsed list of String values
155         *
156         * @exception ConversionException if the syntax of <code>svalue</code>
157         *  is not syntactically valid
158         * @exception NullPointerException if <code>svalue</code>
159         *  is <code>null</code>
160         */
161        protected List parseElements(String svalue) {
162    
163            // Validate the passed argument
164            if (svalue == null) {
165                throw new NullPointerException();
166            }
167    
168            // Trim any matching '{' and '}' delimiters
169            svalue = svalue.trim();
170            if (svalue.startsWith("{") && svalue.endsWith("}")) {
171                svalue = svalue.substring(1, svalue.length() - 1);
172            }
173    
174            try {
175    
176                // Set up a StreamTokenizer on the characters in this String
177                StreamTokenizer st =
178                    new StreamTokenizer(new StringReader(svalue));
179                st.whitespaceChars(',',','); // Commas are delimiters
180                st.ordinaryChars('0', '9');  // Needed to turn off numeric flag
181                st.ordinaryChars('.', '.');
182                st.ordinaryChars('-', '-');
183                st.wordChars('0', '9');      // Needed to make part of tokens
184                st.wordChars('.', '.');
185                st.wordChars('-', '-');
186    
187                // Split comma-delimited tokens into a List
188                ArrayList list = new ArrayList();
189                while (true) {
190                    int ttype = st.nextToken();
191                    if ((ttype == StreamTokenizer.TT_WORD) ||
192                        (ttype > 0)) {
193                        list.add(st.sval);
194                    } else if (ttype == StreamTokenizer.TT_EOF) {
195                        break;
196                    } else {
197                        throw new ConversionException
198                            ("Encountered token of type " + ttype);
199                    }
200                }
201    
202                // Return the completed list
203                return (list);
204    
205            } catch (IOException e) {
206    
207                throw new ConversionException(e);
208    
209            }
210    
211    
212    
213        }
214    
215    
216    }