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 }