Skip to content

Commit

Permalink
Merge pull request #50 from xlate/48_general_schema_include
Browse files Browse the repository at this point in the history
Support multiple `include`s for any schema
  • Loading branch information
MikeEdgar committed Jul 9, 2020
2 parents be356e8 + 31539bc commit 298a28d
Show file tree
Hide file tree
Showing 15 changed files with 818 additions and 1,245 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/build.yml
Expand Up @@ -2,6 +2,10 @@ name: StAEDI Build

on:
push:
branches:
- master
tags:
- '**'
paths-ignore:
- '.gitignore'
- '.travis.yml*'
Expand Down
6 changes: 5 additions & 1 deletion src/main/java/io/xlate/edi/internal/schema/SchemaReader.java
Expand Up @@ -7,6 +7,10 @@

interface SchemaReader {

Map<String, EDIType> readTypes() throws EDISchemaException;
default Map<String, EDIType> readTypes() throws EDISchemaException {
return readTypes(true);
}

Map<String, EDIType> readTypes(boolean setReferences) throws EDISchemaException;

}
59 changes: 35 additions & 24 deletions src/main/java/io/xlate/edi/internal/schema/SchemaReaderBase.java
Expand Up @@ -69,6 +69,7 @@ abstract class SchemaReaderBase implements SchemaReader {
final QName qnInterchange;
final QName qnGroup;
final QName qnTransaction;
final QName qnImplementation;
final QName qnLoop;
final QName qnSegment;
final QName qnComposite;
Expand Down Expand Up @@ -100,6 +101,7 @@ public SchemaReaderBase(String xmlns, XMLStreamReader reader, Map<String, Object
qnInterchange = new QName(xmlns, "interchange");
qnGroup = new QName(xmlns, "group");
qnTransaction = new QName(xmlns, "transaction");
qnImplementation = new QName(xmlns, "implementation");
qnLoop = new QName(xmlns, "loop");
qnSegment = new QName(xmlns, "segment");
qnComposite = new QName(xmlns, LOCALNAME_COMPOSITE);
Expand Down Expand Up @@ -139,34 +141,43 @@ public SchemaReaderBase(String xmlns, XMLStreamReader reader, Map<String, Object
}

@Override
public Map<String, EDIType> readTypes() throws EDISchemaException {
public Map<String, EDIType> readTypes(boolean setReferences) throws EDISchemaException {
Map<String, EDIType> types = new HashMap<>(100);

types.put(StaEDISchema.ANY_ELEMENT_ID, ANY_ELEMENT);
types.put(StaEDISchema.ANY_COMPOSITE_ID, ANY_COMPOSITE);

try {
reader.nextTag();
QName element = reader.getName();

if (qnInclude.equals(element)) {
readInclude(reader, types);
readImplementation(reader, types);
} else if (qnInterchange.equals(element)) {
readInterchange(reader, types);
} else if (qnTransaction.equals(element)) {
readTransaction(reader, types);
readImplementation(reader, types);
} else {
throw unexpectedElement(element, reader);
}
nextTag(reader, "advancing to first schema element");
QName element = reader.getName();

readTypeDefinitions(reader, types);
setReferences(types);
while (qnInclude.equals(element)) {
readInclude(reader, types);
element = reader.getName();
}

if (qnInterchange.equals(element)) {
readInterchange(reader, types);
} else if (qnTransaction.equals(element)) {
readTransaction(reader, types);
readImplementation(reader, types);
} else if (qnImplementation.equals(element)) {
readImplementation(reader, types);
} else if (!typeDefinitions.containsKey(reader.getName())) {
throw unexpectedElement(element, reader);
}

readTypeDefinitions(reader, types);

try {
reader.next();
requireEvent(XMLStreamConstants.END_DOCUMENT, reader);
} catch (XMLStreamException xse) {
throw schemaException("XMLStreamException reading schema", reader, xse);
throw schemaException("XMLStreamException reading end of document", reader, xse);
}

requireEvent(XMLStreamConstants.END_DOCUMENT, reader);

if (setReferences) {
setReferences(types);
}

return types;
Expand Down Expand Up @@ -234,6 +245,7 @@ void readInterchange(XMLStreamReader reader, Map<String, EDIType> types) {
Collections.emptyList());

types.put(interchange.getId(), interchange);
nextTag(reader, "advancing after interchange");
}

Reference readControlStructure(XMLStreamReader reader,
Expand Down Expand Up @@ -299,10 +311,11 @@ Reference createControlReference(XMLStreamReader reader, String attributeName) {
void readTransaction(XMLStreamReader reader, Map<String, EDIType> types) {
QName element = reader.getName();
types.put(StaEDISchema.TRANSACTION_ID, readComplexType(reader, element, types));
nextTag(reader, "seeking next element after transaction");
}

void readTypeDefinitions(XMLStreamReader reader, Map<String, EDIType> types) {
boolean schemaEnd = false;
boolean schemaEnd = reader.getName().equals(qnSchema);

// Cursor is already positioned on a type definition (e.g. from an earlier look-ahead)
if (typeDefinitions.containsKey(reader.getName())
Expand All @@ -314,9 +327,7 @@ void readTypeDefinitions(XMLStreamReader reader, Map<String, EDIType> types) {
if (nextTag(reader, "reading schema types") == XMLStreamConstants.START_ELEMENT) {
readTypeDefinition(types, reader);
} else {
if (reader.getName().equals(qnSchema)) {
schemaEnd = true;
}
schemaEnd = reader.getName().equals(qnSchema);
}
}
}
Expand Down
18 changes: 8 additions & 10 deletions src/main/java/io/xlate/edi/internal/schema/SchemaReaderV3.java
Expand Up @@ -52,7 +52,6 @@ class SchemaReaderV3 extends SchemaReaderBase implements SchemaReader {
private static final String ATTR_DISCRIMINATOR = "discriminator";
private static final String ATTR_TITLE = "title";

final QName qnImplementation;
final Deque<EDITypeImplementation> implementedTypes = new LinkedList<>();

static class ValueSet {
Expand All @@ -75,7 +74,6 @@ void clear() {

protected SchemaReaderV3(String xmlns, XMLStreamReader reader, Map<String, Object> properties) {
super(xmlns, reader, properties);
qnImplementation = new QName(xmlns, "implementation");
}

public SchemaReaderV3(XMLStreamReader reader, Map<String, Object> properties) {
Expand Down Expand Up @@ -106,12 +104,16 @@ protected void readInclude(XMLStreamReader reader, Map<String, EDIType> types) t

@Override
protected void readImplementation(XMLStreamReader reader, Map<String, EDIType> types) {
nextTag(reader, "reading implementation start");
QName element = reader.getName();

LoopImplementation impl = readImplementation(reader, element, types);
if (impl != null) {
types.put(StaEDISchema.IMPLEMENTATION_ID, impl);
if (qnImplementation.equals(element)) {
LoopImplementation impl = readImplementation(reader, element, types);

if (impl != null) {
types.put(StaEDISchema.IMPLEMENTATION_ID, impl);
}

nextTag(reader, "seeking next element after implementation end");
}
}

Expand Down Expand Up @@ -201,10 +203,6 @@ LoopImplementation readImplementation(XMLStreamReader reader,
QName complexType,
Map<String, EDIType> types) {

if (!qnImplementation.equals(complexType)) {
return null;
}

LoopImplementation loop = readLoopImplementation(reader, complexType, true);
String typeId = StaEDISchema.TRANSACTION_ID;
EDIComplexType standard = (EDIComplexType) types.get(typeId);
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/io/xlate/edi/internal/schema/SchemaReaderV4.java
Expand Up @@ -4,6 +4,7 @@

import java.net.URL;
import java.util.Map;
import java.util.logging.Logger;

import javax.xml.stream.XMLStreamReader;

Expand All @@ -13,10 +14,19 @@

public class SchemaReaderV4 extends SchemaReaderV3 {

private static final Logger LOGGER = Logger.getLogger(SchemaReaderV4.class.getName());

public SchemaReaderV4(XMLStreamReader reader, Map<String, Object> properties) {
super(StaEDISchemaFactory.XMLNS_V4, reader, properties);
}

@Override
void nameCheck(String name, Map<String, EDIType> types, XMLStreamReader reader) {
if (types.containsKey(name)) {
LOGGER.fine(() -> "Duplicate type name encountered: [" + name + ']');
}
}

@Override
protected String readReferencedId(XMLStreamReader reader) {
return parseAttribute(reader, "type", String::valueOf);
Expand Down Expand Up @@ -44,5 +54,7 @@ protected void readInclude(XMLStreamReader reader, Map<String, EDIType> types) t
} catch (Exception e) {
throw schemaException("Exception reading included schema", reader, e);
}

nextTag(reader, "seeking next element after include");
}
}
21 changes: 16 additions & 5 deletions src/main/java/io/xlate/edi/internal/schema/SchemaUtils.java
Expand Up @@ -18,20 +18,21 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.Enumeration;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.Properties;
import java.util.TreeMap;

import io.xlate.edi.schema.EDISchemaException;
import io.xlate.edi.schema.Schema;
import io.xlate.edi.schema.SchemaFactory;
import io.xlate.edi.stream.EDIStreamConstants.Standards;

import java.util.NavigableMap;
import java.util.Properties;
import java.util.TreeMap;

public class SchemaUtils {

private SchemaUtils() {
Expand Down Expand Up @@ -109,8 +110,18 @@ static boolean isValidEntry(Entry<String, ?> entry, String standard) {
}

private static Schema getXmlSchema(String resource) throws EDISchemaException {
SchemaFactory schemaFactory = SchemaFactory.newFactory();
URL location = getURL(resource);
URL locationContext;

try {
locationContext = URI.create(location.toString()).resolve(".").toURL();
} catch (MalformedURLException e) {
throw new EDISchemaException("Unable to resolve schema location context", e);
}

SchemaFactory schemaFactory = SchemaFactory.newFactory();
schemaFactory.setProperty(SchemaFactory.SCHEMA_LOCATION_URL_CONTEXT, locationContext);

return schemaFactory.createSchema(location);
}
}
Expand Up @@ -62,7 +62,7 @@ public class StaEDISchemaFactory implements SchemaFactory {

@Override
public Schema createSchema(InputStream stream) throws EDISchemaException {
Map<String, EDIType> types = readSchemaTypes(stream, properties);
Map<String, EDIType> types = readSchemaTypes(stream, properties, true);

StaEDISchema schema = new StaEDISchema(StaEDISchema.INTERCHANGE_ID,
StaEDISchema.TRANSACTION_ID,
Expand Down Expand Up @@ -122,15 +122,15 @@ static Map<String, EDIType> readSchemaTypes(URL location, Map<String, Object> pr
LOGGER.fine(() -> "Reading schema from URL: " + location);

try (InputStream stream = location.openStream()) {
return readSchemaTypes(stream, properties);
return readSchemaTypes(stream, properties, false);
} catch (IOException e) {
throw new EDISchemaException("Unable to read URL stream", e);
}
}

static Map<String, EDIType> readSchemaTypes(InputStream stream, Map<String, Object> properties) throws EDISchemaException {
static Map<String, EDIType> readSchemaTypes(InputStream stream, Map<String, Object> properties, boolean setReferences) throws EDISchemaException {
try {
return getReader(stream, properties).readTypes();
return getReader(stream, properties).readTypes(setReferences);
} catch (StaEDISchemaReadException e) {
throw wrapped(e);
}
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/io/xlate/edi/schema/SchemaFactory.java
Expand Up @@ -28,6 +28,8 @@ public interface SchemaFactory {
* processed by a SchemaFactory. Besides a java.net.URL, any class with a
* toString method that returns a valid URL-formated String may be used as
* the value for this property.
*
* @since 1.8
*/
public static final String SCHEMA_LOCATION_URL_CONTEXT = "io.xlate.edi.schema.SCHEMA_LOCATION_URL_CONTEXT";

Expand Down

0 comments on commit 298a28d

Please sign in to comment.