/* * Copyright (c) 2001-2007, Inversoft Inc., All Rights Reserved * * Licensed 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.primeframework.mvc.parameter.convert.converters; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import org.apache.commons.lang3.StringUtils; import org.primeframework.mvc.parameter.convert.AbstractGlobalConverter; import org.primeframework.mvc.parameter.convert.ConversionException; import org.primeframework.mvc.parameter.convert.ConverterProvider; import org.primeframework.mvc.parameter.convert.ConverterStateException; import org.primeframework.mvc.parameter.convert.GlobalConverter; import org.primeframework.mvc.util.TypeTools; import com.google.inject.Inject; import static java.util.Arrays.*; /** * This converts to and from Collection types. This handles complex parameterized types by first creating the * Collection instance (ArrayList, HashSet, LinkedList, etc) and then by leveraging the other converters for the * parametrized type. * * @author Brian Pontarelli */ @org.primeframework.mvc.parameter.convert.annotation.GlobalConverter @SuppressWarnings("unchecked") public class CollectionConverter extends AbstractGlobalConverter { private final ConverterProvider provider; @Inject public CollectionConverter(ConverterProvider provider) { this.provider = provider; } /** * Creates the correct collection type and then converts the value String to the parameterized type (if one exists) * and then adds it. If there is no parameterized type, this just shoves the String value in the collection. * * @param value The value. * @param convertTo The type to convert to. * @param dynamicAttributes The dynamic attributes used to assist in conversion. * @param expression The full path to the expression that is causing the conversion. * @return The converted value. * @throws ConversionException If the conversion failed. * @throws ConverterStateException if the converter didn't have all of the information it needed to perform the * conversion. */ protected Object stringToObject(String value, Type convertTo, Map<String, String> dynamicAttributes, String expression) throws ConversionException, ConverterStateException { return stringsToObject(StringUtils.split(value, ','), convertTo, dynamicAttributes, expression); } /** * Creates the correct collection type and then converts the value Strings to the parametrized type (if one exists) * and then adds each one. If there is no parameterized type, this just shoves the String values in the collection. * * @param values The values. * @param convertTo The type to convert to. * @param dynamicAttributes The dynamic attributes used to assist in conversion. * @param expression The full path to the expression that is causing the conversion. * @return The converted value. * @throws ConversionException If the conversion failed. * @throws ConverterStateException if the converter didn't have all of the information it needed to perform the * conversion. */ protected Object stringsToObject(String[] values, Type convertTo, Map<String, String> dynamicAttributes, String expression) throws ConversionException, ConverterStateException { Class<?> rawType = TypeTools.rawType(convertTo); Class<?> parameter = parameterType(convertTo); Collection collection = makeCollection(rawType); if (parameter == null) { collection.addAll(asList(values)); } else { GlobalConverter converter = provider.lookup(parameter); if (converter == null) { throw new ConverterStateException("Unable to convert to the type [" + convertTo + "] because the parameter type [" + parameter + "] doesn't have a Converter " + "associated with it."); } for (String value : values) { collection.add(converter.convertFromStrings(parameter, dynamicAttributes, expression, StringUtils.split(value, ','))); } } return collection; } /** * Iterates over the given collection and converts each collection item to a String based on the parameterized type. * If there is no parameterized type, this just joins the collection. * * @param value The value. * @param convertFrom The type to convert from. * @param dynamicAttributes The dynamic attributes used to assist in conversion. * @param expression The full path to the expression that is causing the conversion. * @return The converted value. * @throws ConversionException If the conversion failed. * @throws ConverterStateException if the converter didn't have all of the information it needed to perform the * conversion. */ protected String objectToString(Object value, Type convertFrom, Map<String, String> dynamicAttributes, String expression) throws ConversionException, ConverterStateException { Collection collection = (Collection) value; Class<?> parameter = parameterType(convertFrom); if (parameter == null) { return StringUtils.join(collection, ","); } else { GlobalConverter converter = provider.lookup(parameter); if (converter == null) { throw new ConverterStateException("Unable to convert to the type [" + convertFrom + "] because the parameter type [" + parameter + "] doesn't have a Converter " + "associated with it."); } StringBuilder build = new StringBuilder(); for (Object o : collection) { String str = converter.convertToString(parameter, dynamicAttributes, expression, o); if (build.length() > 0) { build.append(","); } build.append(str); } return build.toString(); } } /** * Creates a new instance of the given collection type. If this type is a collection interface, the most common * implementation is created. Otherwise, the type is instantiated directly. If that fails, this conversion will fail. * * @param type The type of collection. * @return The new collection. */ protected Collection makeCollection(Class<?> type) { if (type == List.class) { return new ArrayList(); } else if (type == SortedSet.class) { return new TreeSet(); } else if (type == Set.class) { return new HashSet(); } else if (type == Queue.class) { return new LinkedList(); } // Try to instantiate it directly try { return (Collection) type.newInstance(); } catch (Exception e) { throw new ConverterStateException("The type [" + type + "] is a collection but could not " + "be instantiated by the converter class"); } } }