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 }