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 * https://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 018package org.apache.commons.beanutils.converters; 019 020import java.io.IOException; 021import java.io.StreamTokenizer; 022import java.io.StringReader; 023import java.util.ArrayList; 024import java.util.List; 025import java.util.Objects; 026 027import org.apache.commons.beanutils.ConversionException; 028import org.apache.commons.beanutils.Converter; 029 030/** 031 * <p>Convenience base class for converters that translate the String 032 * representation of an array into a corresponding array of primitives 033 * object. This class encapsulates the functionality required to parse 034 * the String into a list of String elements that can later be 035 * individually converted to the appropriate primitive type.</p> 036 * 037 * <p>The input syntax accepted by the <code>parseElements()</code> method 038 * is designed to be compatible with the syntax used to initialize arrays 039 * in a Java source program, except that only String literal values are 040 * supported. For maximum flexibility, the surrounding '{' and '}' 041 * characters are optional, and individual elements may be separated by 042 * any combination of whitespace and comma characters.</p> 043 * 044 * @since 1.4 045 * @deprecated Replaced by the new {@link ArrayConverter} implementation 046 */ 047 048@Deprecated 049public abstract class AbstractArrayConverter implements Converter { 050 051 /** 052 * This is a special reference that can be passed as the "default object" 053 * to the constructor to indicate that no default is desired. Note that 054 * the value 'null' cannot be used for this purpose, as the caller may 055 * want a null to be returned as the default. 056 * @since 1.8.0 057 */ 058 public static final Object NO_DEFAULT = new Object(); 059 060 /** 061 * <p>Model object for string arrays.</p> 062 */ 063 protected static String[] strings = {}; 064 065 /** 066 * The default value specified to our Constructor, if any. 067 */ 068 protected Object defaultValue; 069 070 /** 071 * Should we return the default value on conversion errors? 072 */ 073 protected boolean useDefault = true; 074 075 /** 076 * Create a {@link Converter} that will throw a {@link ConversionException} 077 * if a conversion error occurs. 078 */ 079 public AbstractArrayConverter() { 080 081 this.defaultValue = null; 082 this.useDefault = false; 083 084 } 085 086 /** 087 * Create a {@link Converter} that will return the specified default value 088 * if a conversion error occurs. 089 * 090 * @param defaultValue The default value to be returned 091 * @since 1.8.0 092 */ 093 public AbstractArrayConverter(final Object defaultValue) { 094 095 if (defaultValue == NO_DEFAULT) { 096 this.useDefault = false; 097 } else { 098 this.defaultValue = defaultValue; 099 this.useDefault = true; 100 } 101 102 } 103 104 /** 105 * Convert the specified input object into an output object of the 106 * specified type. This method must be implemented by a concrete 107 * subclass. 108 * 109 * @param type Data type to which this value should be converted 110 * @param value The input value to be converted 111 * @return The converted value 112 * @throws ConversionException if conversion cannot be performed 113 * successfully 114 */ 115 @Override 116 public abstract Object convert(Class type, Object value); 117 118 /** 119 * <p>Parse an incoming String of the form similar to an array initializer 120 * in the Java language into a <code>List</code> individual Strings 121 * for each element, according to the following rules.</p> 122 * <ul> 123 * <li>The string is expected to be a comma-separated list of values.</li> 124 * <li>The string may optionally have matching '{' and '}' delimiters 125 * around the list.</li> 126 * <li>Whitespace before and after each element is stripped.</li> 127 * <li>Elements in the list may be delimited by single or double quotes. 128 * Within a quoted elements, the normal Java escape sequences are valid.</li> 129 * </ul> 130 * 131 * @param svalue String value to be parsed 132 * @return The parsed list of String values 133 * @throws ConversionException if the syntax of <code>svalue</code> 134 * is not syntactically valid 135 * @throws NullPointerException if <code>svalue</code> 136 * is <code>null</code> 137 */ 138 protected List<String> parseElements(String svalue) { 139 // Validate the passed argument 140 Objects.requireNonNull(svalue, "svalue"); 141 // Trim any matching '{' and '}' delimiters 142 svalue = svalue.trim(); 143 if (svalue.startsWith("{") && svalue.endsWith("}")) { 144 svalue = svalue.substring(1, svalue.length() - 1); 145 } 146 try { 147 148 // Set up a StreamTokenizer on the characters in this String 149 final StreamTokenizer st = new StreamTokenizer(new StringReader(svalue)); 150 st.whitespaceChars(',', ','); // Commas are delimiters 151 st.ordinaryChars('0', '9'); // Needed to turn off numeric flag 152 st.ordinaryChars('.', '.'); 153 st.ordinaryChars('-', '-'); 154 st.wordChars('0', '9'); // Needed to make part of tokens 155 st.wordChars('.', '.'); 156 st.wordChars('-', '-'); 157 // Split comma-delimited tokens into a List 158 final ArrayList<String> list = new ArrayList<>(); 159 while (true) { 160 final int ttype = st.nextToken(); 161 if (ttype == StreamTokenizer.TT_WORD || ttype > 0) { 162 list.add(st.sval); 163 } else if (ttype == StreamTokenizer.TT_EOF) { 164 break; 165 } else { 166 throw new ConversionException("Encountered token of type " + ttype); 167 } 168 } 169 // Return the completed list 170 return list; 171 } catch (final IOException e) { 172 throw new ConversionException(e); 173 } 174 } 175 176}