You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

StringToCollectionConverter.java 7.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. /*
  2. * Copyright 2000-2014 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.data.util.converter;
  17. import java.io.Serializable;
  18. import java.lang.reflect.Modifier;
  19. import java.util.ArrayList;
  20. import java.util.Collection;
  21. import java.util.HashSet;
  22. import java.util.Iterator;
  23. import java.util.Locale;
  24. /**
  25. * A converter that converts from {@link String} to {@link Collection} of tokens
  26. * and back.
  27. * <p>
  28. * Allows to break a string into tokens using delimiter. Each token can be
  29. * converted to its own model using provided converter.
  30. * <p>
  31. * Default constructor uses <code>", "</code> as delimiter string and
  32. * {@link String} for token types. Other constructors allow to configure
  33. * delimiter and token types.
  34. *
  35. * @since 7.5.0
  36. *
  37. * @author Vaadin Ltd
  38. */
  39. public class StringToCollectionConverter implements
  40. Converter<String, Collection> {
  41. private final String delimiter;
  42. private final Converter<String, ?> tokenConverter;
  43. private final Class<?> tokenType;
  44. private final CollectionFactory factory;
  45. /**
  46. * Creates converter with <code>", "</code> as delimiter and {@link String}
  47. * as token model type in collection.
  48. */
  49. public StringToCollectionConverter() {
  50. this(", ", null, String.class);
  51. }
  52. /**
  53. * Creates converter with given {@code delimiter} and {@link String} as
  54. * token model type in collection.
  55. *
  56. * @param delimiter
  57. * custom delimiter
  58. */
  59. public StringToCollectionConverter(String delimiter) {
  60. this(delimiter, null, String.class);
  61. }
  62. /**
  63. * Creates converter with given {@code tokenConverter} for convert tokens
  64. * and expected {@code tokenType}.
  65. * <p>
  66. * If {@code tokenConverter} is null then no conversation is done and
  67. * {@link String} is used as token type in resulting model collection.
  68. *
  69. * @param tokenConverter
  70. * converter for token
  71. * @param tokenType
  72. * expected token model type
  73. */
  74. public StringToCollectionConverter(Converter<String, ?> tokenConverter,
  75. Class<?> tokenType) {
  76. this(", ", tokenConverter, tokenType);
  77. }
  78. /**
  79. * Creates converter with given {@code tokenConverter} for convert tokens
  80. * and expected {@code tokenType}.
  81. * <p>
  82. * If {@code tokenConverter} is null then no conversation is done and
  83. * {@link String} is used as token type in resulting model collection.
  84. *
  85. * @param tokenConverter
  86. * converter for token
  87. * @param tokenType
  88. * expected token model type
  89. * @param delimiter
  90. * delimiter in presentation string
  91. */
  92. public StringToCollectionConverter(String delimiter,
  93. Converter<String, ?> tokenConverter, Class<?> tokenClass) {
  94. this(delimiter, tokenConverter, tokenClass,
  95. new DefaultCollectionFactory());
  96. }
  97. /**
  98. * Creates converter with given {@code tokenConverter} for convert tokens
  99. * and expected {@code tokenType}.
  100. * <p>
  101. * If {@code tokenConverter} is null then no conversation is done and
  102. * {@link String} is used as token type in resulting model collection.
  103. *
  104. * @param tokenConverter
  105. * converter for token
  106. * @param tokenType
  107. * expected token model type
  108. * @param delimiter
  109. * delimiter in presentation string
  110. * @param factory
  111. * factory to create resulting collection
  112. */
  113. public StringToCollectionConverter(String delimiter,
  114. Converter<String, ?> tokenConverter, Class<?> tokenClass,
  115. CollectionFactory factory) {
  116. if (delimiter == null || delimiter.isEmpty()) {
  117. throw new IllegalArgumentException(
  118. "Delimiter should be non-empty string");
  119. }
  120. this.delimiter = delimiter;
  121. this.tokenConverter = tokenConverter;
  122. tokenType = tokenClass;
  123. this.factory = factory;
  124. }
  125. @Override
  126. public Class<Collection> getModelType() {
  127. return Collection.class;
  128. }
  129. @Override
  130. public Class<String> getPresentationType() {
  131. return String.class;
  132. }
  133. @Override
  134. public Collection convertToModel(String value,
  135. Class<? extends Collection> targetType, Locale locale)
  136. throws Converter.ConversionException {
  137. int index = value.indexOf(delimiter);
  138. int previous = 0;
  139. Collection result = factory.createCollection(targetType);
  140. Converter converter = tokenConverter;
  141. while (index != -1) {
  142. collectToken(value.substring(previous, index), result, converter,
  143. locale);
  144. previous = index + delimiter.length();
  145. index = value.indexOf(delimiter, previous);
  146. }
  147. if (result.size() > 0) {
  148. collectToken(value.substring(previous), result, converter, locale);
  149. }
  150. return result;
  151. }
  152. @Override
  153. public String convertToPresentation(Collection value,
  154. Class<? extends String> targetType, Locale locale)
  155. throws Converter.ConversionException {
  156. StringBuilder builder = new StringBuilder();
  157. Converter converter = tokenConverter;
  158. for (Iterator<?> iterator = value.iterator(); iterator.hasNext();) {
  159. if (converter == null) {
  160. builder.append(iterator.next());
  161. } else {
  162. builder.append(converter.convertToPresentation(iterator.next(),
  163. targetType, locale));
  164. }
  165. builder.append(delimiter);
  166. }
  167. if (builder.length() > 0) {
  168. return builder.substring(0, builder.length() - delimiter.length());
  169. } else {
  170. return builder.toString();
  171. }
  172. }
  173. private void collectToken(String token, Collection collection,
  174. Converter converter, Locale locale) {
  175. if (converter == null) {
  176. collection.add(token);
  177. } else {
  178. collection.add(converter.convertToModel(token, tokenType, locale));
  179. }
  180. }
  181. /**
  182. * Default collection factory implementation.
  183. *
  184. * @author Vaadin Ltd
  185. */
  186. public static class DefaultCollectionFactory implements CollectionFactory {
  187. @Override
  188. public Collection<?> createCollection(Class<? extends Collection> type) {
  189. if (type.isAssignableFrom(ArrayList.class)) {
  190. return new ArrayList();
  191. } else if (type.isAssignableFrom(HashSet.class)) {
  192. return new HashSet();
  193. } else if (!type.isInterface()
  194. && !Modifier.isAbstract(type.getModifiers())) {
  195. try {
  196. return type.newInstance();
  197. } catch (InstantiationException ignore) {
  198. } catch (IllegalAccessException ignore) {
  199. }
  200. }
  201. return new ArrayList();
  202. }
  203. }
  204. /**
  205. * Collection factory. Defines a strategy to create collection by collection
  206. * class.
  207. *
  208. * @author Vaadin Ltd
  209. */
  210. public interface CollectionFactory extends Serializable {
  211. /**
  212. * Create collection by its {@code type}.
  213. *
  214. * @param type
  215. * collection type
  216. * @return instantiated collection with given {@code type}
  217. */
  218. Collection<?> createCollection(Class<? extends Collection> type);
  219. }
  220. }