View Javadoc

1   //$Id: SubelementCreatorAction.java 381 2011-05-26 12:58:13Z 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.lang.reflect.Constructor;
41  import java.util.HashMap;
42  import java.util.List;
43  import java.util.Map;
44  import java.util.Vector;
45  
46  import junitx.ddtunit.DDTException;
47  import junitx.ddtunit.data.DDTTestDataException;
48  import junitx.ddtunit.data.IDataSet;
49  import junitx.ddtunit.data.TestDataSet;
50  import junitx.ddtunit.data.TypedObject;
51  import junitx.ddtunit.data.processing.CollectionCreatorAction.CollectionReferenceInfo;
52  import junitx.ddtunit.util.ClassAnalyser;
53  import junitx.ddtunit.util.PrivilegedAccessor;
54  
55  /**
56   * This class contains object state and other information to create object from
57   * SAX event stream.
58   * 
59   * @author jg
60   */
61  public class SubelementCreatorAction extends ActionBase {
62      /**
63       * 
64       * Constructor used as standard constructor to instanciate actions of this
65       * type
66       * 
67       * @param attrMap
68       */
69      public SubelementCreatorAction(Map attrMap) {
70          super(attrMap);
71      }
72  
73      /*
74       * (non-Javadoc)
75       * 
76       * @see junitx.ddtunit.parser.ActionBase#process()
77       */
78      public IAction process() {
79          log.debug("process SubelementCreator - START");
80          IAction rootAction = this.getPrevious();
81          if (!this.successorProcessed) {
82              processNoSuccessor();
83          }
84          if (rootAction != null) {
85              String hintValue = rootAction.getHint();
86  
87              if (HintTypes.COLLECTION.equals(hintValue)
88                      || HintTypes.MAP.equals(hintValue)
89                      || HintTypes.ATTRLIST.equals(hintValue)
90                      || HintTypes.FIELDS.equals(hintValue)
91                      || HintTypes.CONSTRUCTOR.equals(hintValue)
92                      || HintTypes.CALL.equals(hintValue)
93                      || HintTypes.BEAN.equals(hintValue)
94                      || HintTypes.ARRAY.equals(hintValue)
95                      || HintTypes.INTERNAL_MAPENTRY.equals(hintValue)) {
96                  if (hasReferenceInfo()) {
97                      rootAction.processSuccessor(this);
98                  } else {
99                      rootAction.processSuccessor(this);
100                 }
101             } else {
102                 throw new DDTException("Unknown hint (" + hintValue
103                         + ")- stop processing.");
104             }
105         } else {
106             // process obj element - no rootAction exiats
107             if (hasReferenceInfo()) {
108                 TypedObject destObject;
109                 if (this.attrMap.get(ParserConstants.XML_ATTR_TYPE) == null) {
110                     destObject = new TypedObject(getAttribute("refid"));
111                 } else {
112                     destObject = new TypedObject(getAttribute("refid"),
113                             getType());
114                 }
115                 IReferenceInfo refInfo = new ObjectReferenceInfo(this
116                     .getObject(), destObject);
117                 add(refInfo);
118             }
119         }
120         this.pop();
121         return this;
122     }
123 
124     /**
125      * If no content on tag is provided just instanciate object by default
126      * constructor.
127      */
128     public void processNoSuccessor() {
129         // process direct object call providing no parameters (empty tag)
130         if (hasReferenceInfo()) {
131             TypedObject destObject;
132             if (this.attrMap.get(ParserConstants.XML_ATTR_TYPE) == null) {
133                 destObject = new TypedObject(getAttribute("refid"));
134             } else {
135                 destObject = new TypedObject(getAttribute("refid"), getType());
136             }
137             IReferenceInfo refInfo;
138             if (this.getPrevious() == null) {
139                 refInfo = new ObjectReferenceInfo(this.getObject(), destObject);
140             } else if (HintTypes.ATTRLIST.equals(this.getPrevious().getHint())) {
141                 refInfo = new SubelementReferenceInfo(this.getPrevious()
142                     .getPrevious().getObject(), this.getId(), destObject);
143                 add(refInfo);
144             } else if (HintTypes.COLLECTION.equals(this.getPrevious().getHint())){
145               // if a collection type is processed the subelement must be specified as ARGLIST
146               // the reference is processed inside of rootAction
147               this.attrMap.put(ParserConstants.XML_ATTR_HINT, HintTypes.ATTRLIST.toString());
148             } else if (HintTypes.INTERNAL_MAPENTRY.equals(this.getPrevious().getHint())){
149               // if a map 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, HintTypes.ATTRLIST.toString());
152             } else {
153                 refInfo = new SubelementReferenceInfo(this.getPrevious()
154                     .getObject(), this.getId(), destObject);
155                 add(refInfo);
156             }
157         } else {
158             Map attribMap = new HashMap();
159             IAction action = ActionFactory.getAction(
160                 ActionState.ATTRLIST_CREATION, attribMap);
161             action.inject();
162             action.setValue(new Vector());
163             this.insert(action);
164             action.process();
165         }
166     }
167 
168     /*
169      * (non-Javadoc)
170      * 
171      * @see junitx.ddtunit.parser.ActionBase#inject()
172      */
173     public IAction inject() {
174         String type = (String) this.attrMap.get(ParserConstants.XML_ATTR_TYPE);
175         String id = (String) this.attrMap.get(ParserConstants.XML_ATTR_ID);
176         this.injectedObject = new TypedObject(id, type);
177         if (attrMap.containsKey(ParserConstants.XML_ATTR_BASEID)) {
178             String baseId = (String) attrMap
179                 .get(ParserConstants.XML_ATTR_BASEID);
180             TypedObject baseObj = InstanceFactory.getInstance().getObject(
181                 baseId, type);
182             this.injectedObject.setValue(baseObj.getValue());
183         }
184         return this;
185     }
186 
187     public void processSuccessor(IAction successor) {
188         log.debug("processSuccessor(" + successor + ") - START");
189         // create attribute list action and insert after rootAction
190         if (HintTypes.ATTRLIST.equals(successor.getHint())) {
191             List fields = (List) successor.getObject().getValue();
192             TypedObject field = null;
193             try {
194                 this.getType();
195                 // if TypedObject is not instanciated and it is no call
196                 // operation
197                 // create object by default constructor
198                 if (this.getValue() == null){
199 //                        && !HintTypes.CALL.equals(this.getHint())) {
200                     this.createObject();
201                 }
202                 for (int count = 0; count < fields.size(); count++) {
203                     field = (TypedObject) fields.get(count);
204                     // provided
205                     if (HintTypes.INTERNAL_MAPENTRY.equals(this.getHint())) {
206                         PrivilegedAccessor.setFieldValue(this.getValue(), field
207                             .getId(), field);
208                     } else {
209                         PrivilegedAccessor.setFieldValue(this.getValue(), field
210                             .getId(), field.getValue());
211                     }
212                 }
213             } catch (Exception ex) {
214                 StringBuffer sb = new StringBuffer("Error on setting field ")
215                     .append(field).append(" on ").append(this.getType())
216                     .append(". Check if hint is correct.");
217                 throw new DDTException(sb.toString(), ex);
218             }
219         } else if (HintTypes.INTERNAL_MAPENTRY.equals(this.getHint())) {
220             try {
221                 PrivilegedAccessor.setFieldValue(this.getValue(), successor
222                     .getId(), successor.getObject());
223             } catch (Exception ex) {
224                 throw new DDTTestDataException("Error filling map entry "
225                         + this.getId(), ex);
226             }
227         } else if (HintTypes.CONTENT.equals(successor.getHint())) {
228             String content = successor.getValue().toString();
229             if (ContentCreatorAction.CONTENT_EMPTY.equals(content)) {
230                 content = "";
231                 this.setValue(content);
232             } else if (ContentCreatorAction.CONTENT_BLANK.equals(content)) {
233                 content = " ";
234                 this.setValue(content);
235             } else if (!ContentCreatorAction.CONTENT_NULL.equals(content)) {
236                 // separate specific constructors for base types
237                 try {
238                     Constructor constr = null;
239                     Object obj = null;
240                     String myType = this.getType();
241                     if ("java.lang.Character".equals(myType)) {
242                         obj = new Character(content.charAt(0));
243                     } else {
244                         constr = (Constructor) ClassAnalyser
245                             .findMethodByParams(myType,
246                                 ClassAnalyser.CLASS_CONSTRUCTOR,
247                                 new Class[] { java.lang.String.class });
248                         obj = constr.newInstance(new Object[] { content });
249                     }
250                     this.setValue(obj);
251                 } catch (DDTTestDataException ex) {
252                     throw ex;
253                 } catch (Exception ex) {
254                     throw new DDTTestDataException(
255                             "Error on creation using constructor(String) of "
256                                     + this.getType() + "(\""
257                                     + successor.getValue().toString() + "\")",
258                             ex);
259                 }
260             }
261         } else {
262             // ARRAY successor
263             Map attribMap = new HashMap();
264             IAction attribListAction = ActionFactory.getAction(
265                 ActionState.ATTRLIST_CREATION, attribMap);
266             this.insert(attribListAction);
267             try {
268                 // create container
269                 attribListAction.createObject();
270                 // initialize with first list element
271                 ((List) attribListAction.getValue()).add(successor.getObject());
272             } catch (Exception ex) {
273                 throw new DDTException("Error on action processing", ex);
274             }
275         }
276         this.successorProcessed = true;
277     }
278 
279     class SubelementReferenceInfo extends ReferenceInfoBase {
280         private String field;
281 
282         /**
283          * @param source
284          * @param destination
285          */
286         public SubelementReferenceInfo(TypedObject source, String field,
287                 TypedObject destination) {
288             super(source, destination);
289             this.field = field;
290         }
291 
292         public void resolve(IDataSet dataSet, String groupId, String testId) {
293             if (ParserConstants.UNKNOWN.equals(groupId)
294                     && ParserConstants.UNKNOWN.equals(testId)) {
295                 doResolution(dataSet);
296             } else if (!ParserConstants.UNKNOWN.equals(testId)) {
297                 IDataSet groupSet = dataSet.get(groupId);
298                 IDataSet testDataSet = null;
299                 if (groupSet != null) {
300                     testDataSet = groupSet.get(testId);
301                 }
302                 doResolution(testDataSet);
303             } else {
304                 throw new DDTTestDataException(
305                         "Do not process group data without testId");
306             }
307         }
308 
309         /**
310          * @param dataSet to resolve reference to
311          */
312         private void doResolution(IDataSet dataSet) {
313             TypedObject dest = dataSet.findObject(this.getDestId(), this
314                 .getDestType());
315             TypedObject source = dataSet.getObject(this.getSourceId(), this
316                 .getSourceType());
317             if (source == null && dataSet instanceof TestDataSet) {
318                 source = ((TestDataSet) dataSet).getAssert(this.getSourceId(),
319                     this.getSourceType());
320             }
321             if (dest != null && source != null) {
322                 try {
323                     PrivilegedAccessor.setFieldValue(source.getValue(),
324                         this.field, dest.getValue());
325                 } catch (IllegalAccessException ex) {
326                     throw new DDTTestDataException(
327                             "Error resolving reference of " + dest
328                                     + " to field " + this.field + " on "
329                                     + source, ex);
330                 } catch (NoSuchFieldException ex) {
331                     throw new DDTTestDataException(
332                             "Error resolving reference of " + dest
333                                     + " to field " + this.field + " on "
334                                     + source, ex);
335                 }
336             } else {
337                 throw new DDTTestDataException(
338                         "Error on processing references on testdata.");
339             }
340         }
341 
342         /**
343          * If this object is referencing to the provided info object then raise
344          * rank of referenced object info.<br/> A reference from an
345          * ObjectAction to another Action is detected if destination info is
346          * equal to the source info of provided info.
347          * 
348          * @param info to check reference on and raise rank respectivly
349          */
350         public void raiseRankOf(IReferenceInfo info) {
351             if (this.getDestId().equals(info.getSourceId())
352                     && (this.getDestType().equals(info.getSourceType()) || TypedObject.UNKNOWN_TYPE
353                         .equals(info.getDestType()))) {
354                 if (this.getRank() >= info.getRank()) {
355                     info.setRank(this.getRank() + 1);
356                 }
357             }
358         }
359     }
360 
361 }