001: //$Id: SubelementCreatorAction.java 331 2007-10-18 19:41:43Z jg_hamburg $
002: /********************************************************************************
003: * DDTUnit, a Datadriven Approach to Unit- and Moduletesting
004: * Copyright (c) 2004, Joerg and Kai Gellien
005: * All rights reserved.
006: *
007: * The Software is provided under the terms of the Common Public License 1.0
008: * as provided with the distribution of DDTUnit in the file cpl-v10.html.
009: * Redistribution and use in source and binary forms, with or without
010: * modification, are permitted provided that the following conditions
011: * are met:
012: *
013: * + Redistributions of source code must retain the above copyright
014: * notice, this list of conditions and the following disclaimer.
015: *
016: * + Redistributions in binary form must reproduce the above
017: * copyright notice, this list of conditions and the following
018: * disclaimer in the documentation and/or other materials provided
019: * with the distribution.
020: *
021: * + Neither the id of the authors or DDTUnit, nor the
022: * names of its contributors may be used to endorse or promote
023: * products derived from this software without specific prior
024: * written permission.
025: *
026: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
027: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
028: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
029: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
030: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
031: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
032: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
033: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
034: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
035: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
036: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
037: ********************************************************************************/package junitx.ddtunit.data.processing;
038:
039: import java.lang.reflect.Constructor;
040: import java.util.HashMap;
041: import java.util.List;
042: import java.util.Map;
043: import java.util.Vector;
044:
045: import junitx.ddtunit.DDTException;
046: import junitx.ddtunit.data.DDTTestDataException;
047: import junitx.ddtunit.data.IDataSet;
048: import junitx.ddtunit.data.TestDataSet;
049: import junitx.ddtunit.data.TypedObject;
050: import junitx.ddtunit.data.processing.CollectionCreatorAction.CollectionReferenceInfo;
051: import junitx.ddtunit.util.ClassAnalyser;
052: import junitx.ddtunit.util.PrivilegedAccessor;
053:
054: /**
055: * This class contains object state and other information to create object from
056: * SAX event stream.
057: *
058: * @author jg
059: */
060: public class SubelementCreatorAction extends ActionBase {
061: /**
062: *
063: * Constructor used as standard constructor to instanciate actions of this
064: * type
065: *
066: * @param attrMap
067: */
068: public SubelementCreatorAction(Map attrMap) {
069: super (attrMap);
070: }
071:
072: /*
073: * (non-Javadoc)
074: *
075: * @see junitx.ddtunit.parser.ActionBase#process()
076: */
077: public IAction process() {
078: log.debug("process SubelementCreator - START");
079: IAction rootAction = this .getPrevious();
080: if (!this .successorProcessed) {
081: processNoSuccessor();
082: }
083: if (rootAction != null) {
084: String hintValue = rootAction.getHint();
085:
086: if (HintTypes.COLLECTION.equals(hintValue)
087: || HintTypes.MAP.equals(hintValue)
088: || HintTypes.ATTRLIST.equals(hintValue)
089: || HintTypes.FIELDS.equals(hintValue)
090: || HintTypes.CONSTRUCTOR.equals(hintValue)
091: || HintTypes.CALL.equals(hintValue)
092: || HintTypes.BEAN.equals(hintValue)
093: || HintTypes.ARRAY.equals(hintValue)
094: || HintTypes.INTERNAL_MAPENTRY.equals(hintValue)) {
095: if (hasReferenceInfo()) {
096: rootAction.processSuccessor(this );
097: } else {
098: rootAction.processSuccessor(this );
099: }
100: } else {
101: throw new DDTException("Unknown hint (" + hintValue
102: + ")- stop processing.");
103: }
104: } else {
105: // process obj element - no rootAction exiats
106: if (hasReferenceInfo()) {
107: TypedObject destObject;
108: if (this .attrMap.get(ParserConstants.XML_ATTR_TYPE) == null) {
109: destObject = new TypedObject(getAttribute("refid"));
110: } else {
111: destObject = new TypedObject(getAttribute("refid"),
112: getType());
113: }
114: IReferenceInfo refInfo = new ObjectReferenceInfo(this
115: .getObject(), destObject);
116: add(refInfo);
117: }
118: }
119: this .pop();
120: return this ;
121: }
122:
123: /**
124: * If no content on tag is provided just instanciate object by default
125: * constructor.
126: */
127: public void processNoSuccessor() {
128: // process direct object call providing no parameters (empty tag)
129: if (hasReferenceInfo()) {
130: TypedObject destObject;
131: if (this .attrMap.get(ParserConstants.XML_ATTR_TYPE) == null) {
132: destObject = new TypedObject(getAttribute("refid"));
133: } else {
134: destObject = new TypedObject(getAttribute("refid"),
135: getType());
136: }
137: IReferenceInfo refInfo;
138: if (this .getPrevious() == null) {
139: refInfo = new ObjectReferenceInfo(this .getObject(),
140: destObject);
141: } else if (HintTypes.ATTRLIST.equals(this .getPrevious()
142: .getHint())) {
143: refInfo = new SubelementReferenceInfo(this
144: .getPrevious().getPrevious().getObject(), this
145: .getId(), destObject);
146: add(refInfo);
147: } else if (HintTypes.COLLECTION.equals(this .getPrevious()
148: .getHint())) {
149: // if a collection type is processed the subelement must be specified as ARGLIST
150: // the reference is processed inside of rootAction
151: this .attrMap.put(ParserConstants.XML_ATTR_HINT,
152: HintTypes.ATTRLIST.toString());
153: } else {
154: refInfo = new SubelementReferenceInfo(this
155: .getPrevious().getObject(), this .getId(),
156: destObject);
157: add(refInfo);
158: }
159: } else {
160: Map attribMap = new HashMap();
161: IAction action = ActionFactory.getAction(
162: ActionState.ATTRLIST_CREATION, attribMap);
163: action.inject();
164: action.setValue(new Vector());
165: this .insert(action);
166: action.process();
167: }
168: }
169:
170: /*
171: * (non-Javadoc)
172: *
173: * @see junitx.ddtunit.parser.ActionBase#inject()
174: */
175: public IAction inject() {
176: String type = (String) this .attrMap
177: .get(ParserConstants.XML_ATTR_TYPE);
178: String id = (String) this .attrMap
179: .get(ParserConstants.XML_ATTR_ID);
180: this .injectedObject = new TypedObject(id, type);
181: if (attrMap.containsKey(ParserConstants.XML_ATTR_BASEID)) {
182: String baseId = (String) attrMap
183: .get(ParserConstants.XML_ATTR_BASEID);
184: TypedObject baseObj = InstanceFactory.getInstance()
185: .getObject(baseId, type);
186: this .injectedObject.setValue(baseObj.getValue());
187: }
188: return this ;
189: }
190:
191: public void processSuccessor(IAction successor) {
192: log.debug("processSuccessor(" + successor + ") - START");
193: // create attribute list action and insert after rootAction
194: if (HintTypes.ATTRLIST.equals(successor.getHint())) {
195: List fields = (List) successor.getObject().getValue();
196: TypedObject field = null;
197: try {
198: this .getType();
199: // if TypedObject is not instanciated and it is no call
200: // operation
201: // create object by default constructor
202: if (this .getValue() == null) {
203: // && !HintTypes.CALL.equals(this.getHint())) {
204: this .createObject();
205: }
206: for (int count = 0; count < fields.size(); count++) {
207: field = (TypedObject) fields.get(count);
208: // provided
209: if (HintTypes.INTERNAL_MAPENTRY.equals(this
210: .getHint())) {
211: PrivilegedAccessor.setFieldValue(this
212: .getValue(), field.getId(), field);
213: } else {
214: PrivilegedAccessor.setFieldValue(this
215: .getValue(), field.getId(), field
216: .getValue());
217: }
218: }
219: } catch (Exception ex) {
220: StringBuffer sb = new StringBuffer(
221: "Error on setting field ").append(field)
222: .append(" on ").append(this .getType()).append(
223: ". Check if hint is correct.");
224: throw new DDTException(sb.toString(), ex);
225: }
226: } else if (HintTypes.INTERNAL_MAPENTRY.equals(this .getHint())) {
227: try {
228: PrivilegedAccessor.setFieldValue(this .getValue(),
229: successor.getId(), successor.getObject());
230: } catch (Exception ex) {
231: throw new DDTTestDataException(
232: "Error filling map entry " + this .getId(), ex);
233: }
234: } else if (HintTypes.CONTENT.equals(successor.getHint())) {
235: String content = successor.getValue().toString();
236: if (ContentCreatorAction.CONTENT_EMPTY.equals(content)) {
237: content = "";
238: this .setValue(content);
239: } else if (ContentCreatorAction.CONTENT_BLANK
240: .equals(content)) {
241: content = " ";
242: this .setValue(content);
243: } else if (!ContentCreatorAction.CONTENT_NULL
244: .equals(content)) {
245: // separate specific constructors for base types
246: try {
247: Constructor constr = null;
248: Object obj = null;
249: if ("java.lang.Character".equals(this .getType())) {
250: obj = new Character(content.charAt(0));
251: } else {
252: constr = (Constructor) ClassAnalyser
253: .findMethodByParams(
254: this .getType(),
255: ClassAnalyser.CLASS_CONSTRUCTOR,
256: new Class[] { java.lang.String.class });
257: obj = constr
258: .newInstance(new Object[] { content });
259: }
260: this .setValue(obj);
261: } catch (DDTTestDataException ex) {
262: throw ex;
263: } catch (Exception ex) {
264: throw new DDTTestDataException(
265: "Error on creation using constructor(String) of "
266: + this .getType() + "(\""
267: + successor.getValue().toString()
268: + "\")", ex);
269: }
270: }
271: } else {
272: // ARRAY successor
273: Map attribMap = new HashMap();
274: IAction attribListAction = ActionFactory.getAction(
275: ActionState.ATTRLIST_CREATION, attribMap);
276: this .insert(attribListAction);
277: try {
278: // create container
279: attribListAction.createObject();
280: // initialize with first list element
281: ((List) attribListAction.getValue()).add(successor
282: .getObject());
283: } catch (Exception ex) {
284: throw new DDTException("Error on action processing", ex);
285: }
286: }
287: this .successorProcessed = true;
288: }
289:
290: class SubelementReferenceInfo extends ReferenceInfoBase {
291: private String field;
292:
293: /**
294: * @param source
295: * @param destination
296: */
297: public SubelementReferenceInfo(TypedObject source,
298: String field, TypedObject destination) {
299: super (source, destination);
300: this .field = field;
301: }
302:
303: public void resolve(IDataSet dataSet, String groupId,
304: String testId) {
305: if (ParserConstants.UNKNOWN.equals(groupId)
306: && ParserConstants.UNKNOWN.equals(testId)) {
307: doResolution(dataSet);
308: } else if (!ParserConstants.UNKNOWN.equals(testId)) {
309: IDataSet groupSet = dataSet.get(groupId);
310: IDataSet testDataSet = null;
311: if (groupSet != null) {
312: testDataSet = groupSet.get(testId);
313: }
314: doResolution(testDataSet);
315: } else {
316: throw new DDTTestDataException(
317: "Do not process group data without testId");
318: }
319: }
320:
321: /**
322: * @param dataSet to resolve reference to
323: */
324: private void doResolution(IDataSet dataSet) {
325: TypedObject dest = dataSet.findObject(this .getDestId(),
326: this .getDestType());
327: TypedObject source = dataSet.getObject(this .getSourceId(),
328: this .getSourceType());
329: if (source == null && dataSet instanceof TestDataSet) {
330: source = ((TestDataSet) dataSet).getAssert(this
331: .getSourceId(), this .getSourceType());
332: }
333: if (dest != null && source != null) {
334: try {
335: PrivilegedAccessor.setFieldValue(source.getValue(),
336: this .field, dest.getValue());
337: } catch (IllegalAccessException ex) {
338: throw new DDTTestDataException(
339: "Error resolving reference of " + dest
340: + " to field " + this .field
341: + " on " + source, ex);
342: } catch (NoSuchFieldException ex) {
343: throw new DDTTestDataException(
344: "Error resolving reference of " + dest
345: + " to field " + this .field
346: + " on " + source, ex);
347: }
348: } else {
349: throw new DDTTestDataException(
350: "Error on processing references on testdata.");
351: }
352: }
353:
354: /**
355: * If this object is referencing to the provided info object then raise
356: * rank of referenced object info.<br/> A reference from an
357: * ObjectAction to another Action is detected if destination info is
358: * equal to the source info of provided info.
359: *
360: * @param info to check reference on and raise rank respectivly
361: */
362: public void raiseRankOf(IReferenceInfo info) {
363: if (this .getDestId().equals(info.getSourceId())
364: && (this .getDestType().equals(info.getSourceType()) || TypedObject.UNKNOWN_TYPE
365: .equals(info.getDestType()))) {
366: if (this .getRank() >= info.getRank()) {
367: info.setRank(this .getRank() + 1);
368: }
369: }
370: }
371: }
372:
373: }
|