View Javadoc

1   //$Id: DateCreatorAction.java 351 2008-08-14 20:20:56Z jg_hamburg $
2   /********************************************************************************
3    * DDTUnit, a Datadriven Approach to Unit- and Moduletesting
4    * Copyright (c) 2004, Joerg and Kai Gellien
5    * All rights reserved.
6    *
7    * The Software is provided under the terms of the Common Public License 1.0
8    * as provided with the distribution of DDTUnit in the file cpl-v10.html.
9    * Redistribution and use in source and binary forms, with or without
10   * modification, are permitted provided that the following conditions
11   * are met:
12   *
13   *     + Redistributions of source code must retain the above copyright
14   *       notice, this list of conditions and the following disclaimer.
15   *
16   *     + Redistributions in binary form must reproduce the above
17   *       copyright notice, this list of conditions and the following
18   *       disclaimer in the documentation and/or other materials provided
19   *       with the distribution.
20   *
21   *     + Neither the id of the authors or DDTUnit, nor the
22   *       names of its contributors may be used to endorse or promote
23   *       products derived from this software without specific prior
24   *       written permission.
25   *
26   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29   * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
30   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
31   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
32   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
33   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37   ********************************************************************************/
38  package junitx.ddtunit.data.processing;
39  
40  import java.text.DateFormat;
41  import java.text.ParseException;
42  import java.text.SimpleDateFormat;
43  import java.util.Calendar;
44  import java.util.Date;
45  import java.util.Locale;
46  import java.util.Map;
47  import java.util.Set;
48  
49  import junitx.ddtunit.DDTException;
50  import junitx.ddtunit.data.DDTTestDataException;
51  import junitx.ddtunit.data.TypedObject;
52  import junitx.ddtunit.util.ClassAnalyser;
53  import junitx.ddtunit.util.DDTConfiguration;
54  import junitx.ddtunit.util.DDTDateFormat;
55  
56  import org.slf4j.Logger;
57  import org.slf4j.LoggerFactory;
58  
59  /**
60   * This class contains object state and other information to create object from
61   * SAX event stream.
62   * 
63   * @author jg
64   */
65  public class DateCreatorAction extends ActionBase {
66  	private final static Logger log = LoggerFactory.getLogger(DateCreatorAction.class);
67  
68  	private final String XML_ATTR_DATEFORMAT = "dateformat";
69  
70  	private final String XML_ATTR_DATELOCALE = "locale";
71  
72  	/**
73  	 * special key for actual date only
74  	 */
75  	public final static String SYSDATE = "!SYSDATE!";
76  
77  	/**
78  	 * special key for actual date/time
79  	 */
80  	public final static String SYSTIME = "!SYSTIME!";
81  
82  	public static final String DATE_GENERIC = "generic";
83  
84  	public static final String DATE_LOCALE = "locale";
85  
86  	/**
87  	 * 
88  	 * Constructor used as standard constructor to instanciate actions of this
89  	 * type
90  	 * 
91  	 * @param attrMap
92  	 */
93  	public DateCreatorAction(Map<String, String> attrMap) {
94  		super(attrMap);
95  	}
96  
97  	/*
98  	 * (non-Javadoc)
99  	 * 
100 	 * @see junitx.ddtunit.parser.ActionBase#process()
101 	 */
102 	public IAction process() {
103 		log.debug("process DateCreator - START");
104 		if (!this.successorProcessed) {
105 			processNoSuccessor();
106 		}
107 		IAction rootAction = this.getPrevious();
108 		if (rootAction != null) {
109 			String hintValue = rootAction.getHint();
110 
111 			if (HintTypes.COLLECTION.equals(hintValue)
112 					|| HintTypes.MAP.equals(hintValue)
113 					|| HintTypes.ATTRLIST.equals(hintValue)
114 					|| HintTypes.FIELDS.equals(hintValue)
115 					|| HintTypes.CONSTRUCTOR.equals(hintValue)
116 					|| HintTypes.CALL.equals(hintValue)
117 					|| HintTypes.BEAN.equals(hintValue)
118 					|| HintTypes.ARRAY.equals(hintValue)
119 					|| HintTypes.INTERNAL_MAPENTRY.equals(hintValue)) {
120 				rootAction.processSuccessor(this);
121 			} else {
122 				throw new DDTException("Unknown hint (" + hintValue
123 						+ ")- stop processing.");
124 			}
125 		} else {
126 			if (hasReferenceInfo()) {
127 				TypedObject destObject;
128 				if (this.attrMap.get(ParserConstants.XML_ATTR_TYPE) == null) {
129 					destObject = new TypedObject(getAttribute("refid"));
130 				} else {
131 					destObject = new TypedObject(getAttribute("refid"),
132 							getType());
133 				}
134 				IReferenceInfo refInfo = new ObjectReferenceInfo(this
135 						.getObject(), destObject);
136 				add(refInfo);
137 			}
138 		}
139 		this.pop();
140 		return this;
141 	}
142 
143 	public void processSuccessor(IAction successor) {
144 		log.debug("processSuccessor(" + successor + ") - START");
145 		// create attribute list action and insert after rootAction
146 		if (HintTypes.CONTENT.equals(successor.getHint())) {
147 			String content = successor.getValue().toString();
148 			if (!ContentCreatorAction.CONTENT_NULL.equals(content)) {
149 				String format = (this.getAttribute(XML_ATTR_DATEFORMAT) == null) ? DATE_GENERIC
150 						: this.getAttribute(XML_ATTR_DATEFORMAT);
151 				String locale = ((this.getAttribute(XML_ATTR_DATELOCALE) == null) ? DATE_LOCALE
152 						: this.getAttribute(XML_ATTR_DATELOCALE));
153 				Date date = DateFactory.create(this.getType(), content, format,
154 						locale);
155 				this.setValue(date);
156 			}
157 		}
158 		this.successorProcessed = true;
159 	}
160 
161 	public void processNoSuccessor() {
162 		throw new DDTException(
163 				"Specify !NULL! or date. No empty date tag allowed.");
164 	}
165 
166 	/*
167 	 * (non-Javadoc)
168 	 * 
169 	 * @see junitx.ddtunit.parser.ActionBase#inject()
170 	 */
171 	public IAction inject() {
172 		String type = (String) this.attrMap.get(ParserConstants.XML_ATTR_TYPE);
173 		String id = (String) this.attrMap.get(ParserConstants.XML_ATTR_ID);
174 		this.injectedObject = new TypedObject(id, type);
175 		return this;
176 	}
177 
178 	static class DateFactory {
179 		private static Calendar CAL = Calendar.getInstance();
180 
181 		private final static DateFormat PROP_SYSDATE = new SimpleDateFormat(
182 				"dd.mm.yyyy");
183 
184 		private final static DateFormat PROP_SYSTIME = new SimpleDateFormat(
185 				"dd.mm.yyyy HH:mm:ss.SSSS");
186 
187 		/**
188 		 * Create Date object
189 		 * 
190 		 * @param clazz
191 		 *            of Date to create
192 		 * @param content
193 		 *            representing the date details
194 		 * @param format
195 		 *            of date provided as String
196 		 * @param locale
197 		 *            to use for date parsing
198 		 * @return requested date object or exception on creation error
199 		 */
200 		public static Date create(String clazz, String content, String format,
201 				String locale) {
202 			Date date = null;
203 			checkOnDateAncestor(clazz);
204 			if (SYSDATE.equals(content)) {
205 				date = CAL.getTime();
206 				try {
207 					PROP_SYSDATE.parse(PROP_SYSDATE.format(date));
208 				} catch (Exception ex) {
209 					throw new DDTTestDataException(
210 							"Error on creation of actual Date object '"
211 									+ content + "'", ex);
212 				}
213 			} else if (SYSTIME.equals(content)) {
214 				date = CAL.getTime();
215 			} else if (DATE_GENERIC.equals(format)) {
216 				Map<String, DDTDateFormat> dateFormatMap = DDTConfiguration
217 						.getInstance().getDateMap();
218 				boolean errorOnParsing = false;
219 				boolean formatFound = false;
220 				for (Map.Entry entry : dateFormatMap.entrySet()) {
221 					String name = (String) entry.getKey();
222 					DDTDateFormat formater = (DDTDateFormat) entry.getValue();
223 					try {
224 						if (formater.toString().length() == content.length()) {
225 							date = formater.parse(content);
226 							errorOnParsing = false;
227 							formatFound = true;
228 							break;
229 						}
230 					} catch (Exception ex) {
231 						log.warn("Error on creation of Date object '" + content
232 								+ "' by format '" + formater + "'");
233 						errorOnParsing = true;
234 					}
235 				}
236 				if (errorOnParsing) {
237 					throw new DDTTestDataException(
238 							"Error on creation of Date object (format="
239 									+ format + ", content=" + content + ")");
240 				}
241 				if (!formatFound) {
242 					throw new DDTTestDataException(
243 							"Error on creation of Date object, format not found. (format="
244 									+ format + ", content=" + content + ")");
245 				}
246 			} else {
247 				DDTDateFormat formater = selectDateFormater(format, locale);
248 				try {
249 					if (formater.toString().length() == content.length()) {
250 						date = formater.parse(content);
251 					} else {
252 						throw new DDTTestDataException("Date Formater '"
253 								+ formater + "' does not match '" + content
254 								+ "'.");
255 					}
256 				} catch (ParseException ex) {
257 					throw new DDTTestDataException(
258 							"Error on creation of Date object '" + content
259 									+ "' by format '" + format
260 									+ "' and LOCALE '" + locale + "'", ex);
261 				}
262 			}
263 			return date;
264 		}
265 
266 		/**
267 		 * @param format
268 		 * @param locale
269 		 * @return
270 		 */
271 		private static DDTDateFormat selectDateFormater(String format,
272 				String locale) {
273 			DDTDateFormat formater;
274 			// check for predefined format first
275 			Map<String, DDTDateFormat> dateFormatMap = DDTConfiguration
276 					.getInstance().getDateMap();
277 			if (dateFormatMap.containsKey(format)) {
278 				formater = dateFormatMap.get(format);
279 			} else {
280 				Locale myLocale;
281 				if (DATE_LOCALE.equals(locale)) {
282 					myLocale = DDTConfiguration.getInstance().getActiveLocale();
283 				} else {
284 					String[] details = locale.split("_");
285 					if (details.length != 2) {
286 						throw new DDTTestDataException("Provided wrong locale "
287 								+ locale);
288 					}
289 					myLocale = new Locale(details[0], details[1]);
290 				}
291 
292 				formater = new DDTDateFormat(format, myLocale);
293 			}
294 			return formater;
295 		}
296 
297 		private static void checkOnDateAncestor(String clazz) {
298 			boolean check = false;
299 			try {
300 				Class checkClazz = Class.forName(clazz);
301 				Set superElements = ClassAnalyser.getSuperElements(checkClazz);
302 				check = checkClazz.equals(java.util.Date.class)
303 						|| superElements.contains(java.util.Date.class);
304 			} catch (Exception ex) {
305 				throw new DDTTestDataException("Class " + clazz
306 						+ " is no ancestor of java.util.Date.", ex);
307 			}
308 			if (!check) {
309 				throw new DDTTestDataException("Class " + clazz
310 						+ " is no ancestor of java.util.Date.");
311 			}
312 		}
313 
314 	}
315 }