Skip to content

Commit

Permalink
Merge branch 'test/95--Explore-XML-Schema-validator-warnings'
Browse files Browse the repository at this point in the history
  • Loading branch information
xamde committed Apr 25, 2023
2 parents b8fe05e + 95bac0f commit 92288e1
Show file tree
Hide file tree
Showing 8 changed files with 316 additions and 11 deletions.
43 changes: 43 additions & 0 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Build and Test

on:
push:

jobs:
build-and-test:
# list of OSes and what they contain https://github.com/actions/virtual-environments
runs-on: ubuntu-latest
steps:
- name: ↙️ Check out repository code from git
uses: actions/checkout@v3
with:
repository: Calpano/graphinout
path: graphinout

- name: ☕ Setup Java JDK
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
cache: 'maven'

- name: ⬇️ Install root project
working-directory: graphinout
run: mvn clean -U -N install

- name: 🔨️ Build & ⬇ install base
working-directory: graphinout/base
run: mvn clean -U install

- name: 🔨️ Build & ⬇ install readers
working-directory: graphinout
run: mvn clean -U -pl !base,!engine,!app-cmdline install
env:
MAVEN_OPTS: "-Xmx6144m"

- name: 🔨️ Build & ⬇ install engine & app-cmdline
working-directory: graphinout
run: mvn clean -U -pl engine,app-cmdline install
env:
MAVEN_OPTS: "-Xmx6144m"

Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
package com.calpano.graphinout.base.reader;

import com.calpano.graphinout.base.gio.DelegatingGioWriter;
import com.calpano.graphinout.base.gio.GioWriter;
import com.calpano.graphinout.base.gio.GioWriterImpl;
import com.calpano.graphinout.base.gio.ValidatingGioWriter;
import com.calpano.graphinout.base.input.InputSource;
import com.calpano.graphinout.base.input.SingleInputSource;
import com.calpano.graphinout.base.output.ValidatingGraphMlWriter;
import org.slf4j.Logger;

import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;

import static org.slf4j.LoggerFactory.getLogger;

public interface GioReader {

Logger log = getLogger(GioReader.class);

/**
* Set error handler to the reader. Reader will use it to report errors while parsing.
* IOExceptions remain normal IOExceptions.
* Set error handler to the reader. Reader will use it to report errors while parsing. IOExceptions remain normal
* IOExceptions.
*
* @param errorHandler
*/
void errorHandler(Consumer<ContentError> errorHandler);
Expand All @@ -26,19 +36,26 @@ public interface GioReader {

/**
* Inspect input
*
* @param singleInputSource
* @return
* @throws IOException
*/
default boolean isValid(SingleInputSource singleInputSource) throws IOException {
AtomicBoolean valid = new AtomicBoolean(true);
errorHandler((error) -> {
Consumer<ContentError> eh = error -> {
if (error.level == ContentError.ErrorLevel.Error || error.level == ContentError.ErrorLevel.Warn) {
valid.set(false);
}
});
// FIXME dont use null, use a dummy do-nothing-writer
read(singleInputSource, null);
};
errorHandler(eh);
try {
GioWriter writer = new DelegatingGioWriter(new ValidatingGioWriter(), new GioWriterImpl(new ValidatingGraphMlWriter()));
read(singleInputSource, writer);
} catch (Exception t) {
log.warn("Invalid input in "+singleInputSource.name(), t);
eh.accept(new ContentError(ContentError.ErrorLevel.Error,t.getMessage(), null));
}
return valid.get();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.calpano.graphinout.base.xml;

import com.calpano.graphinout.base.input.SingleInputSource;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;
Expand Down Expand Up @@ -51,4 +53,15 @@ void testWrongElement() throws IOException {
assertFalse(GraphmlValidator.isValidGraphml(SingleInputSource.of("funnybunny", badGraphml)));
}

@BeforeEach
void setUp() {
}

@AfterEach
void tearDown() {
}

@Test
void isValidGraphml() {
}
}
44 changes: 42 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
<mockito-core-version>5.1.1</mockito-core-version>
<mockito-junit-jupiter-version>5.1.1</mockito-junit-jupiter-version>
<lombok-version>1.18.24</lombok-version>
<maven-surefire-plugin.version>3.0.0-M7</maven-surefire-plugin.version>
</properties>

<dependencyManagement>
Expand Down Expand Up @@ -111,10 +112,49 @@
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M7</version>
<version>${maven-surefire-plugin.version}</version>
<executions>
<execution>
<id>default-test</id>
<phase>test</phase>
<goals>
<goal>test</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<!-- create "-sources.jar" at verify-phase just before install-phase -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
<phase>verify</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>

</plugins>
</build>


<dependencies>
<!-- https://mvnrepository.com/artifact/com.google.code.findbugs/jsr305 -->
<dependency>
Expand All @@ -134,4 +174,4 @@
<scope>test</scope>
</dependency>
</dependencies>
</project>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,25 @@
import com.calpano.graphinout.base.reader.ContentError;
import com.calpano.graphinout.base.reader.GioFileFormat;
import com.calpano.graphinout.base.reader.GioReader;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.SchemaFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

public class GraphmlReader implements GioReader {
//TODO This can load from config file
private List<String> externalSchemaList = new ArrayList<>();
private Consumer<ContentError> errorHandler;

@Override
Expand Down Expand Up @@ -47,8 +57,123 @@ public void read(InputSource inputSource, GioWriter writer) throws IOException {
throw new IOException(e);
}
} catch (Throwable t) {
errorHandler.accept(new ContentError(ContentError.ErrorLevel.Error, "Failed reading '" + inputSource.name() + "'. Details: "+t.getMessage(), null));
errorHandler.accept(new ContentError(ContentError.ErrorLevel.Error, "Failed reading '" + inputSource.name() + "'. Details: " + t.getMessage(), null));
throw new RuntimeException("Failed reading '" + inputSource.name() + "'", t);
}
}

@Override
public boolean isValid(SingleInputSource singleInputSource) throws IOException {

try {
validateWellFormed(singleInputSource);
//TODO There is no internal DTD in any of the files that have been processed so far.
//If it exists, it can be verified with this method
//validateInternalDTD(singleInputSource);
//ValidateInternalXSD(singleInputSource);
//ValidateExternalSchema(singleInputSource);
} catch (ParserConfigurationException e) {
throw new RuntimeException(e);
} catch (SAXException e) {
throw new RuntimeException(e);
}
return GioReader.super.isValid(singleInputSource);
}

void validateWellFormed(SingleInputSource inputSource) throws ParserConfigurationException, SAXException, IOException {
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setValidating(false);
factory.setNamespaceAware(true);

SAXParser parser = factory.newSAXParser();

XMLReader reader = parser.getXMLReader();
reader.setErrorHandler(new SimpleErrorHandler(this.errorHandler));
reader.parse(new org.xml.sax.InputSource(inputSource.inputStream()));
}

void validateInternalDTD(SingleInputSource inputSource) throws ParserConfigurationException, SAXException, IOException {
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setValidating(true);
factory.setNamespaceAware(true);

SAXParser parser = factory.newSAXParser();

XMLReader reader = parser.getXMLReader();
reader.setErrorHandler(new SimpleErrorHandler(this.errorHandler));
reader.parse(new org.xml.sax.InputSource(inputSource.inputStream()));
}

void validateInternalXSD(SingleInputSource inputSource) throws ParserConfigurationException, SAXException, IOException {
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setValidating(true);
factory.setNamespaceAware(true);

SAXParser parser = factory.newSAXParser();
parser.setProperty("http://java.sun.com/xml/jaxp/properties/schemaLanguage",
"http://www.w3.org/2001/XMLSchema");

XMLReader reader = parser.getXMLReader();
reader.setErrorHandler(new SimpleErrorHandler(this.errorHandler));
reader.parse(new org.xml.sax.InputSource(inputSource.inputStream()));
}

/**
* Two sub-options: (1) XML file contains "xsi:schemaLocation" -> take XML Schema from there;
* (2) we pre-downloaded Graphml.XSD, use it, and ignore "xsi:schemaLocation" -- http://graphml.graphdrawing.org/xmlns/1.1/graphml.xsd
* @param inputSource
* @throws SAXException
* @throws IOException
* @throws ParserConfigurationException
*/
void validateExternalSchema(SingleInputSource inputSource) throws SAXException, IOException, ParserConfigurationException {
if (externalSchemaList.isEmpty()) return;

Source[] listOfAvailableSchema = externalSchemaList.stream().toArray(StreamSource[]::new);
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setValidating(false);
factory.setNamespaceAware(true);

SchemaFactory schemaFactory =
SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
factory.setSchema(schemaFactory.newSchema(listOfAvailableSchema));

SAXParser parser = factory.newSAXParser();
XMLReader reader = parser.getXMLReader();
reader.setErrorHandler(new SimpleErrorHandler(this.errorHandler));
reader.parse(new org.xml.sax.InputSource(inputSource.inputStream()));

}

class SimpleErrorHandler implements ErrorHandler {


private final Consumer<ContentError> errorConsumer;

public SimpleErrorHandler(Consumer<ContentError> errorConsumer) {
this.errorConsumer = errorConsumer;
}

@Override
public void warning(SAXParseException e) throws SAXException {
ContentError contentError = new ContentError(ContentError.ErrorLevel.Warn, e.getMessage(),
null);
if (errorConsumer != null) {
errorConsumer.accept(contentError);
}
}

@Override
public void error(SAXParseException e) throws SAXException {
//TODO Throw exception Or log to errorConsumer?
throw e;

}

@Override
public void fatalError(SAXParseException e) throws SAXException {
//TODO Throw exception Or log to errorConsumer?
throw e;
}
}
}
Loading

0 comments on commit 92288e1

Please sign in to comment.