<commit_before>/*
 * 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.IOException ;
import java.io.InputStream ;
import java.io.Reader ;

import org.apache.jena.atlas.lib.Pair ;
import org.apache.jena.atlas.web.ContentType;
import org.apache.jena.datatypes.RDFDatatype ;
import org.apache.jena.datatypes.TypeMapper ;
import org.apache.jena.graph.Node ;
import org.apache.jena.graph.NodeFactory ;
import org.apache.jena.graph.Triple ;
import org.apache.jena.rdf.model.RDFErrorHandler ;
import org.apache.jena.rdfxml.xmlinput.* ;
import org.apache.jena.rdfxml.xmlinput.impl.ARPSaxErrorHandler ;
import org.apache.jena.riot.Lang;
import org.apache.jena.riot.ReaderRIOT;
import org.apache.jena.riot.ReaderRIOTFactory;
import org.apache.jena.riot.SysRIOT;
import org.apache.jena.riot.checker.CheckerLiterals ;
import org.apache.jena.riot.system.ErrorHandler ;
import org.apache.jena.riot.system.ParserProfile ;
import org.apache.jena.riot.system.StreamRDF ;
import org.apache.jena.sparql.util.Context;
import org.xml.sax.SAXException ;
import org.xml.sax.SAXParseException ;

/** RDF/XML.
 *
 * @see <a href="http://www.w3.org/TR/rdf-syntax-grammar/">http://www.w3.org/TR/rdf-syntax-grammar/</a>
 */
public class ReaderRIOTRDFXML  implements ReaderRIOT
{
    public static class Factory implements ReaderRIOTFactory {
        @Override
        public ReaderRIOT create(Lang language, ParserProfile parserProfile) {
            return new ReaderRIOTRDFXML(parserProfile.getErrorHandler()) ;
        }
    }
    
    private ARP arp = new ARP() ;
    
    private InputStream input = null ;
    private Reader reader = null ;
    private String xmlBase ;
    private String filename ;
    private StreamRDF sink ;
    private ErrorHandler errorHandler; 
    
    public ReaderRIOTRDFXML(ErrorHandler errorHandler) {
        this.errorHandler = errorHandler; 
    }
    
    @Override
    public void read(InputStream in, String baseURI, ContentType ct, StreamRDF output, Context context) {
        this.input = in ;
        this.xmlBase = baseURI_RDFXML(baseURI) ;
        this.filename = baseURI ;
        this.sink = output ;
        parse();
    }

    @Override
    public void read(Reader reader, String baseURI, ContentType ct, StreamRDF output, Context context) {
        this.reader = reader ;
        this.xmlBase = baseURI_RDFXML(baseURI) ;
        this.filename = baseURI ;
        this.sink = output ;
        parse();
    }

    public static boolean RiotUniformCompatibility = false ;
    // Warnings in ARP that should be errors to be compatible with
    // non-XML-based languages.  e.g. language tags should be
    // syntactically valid.
    private static int[] additionalErrors = new int[] {
        ARPErrorNumbers.WARN_MALFORMED_XMLLANG
        //, ARPErrorNumbers.WARN_STRING_NOT_NORMAL_FORM_C
    } ;
    
    public void parse() {
        // Hacked out of ARP because of all the "private" methods
        sink.start() ;
        HandlerSink rslt = new HandlerSink(sink, errorHandler) ;
        arp.getHandlers().setStatementHandler(rslt) ;
        arp.getHandlers().setErrorHandler(rslt) ;
        arp.getHandlers().setNamespaceHandler(rslt) ;

        if ( RiotUniformCompatibility ) {
            ARPOptions options = arp.getOptions() ;
            // Convert some warnings to errors for compatible behaviour for all parsers.
            for ( int code : additionalErrors )
                options.setErrorMode(code, ARPErrorNumbers.EM_FATAL) ;
            arp.setOptionsWith(options) ;
        }

        try {
            if ( reader != null )
                arp.load(reader, xmlBase) ;
            else
                arp.load(input, xmlBase) ;
        }
        catch (IOException e) {
            errorHandler.error(filename + ": " + ParseException.formatMessage(e), -1, -1) ;
        }
        catch (SAXParseException e) {
            // already reported.
        }
        catch (SAXException sax) {
            errorHandler.error(filename + ": " + ParseException.formatMessage(sax), -1, -1) ;
        }
        sink.finish() ;
    }
    
    /** Sort out the base URI for RDF/XML parsing. */
    private static String baseURI_RDFXML(String baseIRI) {
        if ( baseIRI == null )
            return SysRIOT.chooseBaseIRI() ;
        else
            // This normalizes the URI.
            return SysRIOT.chooseBaseIRI(baseIRI) ;
    }
    
    private static class HandlerSink extends ARPSaxErrorHandler implements StatementHandler, NamespaceHandler {
        private StreamRDF       output ;
        private ErrorHandler    errHandler ;
        private CheckerLiterals checker ;

        HandlerSink(StreamRDF output, ErrorHandler errHandler) {
            super(new ErrorHandlerBridge(errHandler)) ;
            this.output = output ;
            this.errHandler = errHandler ;
            this.checker = new CheckerLiterals(errHandler) ;
        }
        
        @Override
        public void statement(AResource subj, AResource pred, AResource obj)
        { output.triple(convert(subj, pred, obj)); }

        @Override
        public void statement(AResource subj, AResource pred, ALiteral lit)
        { output.triple(convert(subj, pred, lit)) ; }

        // From JenaReader
        private static Node convert(ALiteral lit) {
            String dtURI = lit.getDatatypeURI();
            if (dtURI == null)
                return NodeFactory.createLiteral(lit.toString(), lit.getLang());

            if (lit.isWellFormedXML()) {
                return NodeFactory.createLiteral(lit.toString(), null, true);
            }

            RDFDatatype dt = TypeMapper.getInstance().getSafeTypeByName(dtURI);
            return NodeFactory.createLiteral(lit.toString(), dt);

        }

        private static Node convert(AResource r) {
            if (!r.isAnonymous())
                return NodeFactory.createURI(r.getURI());

            // String id = r.getAnonymousID();
            Node rr = (Node) r.getUserData();
            if (rr == null) {
                rr = NodeFactory.createBlankNode();
                r.setUserData(rr);
            }
            return rr;

        }

        private Triple convert(AResource s, AResource p, AResource o) {
            return Triple.create(convert(s), convert(p), convert(o)) ;
        }

        private Triple convert(AResource s, AResource p, ALiteral o) {
            Node object = convert(o) ;
            checker.check(object, -1, -1) ;
            return Triple.create(convert(s), convert(p), object) ;
        }
<commit_msg>JENA-1324: Check for spaces in URIs (if RDF 1.1)<commit_after>/*
 * 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.IOException ;
import java.io.InputStream ;
import java.io.Reader ;

import org.apache.jena.JenaRuntime;
import org.apache.jena.atlas.lib.Pair ;
import org.apache.jena.atlas.web.ContentType;
import org.apache.jena.datatypes.RDFDatatype ;
import org.apache.jena.datatypes.TypeMapper ;
import org.apache.jena.graph.Node ;
import org.apache.jena.graph.NodeFactory ;
import org.apache.jena.graph.Triple ;
import org.apache.jena.iri.IRIFactory;
import org.apache.jena.rdf.model.RDFErrorHandler ;
import org.apache.jena.rdfxml.xmlinput.* ;
import org.apache.jena.rdfxml.xmlinput.impl.ARPSaxErrorHandler ;
import org.apache.jena.riot.Lang;
import org.apache.jena.riot.ReaderRIOT;
import org.apache.jena.riot.ReaderRIOTFactory;
import org.apache.jena.riot.SysRIOT;
import org.apache.jena.riot.checker.CheckerLiterals ;
import org.apache.jena.riot.system.*;
import org.apache.jena.sparql.util.Context;
import org.xml.sax.SAXException ;
import org.xml.sax.SAXParseException ;

/** RDF/XML.
 *
 * @see <a href="http://www.w3.org/TR/rdf-syntax-grammar/">http://www.w3.org/TR/rdf-syntax-grammar/</a>
 */
public class ReaderRIOTRDFXML  implements ReaderRIOT
{
    public static class Factory implements ReaderRIOTFactory {
        @Override
        public ReaderRIOT create(Lang language, ParserProfile parserProfile) {
            // Ignore the provided ParserProfile
            // ARP predates RIOT and does many things internall already.
            // Thisincludes IRI resolution.
            return new ReaderRIOTRDFXML(parserProfile.getErrorHandler()) ;
        }
    }
    
    // RDF 1.1 is based on URIs/IRIs, where space are not allowed.
    // RDF 1.0 (and RDF/XML) was based on "RDF URI References" which did allow spaces.
    static { 
        if ( JenaRuntime.isRDF11 ) {
            ARPOptions.setIRIFactoryGlobal(IRIFactory.iriImplementation());
        }
    }
    
    private ARP arp = new ARP() ;
    // arp.getOptions.setIRIFactory does not work if called this early : it gets overwritten in JenaReader.
    // Using ARPOptions.setIRIFactoryGlobal does work. 
    
    private InputStream input = null ;
    private Reader reader = null ;
    private String xmlBase ;
    private String filename ;
    private StreamRDF sink ;
    private ErrorHandler errorHandler; 
    
    public ReaderRIOTRDFXML(ErrorHandler errorHandler) {
        this.errorHandler = errorHandler; 
    }
    
    @Override
    public void read(InputStream in, String baseURI, ContentType ct, StreamRDF output, Context context) {
        this.input = in ;
        this.xmlBase = baseURI_RDFXML(baseURI) ;
        this.filename = baseURI ;
        this.sink = output ;
        parse();
    }

    @Override
    public void read(Reader reader, String baseURI, ContentType ct, StreamRDF output, Context context) {
        this.reader = reader ;
        this.xmlBase = baseURI_RDFXML(baseURI) ;
        this.filename = baseURI ;
        this.sink = output ;
        parse();
    }

    public static boolean RiotUniformCompatibility = false ;
    // Warnings in ARP that should be errors to be compatible with
    // non-XML-based languages.  e.g. language tags should be
    // syntactically valid.
    private static int[] additionalErrors = new int[] {
        ARPErrorNumbers.WARN_MALFORMED_XMLLANG
        //, ARPErrorNumbers.WARN_STRING_NOT_NORMAL_FORM_C
    } ;
    
    public void parse() {
        // Hacked out of ARP because of all the "private" methods
        sink.start() ;
        HandlerSink rslt = new HandlerSink(sink, errorHandler) ;
        arp.getHandlers().setStatementHandler(rslt) ;
        arp.getHandlers().setErrorHandler(rslt) ;
        arp.getHandlers().setNamespaceHandler(rslt) ;

        if ( RiotUniformCompatibility ) {
            ARPOptions options = arp.getOptions() ;
            // Convert some warnings to errors for compatible behaviour for all parsers.
            for ( int code : additionalErrors )
                options.setErrorMode(code, ARPErrorNumbers.EM_FATAL) ;
            arp.setOptionsWith(options) ;
        }

        try {
            if ( reader != null )
                arp.load(reader, xmlBase) ;
            else
                arp.load(input, xmlBase) ;
        }
        catch (IOException e) {
            errorHandler.error(filename + ": " + ParseException.formatMessage(e), -1, -1) ;
        }
        catch (SAXParseException e) {
            // already reported.
        }
        catch (SAXException sax) {
            errorHandler.error(filename + ": " + ParseException.formatMessage(sax), -1, -1) ;
        }
        sink.finish() ;
    }
    
    /** Sort out the base URI for RDF/XML parsing. */
    private static String baseURI_RDFXML(String baseIRI) {
        if ( baseIRI == null )
            return SysRIOT.chooseBaseIRI() ;
        else
            // This normalizes the URI.
            return SysRIOT.chooseBaseIRI(baseIRI) ;
    }
    
    private static class HandlerSink extends ARPSaxErrorHandler implements StatementHandler, NamespaceHandler {
        private StreamRDF       output ;
        private ErrorHandler    riotErrorHandler ;
        private CheckerLiterals checker ;

        HandlerSink(StreamRDF output, ErrorHandler errHandler) {
            super(new ErrorHandlerBridge(errHandler)) ;
            this.output = output ;
            this.riotErrorHandler = errHandler ;
            this.checker = new CheckerLiterals(errHandler) ;
        }
        
        @Override
        public void statement(AResource subj, AResource pred, AResource obj)
        { output.triple(convert(subj, pred, obj)); }

        @Override
        public void statement(AResource subj, AResource pred, ALiteral lit)
        { output.triple(convert(subj, pred, lit)) ; }

        // From JenaReader
        private static Node convert(ALiteral lit) {
            String dtURI = lit.getDatatypeURI();
            if (dtURI == null)
                return NodeFactory.createLiteral(lit.toString(), lit.getLang());

            if (lit.isWellFormedXML()) {
                return NodeFactory.createLiteral(lit.toString(), null, true);
            }

            RDFDatatype dt = TypeMapper.getInstance().getSafeTypeByName(dtURI);
            return NodeFactory.createLiteral(lit.toString(), dt);
        }

        private Node convert(AResource r) {
            if (!r.isAnonymous())
                return NodeFactory.createURI(r.getURI());

            // String id = r.getAnonymousID();
            Node rr = (Node) r.getUserData();
            if (rr == null) {
                rr = NodeFactory.createBlankNode();
                r.setUserData(rr);
            }
            return rr;
        }

        private Triple convert(AResource s, AResource p, AResource o) {
            return Triple.create(convert(s), convert(p), convert(o)) ;
        }

        private Triple convert(AResource s, AResource p, ALiteral o) {
            Node object = convert(o) ;
            checker.check(object, -1, -1) ;
            return Triple.create(convert(s), convert(p), object) ;
        }

{'fraction_non_alphanumeric': 0.09392373601936033, 'fraction_numerical': 0.002354634050624632, 'mean_word_length': 2.413708417057379, 'pattern_counts': {'":': 4, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 6, 'xml': 22}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5528632', 'n_tokens_mistral': 4476, 'n_tokens_neox': 4331, 'n_words': 1131}