All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.databene.edifatto.parser.AbstractEdiParser Maven / Gradle / Ivy

Go to download

'Edifatto' is an open source software library for parsing, generating and verifying EDIFACT and X12 documents written by Volker Bergmann.

There is a newer version: 3.0.1
Show newest version
/*
 * 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;
		}
		
	}
	
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy