org.databene.edifatto.parser.AbstractEdiParser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of edifatto Show documentation
Show all versions of edifatto Show documentation
'Edifatto' is an open source software library for parsing,
generating and verifying EDIFACT and X12 documents written by Volker Bergmann.
/*
* Copyright (C) 2013-2015 Volker Bergmann ([email protected]).
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.databene.edifatto.parser;
import java.text.ParsePosition;
import java.util.List;
import java.util.Map;
import org.databene.commons.Assert;
import org.databene.commons.SyntaxError;
import org.databene.edifatto.EdiFormatSymbols;
import org.databene.edifatto.definition.ComponentDefinition;
import org.databene.edifatto.definition.CompositeDefinition;
import org.databene.edifatto.definition.Definition;
import org.databene.edifatto.definition.MessageDefinition;
import org.databene.edifatto.definition.SegmentDefinition;
import org.databene.edifatto.definition.SegmentItemDefinition;
import org.databene.edifatto.model.Component;
import org.databene.edifatto.model.Composite;
import org.databene.edifatto.model.EdiProtocol;
import org.databene.edifatto.model.FunctionalGroup;
import org.databene.edifatto.model.Interchange;
import org.databene.edifatto.model.InterchangeItem;
import org.databene.edifatto.model.Message;
import org.databene.edifatto.model.Segment;
import org.databene.edifatto.model.SegmentGroup;
import org.databene.edifatto.model.SegmentGroupItem;
import org.databene.edifatto.model.SegmentItem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Parent class for EDI-style parsers, providing common features
* and the possibility to override aspects.
* Created: 18.10.2013 10:13:28
* @since 1.0
* @author Volker Bergmann
*/
public abstract class AbstractEdiParser {
private final static Logger LOGGER = LoggerFactory.getLogger(AbstractEdiParser.class);
protected final EdiProtocol protocol;
public AbstractEdiParser(EdiProtocol protocol) {
this.protocol = protocol;
}
public Interchange parse(String text) {
EdiFormatSymbols symbols = determineSymbols(text);
SegmentTokenizer tokenizer = new SegmentTokenizer(symbols);
List segments = tokenizer.tokenize(text);
Interchange interchange = parseSegments(segments, symbols);
normalizeInterchange(interchange);
return interchange;
}
protected abstract EdiFormatSymbols determineSymbols(String text);
protected abstract Interchange parseSegments(List segments, EdiFormatSymbols symbols);
protected Interchange parseInterchange(List segments, ParsePosition pos, Interchange interchange, Map context) {
Segment icHeader = parseRequiredSegment(protocol.getInterchangeHeaderTag(), segments, pos);
interchange.addChild(icHeader);
parseFunctionalGroupsOrMessages(segments, pos, interchange, context);
Segment icTrailer = parseRequiredSegment(protocol.getInterchangeTrailerTag(), segments, pos);
interchange.addChild(icTrailer);
return interchange;
}
protected void parseFunctionalGroupsOrMessages(List segments, ParsePosition pos,
Interchange interchange, Map context) {
Segment fgHeader = parseOptionalSegment(protocol.getFunctionalGroupHeaderTag(), segments, pos);
if (fgHeader != null) {
processFunctionalGroupHeader(fgHeader, context);
interchange.addChild(fgHeader);
parseFunctionalGroups(segments, pos, interchange, context);
Segment fgTrailer = parseRequiredSegment(protocol.getFunctionalGroupTrailerTag(), segments, pos);
interchange.addChild(fgTrailer);
} else {
parseMessages(segments, pos, interchange, context);
}
}
protected void processFunctionalGroupHeader(Segment groupHeader, Map context) {
// callback method for child classes
}
private void parseFunctionalGroups(List segments, ParsePosition pos, Interchange interchange, Map context) {
Message message;
while ((message = parseOptionalMessage(segments, pos, context, interchange.getSymbols())) != null)
interchange.addChild(message);
}
private void parseMessages(List segments, ParsePosition pos, Interchange interchange, Map context) {
Message message;
while ((message = parseOptionalMessage(segments, pos, context, interchange.getSymbols())) != null)
interchange.addChild(message);
}
protected Message parseOptionalMessage(List segments, ParsePosition pos, Map context, EdiFormatSymbols symbols) {
String tag = segments.get(pos.getIndex()).getTag();
if (!tag.equals(protocol.getMessageHeaderTag()))
return null;
return parseRequiredMessage(segments, pos, context, symbols);
}
protected abstract Message parseRequiredMessage(List segments, ParsePosition pos, Map context, EdiFormatSymbols symbols);
protected static Segment parseRequiredSegment(String tag, List segments, ParsePosition pos) {
if (pos.getIndex() >= segments.size())
throw new SyntaxError("Expected " + tag + " segment, but no more segment is available", null);
Segment segment = segments.get(pos.getIndex());
requireTag(tag, segment);
pos.setIndex(pos.getIndex() + 1);
return segment;
}
protected static Segment parseOptionalSegment(String tag, List segments, ParsePosition pos) {
Segment segment = segments.get(pos.getIndex());
if (tag.equals(segment.getTag())) {
pos.setIndex(pos.getIndex() + 1);
return segment;
} else {
return null;
}
}
protected static void requireTag(String tag, Segment segment) {
if (!tag.equals(segment.getTag()))
throw new SyntaxError("Expected '" + tag + "' segment but found '" + segment.getTag() + "'", segment.toString());
}
private static void normalizeInterchange(Interchange interchange) {
NormalizationContext context = new NormalizationContext();
MessageDefinition messageDefinition = null;
for (InterchangeItem item : interchange.getChildren()) {
if (item instanceof FunctionalGroup)
normalizeFunctionalGroup((FunctionalGroup) item, context);
else if (item instanceof Message) {
Message message = (Message) item;
messageDefinition = message.getDefinition();
normalizeMessage(message, context);
}
}
for (InterchangeItem item : interchange.getChildren()) {
if (item instanceof Segment) {
Segment segment = (Segment) item;
String tag = segment.getTag();
if (tag.equals(interchange.getType().getServiceStringAdviceTag()))
segment.setDefinition(messageDefinition.getServiceStringAdvice());
else if (tag.equals(interchange.getType().getInterchangeHeaderTag()))
segment.setDefinition(messageDefinition.getInterchangeHeader());
else if (tag.equals(interchange.getType().getInterchangeTrailerTag()))
segment.setDefinition(messageDefinition.getInterchangeTrailer());
normalizeSegment(segment, context);
}
}
}
private static void normalizeFunctionalGroup(FunctionalGroup group, NormalizationContext context) {
for (InterchangeItem item : group.getChildren()) {
if (item instanceof FunctionalGroup)
normalizeFunctionalGroup((FunctionalGroup) item, context);
else if (item instanceof Message)
normalizeMessage((Message) item, context);
else if (item instanceof Segment)
normalizeSegment((Segment) item, context);
else
throw new UnsupportedOperationException("Unexpected type: " + item.getClass().getName());
}
}
private static void normalizeMessage(Message message, NormalizationContext context) {
message.setNo(context.newMessageNo());
for (SegmentGroupItem item : message.getChildren()) {
if (item instanceof SegmentGroup)
normalizeSegmentGroup((SegmentGroup) item, context);
else if (item instanceof Segment)
normalizeSegment((Segment) item, context);
else
throw new UnsupportedOperationException("Unexpected type: " + item.getClass().getName());
}
}
private static void normalizeSegmentGroup(SegmentGroup group, NormalizationContext context) {
group.setNo(context.newSegmentGroupNo());
for (SegmentGroupItem item : group.getChildren()) {
if (item instanceof SegmentGroup)
normalizeSegmentGroup((SegmentGroup) item, context);
else if (item instanceof Segment)
normalizeSegment((Segment) item, context);
else
throw new UnsupportedOperationException("Unexpected type: " + item.getClass().getName());
}
}
private static void normalizeSegment(Segment segment, NormalizationContext context) {
SegmentDefinition definition = segment.getDefinition();
if (definition != null) {
for (int i = 0; i < segment.getChildCount(); i++) {
SegmentItem child = segment.getChild(i);
SegmentItemDefinition childDefinition = definition.getChild(i);
if (child instanceof Composite) {
Composite composite = (Composite) child;
if (childDefinition instanceof CompositeDefinition)
normalizeComposite(composite, (CompositeDefinition) childDefinition);
else // childDefinition instanceof ComponentDefinition
unpackSingleComponent(segment, i);
} else
normalizeComponent((Component) child, (ComponentDefinition) childDefinition);
}
}
}
private static void unpackSingleComponent(Segment segment, int i) {
Composite composite = (Composite) segment.getChild(i);
Component component = composite.getChild(0);
ComponentDefinition definition = (ComponentDefinition) segment.getDefinition().getChild(i);
normalizeComponent(component, definition);
segment.setChild(i, component);
}
private static void normalizeComposite(Composite composite, Definition definition) {
int n = composite.getChildCount();
if (definition instanceof CompositeDefinition) {
CompositeDefinition compositeDef = (CompositeDefinition) definition;
composite.setDefinition(compositeDef);
for (int i = 0; i < n; i++) {
LOGGER.debug("Normalizing child #{} of {}", i, composite);
Component child = composite.getChild(i);
if (i < compositeDef.getChildCount()) {
ComponentDefinition childDef = compositeDef.getChild(i);
normalizeComponent(child, childDef);
} else {
Segment ownerSegment = composite.getSegment();
String errorMessage = "Element '" + composite + "' contains more child elements than allowed thy the specification";
throw new SyntaxError(errorMessage, ownerSegment.toString());
}
}
} else {
Assert.equals(1, n, "Element '" + definition.getName() + "' is expected to have exactly one child, but had " + n + ": "+ composite.toString());
Component component = composite.getChild(0);
normalizeComponent(component, (ComponentDefinition) definition);
}
}
private static void normalizeComponent(Component component, ComponentDefinition definition) {
component.setDefinition(definition);
}
static class NormalizationContext {
int messageCount = 0;
int segmentGroupCount = 0;
public int newMessageNo() {
return ++this.messageCount;
}
public int newSegmentGroupNo() {
return ++this.segmentGroupCount;
}
}
}