Repository: jena Updated Branches: refs/heads/master c61071b97 -> fab10e555
TriX Project: http://git-wip-us.apache.org/repos/asf/jena/repo Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/d6aa36c8 Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/d6aa36c8 Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/d6aa36c8 Branch: refs/heads/master Commit: d6aa36c880d9d7c25860ba49044a091f8e944123 Parents: 84fdb96 Author: Andy Seaborne <[email protected]> Authored: Sun Oct 5 17:58:53 2014 +0100 Committer: Andy Seaborne <[email protected]> Committed: Sun Oct 5 17:58:53 2014 +0100 ---------------------------------------------------------------------- .../main/java/org/apache/jena/riot/Lang.java | 3 + .../java/org/apache/jena/riot/RDFFormat.java | 81 ++-- .../java/org/apache/jena/riot/RDFLanguages.java | 43 +- .../org/apache/jena/riot/RDFParserRegistry.java | 16 +- .../org/apache/jena/riot/RDFWriterRegistry.java | 76 ++-- .../java/org/apache/jena/riot/WebContent.java | 6 +- .../org/apache/jena/riot/lang/ReaderTriX.java | 440 +++++++++++++++++++ .../java/org/apache/jena/riot/lang/TriX.java | 59 +++ .../jena/riot/system/StreamRDFWriter.java | 13 +- .../jena/riot/writer/StreamWriterTriX.java | 250 +++++++++++ .../org/apache/jena/riot/writer/WriterTriX.java | 91 ++++ .../java/org/apache/jena/riot/lang/TS_Lang.java | 4 + .../org/apache/jena/riot/lang/TestTriXBad.java | 76 ++++ .../apache/jena/riot/lang/TestTriXReader.java | 126 ++++++ .../apache/jena/riot/writer/TS_RiotWriter.java | 1 + .../apache/jena/riot/writer/TestTriXWriter.java | 78 ++++ 16 files changed, 1272 insertions(+), 91 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/jena/blob/d6aa36c8/jena-arq/src/main/java/org/apache/jena/riot/Lang.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/riot/Lang.java b/jena-arq/src/main/java/org/apache/jena/riot/Lang.java index c579296..8ee39ec 100644 --- a/jena-arq/src/main/java/org/apache/jena/riot/Lang.java +++ b/jena-arq/src/main/java/org/apache/jena/riot/Lang.java @@ -84,6 +84,9 @@ public class Lang /** "CSV" - CSV data read into an RDF model with simple conversion : See Jena-625 */ public static Lang CSV ; + /** <a href="http://www.hpl.hp.com/techreports/2004/HPL-2004-56.html">TriX</a> */ + public static Lang TRIX ; + /** The "null" language */ public static Lang RDFNULL ; http://git-wip-us.apache.org/repos/asf/jena/blob/d6aa36c8/jena-arq/src/main/java/org/apache/jena/riot/RDFFormat.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/riot/RDFFormat.java b/jena-arq/src/main/java/org/apache/jena/riot/RDFFormat.java index 4e4942b..0cafd8c 100644 --- a/jena-arq/src/main/java/org/apache/jena/riot/RDFFormat.java +++ b/jena-arq/src/main/java/org/apache/jena/riot/RDFFormat.java @@ -24,58 +24,59 @@ import org.apache.jena.atlas.lib.Lib ; /** Constants for writable formats */ public class RDFFormat { /** Pretty printing variant */ - public static RDFFormatVariant PRETTY = new RDFFormatVariant("pretty") ; + public static final RDFFormatVariant PRETTY = new RDFFormatVariant("pretty") ; /** * Print in blocks, typically all triples with the same subject in an * incoming triple/quad stream */ - public static RDFFormatVariant BLOCKS = new RDFFormatVariant("blocks") ; + public static final RDFFormatVariant BLOCKS = new RDFFormatVariant("blocks") ; /** Print out one per line */ - public static RDFFormatVariant FLAT = new RDFFormatVariant("flat") ; + public static final RDFFormatVariant FLAT = new RDFFormatVariant("flat") ; /** Use ASCII output (N-triples, N-Quads) */ - public static RDFFormatVariant ASCII = new RDFFormatVariant("ascii") ; + public static final RDFFormatVariant ASCII = new RDFFormatVariant("ascii") ; /** Use UTF-8 output (N-triples, N-Quads) */ - public static RDFFormatVariant UTF8 = new RDFFormatVariant("utf-8") ; + public static final RDFFormatVariant UTF8 = new RDFFormatVariant("utf-8") ; /** Variant for RDF Thrift using values */ - public static final RDFFormatVariant ValueEncoding = new RDFFormatVariant("Value") ; + public static final RDFFormatVariant ValueEncoding = new RDFFormatVariant("Value") ; - public static RDFFormat TURTLE_PRETTY = new RDFFormat(Lang.TURTLE, PRETTY) ; - public static RDFFormat TURTLE = TURTLE_PRETTY ; - public static RDFFormat TTL = TURTLE_PRETTY ; - public static RDFFormat TURTLE_BLOCKS = new RDFFormat(Lang.TURTLE, BLOCKS) ; - public static RDFFormat TURTLE_FLAT = new RDFFormat(Lang.TURTLE, FLAT) ; - - public static RDFFormat NTRIPLES_UTF8 = new RDFFormat(Lang.NTRIPLES, UTF8) ; - public static RDFFormat NTRIPLES = NTRIPLES_UTF8 ; - public static RDFFormat NT = NTRIPLES ; - public static RDFFormat NTRIPLES_ASCII = new RDFFormat(Lang.NTRIPLES, ASCII) ; - - public static RDFFormat NQUADS_UTF8 = new RDFFormat(Lang.NQUADS, UTF8) ; - public static RDFFormat NQUADS = NQUADS_UTF8 ; - public static RDFFormat NQ = NQUADS ; - public static RDFFormat NQUADS_ASCII = new RDFFormat(Lang.NQUADS, ASCII) ; - - public static RDFFormat TRIG_PRETTY = new RDFFormat(Lang.TRIG, PRETTY) ; - public static RDFFormat TRIG = TRIG_PRETTY ; - public static RDFFormat TRIG_BLOCKS = new RDFFormat(Lang.TRIG, BLOCKS) ; - public static RDFFormat TRIG_FLAT = new RDFFormat(Lang.TRIG, FLAT) ; - - public static RDFFormat JSONLD_PRETTY = new RDFFormat(Lang.JSONLD, PRETTY) ; - public static RDFFormat JSONLD = JSONLD_PRETTY ; - public static RDFFormat JSONLD_FLAT = new RDFFormat(Lang.JSONLD, FLAT) ; + public static final RDFFormat TURTLE_PRETTY = new RDFFormat(Lang.TURTLE, PRETTY) ; + public static final RDFFormat TURTLE = TURTLE_PRETTY ; + public static final RDFFormat TTL = TURTLE_PRETTY ; + public static final RDFFormat TURTLE_BLOCKS = new RDFFormat(Lang.TURTLE, BLOCKS) ; + public static final RDFFormat TURTLE_FLAT = new RDFFormat(Lang.TURTLE, FLAT) ; + + public static final RDFFormat NTRIPLES_UTF8 = new RDFFormat(Lang.NTRIPLES, UTF8) ; + public static final RDFFormat NTRIPLES = NTRIPLES_UTF8 ; + public static final RDFFormat NT = NTRIPLES ; + public static final RDFFormat NTRIPLES_ASCII = new RDFFormat(Lang.NTRIPLES, ASCII) ; + + public static final RDFFormat NQUADS_UTF8 = new RDFFormat(Lang.NQUADS, UTF8) ; + public static final RDFFormat NQUADS = NQUADS_UTF8 ; + public static final RDFFormat NQ = NQUADS ; + public static final RDFFormat NQUADS_ASCII = new RDFFormat(Lang.NQUADS, ASCII) ; + + public static final RDFFormat TRIG_PRETTY = new RDFFormat(Lang.TRIG, PRETTY) ; + public static final RDFFormat TRIG = TRIG_PRETTY ; + public static final RDFFormat TRIG_BLOCKS = new RDFFormat(Lang.TRIG, BLOCKS) ; + public static final RDFFormat TRIG_FLAT = new RDFFormat(Lang.TRIG, FLAT) ; + + public static final RDFFormat JSONLD_PRETTY = new RDFFormat(Lang.JSONLD, PRETTY) ; + public static final RDFFormat JSONLD = JSONLD_PRETTY ; + public static final RDFFormat JSONLD_FLAT = new RDFFormat(Lang.JSONLD, FLAT) ; /** RDF/XML ABBREV variant */ - public static RDFFormatVariant ABBREV = new RDFFormatVariant("pretty") ; + public static final RDFFormatVariant ABBREV = new RDFFormatVariant("pretty") ; /** Basic RDF/XML variant */ - public static RDFFormatVariant PLAIN = new RDFFormatVariant("plain") ; + public static final RDFFormatVariant PLAIN = new RDFFormatVariant("plain") ; - public static RDFFormat RDFXML_PRETTY = new RDFFormat(Lang.RDFXML, ABBREV) ; - public static RDFFormat RDFXML_ABBREV = RDFXML_PRETTY ; - public static RDFFormat RDFXML = RDFXML_PRETTY ; - public static RDFFormat RDFXML_PLAIN = new RDFFormat(Lang.RDFXML, PLAIN) ; + public static final RDFFormat RDFXML_PRETTY = new RDFFormat(Lang.RDFXML, ABBREV) ; + public static final RDFFormat RDFXML_ABBREV = RDFXML_PRETTY ; + public static final RDFFormat RDFXML = RDFXML_PRETTY ; + public static final RDFFormat RDFXML_PLAIN = new RDFFormat(Lang.RDFXML, PLAIN) ; - public static RDFFormat RDFJSON = new RDFFormat(Lang.RDFJSON) ; + public static final RDFFormat RDFJSON = new RDFFormat(Lang.RDFJSON) ; + public static final RDFFormat TRIX = new RDFFormat(Lang.TRIX) ; /** * RDF Thrift output. This format is faithful representation of RDF writtern @@ -85,7 +86,7 @@ public class RDFFormat { * @see #RDF_THRIFT_VALUES */ - public static final RDFFormat RDF_THRIFT = new RDFFormat(THRIFT) ; + public static final RDFFormat RDF_THRIFT = new RDFFormat(THRIFT) ; /** * A variant of an an RDFFormat that uses value encoding (e.g. integers, * doubles, decimals as binary). This does not preserve exact represenation @@ -97,13 +98,13 @@ public class RDFFormat { * * @see #RDF_THRIFT */ - public static final RDFFormat RDF_THRIFT_VALUES = new RDFFormat(THRIFT, ValueEncoding) ; + public static final RDFFormat RDF_THRIFT_VALUES = new RDFFormat(THRIFT, ValueEncoding) ; /** * The "null" output format (a sink that prints nothing, usually quite * efficiently) */ - public static RDFFormat RDFNULL = new RDFFormat(Lang.RDFNULL) ; + public static final RDFFormat RDFNULL = new RDFFormat(Lang.RDFNULL) ; private final Lang lang ; private final RDFFormatVariant variant ; http://git-wip-us.apache.org/repos/asf/jena/blob/d6aa36c8/jena-arq/src/main/java/org/apache/jena/riot/RDFLanguages.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/riot/RDFLanguages.java b/jena-arq/src/main/java/org/apache/jena/riot/RDFLanguages.java index 4626ea6..2306532 100644 --- a/jena-arq/src/main/java/org/apache/jena/riot/RDFLanguages.java +++ b/jena-arq/src/main/java/org/apache/jena/riot/RDFLanguages.java @@ -48,6 +48,7 @@ public class RDFLanguages public static final String strLangNQuads = "N-Quads" ; public static final String strLangTriG = "TriG" ; public static final String strLangCSV = "CSV"; + public static final String strLangTriX = "TriX"; /* * ".owl" is not a formally registered file extension for OWL @@ -119,27 +120,35 @@ public class RDFLanguages public static final Lang NQ = NQUADS ; /** CSV data. This can be read into an RDF model with simple conversion */ - public static final Lang CSV = LangBuilder.create(strLangCSV, contentTypeTextCSV) - .addAltNames("csv") - .addFileExtensions("csv") - .build() ; + public static final Lang CSV = LangBuilder.create(strLangCSV, contentTypeTextCSV) + .addAltNames("csv") + .addFileExtensions("csv") + .build() ; /** The RDF syntax "RDF Thrift" : see http://jena.apache.org/documentation/io */ - public static final Lang THRIFT = LangBuilder.create("RDF_THRIFT", contentTypeRDFThrift) - .addAltNames("RDF-THRIFT", "TRDF") - .addFileExtensions("rt", "trdf") - .build() ; + public static final Lang THRIFT = LangBuilder.create("RDF_THRIFT", contentTypeRDFThrift) + .addAltNames("RDF-THRIFT", "TRDF") + .addFileExtensions("rt", "trdf") + .build() ; /** Text */ - public static final Lang TEXT = LangBuilder.create("text", contentTypeTextPlain) - .addAltNames("TEXT") - .addFileExtensions("txt") - .build() ; + public static final Lang TEXT = LangBuilder.create("text", contentTypeTextPlain) + .addAltNames("TEXT") + .addFileExtensions("txt") + .build() ; + /** TriX */ + public static final Lang TRIX = LangBuilder.create(strLangTriX, contentTypeTriX) + .addAltContentTypes(contentTypeTriXxml) + .addAltNames("TRIX", "trix") + // Extension "xml" is used for RDF/XML. + .addFileExtensions("trix") + .build() ; + /** The "null" language */ - public static final Lang RDFNULL = LangBuilder.create("rdf/null", "null/rdf") - .addAltNames("NULL", "null") - .build() ; + public static final Lang RDFNULL = LangBuilder.create("rdf/null", "null/rdf") + .addAltNames("NULL", "null") + .build() ; // ---- Central registry @@ -173,8 +182,9 @@ public class RDFLanguages Lang.NQUADS = RDFLanguages.NQUADS ; Lang.NQ = RDFLanguages.NQ ; Lang.TRIG = RDFLanguages.TRIG ; - Lang.RDFTHRIFT = RDFLanguages.THRIFT ; + Lang.RDFTHRIFT = RDFLanguages.THRIFT ; Lang.CSV = RDFLanguages.CSV ; + Lang.TRIX = RDFLanguages.TRIX ; Lang.RDFNULL = RDFLanguages.RDFNULL ; } // ---------------------- @@ -192,6 +202,7 @@ public class RDFLanguages register(NQUADS) ; register(THRIFT) ; register(CSV) ; + register(TRIX) ; register(RDFNULL) ; // Check for JSON-LD engine. http://git-wip-us.apache.org/repos/asf/jena/blob/d6aa36c8/jena-arq/src/main/java/org/apache/jena/riot/RDFParserRegistry.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/riot/RDFParserRegistry.java b/jena-arq/src/main/java/org/apache/jena/riot/RDFParserRegistry.java index c51bff8..642afe1 100644 --- a/jena-arq/src/main/java/org/apache/jena/riot/RDFParserRegistry.java +++ b/jena-arq/src/main/java/org/apache/jena/riot/RDFParserRegistry.java @@ -27,7 +27,7 @@ import static org.apache.jena.riot.RDFLanguages.RDFJSON ; import static org.apache.jena.riot.RDFLanguages.RDFXML ; import static org.apache.jena.riot.RDFLanguages.THRIFT ; import static org.apache.jena.riot.RDFLanguages.TRIG ; -import static org.apache.jena.riot.RDFLanguages.TURTLE ; +import static org.apache.jena.riot.RDFLanguages.* ; import java.io.InputStream ; import java.io.Reader ; @@ -39,6 +39,7 @@ import org.apache.jena.atlas.lib.InternalErrorException ; import org.apache.jena.atlas.web.ContentType ; import org.apache.jena.riot.lang.JsonLDReader ; import org.apache.jena.riot.lang.LangRIOT ; +import org.apache.jena.riot.lang.ReaderTriX ; import org.apache.jena.riot.system.ErrorHandler ; import org.apache.jena.riot.system.ErrorHandlerFactory ; import org.apache.jena.riot.system.ParserProfile ; @@ -46,7 +47,6 @@ import org.apache.jena.riot.system.StreamRDF ; import org.apache.jena.riot.thrift.BinRDF ; import com.hp.hpl.jena.sparql.util.Context ; -//import org.apache.jena.atlas.lib.Sink ; /** The registry of languages and parsers. * To register a new parser: @@ -74,6 +74,7 @@ public class RDFParserRegistry private static ReaderRIOTFactory parserFactory = new ReaderRIOTFactoryImpl() ; private static ReaderRIOTFactory parserFactoryJsonLD = new ReaderRIOTFactoryJSONLD() ; private static ReaderRIOTFactory parserFactoryThrift = new ReaderRIOTFactoryThrift() ; + private static ReaderRIOTFactory parserFactoryTriX = new ReaderRIOTFactoryTriX() ; private static boolean initialized = false ; static { init() ; } @@ -97,11 +98,13 @@ public class RDFParserRegistry registerLangTriples(RDFJSON, parserFactory) ; registerLangTriples(CSV, parserFactory) ; registerLangTriples(THRIFT, parserFactoryThrift) ; + registerLangTriples(TRIX, parserFactoryTriX) ; registerLangQuads(JSONLD, parserFactoryJsonLD) ; registerLangQuads(NQUADS, parserFactory) ; registerLangQuads(TRIG, parserFactory) ; registerLangQuads(THRIFT, parserFactoryThrift) ; + registerLangQuads(TRIX, parserFactoryTriX) ; } /** Register a language and it's parser factory. @@ -197,8 +200,7 @@ public class RDFParserRegistry @Override public void setParserProfile(ParserProfile parserProfile) { this.parserProfile = parserProfile ; } } - private static class ReaderRIOTFactoryJSONLD implements ReaderRIOTFactory - { + private static class ReaderRIOTFactoryJSONLD implements ReaderRIOTFactory { @Override public ReaderRIOT create(Lang language) { if ( !Lang.JSONLD.equals(language) ) @@ -242,5 +244,11 @@ public class RDFParserRegistry } + private static class ReaderRIOTFactoryTriX implements ReaderRIOTFactory { + @Override + public ReaderRIOT create(Lang language) { + return new ReaderTriX() ; + } + } } http://git-wip-us.apache.org/repos/asf/jena/blob/d6aa36c8/jena-arq/src/main/java/org/apache/jena/riot/RDFWriterRegistry.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/riot/RDFWriterRegistry.java b/jena-arq/src/main/java/org/apache/jena/riot/RDFWriterRegistry.java index cca228a..1d1a2fb 100644 --- a/jena-arq/src/main/java/org/apache/jena/riot/RDFWriterRegistry.java +++ b/jena-arq/src/main/java/org/apache/jena/riot/RDFWriterRegistry.java @@ -126,6 +126,22 @@ public class RDFWriterRegistry } } ; + static WriterGraphRIOTFactory wgTriXFactory = new WriterGraphRIOTFactory() { + + @Override + public WriterGraphRIOT create(RDFFormat syntaxForm) { + return new WriterTriX() ; + } + } ; + + static WriterDatasetRIOTFactory wdsTriXFactory = new WriterDatasetRIOTFactory() { + + @Override + public WriterDatasetRIOT create(RDFFormat syntaxForm) { + return new WriterTriX() ; + } + } ; + public static void init() {} static { init$() ; } private static void init$() @@ -142,7 +158,8 @@ public class RDFWriterRegistry register(Lang.TRIG, RDFFormat.TRIG) ; register(Lang.NQUADS, RDFFormat.NQUADS) ; register(Lang.RDFNULL, RDFFormat.RDFNULL) ; - register(Lang.RDFTHRIFT, RDFFormat.RDF_THRIFT) ; + register(Lang.RDFTHRIFT, RDFFormat.RDF_THRIFT) ; + register(Lang.TRIX, RDFFormat.TRIX) ; // Writer factories. register(RDFFormat.TURTLE_PRETTY, wgfactory) ; @@ -173,6 +190,8 @@ public class RDFWriterRegistry register(RDFFormat.RDF_THRIFT, wgThriftFactory) ; register(RDFFormat.RDF_THRIFT_VALUES, wgThriftFactory) ; + register(RDFFormat.TRIX, wgTriXFactory) ; + // Datasets register(RDFFormat.TRIG_PRETTY, wdsfactory) ; register(RDFFormat.TRIG_BLOCKS, wdsfactory) ; @@ -188,7 +207,8 @@ public class RDFWriterRegistry register(RDFFormat.RDF_THRIFT, wdsThriftFactory) ; register(RDFFormat.RDF_THRIFT_VALUES, wdsThriftFactory) ; - + + register(RDFFormat.TRIX, wdsTriXFactory) ; } /** Register the serialization for graphs and it's associated factory @@ -213,27 +233,31 @@ public class RDFWriterRegistry private static void register(RDFFormat serialization) { } - /** Register the default serialization for the language (replace any existing registration). - * @param lang Languages - * @param format The serialization forma to use when the language is used for writing. + /** + * Register the default serialization for the language (replace any existing + * registration). + * + * @param lang + * Languages + * @param format + * The serialization forma to use when the language is used for + * writing. */ - public static void register(Lang lang, RDFFormat format) - { + public static void register(Lang lang, RDFFormat format) { register(format) ; langToFormat.put(lang, format) ; } - - /** Return the format registered as the default for the language */ - public static RDFFormat defaultSerialization(Lang lang) - { + + /** Return the format registered as the default for the language */ + public static RDFFormat defaultSerialization(Lang lang) { return langToFormat.get(lang) ; } /** Does the language have a registered output format? */ - public static boolean contains(Lang lang) - { - if ( ! langToFormat.containsKey(lang) ) return false ; - + public static boolean contains(Lang lang) { + if ( !langToFormat.containsKey(lang) ) + return false ; + RDFFormat fmt = langToFormat.get(lang) ; return contains(fmt) ; } @@ -254,34 +278,30 @@ public class RDFWriterRegistry } /** Get the graph writer factory asscoiated with the language */ - public static WriterGraphRIOTFactory getWriterGraphFactory(Lang lang) - { + public static WriterGraphRIOTFactory getWriterGraphFactory(Lang lang) { RDFFormat serialization = defaultSerialization(lang) ; if ( serialization == null ) - throw new RiotException("No default serialization for language "+lang) ; + throw new RiotException("No default serialization for language " + lang) ; return getWriterGraphFactory(serialization) ; } /** Get the graph writer factory asscoiated with the output format */ - public static WriterGraphRIOTFactory getWriterGraphFactory(RDFFormat serialization) - { + public static WriterGraphRIOTFactory getWriterGraphFactory(RDFFormat serialization) { return registryGraph.get(serialization) ; } - + /** Get the dataset writer factory asscoiated with the language */ - public static WriterDatasetRIOTFactory getWriterDatasetFactory(Lang lang) - { + public static WriterDatasetRIOTFactory getWriterDatasetFactory(Lang lang) { RDFFormat serialization = defaultSerialization(lang) ; if ( serialization == null ) - throw new RiotException("No default serialization for language "+lang) ; - return getWriterDatasetFactory(serialization) ; + throw new RiotException("No default serialization for language " + lang) ; + return getWriterDatasetFactory(serialization) ; } /** Get the dataset writer factory asscoiated with the output format */ - public static WriterDatasetRIOTFactory getWriterDatasetFactory(RDFFormat serialization) - { + public static WriterDatasetRIOTFactory getWriterDatasetFactory(RDFFormat serialization) { if ( serialization == null ) - return null ; + return null ; return registryDataset.get(serialization) ; } } http://git-wip-us.apache.org/repos/asf/jena/blob/d6aa36c8/jena-arq/src/main/java/org/apache/jena/riot/WebContent.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/riot/WebContent.java b/jena-arq/src/main/java/org/apache/jena/riot/WebContent.java index fd5875f..8cd1265 100644 --- a/jena-arq/src/main/java/org/apache/jena/riot/WebContent.java +++ b/jena-arq/src/main/java/org/apache/jena/riot/WebContent.java @@ -82,8 +82,10 @@ public class WebContent public static final String contentTypeNQuadsAlt2 = "text/nquads" ; public static final ContentType ctNQuadsAlt2 = ContentType.create(contentTypeNQuadsAlt2) ; - public static final String contentTypeTriX = "application/trix+xml" ; + public static final String contentTypeTriX = "application/trix" ; public static final ContentType ctTriX = ContentType.create(contentTypeTriX) ; + public static final String contentTypeTriXxml = "application/trix+xml" ; + public static final ContentType ctTriXxml = ContentType.create(contentTypeTriXxml) ; public static final String contentTypeOctets = "application/octet-stream" ; public static final ContentType ctOctets = ContentType.create(contentTypeOctets) ; @@ -119,7 +121,7 @@ public class WebContent public static final String contentTypeResultsThrift = "application/sparql-results+thrift" ; public static final ContentType ctResultsThrift = ContentType.create(contentTypeResultsThrift) ; - + // Unofficial public static final String contentTypeResultsBIO = "application/sparql-results+bio" ; public static final ContentType ctResultsBIO = ContentType.create(contentTypeResultsBIO) ; http://git-wip-us.apache.org/repos/asf/jena/blob/d6aa36c8/jena-arq/src/main/java/org/apache/jena/riot/lang/ReaderTriX.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/riot/lang/ReaderTriX.java b/jena-arq/src/main/java/org/apache/jena/riot/lang/ReaderTriX.java new file mode 100644 index 0000000..9519d5c --- /dev/null +++ b/jena-arq/src/main/java/org/apache/jena/riot/lang/ReaderTriX.java @@ -0,0 +1,440 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.jena.riot.lang; + +import static org.apache.jena.riot.lang.ReaderTriX.State.GRAPH ; +import static org.apache.jena.riot.lang.ReaderTriX.State.OUTER ; +import static org.apache.jena.riot.lang.ReaderTriX.State.TRIPLE ; +import static org.apache.jena.riot.lang.ReaderTriX.State.TRIX ; + +import java.io.InputStream ; +import java.io.Reader ; +import java.util.ArrayList ; +import java.util.Collection ; +import java.util.List ; +import java.util.Objects ; + +import javax.xml.namespace.QName ; +import javax.xml.stream.* ; + +import org.apache.jena.atlas.web.ContentType ; +import org.apache.jena.riot.ReaderRIOT ; +import org.apache.jena.riot.RiotException ; +import org.apache.jena.riot.system.* ; +import org.apache.jena.riot.writer.StreamWriterTriX ; +import org.apache.jena.riot.writer.WriterTriX ; + +import com.hp.hpl.jena.datatypes.RDFDatatype ; +import com.hp.hpl.jena.datatypes.xsd.XSDDatatype ; +import com.hp.hpl.jena.datatypes.xsd.impl.XMLLiteralType ; +import com.hp.hpl.jena.graph.Node ; +import com.hp.hpl.jena.graph.NodeFactory ; +import com.hp.hpl.jena.graph.Triple ; +import com.hp.hpl.jena.sparql.core.Quad ; +import com.hp.hpl.jena.sparql.resultset.ResultSetException ; +import com.hp.hpl.jena.sparql.util.Context ; +import com.hp.hpl.jena.vocabulary.RDF ; + +/** Read TriX. + * See {@link TriX} for details. + * @see TriX + * @see WriterTriX + * @see StreamWriterTriX + */ +public class ReaderTriX implements ReaderRIOT { + + // DTD for TrIX : The schema is a much longer. +/* +<!-- TriX: RDF Triples in XML --> +<!ELEMENT TriX (graph*)> +<!ATTLIST TriX xmlns CDATA #FIXED "http://www.w3.org/2004/03/trix/trix-1/"> +<!ELEMENT graph (uri*, triple*)> +<!ELEMENT triple ((id|uri|plainLiteral|typedLiteral), uri, (id|uri|plainLiteral|typedLiteral))> +<!ELEMENT id (#PCDATA)> +<!ELEMENT uri (#PCDATA)> +<!ELEMENT plainLiteral (#PCDATA)> +<!ATTLIST plainLiteral xml:lang CDATA #IMPLIED> +<!ELEMENT typedLiteral (#PCDATA)> +<!ATTLIST typedLiteral datatype CDATA #REQUIRED> + */ + + + + private ErrorHandler errorHandler = ErrorHandlerFactory.getDefaultErrorHandler() ; + private ParserProfile parserProfile = null ; + + @Override + public void read(InputStream in, String baseURI, ContentType ct, StreamRDF output, Context context) { + XMLInputFactory xf = XMLInputFactory.newInstance() ; + XMLStreamReader xReader ; + try { + xReader = xf.createXMLStreamReader(in) ; + } catch (XMLStreamException e) { throw new RiotException("Can't initialize StAX parsing engine", e) ; } + read(xReader, baseURI, output) ; + } + + @Override + public void read(Reader reader, String baseURI, ContentType ct, StreamRDF output, Context context) { + XMLInputFactory xf = XMLInputFactory.newInstance() ; + XMLStreamReader xReader ; + try { + xReader = xf.createXMLStreamReader(reader) ; + } catch (XMLStreamException e) { throw new ResultSetException("Can't initialize StAX parsing engine", e) ; } + read(xReader, baseURI, output) ; + } + + private static String nsRDF = RDF.getURI() ; + private static String nsXSD = XSDDatatype.XSD ; // No "#" + private static String nsXML0 = "http://www.w3.org/XML/1998/namespace" ; + private static String rdfXMLLiteral = XMLLiteralType.theXMLLiteralType.getURI() ; + + enum State { OUTER, TRIX, GRAPH, TRIPLE } + + private void read(XMLStreamReader parser, String baseURI, StreamRDF output) { + ParserProfile profile = parserProfile ; + if ( profile == null ) + profile = RiotLib.profile(baseURI, false, false, errorHandler) ; + if ( errorHandler == null ) + setErrorHandler(profile.getHandler()) ; + + State state = OUTER ; + Node g = null ; + List<Node> terms = new ArrayList<>() ; + + try { + while(parser.hasNext()) { + int event = parser.next() ; + switch (event) { + case XMLStreamConstants.NAMESPACE: + break ; + case XMLStreamConstants.START_DOCUMENT : + break ; + case XMLStreamConstants.END_DOCUMENT : + if ( state != OUTER ) + staxError(parser.getLocation(), "End of document while processing XML element") ; + return ; + case XMLStreamConstants.END_ELEMENT : { + String tag = parser.getLocalName() ; + switch(tag) { + case TriX.tagTriple: { + int line = parser.getLocation().getLineNumber() ; + int col = parser.getLocation().getColumnNumber() ; + if ( terms.size() != 3 ) + staxError(parser.getLocation(), "Wrong number of terms for a triple. Want 3, got "+terms.size()) ; + Node s = terms.get(0) ; + Node p = terms.get(1) ; + Node o = terms.get(2) ; + if ( p.isLiteral() ) + staxError(parser.getLocation(), "Predicate is a literal") ; + if ( s.isLiteral() ) + staxError(parser.getLocation(), "Subject is a literal") ; + if ( g == null ) { + Triple t = profile.createTriple(s, p, o, line, col) ; + output.triple(t) ; + } + else { + if ( g.isLiteral() ) + staxError(parser.getLocation(), "graph name is a literal") ; + Quad q = profile.createQuad(g, s, p, o, line, col) ; + output.quad(q) ; + } + terms.clear(); + // Next is either end of <graph> or another <triple> + state = GRAPH ; + break ; + } + case TriX.tagGraph: + state = TRIX ; + g = null ; + break ; + case TriX.tagTriX: + state = OUTER ; + break ; + } + break ; + } + case XMLStreamConstants.START_ELEMENT : { + String tag = parser.getLocalName() ; + + switch (tag) { + case TriX.tagTriX: + if ( state != OUTER ) + staxErrorOutOfPlaceElement(parser) ; + state = TRIX ; + break ; + // structure + case TriX.tagGraph: + if ( state != TRIX ) + staxErrorOutOfPlaceElement(parser) ; + // URI? + state = GRAPH ; + break ; + case TriX.tagTriple: { + if ( state != GRAPH ) + staxErrorOutOfPlaceElement(parser) ; + state = TRIPLE ; + break ; + } + // Can occur in <graph> and <triple> + case TriX.tagId: + case TriX.tagQName: + case TriX.tagURI: { + if ( state != GRAPH && state != TRIPLE ) + staxErrorOutOfPlaceElement(parser) ; + Node n = term(parser, profile) ; + if ( state == GRAPH ) { + if ( g != null ) + staxError(parser.getLocation(), "Duplicate graph name") ; + g = n ; + if ( g.isLiteral() ) + staxError(parser.getLocation(), "graph name is a literal") ; + } + else + add(terms, n, 3, parser) ; + break ; + } + + case TriX.tagPlainLiteral: + case TriX.tagTypedLiteral: { + if ( state != TRIPLE ) + staxErrorOutOfPlaceElement(parser) ; + Node n = term(parser, profile) ; + add(terms, n, 3, parser) ; + break ; + } + default: + staxError(parser.getLocation(), "Unrecognized XML element: "+qnameAsString(parser.getName())) ; + break ; + } + } + } + } + staxError("Premature end of file") ; + return ; + } catch (XMLStreamException ex) { + staxError(parser.getLocation(), "XML error: "+ex.getMessage()) ; + } + } + + private void add(Collection<Node> acc, Node node, int max, XMLStreamReader parser) { + if ( acc.size() >= max ) + staxError(parser.getLocation(), "Too many terms for a triple: "+node) ; + acc.add(node) ; + } + + private void staxErrorOutOfPlaceElement(XMLStreamReader parser) { + staxError(parser.getLocation(), "Out of place XML element: "+tagName(parser)) ; + } + + private Node term(XMLStreamReader parser, ParserProfile profile) throws XMLStreamException { + String tag = parser.getLocalName() ; + int line = parser.getLocation().getLineNumber() ; + int col = parser.getLocation().getColumnNumber() ; + + switch(tag) { + case TriX.tagURI: { + // Two uses! + String x = parser.getElementText() ; + Node n = profile.createURI(x, line, col) ; + return n ; + } + case TriX.tagQName: { + String x = parser.getElementText() ; + int idx = x.indexOf(':') ; + if ( idx == -1 ) + staxError(parser.getLocation(), "Expected ':' in prefixed name. Found "+x) ; + String[] y = x.split(":", 2) ; // Allows additional ':' + String prefUri = parser.getNamespaceURI(y[0]) ; + String local = y[1] ; + return profile.createURI(prefUri+local, line, col) ; + } + case TriX.tagId: { + String x = parser.getElementText() ; + return profile.createBlankNode(null, x, line, col) ; + } + case TriX.tagPlainLiteral: { + // xml:lang + int x = parser.getAttributeCount() ; + if ( x > 1 ) + // Namespaces? + staxError(parser.getLocation(), "Multiple attributes : only one allowed") ; + String lang = null ; + if ( x == 1 ) + lang = attribute(parser, nsXML0, TriX.attrXmlLang) ; + String lex = parser.getElementText() ; + if ( lang == null ) + return profile.createStringLiteral(lex, line, col) ; + else + return profile.createLangLiteral(lex, lang, line, col) ; + } + case TriX.tagTypedLiteral: { + int nAttr = parser.getAttributeCount() ; + if ( nAttr != 1 ) + staxError(parser.getLocation(), "Multiple attributes : only one allowed") ; + String dt = attribute(parser, TriX.NS, TriX.attrDatatype) ; + if ( dt == null ) + staxError(parser.getLocation(), "No datatype attribute") ; + RDFDatatype rdt = NodeFactory.getType(dt) ; + + String lex = (rdfXMLLiteral.equals(dt)) + ? slurpRDFXMLLiteral(parser) + : parser.getElementText() ; + return profile.createTypedLiteral(lex, rdt, line, col) ; + } + default: { + QName qname = parser.getName() ; + staxError(parser.getLocation(), "Unrecognized tag -- "+qnameAsString(qname)) ; + return null ; + } + } + } + + private String slurpRDFXMLLiteral(XMLStreamReader parser) throws XMLStreamException { + StringBuffer content = new StringBuffer(); + int depth = 0 ; + + while(parser.hasNext()) { + int event = parser.next(); + switch (event) { + case XMLStreamConstants.START_ELEMENT: { + QName qname = parser.getName() ; + content.append("<") ; + content.append(qnameAsString(qname)) ; + int N = parser.getNamespaceCount() ; + for ( int i = 0 ; i < N ; i ++ ) { + String p = parser.getNamespacePrefix(i) ; + if ( p == null ) + p = "xmlns" ; + else + p = "xmlns:"+p ; + String v = parser.getNamespaceURI(i) ; + content.append(" ") ; + content.append(p) ; + content.append("=\"") ; + content.append(v) ; + content.append("\"") ; + } + + N = parser.getAttributeCount() ; + for ( int i = 0 ; i < N ; i ++ ) { + QName name = parser.getAttributeName(i) ; + String a = qnameAsString(name) ; + String v = parser.getAttributeValue(i) ; + content.append(" ") ; + content.append(a) ; + content.append("=\"") ; + content.append(v) ; + content.append("\"") ; + } + content.append(">") ; + depth++ ; + break ; + } + case XMLStreamConstants.END_ELEMENT: { + depth-- ; + if ( depth == -1 ) { + // Close tag of typed Literal. + return content.toString(); + } + QName qname = parser.getName() ; + String x = qnameAsString(qname) ; + content.append("</"+x+">") ; + // Final whitespace? + break ; + } + case XMLStreamConstants.CHARACTERS: + case XMLStreamConstants.CDATA: + case XMLStreamConstants.SPACE: + case XMLStreamConstants.ENTITY_REFERENCE: + case XMLStreamConstants.PROCESSING_INSTRUCTION: + case XMLStreamConstants.COMMENT: +// String $ = parser.getText() ; +// System.out.println("----") ; +// System.out.println($) ; +// System.out.println("----") ; + content.append(parser.getText()) ; + break ; + case XMLStreamConstants.END_DOCUMENT: + staxError(parser.getLocation(), "End of file") ; + } + } + staxError(parser.getLocation(), "End of file") ; + return null ; + } + + private String tagName(XMLStreamReader parser) { + return qnameAsString(parser.getName()) ; + } + + private String qnameAsString(QName qname) { + String x = qname.getPrefix() ; + if ( x == null || x.isEmpty() ) + return qname.getLocalPart() ; + return x+":"+qname.getLocalPart() ; + } + + private String attribute(XMLStreamReader parser, String nsURI, String localname) { + int x = parser.getAttributeCount() ; + if ( x > 1 ) + // Namespaces? + staxError(parser.getLocation(), "Multiple attributes : only one allowed : "+tagName(parser)) ; + if ( x == 0 ) + return null ; + + String attrPX = parser.getAttributePrefix(0) ; + String attrNS = parser.getAttributeNamespace(0) ; + if ( attrNS == null ) + attrNS = parser.getName().getNamespaceURI() ; + String attrLN = parser.getAttributeLocalName(0) ; + if ( ! Objects.equals(nsURI, attrNS) || ! Objects.equals(attrLN, localname) ) { + staxError(parser.getLocation(), "Unexpected attribute : "+attrPX+":"+attrLN+" at "+tagName(parser)) ; + } + String attrVal = parser.getAttributeValue(0) ; + return attrVal ; + } + + private void staxError(String msg) { + staxError(-1, -1, msg) ; + } + + private void staxError(Location loc, String msg) { + staxError(loc.getLineNumber(), loc.getColumnNumber(), msg) ; + } + + private void staxError(int line, int col, String msg) { + getErrorHandler().error(msg, line, col) ; + } + + @Override + public ErrorHandler getErrorHandler() { + return errorHandler ; + } + + @Override + public void setErrorHandler(ErrorHandler errorHandler) { this.errorHandler = errorHandler ; } + + @Override + public ParserProfile getParserProfile() { + return parserProfile ; + } + + @Override + public void setParserProfile(ParserProfile profile) { this.parserProfile = profile ; } +} + http://git-wip-us.apache.org/repos/asf/jena/blob/d6aa36c8/jena-arq/src/main/java/org/apache/jena/riot/lang/TriX.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/riot/lang/TriX.java b/jena-arq/src/main/java/org/apache/jena/riot/lang/TriX.java new file mode 100644 index 0000000..b229ba4 --- /dev/null +++ b/jena-arq/src/main/java/org/apache/jena/riot/lang/TriX.java @@ -0,0 +1,59 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.jena.riot.lang; + +/** TriX - see <a href="http://www.hpl.hp.com/techreports/2004/HPL-2004-56.html">HPL-2004-56</a> Jeremy Carroll and Patrick Stickler. + * Supported: + * <li>Basic TriX as per the DTD in HPL-2004-56 + * <li>Typed literal rdf:XMLLiteral with inline XML. + * <li><qname> (on reading) + */ + +public class TriX { + // DTD for TrIX : The schema is a much longer. + /* + <!-- TriX: RDF Triples in XML --> + <!ELEMENT TriX (graph*)> + <!ATTLIST TriX xmlns CDATA #FIXED "http://www.w3.org/2004/03/trix/trix-1/"> + <!ELEMENT graph (uri*, triple*)> + <!ELEMENT triple ((id|uri|plainLiteral|typedLiteral), uri, (id|uri|plainLiteral|typedLiteral))> + <!ELEMENT id (#PCDATA)> + <!ELEMENT uri (#PCDATA)> + <!ELEMENT plainLiteral (#PCDATA)> + <!ATTLIST plainLiteral xml:lang CDATA #IMPLIED> + <!ELEMENT typedLiteral (#PCDATA)> + <!ATTLIST typedLiteral datatype CDATA #REQUIRED> + */ + + /* Constants for TriX */ + public final static String NS = "http://www.w3.org/2004/03/trix/trix-1/" ; + public final static String tagTriX = "TriX" ; + + public final static String tagGraph = "graph" ; + public final static String tagTriple = "triple" ; + public final static String tagURI = "uri" ; + public final static String tagId = "id" ; + public final static String tagQName = "qname" ; + public final static String tagPlainLiteral = "plainLiteral" ; + public final static String tagTypedLiteral = "typedLiteral" ; + + public final static String attrXmlLang = "lang" ; + public final static String attrDatatype = "datatype" ; +} + http://git-wip-us.apache.org/repos/asf/jena/blob/d6aa36c8/jena-arq/src/main/java/org/apache/jena/riot/system/StreamRDFWriter.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/riot/system/StreamRDFWriter.java b/jena-arq/src/main/java/org/apache/jena/riot/system/StreamRDFWriter.java index 4fd5f6d..a37a974 100644 --- a/jena-arq/src/main/java/org/apache/jena/riot/system/StreamRDFWriter.java +++ b/jena-arq/src/main/java/org/apache/jena/riot/system/StreamRDFWriter.java @@ -29,6 +29,7 @@ import org.apache.jena.atlas.io.IO ; import org.apache.jena.riot.* ; import org.apache.jena.riot.out.CharSpace ; import org.apache.jena.riot.thrift.BinRDF ; +import org.apache.jena.riot.writer.StreamWriterTriX ; import org.apache.jena.riot.writer.WriterStreamRDFBlocks ; import org.apache.jena.riot.writer.WriterStreamRDFFlat ; import org.apache.jena.riot.writer.WriterStreamRDFPlain ; @@ -85,6 +86,13 @@ public class StreamRDFWriter { } } ; + private static StreamRDFWriterFactory streamWriterFactoryTriX = new StreamRDFWriterFactory() { + @Override + public StreamRDF create(OutputStream output, RDFFormat format) { + return new StreamWriterTriX(output) ; + } + } ; + private static WriterRegistry<StreamRDFWriterFactory> registry = new WriterRegistry<>() ; /** Register the default serialization for the language (replace any existing registration). @@ -113,7 +121,8 @@ public class StreamRDFWriter { register(Lang.TRIG, RDFFormat.TRIG_BLOCKS) ; register(Lang.NTRIPLES, RDFFormat.NTRIPLES) ; register(Lang.NQUADS, RDFFormat.NQUADS) ; - register(Lang.RDFTHRIFT, RDFFormat.RDF_THRIFT) ; + register(Lang.RDFTHRIFT, RDFFormat.RDF_THRIFT) ; + register(Lang.TRIX, RDFFormat.TRIX) ; register(RDFFormat.TURTLE_BLOCKS, streamWriterFactoryBlocks) ; register(RDFFormat.TURTLE_FLAT, streamWriterFactoryFlat) ; @@ -130,6 +139,8 @@ public class StreamRDFWriter { register(RDFFormat.RDF_THRIFT, streamWriterFactoryThrift) ; register(RDFFormat.RDF_THRIFT_VALUES, streamWriterFactoryThrift) ; + + register(RDFFormat.TRIX, streamWriterFactoryTriX) ; } /** Get a StreamRDF destination that will output in syntax <tt>Lang</tt> http://git-wip-us.apache.org/repos/asf/jena/blob/d6aa36c8/jena-arq/src/main/java/org/apache/jena/riot/writer/StreamWriterTriX.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/riot/writer/StreamWriterTriX.java b/jena-arq/src/main/java/org/apache/jena/riot/writer/StreamWriterTriX.java new file mode 100644 index 0000000..5f01858 --- /dev/null +++ b/jena-arq/src/main/java/org/apache/jena/riot/writer/StreamWriterTriX.java @@ -0,0 +1,250 @@ +/* + * Copyright 2013, 2014 Andy Seaborne + * + * 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.apache.jena.riot.writer; + +import java.io.OutputStream ; +import java.util.Objects ; + +import org.apache.jena.atlas.io.IndentedWriter ; +import org.apache.jena.riot.RiotException ; +import org.apache.jena.riot.lang.ReaderTriX ; +import org.apache.jena.riot.lang.TriX ; +import org.apache.jena.riot.system.PrefixMap ; +import org.apache.jena.riot.system.PrefixMapFactory ; +import org.apache.jena.riot.system.StreamRDF ; + +import com.hp.hpl.jena.datatypes.xsd.impl.XMLLiteralType ; +import com.hp.hpl.jena.graph.Node ; +import com.hp.hpl.jena.graph.Triple ; +import com.hp.hpl.jena.rdf.model.impl.Util ; +import com.hp.hpl.jena.sparql.core.Quad ; + + +/** Write TriX by streaming. + * See {@link TriX} for details. + * + * @see TriX + * @see ReaderTriX + * @see WriterTriX + */ +public class StreamWriterTriX implements StreamRDF { + private static String rdfXMLLiteral = XMLLiteralType.theXMLLiteralType.getURI() ; + private IndentedWriter out ; + private Node gn = null ; + private boolean inGraph = false ; + private PrefixMap pmap = PrefixMapFactory.create() ; + + public StreamWriterTriX(OutputStream out) { this.out = new IndentedWriter(out) ; } + public StreamWriterTriX(IndentedWriter out) { this.out = out ; } + + // Batching. + @Override public void start() { + StreamWriterTriX.startXML(out) ; + StreamWriterTriX.startTag(out, TriX.tagTriX, "xmlns", TriX.NS) ; + out.println() ; + } + + @Override public void base(String base) {} // Ignore. + + @Override public void prefix(String prefix, String iri) { + pmap.add(prefix, iri) ; + } + + @Override public void finish() { + if ( inGraph ) { + StreamWriterTriX.endTag(out, TriX.tagGraph) ; + out.println() ; + } + StreamWriterTriX.endTag(out, TriX.tagTriX) ; + out.println() ; + out.flush() ; + } + + @Override + public void triple(Triple triple) { + if ( inGraph && gn != null ) { + StreamWriterTriX.endTag(out, TriX.tagGraph) ; + out.println() ; + inGraph = false ; + } + + if ( ! inGraph ) { + StreamWriterTriX.startTag(out, TriX.tagGraph) ; + out.println() ; + } + inGraph = true ; + gn = null ; + //end graph? + StreamWriterTriX.write(out, triple, pmap) ; + } + + @Override + public void quad(Quad quad) { + Node g = quad.getGraph() ; + + if ( inGraph ) { + if ( ! Objects.equals(g, gn) ) { + StreamWriterTriX.endTag(out, TriX.tagGraph) ; + out.println() ; + inGraph = false ; + } + } + if ( ! inGraph ) { + StreamWriterTriX.startTag(out, TriX.tagGraph) ; + out.println() ; + if ( gn == null || ! Quad.isDefaultGraph(gn) ) { + gn = quad.getGraph() ; + StreamWriterTriX.write(out, gn, pmap) ; + } + } + inGraph = true ; + gn = g ; + StreamWriterTriX.write(out, quad.asTriple(), pmap) ; + + } + + private void graph(Node gn2) { + if ( ! Objects.equals(gn, gn2) ) { + + } + } + + static void write(IndentedWriter out, Triple triple, PrefixMap prefixMap) { + out.println("<triple>") ; + out.incIndent(); + write(out, triple.getSubject(), prefixMap) ; + write(out, triple.getPredicate(), prefixMap) ; + write(out, triple.getObject(), prefixMap) ; + out.decIndent(); + out.println("</triple>") ; + } + + static void write(IndentedWriter out, Node node, PrefixMap prefixMap) { + // The decent use of TriX is very regular output as we do not use <qname>. + if ( node.isURI() ) { + String uri = node.getURI() ; + if ( prefixMap != null ) { + String abbrev = prefixMap.abbreviate(uri) ; + if ( abbrev != null ) { + startTag(out, TriX.tagQName) ; + writeText(out, abbrev) ; + endTag(out, TriX.tagQName) ; + return ; + } + } + + startTag(out, TriX.tagURI) ; + writeText(out, node.getURI()) ; + endTag(out, TriX.tagURI) ; + out.println() ; + return ; + } + + if ( node.isBlank() ) { + startTag(out, TriX.tagId) ; + writeText(out, node.getBlankNodeLabel()) ; + endTag(out, TriX.tagId) ; + out.println() ; + return ; + } + + if ( node.isLiteral() ) { + // RDF 1.1 + String lang = node.getLiteralLanguage() ; + if ( lang != null && lang.isEmpty() ) + lang = null ; + + String dt = node.getLiteralDatatypeURI() ; + if ( lang != null ) { + startTag(out, TriX.tagPlainLiteral, "xml:lang", lang) ; + writeTextNoIndent(out, node.getLiteralLexicalForm()) ; + endTag(out, TriX.tagPlainLiteral) ; + out.println() ; + return ; + } + + if ( dt == null ) { + startTag(out, TriX.tagPlainLiteral) ; + writeTextNoIndent(out, node.getLiteralLexicalForm()) ; + endTag(out, TriX.tagPlainLiteral) ; + out.println() ; + return ; + } + + //if ( lang == null && dt != null ) + + startTag(out, TriX.tagTypedLiteral, TriX.attrDatatype, dt) ; + String lex = node.getLiteralLexicalForm() ; + if ( rdfXMLLiteral.equals(dt) ) { + int x = out.getAbsoluteIndent() ; + out.setAbsoluteIndent(0) ; + out.print(lex) ; // Write raw + out.setAbsoluteIndent(x) ; + } + else + writeTextNoIndent(out, lex) ; + endTag(out, TriX.tagTypedLiteral) ; + out.println() ; + return ; + //throw new RiotException("internal error") ; + } + + throw new RiotException("Not a concrete node: "+node) ; + } + + static void writeText(IndentedWriter out, String string) { + //throw new NotImplementedException() ; + string = Util.substituteEntitiesInElementContent(string) ; + out.print(string) ; + } + + static void writeTextNoIndent(IndentedWriter out, String string) { + int x = out.getAbsoluteIndent() ; + out.setAbsoluteIndent(0) ; + writeText(out, string) ; + out.setAbsoluteIndent(x) ; + } + + static void startXML(IndentedWriter out) { + //out.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>") ; + } + static void startTag(IndentedWriter out, String text) { + out.print("<") ; + out.print(text) ; + out.print(">") ; + out.incIndent(); + } + static void startTag(IndentedWriter out, String text, String attr, String attrValue) { + out.print("<") ; + out.print(text) ; + out.print(" ") ; + out.print(attr) ; + out.print("=\"") ; + attrValue = Util.substituteStandardEntities(attrValue) ; + out.print(attrValue) ; + out.print("\"") ; + out.print(">") ; + out.incIndent(); + } + static void endTag(IndentedWriter out, String text) { + out.decIndent(); + out.print("</") ; + out.print(text) ; + out.print(">") ; + } +} + http://git-wip-us.apache.org/repos/asf/jena/blob/d6aa36c8/jena-arq/src/main/java/org/apache/jena/riot/writer/WriterTriX.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/riot/writer/WriterTriX.java b/jena-arq/src/main/java/org/apache/jena/riot/writer/WriterTriX.java new file mode 100644 index 0000000..e0ceb03 --- /dev/null +++ b/jena-arq/src/main/java/org/apache/jena/riot/writer/WriterTriX.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.jena.riot.writer; + +import java.io.OutputStream ; +import java.io.Writer ; + +import org.apache.jena.atlas.io.IndentedWriter ; +import org.apache.jena.riot.Lang ; +import org.apache.jena.riot.WriterDatasetRIOT ; +import org.apache.jena.riot.WriterGraphRIOT ; +import org.apache.jena.riot.lang.ReaderTriX ; +import org.apache.jena.riot.lang.TriX ; +import org.apache.jena.riot.system.PrefixMap ; +import org.apache.jena.riot.system.RiotLib ; +import org.apache.jena.riot.system.StreamOps ; + +import com.hp.hpl.jena.datatypes.xsd.impl.XMLLiteralType ; +import com.hp.hpl.jena.graph.Graph ; +import com.hp.hpl.jena.sparql.core.DatasetGraph ; +import com.hp.hpl.jena.sparql.util.Context ; + +/** Write TriX. + * See {@link TriX} for details. + * The writer defers to {@linkplain StreamWriterTriX}. + * @see TriX + * @see ReaderTriX + * @see StreamWriterTriX + */ +public class WriterTriX implements WriterDatasetRIOT, WriterGraphRIOT { + private static String rdfXMLLiteral = XMLLiteralType.theXMLLiteralType.getURI() ; + + // Common pattern. + @Override + public Lang getLang() { + return Lang.TRIX ; + } + + // Dataset + @Override + public void write(OutputStream out, DatasetGraph datasetGraph, PrefixMap prefixMap, String baseURI, Context context) { + IndentedWriter iOut = new IndentedWriter(out) ; + write(iOut, datasetGraph, prefixMap, baseURI, null) ; + } + + @Override + public void write(Writer out, DatasetGraph datasetGraph, PrefixMap prefixMap, String baseURI, Context context) { + IndentedWriter iOut = RiotLib.create(out) ; + write(iOut, datasetGraph, prefixMap, baseURI, null) ; + } + + private void write(IndentedWriter out, DatasetGraph datasetGraph, PrefixMap prefixMap, String baseURI, Context context) { + StreamWriterTriX w = new StreamWriterTriX(out) ; + StreamOps.datasetToStream(datasetGraph, w) ; + } + + // Graph + @Override + public void write(OutputStream out, Graph graph, PrefixMap prefixMap, String baseURI, Context context) { + IndentedWriter iOut = new IndentedWriter(out) ; + write(iOut, graph, prefixMap, baseURI, null) ; + } + + @Override + public void write(Writer out, Graph graph, PrefixMap prefixMap, String baseURI, Context context) { + IndentedWriter iOut = RiotLib.create(out) ; + write(iOut, graph, prefixMap, baseURI, null) ; + } + + private static void write(IndentedWriter out, Graph graph, PrefixMap prefixMap, String baseURI, Object context) { + StreamWriterTriX w = new StreamWriterTriX(out) ; + StreamOps.graphToStream(graph, w) ; + } +} + http://git-wip-us.apache.org/repos/asf/jena/blob/d6aa36c8/jena-arq/src/test/java/org/apache/jena/riot/lang/TS_Lang.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/test/java/org/apache/jena/riot/lang/TS_Lang.java b/jena-arq/src/test/java/org/apache/jena/riot/lang/TS_Lang.java index fa2869d..517ea95 100644 --- a/jena-arq/src/test/java/org/apache/jena/riot/lang/TS_Lang.java +++ b/jena-arq/src/test/java/org/apache/jena/riot/lang/TS_Lang.java @@ -38,9 +38,13 @@ import org.junit.runners.Suite ; , TestLangTurtle.class , TestLangTrig.class , TestLangRdfJson.class + , TestTriXReader.class + , TestTriXBad.class + // Thrift is done in the "thift" package , TestParserFactory.class , TestPipedRDFIterators.class , TestCollectorStream.class + }) http://git-wip-us.apache.org/repos/asf/jena/blob/d6aa36c8/jena-arq/src/test/java/org/apache/jena/riot/lang/TestTriXBad.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/test/java/org/apache/jena/riot/lang/TestTriXBad.java b/jena-arq/src/test/java/org/apache/jena/riot/lang/TestTriXBad.java new file mode 100644 index 0000000..849e74b --- /dev/null +++ b/jena-arq/src/test/java/org/apache/jena/riot/lang/TestTriXBad.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.jena.riot.lang; + +import java.io.InputStream ; +import java.util.Arrays ; + +import org.apache.jena.atlas.io.IO ; +import org.apache.jena.riot.Lang ; +import org.apache.jena.riot.RDFDataMgr ; +import org.apache.jena.riot.RiotException ; +import org.apache.jena.riot.system.ErrorHandler ; +import org.apache.jena.riot.system.ErrorHandlerFactory ; +import org.apache.jena.riot.system.StreamRDF ; +import org.apache.jena.riot.system.StreamRDFLib ; +import org.junit.Assert ; +import org.junit.Test ; +import org.junit.runner.RunWith ; +import org.junit.runners.Parameterized ; +import org.junit.runners.Parameterized.Parameter ; +import org.junit.runners.Parameterized.Parameters ; + +@RunWith(Parameterized.class) + +public class TestTriXBad extends Assert /*BaseTest*/ { + + static String DIR = "testing/RIOT/Lang/TriX" ; + + @Parameters(name="{0}") + public static Iterable<Object[]> data() { + return Arrays.asList(new Object[][] { + { DIR+"/trix-bad-01.trix" } , + { DIR+"/trix-bad-02.trix" } , + { DIR+"/trix-bad-03.trix" } , + { DIR+"/trix-bad-04.trix" } , + { DIR+"/trix-bad-05.trix" } , + { DIR+"/trix-bad-06.trix" } , + { DIR+"/trix-bad-07.trix" } , + { DIR+"/trix-bad-08.trix" } , + { DIR+"/trix-bad-09.trix" } , + }); + } + + @Parameter(0) + public String fInput; + + @Test(expected=RiotException.class) + public void trix_bad() { + ErrorHandler err = ErrorHandlerFactory.getDefaultErrorHandler() ; + try { + ErrorHandlerFactory.setDefaultErrorHandler(ErrorHandlerFactory.errorHandlerSimple()) ; + InputStream in = IO.openFile(fInput) ; + StreamRDF sink = StreamRDFLib.sinkNull() ; + RDFDataMgr.parse(sink, in, Lang.TRIX) ; + } finally { + ErrorHandlerFactory.setDefaultErrorHandler(err) ; + } + } +} + http://git-wip-us.apache.org/repos/asf/jena/blob/d6aa36c8/jena-arq/src/test/java/org/apache/jena/riot/lang/TestTriXReader.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/test/java/org/apache/jena/riot/lang/TestTriXReader.java b/jena-arq/src/test/java/org/apache/jena/riot/lang/TestTriXReader.java new file mode 100644 index 0000000..4d335eb --- /dev/null +++ b/jena-arq/src/test/java/org/apache/jena/riot/lang/TestTriXReader.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.jena.riot.lang; + +import java.io.InputStream ; +import java.util.Arrays ; + +import org.apache.jena.atlas.io.IO ; +import org.apache.jena.atlas.junit.BaseTest ; +import org.apache.jena.riot.RDFDataMgr ; +import org.apache.jena.riot.ReaderRIOT ; +import org.apache.jena.riot.system.ErrorHandler ; +import org.apache.jena.riot.system.ErrorHandlerFactory ; +import org.apache.jena.riot.system.StreamRDF ; +import org.apache.jena.riot.system.StreamRDFLib ; +import org.junit.Assert ; +import org.junit.Test ; +import org.junit.runner.RunWith ; +import org.junit.runners.Parameterized ; +import org.junit.runners.Parameterized.Parameter ; +import org.junit.runners.Parameterized.Parameters ; + +import com.hp.hpl.jena.rdf.model.Model ; +import com.hp.hpl.jena.sparql.core.DatasetGraph ; +import com.hp.hpl.jena.sparql.core.DatasetGraphFactory ; +import com.hp.hpl.jena.sparql.util.IsoMatcher ; + +@RunWith(Parameterized.class) +public class TestTriXReader extends BaseTest { + + static String DIR = "testing/RIOT/Lang/TriX" ; + + @Parameters(name="{0}") + public static Iterable<Object[]> data() { + return Arrays.asList(new Object[][] { + { DIR+"/trix-01.trix", DIR+"/trix-01.nq" } , + { DIR+"/trix-02.trix", DIR+"/trix-02.nq" } , + { DIR+"/trix-03.trix", DIR+"/trix-03.nq" } , + { DIR+"/trix-04.trix", DIR+"/trix-04.nq" } , + { DIR+"/trix-05.trix", DIR+"/trix-05.nq" } , + { DIR+"/trix-06.trix", DIR+"/trix-06.nq" } , + { DIR+"/trix-10.trix", DIR+"/trix-10.nq" } , + { DIR+"/trix-11.trix", DIR+"/trix-11.nq" } , + { DIR+"/trix-12.trix", DIR+"/trix-12.nq" } , + { DIR+"/trix-13.trix", DIR+"/trix-13.nq" } , + { DIR+"/trix-14.trix", DIR+"/trix-14.nq" } , + { DIR+"/trix-15.trix", DIR+"/trix-15.nq" } , + // The example from HPL-2004-56 + { DIR+"/trix-ex-1.trix", null }, +// //{ "trix-ex-2.trix", null }, // Contains <integer> + { DIR+"/trix-ex-3.trix", null }, + { DIR+"/trix-ex-4.trix", null }, + { DIR+"/trix-ex-5.trix", null } + }); + } + + @Parameter(0) + public String fInput; + + @Parameter(1) + public String fExpected; + + @Test + public void trix_direct() { + ReaderRIOT r = new ReaderTriX() ; + InputStream in = IO.openFile(fInput) ; + DatasetGraph dsg = DatasetGraphFactory.createMem() ; + //StreamRDF stream = StreamRDFLib.writer(System.out) ; + StreamRDF stream = StreamRDFLib.dataset(dsg) ; + stream.start(); + r.read(in, null, null, stream, null) ; + stream.finish(); + if ( fExpected != null ) { + DatasetGraph dsg2 = RDFDataMgr.loadDatasetGraph(fExpected) ; + boolean b = IsoMatcher.isomorphic(dsg, dsg2) ; + if ( ! b ) { + Assert.fail("Not isomorphic") ; + } + } + } + + @Test + public void trix_model() { + // Ignore warnings of skipping quads reading into a model + Model m1 = null ; + Model m2 = null ; + ErrorHandler err = ErrorHandlerFactory.getDefaultErrorHandler() ; + try { + ErrorHandlerFactory.setDefaultErrorHandler(ErrorHandlerFactory.errorHandlerNoWarnings) ; + m1 = RDFDataMgr.loadModel(fInput) ; + if ( fExpected != null ) + m2 = RDFDataMgr.loadModel(fExpected) ; + } finally { + ErrorHandlerFactory.setDefaultErrorHandler(err) ; + } + if ( m2 != null ) + assertTrue("Models not isomorphic", m1.isIsomorphicWith(m2)) ; + } + + @Test + public void trix_dataset() { + DatasetGraph ds1 = RDFDataMgr.loadDatasetGraph(fInput) ; + DatasetGraph ds2 = null ; + if ( fExpected != null ) + ds2 = RDFDataMgr.loadDatasetGraph(fExpected) ; + if ( ds2 != null ) + assertTrue("Datasets not isomorphic", IsoMatcher.isomorphic(ds1, ds2)) ; + } +} + http://git-wip-us.apache.org/repos/asf/jena/blob/d6aa36c8/jena-arq/src/test/java/org/apache/jena/riot/writer/TS_RiotWriter.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/test/java/org/apache/jena/riot/writer/TS_RiotWriter.java b/jena-arq/src/test/java/org/apache/jena/riot/writer/TS_RiotWriter.java index 65033e6..91c275f 100644 --- a/jena-arq/src/test/java/org/apache/jena/riot/writer/TS_RiotWriter.java +++ b/jena-arq/src/test/java/org/apache/jena/riot/writer/TS_RiotWriter.java @@ -32,6 +32,7 @@ import org.junit.runners.Suite.SuiteClasses ; , TestRiotWriterDataset.class , TestJenaWriters.class , TestRDFJSON.class + , TestTriXWriter.class }) public class TS_RiotWriter http://git-wip-us.apache.org/repos/asf/jena/blob/d6aa36c8/jena-arq/src/test/java/org/apache/jena/riot/writer/TestTriXWriter.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/test/java/org/apache/jena/riot/writer/TestTriXWriter.java b/jena-arq/src/test/java/org/apache/jena/riot/writer/TestTriXWriter.java new file mode 100644 index 0000000..57459c1 --- /dev/null +++ b/jena-arq/src/test/java/org/apache/jena/riot/writer/TestTriXWriter.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.jena.riot.writer; + +import java.io.ByteArrayInputStream ; +import java.io.ByteArrayOutputStream ; +import java.util.Arrays ; + +import org.apache.jena.atlas.junit.BaseTest ; +import org.apache.jena.riot.Lang ; +import org.apache.jena.riot.RDFDataMgr ; +import org.junit.Test ; +import org.junit.runner.RunWith ; +import org.junit.runners.Parameterized ; +import org.junit.runners.Parameterized.Parameter ; +import org.junit.runners.Parameterized.Parameters ; + +import com.hp.hpl.jena.sparql.core.DatasetGraph ; +import com.hp.hpl.jena.sparql.core.DatasetGraphFactory ; +import com.hp.hpl.jena.sparql.util.IsoMatcher ; + +@RunWith(Parameterized.class) +public class TestTriXWriter extends BaseTest { + + static String DIR = "testing/RIOT/Lang/TriX" ; + + @Parameters(name="{0}") + public static Iterable<Object[]> data() { + return Arrays.asList(new Object[][] { + { DIR+"/trix-01.trix", DIR+"/trix-01.nq" } , + { DIR+"/trix-02.trix", DIR+"/trix-02.nq" } , + { DIR+"/trix-03.trix", DIR+"/trix-03.nq" } , + { DIR+"/trix-04.trix", DIR+"/trix-04.nq" } , + { DIR+"/trix-05.trix", DIR+"/trix-05.nq" } , + { DIR+"/trix-06.trix", DIR+"/trix-06.nq" } , + { DIR+"/trix-10.trix", DIR+"/trix-10.nq" } , + { DIR+"/trix-11.trix", DIR+"/trix-11.nq" } , + { DIR+"/trix-12.trix", DIR+"/trix-12.nq" } , + { DIR+"/trix-13.trix", DIR+"/trix-13.nq" } , + { DIR+"/trix-14.trix", DIR+"/trix-14.nq" } , + { DIR+"/trix-15.trix", DIR+"/trix-15.nq" } + }); + } + @Parameter(0) + public String fTrix; + + @Parameter(1) + public String fNQuads ; + + @Test + public void trix_writer() { + DatasetGraph dsg = RDFDataMgr.loadDatasetGraph(fNQuads) ; + ByteArrayOutputStream bout = new ByteArrayOutputStream() ; + RDFDataMgr.write(bout, dsg, Lang.TRIX) ; + ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray()) ; + DatasetGraph dsg2 = DatasetGraphFactory.createMem() ; + RDFDataMgr.read(dsg2, bin, Lang.TRIX) ; + boolean b = IsoMatcher.isomorphic(dsg, dsg2) ; + assertTrue("Not isomorphic", b) ; + } +} +
