alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Commons Beanutils example source code file (ArrayConverter.java)

This example Commons Beanutils source code file (ArrayConverter.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Commons Beanutils tags/keywords

arrayconverter, class, collection, collection, conversionexception, converter, illegalargumentexception, io, list, object, object, reflection, string, string, stringbuffer, stringbuffer, util

The Commons Beanutils ArrayConverter.java source code

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.commons.beanutils.converters;

import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Collection;
import java.io.StreamTokenizer;
import java.io.StringReader;
import java.io.IOException;
import java.lang.reflect.Array;
import org.apache.commons.beanutils.ConversionException;
import org.apache.commons.beanutils.Converter;

/**
 * Generic {@link Converter} implementaion that handles conversion
 * to and from <b>array objects.
 * <p>
 * Can be configured to either return a <i>default value or throw a
 * <code>ConversionException if a conversion error occurs.
 * <p>
 * The main features of this implementation are:
 * <ul>
 *     <li>Element Conversion - delegates to a {@link Converter},
 *         appropriate for the type, to convert individual elements
 *         of the array. This leverages the power of existing converters
 *         without having to replicate their functionality for converting
 *         to the element type and removes the need to create a specifc
 *         array type converters.</li>
 *     <li>Arrays or Collections - can convert from either arrays or
 *         Collections to an array, limited only by the capability
 *         of the delegate {@link Converter}.</li>
 *     <li>Delimited Lists - can Convert to and from a
 *         delimited list in String format.</li>
 *     <li>Conversion to String - converts an array to a 
 *         <code>String in one of two ways: as a delimited list
 *         or by converting the first element in the array to a String - this
 *         is controlled by the {@link ArrayConverter#setOnlyFirstToString(boolean)}
 *         parameter.</li>
 *     <li>Multi Dimensional Arrays - its possible to convert a String
 *         to a multi-dimensional arrays, by embedding {@link ArrayConverter}
 *         within each other - see example below.</li>
 *     <li>Default Value
 *         <ul>
 *             <li>No Default - use the 
 *                 {@link ArrayConverter#ArrayConverter(Class, Converter)}
 *                 constructor to create a converter which throws a
 *                 {@link ConversionException} if the value is missing or
 *                 invalid.</li>
 *             <li>Default values - use the 
 *                 {@link ArrayConverter#ArrayConverter(Class, Converter, int)}
 *                 constructor to create a converter which returns a <i>default
 *                 value</i>. The defaultSize parameter controls the 
 *                 <i>default value in the following way:
 *                 <ul>
 *                    <li>defaultSize < 0 - default is null
 *                    <li>defaultSize = 0 - default is an array of length zero
 *                    <li>defaultSize > 0 - default is an array with a
 *                        length specified by <code>defaultSize (N.B. elements
 *                        in the array will be <code>null)
 *                 </ul>
 *         </ul>
 * </ul>
 *
 * <h3>Parsing Delimited Lists
 * This implementation can convert a delimited list in <code>String format
 * into an array of the appropriate type. By default, it uses a comma as the delimiter
 * but the following methods can be used to configure parsing:
 * <ul>
 *     <li>setDelimiter(char) - allows the character used as
 *         the delimiter to be configured [default is a comma].</li>
 *     <li>setAllowedChars(char[]) - adds additional characters
 *         (to the default alphabetic/numeric) to those considered to be
 *         valid token characters.
 * </ul>
 *
 * <h3>Multi Dimensional Arrays
 * It is possible to convert a <code>String to mulit-dimensional arrays by using
 * {@link ArrayConverter} as the element {@link Converter}
 * within another {@link ArrayConverter}.
 * <p>
 * For example, the following code demonstrates how to construct a {@link Converter}
 * to convert a delimited <code>String into a two dimensional integer array:
 * <p>
 * <pre>
 *    // Construct an Integer Converter
 *    IntegerConverter integerConverter = new IntegerConverter();
 *
 *    // Construct an array Converter for an integer array (i.e. int[]) using
 *    // an IntegerConverter as the element converter.
 *    // N.B. Uses the default comma (i.e. ",") as the delimiter between individual numbers
 *    ArrayConverter arrayConverter = new ArrayConverter(int[].class, integerConverter);
 *
 *    // Construct a "Matrix" Converter which converts arrays of integer arrays using
 *    // the pre-ceeding ArrayConverter as the element Converter.
 *    // N.B. Uses a semi-colon (i.e. ";") as the delimiter to separate the different sets of numbers.
 *    //      Also the delimiter used by the first ArrayConverter needs to be added to the
 *    //      "allowed characters" for this one.
 *    ArrayConverter matrixConverter = new ArrayConverter(int[][].class, arrayConverter);
 *    matrixConverter.setDelimiter(';');
 *    matrixConverter.setAllowedChars(new char[] {','});
 *
 *    // Do the Conversion
 *    String matrixString = "11,12,13 ; 21,22,23 ; 31,32,33 ; 41,42,43";
 *    int[][] result = (int[][])matrixConverter.convert(int[][].class, matrixString);
 * </pre>
 *
 * @version $Revision: 640131 $ $Date: 2008-03-23 02:10:31 +0000 (Sun, 23 Mar 2008) $
 * @since 1.8.0
 */
public class ArrayConverter extends AbstractConverter {

    private Object defaultTypeInstance;
    private Converter elementConverter;
    private int defaultSize;
    private char delimiter    = ',';
    private char[] allowedChars = new char[] {'.', '-'};
    private boolean onlyFirstToString = true;

    // ----------------------------------------------------------- Constructors

    /**
     * Construct an <b>array Converter with the specified
     * <b>component Converter that throws a
     * <code>ConversionException if an error occurs.
     *
     * @param defaultType The default array type this
     *  <code>Converter handles
     * @param elementConverter Converter used to convert
     *  individual array elements.
     */
    public ArrayConverter(Class defaultType, Converter elementConverter) {
        super();
        if (defaultType == null) {
            throw new IllegalArgumentException("Default type is missing");
        }
        if (!defaultType.isArray()) {
            throw new IllegalArgumentException("Default type must be an array.");
        }
        if (elementConverter == null) {
            throw new IllegalArgumentException("Component Converter is missing.");
        }
        this.defaultTypeInstance = Array.newInstance(defaultType.getComponentType(), 0);
        this.elementConverter = elementConverter;
    }

    /**
     * Construct an <b>array Converter with the specified
     * <b>component Converter that returns a default
     * array of the specified size (or <code>null) if an error occurs.
     *
     * @param defaultType The default array type this
     *  <code>Converter handles
     * @param elementConverter Converter used to convert
     *  individual array elements.
     * @param defaultSize Specifies the size of the default array value or if less
     *  than zero indicates that a <code>null default value should be used.
     */
    public ArrayConverter(Class defaultType, Converter elementConverter, int defaultSize) {
        this(defaultType, elementConverter);
        this.defaultSize = defaultSize;
        Object defaultValue = null;
        if (defaultSize >= 0) {
            defaultValue = Array.newInstance(defaultType.getComponentType(), defaultSize);
        }
        setDefaultValue(defaultValue);
    }

    /**
     * Set the delimiter to be used for parsing a delimited String.
     *
     * @param delimiter The delimiter [default ',']
     */
    public void setDelimiter(char delimiter) {
        this.delimiter = delimiter;
    }

    /**
     * Set the allowed characters to be used for parsing a delimited String.
     *
     * @param allowedChars Characters which are to be considered as part of
     * the tokens when parsing a delimited String [default is '.' and '-']
     */
    public void setAllowedChars(char[] allowedChars) {
        this.allowedChars = allowedChars;
    }

    /**
     * Indicates whether converting to a String should create
     * a delimited list or just convert the first value.
     *
     * @param onlyFirstToString <code>true converts only
     * the first value in the array to a String, <code>false
     * converts all values in the array into a delimited list (default
     * is <code>true 
     */
    public void setOnlyFirstToString(boolean onlyFirstToString) {
        this.onlyFirstToString = onlyFirstToString;
    }

    /**
     * Return the default type this <code>Converter handles.
     *
     * @return The default type this <code>Converter handles.
     */
    protected Class getDefaultType() {
        return defaultTypeInstance.getClass();
    }

    /**
     * Handles conversion to a String.
     *
     * @param value The value to be converted.
     * @return the converted String value.
     * @throws Throwable if an error occurs converting to a String
     */
    protected String convertToString(Object value) throws Throwable {

        int size = 0;
        Iterator iterator = null;
        Class type = value.getClass();
        if (type.isArray()) {
            size = Array.getLength(value);
        } else {
            Collection collection = convertToCollection(type, value);
            size = collection.size();
            iterator = collection.iterator();
        }

        if (size == 0) {
            return (String)getDefault(String.class);
        }

        if (onlyFirstToString) {
            size = 1;
        }

        // Create a StringBuffer containing a delimited list of the values
        StringBuffer buffer = new StringBuffer();
        for (int i = 0; i < size; i++) {
            if (i > 0) {
                buffer.append(delimiter);
            }
            Object element = iterator == null ? Array.get(value, i) : iterator.next();
            element = elementConverter.convert(String.class, element);
            if (element != null) {
                buffer.append(element);
            }
        }

        return buffer.toString();

    }

    /**
     * Handles conversion to an array of the specified type.
     *
     * @param type The type to which this value should be converted.
     * @param value The input value to be converted.
     * @return The converted value.
     * @throws Throwable if an error occurs converting to the specified type
     */
    protected Object convertToType(Class type, Object value) throws Throwable {

        if (!type.isArray()) {
            throw new ConversionException(toString(getClass())
                    + " cannot handle conversion to '"
                    + toString(type) + "' (not an array).");
        }

        // Handle the source
        int size = 0;
        Iterator iterator = null;
        if (value.getClass().isArray()) {
            size = Array.getLength(value);
        } else {
            Collection collection = convertToCollection(type, value);
            size = collection.size();
            iterator = collection.iterator();
        }

        // Allocate a new Array
        Class componentType = type.getComponentType();
        Object newArray = Array.newInstance(componentType, size);

        // Convert and set each element in the new Array
        for (int i = 0; i < size; i++) {
            Object element = iterator == null ? Array.get(value, i) : iterator.next();
            // TODO - probably should catch conversion errors and throw
            //        new exception providing better info back to the user
            element = elementConverter.convert(componentType, element);
            Array.set(newArray, i, element);
        }

        return newArray;
    }

    /**
     * Returns the value unchanged.
     *
     * @param value The value to convert
     * @return The value unchanged
     */
    protected Object convertArray(Object value) {
        return value;
    }

    /**
     * Converts non-array values to a Collection prior
     * to being converted either to an array or a String.
     * </p>
     * <ul>
     *   <li>{@link Collection} values are returned unchanged
     *   <li>{@link Number}, {@link Boolean}  and {@link java.util.Date} 
     *       values returned as a the only element in a List.</li>
     *   <li>All other types are converted to a String and parsed
     *       as a delimited list.</li>
     * </ul>
     *
     * <strong>N.B. The method is called by both the
     * {@link ArrayConverter#convertToType(Class, Object)} and
     * {@link ArrayConverter#convertToString(Object)} methods for
     * <i>non-array types.
     *
     * @param type The type to convert the value to
     * @param value value to be converted
     * @return Collection elements.
     */
    protected Collection convertToCollection(Class type, Object value) {
        if (value instanceof Collection) {
            return (Collection)value;
        }
        if (value instanceof Number ||
            value instanceof Boolean ||
            value instanceof java.util.Date) {
            List list = new ArrayList(1);
            list.add(value);
            return list;
        }
        
        return parseElements(type, value.toString());
    }

    /**
     * Return the default value for conversions to the specified
     * type.
     * @param type Data type to which this value should be converted.
     * @return The default value for the specified type.
     */
    protected Object getDefault(Class type) {
        if (type.equals(String.class)) {
            return null;
        }

        Object defaultValue = super.getDefault(type);
        if (defaultValue == null) {
            return null;
        }

        if (defaultValue.getClass().equals(type)) {
            return defaultValue;
        } else {
            return Array.newInstance(type.getComponentType(), defaultSize);
        }

    }

    /**
     * Provide a String representation of this array converter.
     *
     * @return A String representation of this array converter
     */
    public String toString() {
        StringBuffer buffer = new StringBuffer();
        buffer.append(toString(getClass()));
        buffer.append("[UseDefault=");
        buffer.append(isUseDefault());
        buffer.append(", ");
        buffer.append(elementConverter.toString());
        buffer.append(']');
        return buffer.toString();
    }

    /**
     * <p>Parse an incoming String of the form similar to an array initializer
     * in the Java language into a <code>List individual Strings
     * for each element, according to the following rules.</p>
     * <ul>
     * <li>The string is expected to be a comma-separated list of values.
     * <li>The string may optionally have matching '{' and '}' delimiters
     *   around the list.</li>
     * <li>Whitespace before and after each element is stripped.
     * <li>Elements in the list may be delimited by single or double quotes.
     *  Within a quoted elements, the normal Java escape sequences are valid.</li>
     * </ul>
     *
     * @param type The type to convert the value to
     * @param value String value to be parsed
     * @return List of parsed elements.
     *
     * @throws ConversionException if the syntax of <code>svalue
     *  is not syntactically valid
     * @throws NullPointerException if <code>svalue
     *  is <code>null
     */
    private List parseElements(Class type, String value) {

        if (log().isDebugEnabled()) {
            log().debug("Parsing elements, delimiter=[" + delimiter + "], value=[" + value + "]");
        }

        // Trim any matching '{' and '}' delimiters
        value = value.trim();
        if (value.startsWith("{") && value.endsWith("}")) {
            value = value.substring(1, value.length() - 1);
        }

        try {

            // Set up a StreamTokenizer on the characters in this String
            StreamTokenizer st = new StreamTokenizer(new StringReader(value));
            st.whitespaceChars(delimiter , delimiter); // Set the delimiters
            st.ordinaryChars('0', '9');  // Needed to turn off numeric flag
            st.wordChars('0', '9');      // Needed to make part of tokens
            for (int i = 0; i < allowedChars.length; i++) {
                st.ordinaryChars(allowedChars[i], allowedChars[i]);
                st.wordChars(allowedChars[i], allowedChars[i]);
            }

            // Split comma-delimited tokens into a List
            List list = null;
            while (true) {
                int ttype = st.nextToken();
                if ((ttype == StreamTokenizer.TT_WORD) || (ttype > 0)) {
                    if (st.sval != null) {
                        if (list == null) {
                            list = new ArrayList();
                        }
                        list.add(st.sval);
                    }
                } else if (ttype == StreamTokenizer.TT_EOF) {
                    break;
                } else {
                    throw new ConversionException("Encountered token of type "
                        + ttype + " parsing elements to '" + toString(type) + ".");
                }
            }

            if (list == null) {
                list = Collections.EMPTY_LIST;
            }
            if (log().isDebugEnabled()) {
                log().debug(list.size() + " elements parsed");
            }

            // Return the completed list
            return (list);

        } catch (IOException e) {

            throw new ConversionException("Error converting from String to '"
                    + toString(type) + "': " + e.getMessage(), e);

        }

    }

}

Other Commons Beanutils examples (source code examples)

Here is a short list of links related to this Commons Beanutils ArrayConverter.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.