View Javadoc

1   /*
2    * Copyright 2007-2008 the original author or authors.
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 net.sf.morph.transform.copiers;
17  
18  import java.util.Enumeration;
19  import java.util.Locale;
20  
21  import net.sf.morph.Defaults;
22  import net.sf.morph.transform.Converter;
23  import net.sf.morph.transform.Copier;
24  import net.sf.morph.transform.DecoratedConverter;
25  import net.sf.morph.transform.DecoratedCopier;
26  import net.sf.morph.transform.transformers.BaseTransformer;
27  import net.sf.morph.util.MorphStringTokenizer;
28  
29  /**
30   * Parses text into multiple parts for storage in a container. For example, the
31   * text <code>{ 1, 2, 3 }</code> code be converted into an
32   * <code>Integer[]</code> array like this:
33   * 
34   * <pre>
35   * String str = &quot;{ 1, 2, 3 }&quot;;
36   * 
37   * TextToContainerCopier copier = new TextToContainerCopier();
38   * 
39   * Integer[] array = (Integer[]) copier.convert(Integer[].class, str);
40   * </pre>
41   * 
42   * <b>Configuration parameters</b>
43   * <ul>
44   * <li><code>ignoredCharacters</code> characters that are completely ignored
45   * in the source. The default characters (specified by
46   * {@link #DEFAULT_IGNORED_CHARACTERS}) are often used as grouping type
47   * characters and probably aren't part of the information to be extracted to the
48   * destination object.</li>
49   * <li><code>delimiter</code> characters that are used to separate the
50   * different elements to be copied to the container. The default characters are
51   * specified by {@link #DEFAULT_DELIMITERS}.
52   * </ul>
53   * 
54   * @author Matt Sgarlata
55   * @since Apr 9, 2007
56   */
57  public class TextToContainerCopier extends BaseTransformer implements DecoratedConverter, DecoratedCopier {
58  	/** Default delimiters */
59  	public static final String DEFAULT_DELIMITERS = " ,|";
60  
61  	/** Default ignored characters */
62  	public static final String DEFAULT_IGNORED_CHARACTERS  = "()[]{}";
63  
64  	private String delimiters = DEFAULT_DELIMITERS;
65  	private String ignoredCharacters = DEFAULT_IGNORED_CHARACTERS;
66  
67  	private Converter textConverter = Defaults.createTextConverter();
68  	private Copier containerCopier = Defaults.createContainerCopier();
69  
70  	/**
71  	 * {@inheritDoc}
72  	 */
73  	protected Object convertImpl(Class destinationClass, Object source, Locale locale) throws Exception {
74  		Enumeration tokenizer = getTokenizer(source, locale);
75  		// this call is the key reason we can't just fall back on the behavior
76  		// of the superclass.  a string like "1,2" is going to look to the
77  		// superclass like it has a size as 1
78  		Object destination = createNewInstance(destinationClass, tokenizer);
79  		getContainerCopier().copy(destination, tokenizer, locale);
80  		return destination;
81      }
82  
83  	/**
84  	 * {@inheritDoc}
85  	 */
86  	protected void copyImpl(Object destination, Object source, Locale locale, Integer preferredTransformationType) throws Exception {
87  		Enumeration tokenizer = getTokenizer(source, locale);	    
88  	    getContainerCopier().copy(destination, tokenizer, locale);
89      }
90  
91  	/**
92  	 * Constructs a StringTokenizer that can be passed directly to the
93  	 * <code>containerCopier</code> to complete the transformation.
94  	 * 
95  	 * @param source
96  	 *            the source, as passed in by the user
97  	 * @param locale
98  	 *            the locale in which the transformation is to take place
99  	 * @return a StringTokenizer that can be passed directly to the
100 	 *         <code>containerCopier</code> to complete the transformation
101 	 */
102 	protected Enumeration getTokenizer(Object source, Locale locale) {
103 	    // prepare the source
104 	    String sourceStr = (String) getTextConverter().convert(String.class, source, locale);
105 	    sourceStr = removeIgnoredCharacters(sourceStr, getIgnoredCharacters());
106 	    // parse the source into separate tokens
107 	    return new MorphStringTokenizer(sourceStr, getDelimiters());
108     }
109 	
110 	/**
111 	 * Remove all characters contained in <code>ignoredCharacters</code> from
112 	 * <code>source</code>.
113 	 * 
114 	 * @param source
115 	 *            the String from which characters are to be removed
116 	 * @param ignoredCharacters
117 	 *            the characters to be removed
118 	 * @return a copy of <code>source</code>, but with all characters
119 	 *         contained in <code>ignoredCharacters</code> removed
120 	 */
121 	protected String removeIgnoredCharacters(String source, String ignoredCharacters) {
122 		if (source == null) {
123 			return null;
124 		}
125 		if (ignoredCharacters == null || "".equals(ignoredCharacters)) {
126 			return source;
127 		}
128 
129 		StringBuffer buffer = new StringBuffer(source.length());
130 		for (int i=0; i<source.length(); i++) {
131 			for (int j=0; j<ignoredCharacters.length(); j++	) {
132 				// if we find an ignored character, break out of this loop
133 				if (source.charAt(i) == ignoredCharacters.charAt(j)) {
134 					break;
135 				}
136 				// if we get here, the character should not be ignored
137 				if (j == ignoredCharacters.length() - 1) {
138 					buffer.append(source.charAt(i));
139 				}
140 			}			
141 		}
142 		return buffer.toString();
143 	}
144 
145 	/**
146 	 * {@inheritDoc}
147 	 */
148 	protected Class[] getDestinationClassesImpl() throws Exception {
149 		return getContainerCopier().getDestinationClasses();
150 	}
151 
152 	/**
153 	 * {@inheritDoc}
154 	 */
155 	protected Class[] getSourceClassesImpl() throws Exception {
156 		return getTextConverter().getSourceClasses();
157 	}
158 
159 	/**
160 	 * {@inheritDoc}
161 	 */
162 	protected boolean isWrappingRuntimeExceptions() {
163 	    return false;
164     }
165 
166 	/**
167 	 * Returns the characters that are used to separate the different elements
168 	 * to be copied to the container.
169 	 * 
170 	 * @return the characters that are used to separate the different elements
171 	 *         to be copied to the container.
172 	 */
173 	public String getDelimiters() {
174 		return delimiters;
175 	}
176 
177 	/**
178 	 * Sets the characters that are used to separate the different elements to
179 	 * be copied to the container.
180 	 * 
181 	 * @param delimiters
182 	 *            the characters that are used to separate the different
183 	 *            elements to be copied to the container
184 	 */
185 	public void setDelimiters(String delimiters) {
186     	this.delimiters = delimiters;
187     }
188 
189 	/**
190 	 * Returns characters that are completely ignored in the source.
191 	 * 
192 	 * @return characters that are completely ignored in the source
193 	 */
194 	public String getIgnoredCharacters() {
195 		return ignoredCharacters;
196 	}
197 
198 	/**
199 	 * Sets the characters that are completely ignored in the source.
200 	 * 
201 	 * @param ignoredCharacters
202 	 *            the characters that are completely ignored in the source
203 	 */
204 	public void setIgnoredCharacters(String ignoredCharacters) {
205     	this.ignoredCharacters = ignoredCharacters;
206     }
207 
208 	/**
209 	 * Get the text converter used by this TextToContainerCopier.
210 	 * @return Converter
211 	 */
212 	public Converter getTextConverter() {
213     	return textConverter;
214     }
215 
216 	/**
217 	 * Set the text converter used by this TextToContainerCopier.
218 	 * @param textConverter
219 	 */
220 	public void setTextConverter(Converter textConverter) {
221     	this.textConverter = textConverter;
222     }
223 
224 	/**
225 	 * Get the container copier used by this TextToContainerCopier.
226 	 * @return Copier
227 	 */
228 	public Copier getContainerCopier() {
229     	return containerCopier;
230     }
231 
232 	/**
233 	 * Set the container copier used by this TextToContainerCopier.
234 	 * @param containerCopier
235 	 */
236 	public void setContainerCopier(Copier containerCopier) {
237     	this.containerCopier = containerCopier;
238     }
239 
240 }