View Javadoc

1   //$Id: MapCreatorAction.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.util.Collection;
41  import java.util.HashMap;
42  import java.util.List;
43  import java.util.Map;
44  
45  import junitx.ddtunit.DDTException;
46  import junitx.ddtunit.data.DDTTestDataException;
47  import junitx.ddtunit.data.IDataSet;
48  import junitx.ddtunit.data.TestDataSet;
49  import junitx.ddtunit.data.TypedObject;
50  import junitx.ddtunit.data.processing.CollectionCreatorAction.CollectionReferenceInfo;
51  
52  import org.slf4j.Logger;
53  import org.slf4j.LoggerFactory;
54  
55  /**
56   * This class contains object state and other information to create object from
57   * SAX event stream.
58   * @author jg
59   */
60  public class MapCreatorAction extends ActionBase {
61    private Logger log = LoggerFactory.getLogger(MapCreatorAction.class);
62  
63    /**
64     * Constructor used as standard constructor to instanciate actions of this
65     * type
66     * @param attrMap
67     */
68    public MapCreatorAction(Map<String, String> attrMap) {
69      super(attrMap);
70    }
71  
72    /*
73     * (non-Javadoc)
74     * @see junitx.ddtunit.parser.ActionBase#process()
75     */
76    public IAction process() {
77      log.debug("process MapCreator Action - START");
78      if (!this.successorProcessed) {
79        processNoSuccessor();
80      }
81      IAction rootAction = getPrevious();
82      if (rootAction != null) {
83        String hintValue = rootAction.getHint();
84        if (HintTypes.CONSTRUCTOR.equals(hintValue)
85            || HintTypes.CALL.equals(hintValue)) {
86          log.debug("createRecord - process addition of field '" + this.getId()
87              + "'...");
88          // create attribute list action and insert after rootAction
89          Map attribMap = new HashMap();
90          IAction attribListAction = ActionFactory.getAction(
91              ActionState.ATTRLIST_CREATION, attribMap);
92          rootAction.insert(attribListAction);
93          try {
94            // create container
95            attribListAction.createObject();
96            // initialize with first list element
97            ((List) attribListAction.getValue()).add(this.getObject());
98            rootAction = attribListAction;
99          } catch (Exception ex) {
100           throw new DDTException("Error on action processing", ex);
101         }
102 
103       } else if (HintTypes.FIELDS.equals(hintValue)) {
104         rootAction.processSuccessor(this);
105       } else if (HintTypes.COLLECTION.equals(hintValue)) {
106         rootAction.processSuccessor(this);
107       } else if (HintTypes.MAP.equals(hintValue)) {
108         rootAction.processSuccessor(this);
109       } else {
110         throw new UnsupportedOperationException(
111             "MapCreatorAction does not support hint '" + hintValue + "'");
112       }
113     } else {
114       rootAction = this;
115     }
116     pop();
117     return rootAction;
118   }
119 
120   public void processSuccessor(IAction successor) {
121     log.debug("processSuccessor(" + successor + ") - START");
122     if (HintTypes.CONTENT.equals(successor.getHint())) {
123       String content = successor.getValue().toString();
124       if (!ContentCreatorAction.CONTENT_NULL.equals(content)) {
125         throw new DDTException("Only \"!NULL!\" supported in Map type");
126       }
127     } else {
128       try {
129         this.createObject();
130         // check if successor contains referenceInfo
131         MapEntry entry = (MapEntry) successor.getValue();
132         if (HintTypes.INTERNAL_MAPENTRY.equals(successor.getHint())
133             && ((SubelementCreatorAction) successor).hasReferenceInfo()) {
134           TypedObject destObject;
135           String linkType = successor.getAttribute(ParserConstants.XML_ATTR_VALUETYPE);
136           if (linkType == null) {
137             destObject = new TypedObject(successor.getAttribute("refid"));
138           } else {
139             destObject = new TypedObject(successor.getAttribute("refid"),
140                 linkType);
141           }
142           IReferenceInfo refInfo;
143           refInfo = new MapValueReferenceInfo(this.getObject(), destObject,
144               (String) entry.getKey().getValue());
145           add(refInfo);
146         } else {
147           if (entry == null || entry.getKey() == null
148               || entry.getValue() == null) {
149             throw new DDTException(
150                 "<key> or <value> tag of map element missing");
151           }
152           ((Map<Object, Object>) this.getValue()).put(
153               entry.getKey().getValue(), entry.getValue().getValue());
154         }
155       } catch (Exception e) {
156         throw new DDTException("Error on map processing", e);
157       }
158     }
159     this.successorProcessed = true;
160   }
161 
162   public void processNoSuccessor() {
163     this.createObject();
164   }
165 
166   /*
167    * (non-Javadoc)
168    * @see junitx.ddtunit.parser.ActionBase#inject()
169    */
170   public IAction inject() {
171     String type = (String) this.attrMap.get(ParserConstants.XML_ATTR_TYPE);
172     String id = (String) this.attrMap.get(ParserConstants.XML_ATTR_ID);
173     // check if type is based on java.util.Collection
174     try {
175       Class clazz = Class.forName(type);
176       Class superclazz = clazz;
177 
178       boolean found = false;
179       while (!found && superclazz != null) {
180         Class[] interfaces = superclazz.getInterfaces();
181         for (int i = 0; i < interfaces.length; i++) {
182           Class iface = interfaces[i];
183           if ("java.util.Map".equals(iface.getName())) {
184             found = true;
185             break;
186           }
187         }
188         if (found) {
189           break;
190         }
191         superclazz = superclazz.getSuperclass();
192       }
193       if (!found) {
194         throw new DDTException("Container class '" + type
195             + "' does not implement java.util.Map.");
196       }
197     } catch (Exception ex) {
198       throw new DDTException("Container class '" + type
199           + "' does not implement java.util.Map.", ex);
200     }
201     this.injectedObject = new TypedObject(id, type);
202     return this;
203   }
204 
205   /**
206    * Class for processing references in value of map entrys
207    * @author JGELLIEN
208    */
209   static class MapValueReferenceInfo extends ReferenceInfoBase {
210     protected String key;
211 
212     public MapValueReferenceInfo(TypedObject source, TypedObject destination,
213         String key) {
214       super(source, destination);
215       this.key = key;
216     }
217 
218     public void raiseRankOf(IReferenceInfo info) {
219       if (this.getDestId().equals(info.getSourceId())
220           && (this.getDestType().equals(info.getSourceType()) || TypedObject.UNKNOWN_TYPE
221               .equals(info.getDestType()))) {
222         if (this.getRank() >= info.getRank()) {
223           info.setRank(this.getRank() + 1);
224         }
225       }
226     }
227 
228     public void resolve(IDataSet dataSet, String groupId, String testId) {
229       if (ParserConstants.UNKNOWN.equals(groupId)
230           && ParserConstants.UNKNOWN.equals(testId)) {
231         doResolution(dataSet);
232       } else if (!ParserConstants.UNKNOWN.equals(testId)) {
233         IDataSet groupSet = dataSet.get(groupId);
234         IDataSet testDataSet = null;
235         if (groupSet != null) {
236           testDataSet = groupSet.get(testId);
237         }
238         doResolution(testDataSet);
239       } else {
240         throw new DDTTestDataException(
241             "Do not process group data without testId");
242       }
243     }
244 
245     /**
246      * @param dataSet to resolve reference to
247      */
248     private void doResolution(IDataSet dataSet) {
249       TypedObject dest = dataSet.findObject(this.getDestId(),
250           this.getDestType());
251       TypedObject source = dataSet.getObject(this.getSourceId(),
252           this.getSourceType());
253       if (source == null && dataSet instanceof TestDataSet) {
254         source = ((TestDataSet) dataSet).getAssert(this.getSourceId(),
255             this.getSourceType());
256       }
257       if (dest != null && source != null) {
258         ((Map) source.getValue()).put(this.key, dest.getValue());
259       } else {
260         throw new DDTTestDataException(
261             "Error on processing references on testdata.");
262       }
263     }
264 
265   }
266 
267 }