/* * 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; import com.berryworks.edireader.error.*; import com.berryworks.edireader.plugin.PluginControllerFactory; import com.berryworks.edireader.plugin.PluginControllerFactoryInterface; import com.berryworks.edireader.tokenizer.Token; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import java.io.IOException; import java.util.List; import static com.berryworks.edireader.util.FixedLength.isPresent; /** * Common parent class to several EDIReader subclasses that provide for the * parsing of specific EDI standards. This common parent provides an opportunity * to factor and share common concepts and logic. */ public abstract class StandardReader extends EDIReader { /** * Interchange Control Number */ private String interchangeControlNumber; /** * Group-level control number */ private String groupControlNumber; private int groupCount; private int documentCount; private ReplyGenerator ackGenerator; private ReplyGenerator alternateAckGenerator; private RecoverableSyntaxException syntaxException; private PluginControllerFactoryInterface pluginControllerFactory; protected PluginController segmentPluginController; protected abstract Token recognizeBeginning() throws IOException, SAXException; protected abstract Token parseInterchange(Token t) throws SAXException, IOException; @Override public void parse(InputSource source) throws SAXException, IOException { if (source == null) throw new IOException("parse called with null InputSource"); if (getContentHandler() == null) throw new IOException("parse called with null ContentHandler"); if (!isExternalXmlDocumentStart()) startXMLDocument(); parseSetup(source); getTokenizer().setDelimiter(getDelimiter()); getTokenizer().setSubDelimiter(getSubDelimiter()); getTokenizer().setRelease(getRelease()); getTokenizer().setRepetitionSeparator(getRepetitionSeparator()); getTokenizer().setTerminator(getTerminator()); try { parseInterchange(recognizeBeginning()); } catch (EDISyntaxException e) { if (ackGenerator != null) ackGenerator.generateNegativeACK(); if (alternateAckGenerator != null) alternateAckGenerator.generateNegativeACK(); throw e; } if (!isExternalXmlDocumentStart()) endXMLDocument(); } /** * Issue SAX calls on behalf of an EDI element. The token passed as an * argument is first token of a field. * * @param t the parsed token * @throws SAXException for problem emitting SAX events */ protected void parseSegmentElement(Token t) throws SAXException { EDIAttributes attributes; String elementId = t.getElementId(); switch (t.getType()) { case SIMPLE: // Take a quick exit for empty fields, a very common case if (t.getValueLength() == 0 || !t.containsNonSpace()) return; attributes = getDocumentAttributes(); attributes.clear(); attributes.addCDATA(getXMLTags().getIdAttribute(), elementId); startElement(getXMLTags().getElementTag(), attributes); getContentHandler().characters(t.getValueChars(), 0, t.getValueLength()); endElement(getXMLTags().getElementTag()); if (segmentPluginController != null) segmentPluginController.noteElement(getContentHandler(), elementId, t.getValueChars(), 0, t.getValueLength()); break; case SUB_ELEMENT: attributes = getDocumentAttributes(); if (t.isFirst()) { attributes.clear(); attributes.addCDATA(getXMLTags().getIdAttribute(), elementId); attributes.addCDATA(getXMLTags().getCompositeIndicator(), "yes"); startElement(getXMLTags().getElementTag(), attributes); } attributes.clear(); attributes.addAttribute( "", getXMLTags().getSubElementSequence(), getXMLTags().getSubElementSequence(), "CDATA", String.valueOf(1 + t.getSubIndex())); startElement(getXMLTags().getSubElementTag(), attributes); getContentHandler().characters(t.getValueChars(), 0, t.getValueLength()); endElement(getXMLTags().getSubElementTag()); if (t.isLast()) { endElement(getXMLTags().getElementTag()); } break; case SUB_EMPTY: if (t.isFirst()) { attributes = getDocumentAttributes(); attributes.clear(); attributes.addCDATA(getXMLTags().getIdAttribute(), elementId); attributes.addCDATA(getXMLTags().getCompositeIndicator(), "yes"); startElement(getXMLTags().getElementTag(), attributes); } if (t.isLast()) { endElement(getXMLTags().getElementTag()); } break; } } /** * Set an override value to be used whenever generating a control date and * time. This method is used for automated testing. * * @param overrideValue to be used in lieu of current date and time */ public void setControlDateAndTime(String overrideValue) { ReplyGenerator generator = getAckGenerator(); if (generator != null) { generator.setControlDateAndTime(overrideValue); } generator = getAlternateAckGenerator(); if (generator != null) { generator.setControlDateAndTime(overrideValue); } } protected boolean recover(RecoverableSyntaxException e) { return getSyntaxExceptionHandler() != null && getSyntaxExceptionHandler().process(e); } public int getGroupCount() { return groupCount; } public void setGroupCount(int groupCount) { this.groupCount = groupCount; } public String getInterchangeControlNumber() { return interchangeControlNumber; } public void setInterchangeControlNumber(String interchangeControlNumber) { this.interchangeControlNumber = interchangeControlNumber; } public String getGroupControlNumber() { return groupControlNumber; } public void setGroupControlNumber(String groupControlNumber) { this.groupControlNumber = groupControlNumber; } public int getDocumentCount() { return documentCount; } public void setDocumentCount(int documentCount) { this.documentCount = documentCount; } public RecoverableSyntaxException getSyntaxException() { return syntaxException; } public void setSyntaxException(RecoverableSyntaxException syntaxException) { this.syntaxException = syntaxException; } protected String parseStringFromNextElement() throws IOException, EDISyntaxException { List<String> v = getTokenizer().nextCompositeElement(); if (isPresent(v)) { String obj = v.get(0); if (obj != null) return obj; } throw new EDISyntaxException(ErrorMessages.MANDATORY_ELEMENT_MISSING, getTokenizer()); } protected void checkGroupCount(int groupCount, int n, String errorMessage) throws GroupCountException { if (groupCount != n) { GroupCountException exception = new GroupCountException(errorMessage, groupCount, n, getTokenizer()); setSyntaxException(exception); if (!recover(exception)) throw exception; } } protected void checkTransactionCount(int segCount, int n, String errorMessage) throws TransactionCountException { if (segCount != n) { TransactionCountException exception = new TransactionCountException(errorMessage, segCount, n, getTokenizer()); setSyntaxException(exception); if (!recover(exception)) throw exception; } } protected void checkSegmentCount(int segCount, int n, String errorMessage) throws SegmentCountException { if (segCount != n) { SegmentCountException exception = new SegmentCountException(errorMessage, segCount, n, getTokenizer()); setSyntaxException(exception); if (!recover(exception)) throw exception; } } protected void checkInterchangeControlNumber(String control, String s, String errorMessage) throws InterchangeControlNumberException { if (!s.equals(control)) { InterchangeControlNumberException exception = new InterchangeControlNumberException(errorMessage, control, s, getTokenizer()); setSyntaxException(exception); if (!recover(exception)) throw exception; } } protected void checkGroupControlNumber(String control, String s, String errorMessage) throws GroupControlNumberException { if (!s.equals(control)) { GroupControlNumberException exception = new GroupControlNumberException(errorMessage, control, s, getTokenizer()); setSyntaxException(exception); if (!recover(exception)) throw exception; } } protected void checkTransactionControlNumber(String control, String s, String errorMessage) throws TransactionControlNumberException { if (!s.equals(control)) { TransactionControlNumberException exception = new TransactionControlNumberException(errorMessage, control, s, getTokenizer()); setSyntaxException(exception); if (!recover(exception)) throw exception; } } public ReplyGenerator getAckGenerator() { return ackGenerator; } public void setAckGenerator(ReplyGenerator generator) { this.ackGenerator = generator; } public ReplyGenerator getAlternateAckGenerator() { return alternateAckGenerator; } public void setAlternateAckGenerator(ReplyGenerator generator) { this.alternateAckGenerator = generator; } public PluginControllerFactoryInterface getPluginControllerFactory() { // Lazy load if (pluginControllerFactory == null) { pluginControllerFactory = new PluginControllerFactory(); } return pluginControllerFactory; } @Override public void setPluginControllerFactory(PluginControllerFactoryInterface pluginControllerFactory) { this.pluginControllerFactory = pluginControllerFactory; } protected void parseSegment(PluginController pluginController, String segmentType) throws SAXException, IOException { segmentPluginController = pluginController; if (pluginController.transition(segmentType)) { // First close off any loops that were closed as the result of // the transition int toClose = pluginController.closedCount(); if (debug) trace("closing " + toClose + " loops"); for (; toClose > 0; toClose--) endElement(getXMLTags().getLoopTag()); String s = pluginController.getLoopEntered(); if (pluginController.isResumed()) { // We are resuming some outer loop, so we do not // start a new instance of the loop. } else { getDocumentAttributes().clear(); getDocumentAttributes().addCDATA(getXMLTags().getIdAttribute(), s); startElement(getXMLTags().getLoopTag(), getDocumentAttributes()); } } getDocumentAttributes().clear(); getDocumentAttributes().addCDATA(getXMLTags().getIdAttribute(), segmentType); startElement(getXMLTags().getSegTag(), getDocumentAttributes()); if (segmentPluginController != null) segmentPluginController.noteBeginningOfSegment(getContentHandler(), segmentType); Token t; while ((t = getTokenizer().nextToken()).getType() != Token.TokenType.SEGMENT_END) { switch (t.getType()) { case SIMPLE: case EMPTY: case SUB_ELEMENT: case SUB_EMPTY: break; case END_OF_DATA: throw new EDISyntaxException(UNEXPECTED_EOF, getTokenizer()); default: throw new EDISyntaxException(MALFORMED_EDI_SEGMENT, getTokenizer()); } parseSegmentElement(t); } if (segmentPluginController != null) segmentPluginController.noteEndOfSegment(getContentHandler(), segmentType); endElement(getXMLTags().getSegTag()); } protected void startInterchange(EDIAttributes attributes) throws SAXException { startElement(getXMLTags().getInterchangeTag(), attributes); } protected void endInterchange() throws SAXException { endElement(getXMLTags().getInterchangeTag()); } protected void startMessage(EDIAttributes attributes) throws SAXException { startElement(getXMLTags().getDocumentTag(), attributes); } protected String getSubElement(List<String> compositeList, int i) { String result = ""; try { result = compositeList.get(i); } catch (IndexOutOfBoundsException e) { // ignore } return result; } protected void generatedSenderAndReceiver(String fromId, String fromQual, String fromExtra, String toId, String toQual, String toExtra) throws SAXException { getInterchangeAttributes().clear(); startElement(getXMLTags().getSenderTag(), getInterchangeAttributes()); getInterchangeAttributes().addCDATA(getXMLTags().getIdAttribute(), fromId); getInterchangeAttributes().addCDATA(getXMLTags().getQualifierAttribute(), fromQual); if (isPresent(fromExtra)) { getInterchangeAttributes().addCDATA("Extra", fromExtra); } startSenderAddress(getInterchangeAttributes()); endElement(getXMLTags().getAddressTag()); endElement(getXMLTags().getSenderTag()); getInterchangeAttributes().clear(); startElement(getXMLTags().getReceiverTag(), getInterchangeAttributes()); getInterchangeAttributes().addCDATA(getXMLTags().getIdAttribute(), toId); getInterchangeAttributes().addCDATA(getXMLTags().getQualifierAttribute(), toQual); if (isPresent(toExtra)) { getInterchangeAttributes().addCDATA(getXMLTags() .getAddressExtraAttribute(), toExtra); } startReceiverAddress(getInterchangeAttributes()); endElement(getXMLTags().getAddressTag()); endElement(getXMLTags().getReceiverTag()); } protected void startSenderAddress(EDIAttributes attributes) throws SAXException { startElement(getXMLTags().getAddressTag(), attributes); } protected void startReceiverAddress(EDIAttributes attributes) throws SAXException { startElement(getXMLTags().getAddressTag(), attributes); } }