/* * Copyright 2005-2015 by BerryWorks Software, LLC. All rights reserved. * * This file is part of EDIReader. You may obtain a license for its use directly from * BerryWorks Software, and you may also choose to use this software under the terms of the * GPL version 3. Other products in the EDIReader software suite are available only by licensing * with BerryWorks. Only those files bearing the GPL statement below are available under the GPL. * * EDIReader is free software: you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * EDIReader is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with EDIReader. If not, * see <http://www.gnu.org/licenses/>. */ package com.berryworks.edireader.splitter; import com.berryworks.edireader.*; import com.berryworks.edireader.error.EDISyntaxExceptionHandler; import com.berryworks.edireader.error.RecoverableSyntaxException; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; import org.xml.sax.helpers.DefaultHandler; import java.io.IOException; /** * Splits an EDI interchange containing multiple * documents into a series of interchanges containing one document each. * <p> * This implementation of a SAX ContentHandler filters the SAX * calls produced by parsing an interchange into an equivalent sequence of * SAX calls that make each EDI document appear to have been in an interchange * containing only that one document. */ public class SplittingHandler extends DefaultHandler { private final HandlerFactory handlerFactory; private final XMLTags xmlTags = DefaultXMLTags.getInstance(); private final String interchangeTagName = xmlTags.getInterchangeTag(); private final String senderTagName = xmlTags.getSenderTag(); private final String receiverTagName = xmlTags.getReceiverTag(); private final String addressTagName = xmlTags.getAddressTag(); private ContentHandler contentHandler; private boolean pendingDocumentClose; private boolean senderAddress; private Attributes interchangeAttributes; private Attributes senderAttributes; private Attributes receiverAttributes; private Attributes groupAttributes; private Attributes documentAttributes; private final Attributes emptyAttributes = new AttributesImpl(); private int transactionCountLimit; private int transactionsInGroupCount; public SplittingHandler(HandlerFactory handlerFactory) { this.handlerFactory = handlerFactory; } public void split(InputSource inputSource) throws IOException, SAXException { char[] leftOver = null; EDIReader parser; while ((parser = EDIReaderFactory.createEDIReader(inputSource, leftOver)) != null) { parser.setContentHandler(this); parser.setSyntaxExceptionHandler(new MyErrorHandler()); parser.parse(inputSource); leftOver = parser.getTokenizer().getBuffered(); } handlerFactory.markEndOfStream(); } @Override public void startDocument() throws SAXException { try { contentHandler = handlerFactory.createDocument(); } catch (Exception e) { throw new SAXException(e); } contentHandler.startDocument(); } @Override public void endDocument() throws SAXException { contentHandler.endDocument(); ClosingDetails closingDetails = new ClosingDetails(); closingDetails.setSenderQualifier(senderAttributes.getValue(xmlTags.getQualifierAttribute())); closingDetails.setSenderId(senderAttributes.getValue(xmlTags.getIdAttribute())); closingDetails.setReceiverQualifier(receiverAttributes.getValue(xmlTags.getQualifierAttribute())); closingDetails.setReceiverId(receiverAttributes.getValue(xmlTags.getIdAttribute())); closingDetails.setInterchangeControlNumber(interchangeAttributes.getValue(xmlTags.getControl())); closingDetails.setGroupSender(groupAttributes.getValue(xmlTags.getApplSender())); closingDetails.setGroupReceiver(groupAttributes.getValue(xmlTags.getApplReceiver())); closingDetails.setGroupControlNumber(groupAttributes.getValue(xmlTags.getControl())); closingDetails.setDocumentControlNumber(documentAttributes.getValue(xmlTags.getControl())); closingDetails.setDocumentType(documentAttributes.getValue(xmlTags.getDocumentType())); closingDetails.setVersion(groupAttributes.getValue(xmlTags.getStandardVersion())); try { handlerFactory.closeDocument(closingDetails); } catch (Exception e) { throw new SAXException(e); } } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if (xmlTags.getInterchangeTag().equals(localName)) { interchangeAttributes = new EDIAttributes(attributes); } else if (xmlTags.getSenderTag().equals(localName)) { senderAddress = true; } else if (xmlTags.getReceiverTag().equals(localName)) { senderAddress = false; } else if (xmlTags.getAddressTag().equals(localName)) { if (senderAddress) { senderAttributes = new EDIAttributes(attributes); } else { receiverAttributes = new EDIAttributes(attributes); } } else if (xmlTags.getGroupTag().equals(localName)) { transactionsInGroupCount = 0; if (pendingDocumentClose) { generateArtificialBoundaryForNewGroup(); pendingDocumentClose = false; } groupAttributes = new EDIAttributes(attributes); documentAttributes = null; } else if (xmlTags.getDocumentTag().equals(localName)) { if (pendingDocumentClose) { generateArtificialBoundaryForNewDocument(); pendingDocumentClose = false; } documentAttributes = new EDIAttributes(attributes); } contentHandler.startElement(uri, localName, qName, attributes); } private void generateArtificialBoundaryForNewDocument() throws SAXException { pendingDocumentClose = false; // Close off the current group String groupTagName = xmlTags.getGroupTag(); endElement("", groupTagName, groupTagName); generateArtificialBoundaryForNewGroup(); // Open an new group startElement("", groupTagName, groupTagName, groupAttributes); } private void generateArtificialBoundaryForNewGroup() throws SAXException { pendingDocumentClose = false; // Close off the current interchange, transaction, root, and XML document endElement("", interchangeTagName, interchangeTagName); String rootTagName = xmlTags.getRootTag(); endElement("", rootTagName, rootTagName); endDocument(); // Open up new ones startDocument(); startElement("", rootTagName, rootTagName, emptyAttributes); startElement("", interchangeTagName, interchangeTagName, interchangeAttributes); // Take care of the <sender> and <receiver> startElement("", senderTagName, senderTagName, emptyAttributes); startElement("", addressTagName, addressTagName, senderAttributes); endElement("", addressTagName, addressTagName); endElement("", senderTagName, senderTagName); startElement("", receiverTagName, receiverTagName, emptyAttributes); startElement("", addressTagName, addressTagName, receiverAttributes); endElement("", addressTagName, addressTagName); endElement("", receiverTagName, receiverTagName); } @Override public void endElement(String uri, String localName, String qName) throws SAXException { contentHandler.endElement(uri, localName, qName); if (xmlTags.getDocumentTag().equals(localName)) { transactionsInGroupCount++; if (transactionsInGroupCount >= transactionCountLimit) { pendingDocumentClose = true; } } else if (xmlTags.getInterchangeTag().equals(localName)) { pendingDocumentClose = false; } } @Override public void characters(char ch[], int start, int length) throws SAXException { contentHandler.characters(ch, start, length); } @Override public void processingInstruction(String target, String data) throws SAXException { contentHandler.processingInstruction(target, data); } public void setTransactionCountLimit(int transactionCountLimit) { this.transactionCountLimit = transactionCountLimit; } private class MyErrorHandler implements EDISyntaxExceptionHandler { @Override public boolean process(RecoverableSyntaxException syntaxException) { // Return true to indicate that we want parsing to continue. return true; } } }