001/* Copyright (C) 2014 konik.io
002 *
003 * This file is part of the Konik library.
004 *
005 * The Konik library is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * The Konik library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013 * GNU Affero General Public License for more details.
014 *
015 * You should have received a copy of the GNU Affero General Public License
016 * along with the Konik library. If not, see <http://www.gnu.org/licenses/>.
017 */
018package io.konik;
019
020import static javax.xml.bind.JAXBContext.newInstance;
021import io.konik.exception.TransformationException;
022import io.konik.zugferd.Invoice;
023
024import java.io.ByteArrayOutputStream;
025import java.io.File;
026import java.io.InputStream;
027import java.io.OutputStream;
028import java.net.URL;
029
030import javax.inject.Named;
031import javax.inject.Singleton;
032import javax.xml.XMLConstants;
033import javax.xml.bind.JAXBContext;
034import javax.xml.bind.JAXBElement;
035import javax.xml.bind.JAXBException;
036import javax.xml.bind.Marshaller;
037import javax.xml.bind.Unmarshaller;
038import javax.xml.transform.stream.StreamSource;
039import javax.xml.validation.Schema;
040import javax.xml.validation.SchemaFactory;
041import javax.xml.validation.Validator;
042
043import org.xml.sax.SAXException;
044
045/**
046 * Transforms invoices from one representation to another. In other words marshaling and unmarshalling.
047 * 
048 */
049@Named
050@Singleton
051public class InvoiceTransformer {
052
053   private static final String MARSHALLING_ERROR = "Marshalling error";
054
055   private static final String KONIK_CONTEXT = "io.konik.zugferd";
056
057   private final JAXBContext jaxbContext;
058
059   /**
060    * Instantiates a default invoice transformer.
061    */
062   public InvoiceTransformer() {
063      try {
064         this.jaxbContext = newInstance(KONIK_CONTEXT);
065      } catch (JAXBException e) {
066         throw new TransformationException("Could not instantiate JaxB Context", e);
067      }
068   }
069
070   /**
071    * Instantiates a new invoice transformer providing a JAXB Context
072    *
073    * @param jaxbContext the JAXB context
074    */
075   InvoiceTransformer(JAXBContext jaxbContext) {
076      this.jaxbContext = jaxbContext;
077   }
078
079   /**
080    * Transform from XML input stream to the invoice model.
081    * 
082    * @param xmlInputStream the xml input stream
083    * @return the invoice model
084    */
085   public Invoice toModel(InputStream xmlInputStream) {
086      try {
087         Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
088         return unmarshaller.unmarshal(new StreamSource(xmlInputStream), Invoice.class).getValue();
089      } catch (JAXBException e) {
090         throw new TransformationException(MARSHALLING_ERROR, e);
091      }
092   }
093
094   /**
095    * Transform from XML content from File to the invoice model.
096    * 
097    * @param file the file
098    * @return the invoice
099    */
100   @SuppressWarnings("unchecked")
101   public Invoice toModel(File file) {
102      try {
103         Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
104         return ((JAXBElement<Invoice>) unmarshaller.unmarshal(file)).getValue();
105      } catch (JAXBException e) {
106         throw new TransformationException(MARSHALLING_ERROR, e);
107      }
108   }
109
110   /**
111    * Transform from Invoice model to xml byte array.
112    * 
113    * @param invoice the invoice
114    * @return the byte[]
115    */
116   public byte[] fromModel(Invoice invoice) {
117      ByteArrayOutputStream outputStream = new ByteArrayOutputStream(16000);
118      try {
119         Marshaller marshaller = createMarshaller();
120         marshaller.marshal(invoice, outputStream);
121      } catch (JAXBException e) {
122         throw new TransformationException(MARSHALLING_ERROR, e);
123      }
124      return outputStream.toByteArray();
125   }
126
127   /**
128    * Transform from Invoice model to output stream.
129    *
130    * @param invoice the invoice
131    * @param outputStream the output stream
132    */
133   public void fromModel(Invoice invoice, OutputStream outputStream) {
134      try {
135         Marshaller marshaller = createMarshaller();
136         marshaller.marshal(invoice, outputStream);
137      } catch (JAXBException e) {
138         throw new TransformationException(MARSHALLING_ERROR, e);
139      }
140   }
141
142   /**
143    * Gets the ZUGFeRD schema Validator.
144    * 
145    * @return the Schema Validator
146    * @throws SAXException the SAX exception
147    */
148   public Validator getZfSchemaValidator() throws SAXException {
149      SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI);
150      URL schemaInvoice = InvoiceTransformer.class.getResource("/zfSchema/ZUGFeRD_1p0.xsd");
151      Schema invoiceSchema = sf.newSchema(schemaInvoice);
152      return invoiceSchema.newValidator();
153   }
154
155   private Marshaller createMarshaller() throws JAXBException {
156      Marshaller marshaller = jaxbContext.createMarshaller();
157      marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, formatXmlOutput());
158      return marshaller;
159   }
160
161   protected Boolean formatXmlOutput() {
162      return Boolean.FALSE;
163   }
164
165}