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 java.util.logging.Level.WARNING; 021import static javax.xml.bind.JAXBContext.newInstance; 022import io.konik.exception.TransformationException; 023import io.konik.zugferd.Invoice; 024 025import java.io.ByteArrayOutputStream; 026import java.io.File; 027import java.io.IOException; 028import java.io.InputStream; 029import java.io.OutputStream; 030import java.net.URL; 031import java.util.logging.Logger; 032 033import javax.inject.Named; 034import javax.inject.Singleton; 035import javax.xml.XMLConstants; 036import javax.xml.bind.JAXBContext; 037import javax.xml.bind.JAXBElement; 038import javax.xml.bind.JAXBException; 039import javax.xml.bind.Marshaller; 040import javax.xml.bind.Unmarshaller; 041import javax.xml.transform.stream.StreamSource; 042import javax.xml.validation.Schema; 043import javax.xml.validation.SchemaFactory; 044import javax.xml.validation.Validator; 045 046import org.xml.sax.SAXException; 047 048/** 049 * Transforms invoices from one representation to another. In other words marshaling and unmarshalling. 050 * 051 */ 052@Named 053@Singleton 054public class InvoiceTransformer { 055 056 private static final Logger LOG = Logger.getLogger(InvoiceTransformer.class.getName()); 057 058 private static final String MARSHALLING_ERROR = "Marshalling error"; 059 060 private static final String KONIK_CONTEXT = "io.konik.zugferd"; 061 062 private final JAXBContext jaxbContext; 063 064 /** 065 * Instantiates a default invoice transformer. 066 */ 067 public InvoiceTransformer() { 068 try { 069 this.jaxbContext = newInstance(KONIK_CONTEXT); 070 } catch (JAXBException e) { 071 throw new TransformationException("Could not instantiate JaxB Context", e); 072 } 073 } 074 075 /** 076 * Instantiates a new invoice transformer providing a JAXB Context 077 * 078 * @param jaxbContext the JAXB context 079 */ 080 InvoiceTransformer(JAXBContext jaxbContext) { 081 this.jaxbContext = jaxbContext; 082 } 083 084 /** 085 * Transform from XML input stream to the invoice model. 086 * 087 * @param xmlInputStream the xml input stream 088 * @return the invoice model 089 */ 090 public Invoice toModel(InputStream xmlInputStream) { 091 try { 092 Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); 093 return unmarshaller.unmarshal(new StreamSource(xmlInputStream), Invoice.class).getValue(); 094 } catch (JAXBException e) { 095 throw new TransformationException(MARSHALLING_ERROR, e); 096 } 097 } 098 099 /** 100 * Transform from XML content from File to the invoice model. 101 * 102 * @param file the file 103 * @return the invoice 104 */ 105 @SuppressWarnings("unchecked") 106 public Invoice toModel(File file) { 107 try { 108 Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); 109 return ((JAXBElement<Invoice>) unmarshaller.unmarshal(file)).getValue(); 110 } catch (JAXBException e) { 111 throw new TransformationException(MARSHALLING_ERROR, e); 112 } 113 } 114 115 /** 116 * Transform from Invoice model to xml byte array. 117 * 118 * @param invoice the invoice 119 * @return the byte[] 120 */ 121 public byte[] fromModel(Invoice invoice) { 122 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(16000); 123 try { 124 Marshaller marshaller = createMarshaller(); 125 marshaller.marshal(invoice, outputStream); 126 } catch (JAXBException e) { 127 throw new TransformationException(MARSHALLING_ERROR, e); 128 } 129 return outputStream.toByteArray(); 130 } 131 132 /** 133 * Transform from Invoice model to output stream. 134 * 135 * @param invoice the invoice 136 * @param outputStream the output stream 137 */ 138 public void fromModel(Invoice invoice, OutputStream outputStream) { 139 try { 140 Marshaller marshaller = createMarshaller(); 141 marshaller.marshal(invoice, outputStream); 142 } catch (JAXBException e) { 143 throw new TransformationException(MARSHALLING_ERROR, e); 144 } 145 } 146 147 /** 148 * From model Async. 149 * 150 * Will start a new Thread for the transformation. 151 * 152 * @param invoice the invoice 153 * @param outputStream the output stream 154 */ 155 public void fromModelAsync(final Invoice invoice, final OutputStream outputStream) { 156 new Thread(new Runnable() { 157 @Override 158 public void run() { 159 fromModel(invoice, outputStream); 160 try { 161 outputStream.flush(); 162 outputStream.close(); 163 } catch (IOException e) { 164 LOG.log(WARNING, "Faild to Transform Model", e); 165 } 166 } 167 }).start(); 168 } 169 170 /** 171 * Gets the ZUGFeRD schema Validator. 172 * 173 * @return the Schema Validator 174 * @throws SAXException the SAX exception 175 */ 176 public Validator getZfSchemaValidator() throws SAXException { 177 SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); 178 URL schemaInvoice = InvoiceTransformer.class.getResource("/zfSchema/ZUGFeRD_1p0.xsd"); 179 Schema invoiceSchema = sf.newSchema(schemaInvoice); 180 return invoiceSchema.newValidator(); 181 } 182 183 private Marshaller createMarshaller() throws JAXBException { 184 Marshaller marshaller = jaxbContext.createMarshaller(); 185 marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, formatXmlOutput()); 186 return marshaller; 187 } 188 189 protected Boolean formatXmlOutput() { 190 return Boolean.FALSE; 191 } 192 193}