View Javadoc
1   /*
2    * Copyright (C) 2010-2014 Hamburg Sud and the contributors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.aludratest.service.edifactfile.edifatto;
17  
18  import java.io.ByteArrayInputStream;
19  import java.io.OutputStream;
20  import java.util.ArrayList;
21  import java.util.List;
22  import java.util.Map;
23  
24  import javax.xml.namespace.QName;
25  import javax.xml.xpath.XPathConstants;
26  
27  import org.aludratest.content.edifact.EdifactContent;
28  import org.aludratest.exception.FunctionalFailure;
29  import org.aludratest.exception.TechnicalException;
30  import org.aludratest.service.SystemConnector;
31  import org.aludratest.service.edifactfile.EdifactFileCondition;
32  import org.aludratest.service.edifactfile.EdifactFileInteraction;
33  import org.aludratest.service.edifactfile.EdifactFileVerification;
34  import org.aludratest.service.file.FileService;
35  import org.aludratest.testcase.event.attachment.Attachment;
36  import org.aludratest.testcase.event.attachment.StringAttachment;
37  import org.databene.commons.SystemInfo;
38  import org.databene.edifatto.ComparisonSettings;
39  import org.databene.edifatto.EdiFormatSymbols;
40  import org.databene.edifatto.compare.AggregateDiff;
41  import org.databene.edifatto.compare.ComparisonModel;
42  import org.databene.edifatto.compare.Diff;
43  import org.databene.edifatto.compare.HTMLDiffFormatter;
44  import org.databene.edifatto.format.StandardInterchangeFormatter;
45  import org.databene.edifatto.format.TextTreeInterchangeFormatter;
46  import org.databene.edifatto.model.Interchange;
47  import org.w3c.dom.Element;
48  
49  /**
50   * Action class for {@link EdifattoFileService}, implementing all Edifact action interfaces.
51   * @author Volker Bergmann
52   */
53  public class EdifattoFileAction implements EdifactFileInteraction, EdifactFileVerification, EdifactFileCondition {
54  
55      /** A reference to the underlying FileService. */
56      private FileService fileService;
57  
58      EdifactContent contentHandler;
59  
60      /** The recently expected interchange to be stored as attachment if requested. */
61      private Interchange recentExpectedInterchange;
62  
63      /** The recent actual interchange to be stored as attachment if requested. */
64      private Interchange recentActualInterchange;
65  
66      private AggregateDiff recentDiff;
67  
68      /** Constructor.
69       *  @param contentHandler
70       *  @param fileService */
71      public EdifattoFileAction(EdifactContent contentHandler, FileService fileService) {
72          this.contentHandler = contentHandler;
73          this.fileService = fileService;
74          memorizeInterchanges(null, null, null);
75      }
76  
77      @Override
78      public void setSystemConnector(SystemConnector systemConnector) {
79          // empty implementation
80      }
81  
82      // general file operations -------------------------------------------------
83  
84      @Override
85      public boolean exists(String elementType, String elementName, String filePath) {
86          return fileService.check().exists(filePath);
87      }
88  
89      @Override
90      public void delete(String elementType, String elementName, String filePath) {
91          fileService.perform().delete(filePath);
92      }
93  
94      @Override
95      public void waitUntilExists(String elementType, String elementName, String filePath) {
96          fileService.perform().waitUntilExists(elementType, filePath);
97      }
98  
99      @Override
100     public void waitUntilNotExists(String elementType, String elementName, String filePath) {
101         fileService.perform().waitUntilNotExists(filePath);
102     }
103 
104     // EdifactInteraction interface implementation -----------------------------
105 
106     /** Writes an EDIFACT or X12 interchange to an {@link OutputStream}. */
107     @Override
108     public void writeInterchange(String elementType, String elementName, Interchange interchange, String filePath, boolean overwrite) {
109         memorizeInterchanges(null, null, null);
110         String content = toString(interchange, false);
111         fileService.perform().writeBinaryFile(filePath, content.getBytes(), overwrite); // ENHANCE which encoding to use?
112     }
113 
114     @Override
115     public Interchange readInterchange(String elementType, String elementName, String filePath) {
116         String content = fileService.perform().readTextFile(filePath);
117         ByteArrayInputStream in = new ByteArrayInputStream(content.getBytes()); // ENHANCE which encoding to use?
118         Interchange interchange = contentHandler.readInterchange(in);
119         memorizeInterchanges(null, interchange, null);
120         return interchange;
121     }
122 
123     @Override
124     public Interchange createInterchange(String elementType, String elementName, String templateUri, EdiFormatSymbols symbols, Map<String, Object> variables) {
125         Interchange interchange = contentHandler.createInterchange(templateUri, symbols, variables);
126         memorizeInterchanges(null, interchange, null);
127         return interchange;
128     }
129 
130     // EdifactVerification interface implementation ----------------------------
131 
132     /** Asserts that two EDIFACT or X12 interchanges are equal.
133      *  @throws ValueNotAsExpectedException if they are not equal. */
134     @Override
135     public void assertInterchangesMatch(String elementType, String elementName,
136             Interchange expected, Interchange actual,
137             ComparisonSettings settings, ComparisonModel<Element> model) {
138         AggregateDiff diffs = contentHandler.diff(expected, actual, settings, model);
139         memorizeInterchanges(expected, actual, diffs);
140         if (diffs.getDetailCount() > 0) {
141             String lf = SystemInfo.getLineSeparator();
142             StringBuilder message = new StringBuilder("Interchanges do not match. Found " + diffs.getDetailCount() + " difference");
143             if (diffs.getDetailCount() > 1) {
144                 message.append("s");
145             }
146             for (Diff<?> diff : diffs.getDetails()) {
147                 message.append(lf).append(diff);
148             }
149             throw new FunctionalFailure(message.toString());
150         }
151     }
152 
153     // EdifactCondition interface implementation -------------------------------
154 
155     /**
156      * Creates an XML representation of the interchange and performs an XPath query on it.
157      * @param interchange the interchange to query
158      * @param expression the XPath query to perform
159      * @param returnType determines the type of the returned object:
160      *   {@link XPathConstants#STRING} for a single {@link java.lang.String},
161      *   {@link XPathConstants#NODE} for a single {@link org.w3c.dom.Element},
162      *   {@link XPathConstants#NODESET} for a {@link org.w3c.dom.NodeList}
163      */
164     @Override
165     public Object queryXML(String elementType, String elementName, Interchange interchange, String expression, QName returnType) {
166         memorizeInterchanges(null, interchange, null);
167         return contentHandler.queryXML(interchange, expression, returnType);
168     }
169 
170     /** Finds out the differences between two EDIFACT or X12 interchanges,
171      *  ignoring elements that match the XPath exclusion paths.
172      *  @param elementType
173      *  @param elementName
174      *  @param expected
175      *  @param actual
176      *  @param settings
177      *  @param model
178      *  @return an {@link AggregateDiff} of the interchanges */
179     @Override
180     public AggregateDiff diff(String elementType, String elementName, Interchange expected, Interchange actual,
181             ComparisonSettings settings, ComparisonModel<Element> model) {
182         try {
183             memorizeInterchanges(expected, actual, null);
184             return contentHandler.diff(expected, actual, settings, model);
185         } catch (Exception e) {
186             throw new TechnicalException("Error comparing Edifact interchanges", e);
187         }
188     }
189 
190     // Action interface implementation -----------------------------------------
191 
192     /** Saves the XML document(s) used in the most recent invocation as attachment */
193     @Override
194     public List<Attachment> createDebugAttachments() {
195         List<Attachment> attachments = new ArrayList<Attachment>();
196         if (this.recentExpectedInterchange != null) {
197             attachments.add(createAttachment("expected interchange", this.recentExpectedInterchange));
198         }
199         if (this.recentActualInterchange != null) {
200             attachments.add(createAttachment("actual interchange", this.recentActualInterchange));
201         }
202         if (this.recentDiff != null) {
203             attachments.add(createDiffAttachment("diff", this.recentDiff));
204         }
205         return attachments;
206     }
207 
208     @Override
209     public List<Attachment> createAttachments(Object object, String label) {
210         if (object instanceof Interchange) {
211             List<Attachment> attachments = new ArrayList<Attachment>();
212             Interchange interchange = (Interchange) object;
213             attachments.add(new StringAttachment(label + " as raw data", toString(interchange, false), "edi"));
214             attachments.add(new StringAttachment(label + " as text", toString(interchange, true), "txt"));
215             attachments.add(new StringAttachment(label + " as text tree", new TextTreeInterchangeFormatter(false, "\n").format(interchange), "txt"));
216             return attachments;
217         } else {
218             throw new TechnicalException("Not a supported type: " + object);
219         }
220     }
221 
222     // private helpers ---------------------------------------------------------
223 
224     /** saves the most recently used interchange(s)
225      * in order to provide them as attachment on request. */
226     private void memorizeInterchanges(Interchange expected, Interchange actual, AggregateDiff diff) {
227         this.recentExpectedInterchange = expected;
228         this.recentActualInterchange = actual;
229         this.recentDiff = diff;
230     }
231 
232     /** Creates an attachment that contains the provides EDIFACT or X12 interchange. */
233     private StringAttachment createAttachment(String title, Interchange interchange) {
234         String text = contentHandler.formatRecursively(interchange);
235         return new StringAttachment(title, text, "txt");
236     }
237 
238     private StringAttachment createDiffAttachment(String title, AggregateDiff diff) {
239         String cssPath = "org/databene/edifatto/edifatto-gui.css";
240         String text = new HTMLDiffFormatter(cssPath).formatDiffAsHtml(diff);
241         return new StringAttachment(title, text, "html");
242     }
243 
244     private String toString(Interchange interchange, boolean linefeed) {
245         return new StandardInterchangeFormatter(linefeed ? "\n" : null).format(interchange);
246     }
247 
248 }