View Javadoc

1   //$Id: CollectionCreatorAction.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.util.Collection;
41  import java.util.List;
42  import java.util.Map;
43  
44  import junitx.ddtunit.DDTException;
45  import junitx.ddtunit.data.DDTTestDataException;
46  import junitx.ddtunit.data.IDataSet;
47  import junitx.ddtunit.data.TestDataSet;
48  import junitx.ddtunit.data.TypedObject;
49  
50  import org.slf4j.Logger;
51  import org.slf4j.LoggerFactory;
52  
53  /**
54   * This class contains object state and other information to create object from
55   * SAX event stream.
56   * 
57   * @author jg
58   */
59  public class CollectionCreatorAction extends ActionBase {
60  	protected Logger log = LoggerFactory.getLogger(CollectionCreatorAction.class);
61  	protected int elementCount;
62  
63  	/**
64  	 * 
65  	 * Constructor used as standard constructor to instanciate actions of this
66  	 * type
67  	 * 
68  	 * @param attrMap
69  	 */
70  	public CollectionCreatorAction(Map<String, String> attrMap) {
71  		super(attrMap);
72  		this.elementCount = 0;
73  	}
74  
75  	/*
76  	 * (non-Javadoc)
77  	 * 
78  	 * @see junitx.ddtunit.parser.ActionBase#process()
79  	 */
80  	public IAction process() {
81  		log.debug("process CollectionCreator - START");
82  		IAction rootAction = getPrevious();
83  		if (!this.successorProcessed) {
84  			processNoSuccessor();
85  		}
86  		if (rootAction != null) {
87  			String hintValue = rootAction.getHint();
88  
89  			if (HintTypes.COLLECTION.equals(hintValue)) {
90  				rootAction.processSuccessor(this);
91  			} else if (HintTypes.ARRAY.equals(hintValue)) {
92  				rootAction.processSuccessor(this);
93  			} else if (HintTypes.ATTRLIST.equals(hintValue)) {
94  				rootAction.processSuccessor(this);
95  			} else if (HintTypes.BEAN.equals(hintValue)) {
96  				rootAction.processSuccessor(this);
97  			} else if (HintTypes.FIELDS.equals(hintValue)) {
98  				rootAction.processSuccessor(this);
99  			} else {
100 				throw new UnsupportedOperationException(
101 						"CollectionCreatorAction does not support hint '"
102 								+ hintValue + "'");
103 			}
104 		} else {
105 			rootAction = this;
106 		}
107 		// if rootAction is null simply return action -> base object is a
108 		// collection
109 		pop();
110 		// do not forget to process the actual root element
111 		IAction previousRootAction = rootAction.getPrevious();
112 		if (previousRootAction != null) {
113 			if (!((previousRootAction instanceof CollectionCreatorAction) || (previousRootAction instanceof SubelementCreatorAction))) {
114 				rootAction = rootAction.process();
115 			}
116 		}
117 		return rootAction;
118 	}
119 
120 	public void processNoSuccessor() {
121 		this.createObject();
122 	}
123 
124 	public void processSuccessor(IAction successor) {
125 		log.debug("processSuccessor(" + successor + ") - START");
126 		if (HintTypes.CONTENT.equals(successor.getHint())) {
127 			String content = successor.getValue().toString();
128 			if (!ContentCreatorAction.CONTENT_NULL.equals(content)) {
129 				throw new DDTException(
130 						"Only \"!NULL!\" supported in Collection type");
131 			}
132 		} else {
133 			try {
134 				this.createObject();
135 				this.elementCount += 1;
136 				// if no reference is set or if collection is sorted simply add
137 				// object (maybe an empty one)
138 
139 				// check if successor contains referenceInfo
140 				if (HintTypes.ATTRLIST.equals(successor.getHint())
141 						&& ((SubelementCreatorAction) successor)
142 								.hasReferenceInfo()) {
143 					TypedObject destObject;
144 					String linkType = successor.getType();
145 					if (linkType == null) {
146 						destObject = new TypedObject(successor
147 								.getAttribute("refid"));
148 					} else {
149 						destObject = new TypedObject(successor
150 								.getAttribute("refid"), linkType);
151 					}
152 					IReferenceInfo refInfo;
153 					if (isSorted()) {
154 						refInfo = new CollectionReferenceInfo(this.getObject(),
155 								destObject, this.elementCount);
156 						// reserve position for referenced object
157 						((List) this.getValue()).add(null);
158 					} else {
159 						refInfo = new CollectionReferenceInfo(this.getObject(),
160 								destObject, CollectionReferenceInfo.noPosition);
161 					}
162 					add(refInfo);
163 				} else {
164 					((Collection) this.getValue()).add(successor.getValue());
165 				}
166 			} catch (Exception ex) {
167 				throw new DDTException(
168 						"Error on Collection element processing", ex);
169 			}
170 		}
171 		this.successorProcessed = true;
172 	}
173 
174 	private boolean isSorted() {
175 		boolean found = verifyInterface(this.getType(), List.class);
176 		return found;
177 	}
178 
179 	/*
180 	 * (non-Javadoc)
181 	 * 
182 	 * @see junitx.ddtunit.parser.ActionBase#inject()
183 	 */
184 	public IAction inject() {
185 		String type = (String) this.attrMap.get(ParserConstants.XML_ATTR_TYPE);
186 		String id = (String) this.attrMap.get(ParserConstants.XML_ATTR_ID);
187 		// check if type is based on java.util.Collection
188 		try {
189 			if (type == null && ParserConstants.XML_ELEM_ITEM.equals(id)) {
190 				type = TypedObject.UNKNOWN_TYPE;
191 			} else if (HintTypes.ARRAY.equals(this.getHint())) {
192 				// do nothing
193 			} else {
194 				boolean found = verifyInterface(type, Collection.class);
195 				if (!found) {
196 					throw new DDTException("Container class '" + type
197 							+ "' does not implement java.util.Collection.");
198 				}
199 			}
200 		} catch (Exception ex) {
201 			throw new DDTException("Container class '" + type
202 					+ "' does not implement java.util.Collection.", ex);
203 		}
204 		this.injectedObject = new TypedObject(id, type);
205 		return this;
206 	}
207 
208 	private boolean verifyInterface(String clazzType, Class interFace) {
209 		Class clazz;
210 		try {
211 			clazz = Class.forName(clazzType);
212 		} catch (ClassNotFoundException ex) {
213 			throw new DDTTestDataException("Wrong data type " + clazzType, ex);
214 		}
215 		Class superclazz = clazz;
216 		Class[] interfaces = null;
217 		boolean found = false;
218 		while (!found && superclazz != null) {
219 			found = isImplementing(superclazz, interFace);
220 			if (!found) {
221 				superclazz = superclazz.getSuperclass();
222 			}
223 		}
224 		return found;
225 	}
226 
227 	private boolean isImplementing(Class clazz, Class interFace) {
228 		boolean found = false;
229 		Class[] interfaces = clazz.getInterfaces();
230 		for (Class inter : interfaces) {
231 			if (interFace.equals(inter)) {
232 				found = true;
233 				break;
234 			} else {
235 				found = isImplementing(inter, interFace);
236 			}
237 		}
238 		return found;
239 	}
240 
241 	static class CollectionReferenceInfo extends ReferenceInfoBase {
242 		protected int position;
243 		static int noPosition = -1;
244 
245 		public CollectionReferenceInfo(TypedObject source,
246 				TypedObject destination, int position) {
247 			super(source, destination);
248 			this.position = position;
249 		}
250 
251 		public void raiseRankOf(IReferenceInfo info) {
252 			if (this.getDestId().equals(info.getSourceId())
253 					&& (this.getDestType().equals(info.getSourceType()) || TypedObject.UNKNOWN_TYPE
254 							.equals(info.getDestType()))) {
255 				if (this.getRank() >= info.getRank()) {
256 					info.setRank(this.getRank() + 1);
257 				}
258 			}
259 		}
260 
261 		public void resolve(IDataSet dataSet, String groupId, String testId) {
262 			if (ParserConstants.UNKNOWN.equals(groupId)
263 					&& ParserConstants.UNKNOWN.equals(testId)) {
264 				doResolution(dataSet);
265 			} else if (!ParserConstants.UNKNOWN.equals(testId)) {
266 				IDataSet groupSet = dataSet.get(groupId);
267 				IDataSet testDataSet = null;
268 				if (groupSet != null) {
269 					testDataSet = groupSet.get(testId);
270 				}
271 				doResolution(testDataSet);
272 			} else {
273 				throw new DDTTestDataException(
274 						"Do not process group data without testId");
275 			}
276 		}
277 
278 		/**
279 		 * @param dataSet
280 		 *            to resolve reference to
281 		 */
282 		private void doResolution(IDataSet dataSet) {
283 			TypedObject dest = dataSet.findObject(this.getDestId(), this
284 					.getDestType());
285 			TypedObject source = dataSet.getObject(this.getSourceId(), this
286 					.getSourceType());
287 			if (source == null && dataSet instanceof TestDataSet) {
288 				source = ((TestDataSet) dataSet).getAssert(this.getSourceId(),
289 						this.getSourceType());
290 			}
291 			if (dest != null && source != null) {
292 				// if unsorted collection then just add to end
293 				if (this.position == this.noPosition) {
294 					((Collection) source.getValue()).add(dest.getValue());
295 				} else {
296 					((List) source.getValue()).set(this.position - 1, dest
297 							.getValue());
298 				}
299 			} else {
300 				throw new DDTTestDataException(
301 						"Error on processing references on testdata.");
302 			}
303 		}
304 
305 	}
306 }