001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.commons.rdf.jsonldjava.experimental; 019 020import java.io.IOException; 021import java.io.InputStream; 022import java.net.MalformedURLException; 023import java.net.URI; 024import java.net.URISyntaxException; 025import java.net.URL; 026import java.nio.file.Files; 027import java.util.function.Predicate; 028 029import org.apache.commons.rdf.api.Dataset; 030import org.apache.commons.rdf.api.Graph; 031import org.apache.commons.rdf.api.IRI; 032import org.apache.commons.rdf.api.RDFSyntax; 033import org.apache.commons.rdf.jsonldjava.JsonLdDataset; 034import org.apache.commons.rdf.jsonldjava.JsonLdGraph; 035import org.apache.commons.rdf.jsonldjava.JsonLdRDF; 036import org.apache.commons.rdf.simple.experimental.AbstractRDFParser; 037 038import com.github.jsonldjava.core.JsonLdError; 039import com.github.jsonldjava.core.JsonLdOptions; 040import com.github.jsonldjava.core.JsonLdProcessor; 041import com.github.jsonldjava.core.RDFDataset; 042import com.github.jsonldjava.utils.JsonUtils; 043 044public class JsonLdParser extends AbstractRDFParser<JsonLdParser> { 045 046 @Override 047 protected JsonLdRDF createRDFTermFactory() { 048 return new JsonLdRDF(); 049 } 050 051 @Override 052 public JsonLdParser contentType(final RDFSyntax rdfSyntax) throws IllegalArgumentException { 053 if (rdfSyntax != null && rdfSyntax != RDFSyntax.JSONLD) { 054 throw new IllegalArgumentException("Unsupported contentType: " + rdfSyntax); 055 } 056 return super.contentType(rdfSyntax); 057 } 058 059 @Override 060 public JsonLdParser contentType(final String contentType) throws IllegalArgumentException { 061 final JsonLdParser c = super.contentType(contentType); 062 if (c.getContentType().filter(Predicate.isEqual(RDFSyntax.JSONLD).negate()).isPresent()) { 063 throw new IllegalArgumentException("Unsupported contentType: " + contentType); 064 } 065 return c; 066 } 067 068 private static URL asURL(final IRI iri) throws IllegalStateException { 069 try { 070 return new URI(iri.getIRIString()).toURL(); 071 } catch (MalformedURLException | URISyntaxException e) { 072 throw new IllegalStateException("Invalid URL: " + iri.getIRIString()); 073 } 074 } 075 076 @Override 077 protected void checkSource() throws IOException { 078 super.checkSource(); 079 // Might throw IllegalStateException if invalid 080 getSourceIri().map(JsonLdParser::asURL); 081 } 082 083 @Override 084 protected void parseSynchronusly() throws IOException { 085 final Object json = readSource(); 086 final JsonLdOptions options = new JsonLdOptions(); 087 getBase().map(IRI::getIRIString).ifPresent(options::setBase); 088 // TODO: base from readSource() (after redirection and Content-Location 089 // header) 090 // should be forwarded 091 092 // TODO: Modify JsonLdProcessor to accept the target RDFDataset 093 RDFDataset rdfDataset; 094 try { 095 rdfDataset = (RDFDataset) JsonLdProcessor.toRDF(json, options); 096 } catch (final JsonLdError e) { 097 throw new IOException("Could not parse Json-LD", e); 098 } 099 if (getTargetGraph().isPresent()) { 100 final Graph intoGraph = getTargetGraph().get(); 101 if (intoGraph instanceof JsonLdGraph && !intoGraph.contains(null, null, null)) { 102 // Empty graph, we can just move over the map content directly: 103 final JsonLdGraph jsonLdGraph = (JsonLdGraph) intoGraph; 104 jsonLdGraph.getRdfDataSet().putAll(rdfDataset); 105 return; 106 // otherwise we have to merge as normal 107 } 108 // TODO: Modify JsonLdProcessor to have an actual triple callback 109 final Graph parsedGraph = getJsonLdFactory().asGraph(rdfDataset); 110 // sequential() as we don't know if destination is thread safe :-/ 111 parsedGraph.stream().sequential().forEach(intoGraph::add); 112 } else if (getTargetDataset().isPresent()) { 113 final Dataset intoDataset = getTargetDataset().get(); 114 if (intoDataset instanceof JsonLdDataset && !intoDataset.contains(null, null, null, null)) { 115 final JsonLdDataset jsonLdDataset = (JsonLdDataset) intoDataset; 116 // Empty - we can just do a brave replace! 117 jsonLdDataset.getRdfDataSet().putAll(rdfDataset); 118 return; 119 // otherwise we have to merge.. but also avoid duplicate 120 // triples, 121 // map blank nodes etc, so we'll fall back to normal Dataset 122 // appending. 123 } 124 final Dataset fromDataset = getJsonLdFactory().asDataset(rdfDataset); 125 // .sequential() as we don't know if destination is thread-safe :-/ 126 fromDataset.stream().sequential().forEach(intoDataset::add); 127 } else { 128 final Dataset fromDataset = getJsonLdFactory().asDataset(rdfDataset); 129 // No need for .sequential() here 130 fromDataset.stream().forEach(getTarget()); 131 } 132 } 133 134 private JsonLdRDF getJsonLdFactory() { 135 if (getRdfTermFactory().isPresent() && getRdfTermFactory().get() instanceof JsonLdRDF) { 136 return (JsonLdRDF) getRdfTermFactory().get(); 137 } 138 return createRDFTermFactory(); 139 } 140 141 private Object readSource() throws IOException { 142 // Due to checked IOException we can't easily 143 // do this with .map and .orElseGet() 144 145 if (getSourceInputStream().isPresent()) { 146 return JsonUtils.fromInputStream(getSourceInputStream().get()); 147 } 148 if (getSourceIri().isPresent()) { 149 // TODO: propagate @base from content 150 return JsonUtils.fromURL(asURL(getSourceIri().get()), JsonUtils.getDefaultHttpClient()); 151 } 152 if (getSourceFile().isPresent()) { 153 try (InputStream inputStream = Files.newInputStream(getSourceFile().get())) { 154 return JsonUtils.fromInputStream(inputStream); 155 } 156 } 157 throw new IllegalStateException("No known source found"); 158 } 159 160}