| /** |
| * 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.thrift; |
| |
| import static org.apache.jena.riot.thrift.TRDF.ANY ; |
| |
| import java.math.BigDecimal ; |
| import java.math.BigInteger ; |
| |
| import org.apache.jena.JenaRuntime ; |
| import org.apache.jena.atlas.lib.Pair ; |
| import org.apache.jena.datatypes.RDFDatatype ; |
| import org.apache.jena.datatypes.xsd.XSDDatatype ; |
| import org.apache.jena.datatypes.xsd.impl.RDFLangString ; |
| import org.apache.jena.graph.Node ; |
| import org.apache.jena.graph.NodeFactory ; |
| import org.apache.jena.graph.Triple ; |
| import org.apache.jena.riot.system.PrefixMap ; |
| import org.apache.jena.riot.system.PrefixMapFactory ; |
| import org.apache.jena.riot.thrift.wire.* ; |
| import org.apache.jena.sparql.core.Quad ; |
| import org.apache.jena.sparql.core.Var ; |
| import org.apache.thrift.TDeserializer; |
| import org.apache.thrift.TException; |
| import org.apache.thrift.TSerializer; |
| import org.apache.thrift.protocol.TCompactProtocol; |
| |
| /** Convert to and from Thrift wire objects. |
| * See {@link StreamRDF2Thrift} and {@link Thrift2StreamRDF} |
| * for ways to convert as streams (they recycle intermediate objects). |
| * @see StreamRDF2Thrift |
| * @see Thrift2StreamRDF |
| */ |
| public class ThriftConvert |
| { |
| /** Attempt to encode a node by value (integer, decimal, double) into an RDF_term. |
| * @param node |
| * @param term |
| * @return true if the term was set, else false. |
| */ |
| |
| public static boolean toThriftValue(Node node, RDF_Term term) { |
| if ( ! node.isLiteral() ) |
| return false ; |
| |
| // Value cases : Integer, Double, Decimal |
| String lex = node.getLiteralLexicalForm() ; |
| RDFDatatype rdt = node.getLiteralDatatype() ; |
| |
| if ( rdt == null ) |
| return false ; |
| |
| if ( rdt.equals(XSDDatatype.XSDdecimal) ) { |
| if ( rdt.isValid(lex)) { |
| BigDecimal decimal = new BigDecimal(lex.trim()) ; |
| int scale = decimal.scale() ; |
| BigInteger bigInt = decimal.unscaledValue() ; |
| if ( bigInt.compareTo(MAX_I) <= 0 && bigInt.compareTo(MIN_I) >= 0 ) { |
| // This check makes sure that bigInt.longValue() is safe |
| RDF_Decimal d = new RDF_Decimal(bigInt.longValue(), scale) ; |
| RDF_Term literal = new RDF_Term() ; |
| term.setValDecimal(d) ; |
| return true ; |
| } |
| } |
| } else if ( |
| rdt.equals(XSDDatatype.XSDinteger) || |
| rdt.equals(XSDDatatype.XSDlong) || |
| rdt.equals(XSDDatatype.XSDint) || |
| rdt.equals(XSDDatatype.XSDshort) || |
| rdt.equals(XSDDatatype.XSDbyte) |
| ) { |
| // and 4 unsigned equivalents |
| // and positive, negative, nonPostive nonNegativeInteger |
| |
| // Conservative - no derived types. |
| if ( rdt.isValid(lex)) { |
| try { |
| long v = ((Number)node.getLiteralValue()).longValue() ; |
| term.setValInteger(v) ; |
| return true ; |
| } |
| // Out of range for the type, not a long etc etc. |
| catch (Throwable ex) { } |
| } |
| } else if ( rdt.equals(XSDDatatype.XSDdouble) ) { |
| // XSDfloat?? |
| if ( rdt.isValid(lex)) { |
| try { |
| double v = ((Double)node.getLiteralValue()).doubleValue() ; |
| term.setValDouble(v) ; |
| return true ; |
| } |
| // Out of range for the type, ... |
| catch (Throwable ex) { } |
| } |
| } |
| return false ; |
| } |
| |
| /** |
| * Encode a {@link Node} into an {@link RDF_Term}, |
| * using values (integer, decimal, double) if possible. |
| */ |
| public static void toThrift(Node node, RDF_Term term) { |
| toThrift(node, emptyPrefixMap, term, true); |
| } |
| |
| /** |
| * Encode a {@link Node} into an {@link RDF_Term}. Control whether to use values |
| * (integer, decimal, double) if possible. |
| */ |
| public static void toThrift(Node node, RDF_Term term, boolean allowValues) { |
| toThrift(node, emptyPrefixMap, term, allowValues); |
| } |
| |
| /** Encode a {@link Node} into an {@link RDF_Term} */ |
| public static void toThrift(Node node, PrefixMap pmap, RDF_Term term, boolean allowValues) { |
| if ( node == null) { |
| term.setUndefined(TRDF.UNDEF); |
| return; |
| } |
| |
| if ( node.isURI() ) { |
| RDF_PrefixName prefixName = abbrev(node.getURI(), pmap) ; |
| if ( prefixName != null ) { |
| term.setPrefixName(prefixName) ; |
| return ; |
| } |
| } |
| |
| if ( node.isBlank() ) { |
| RDF_BNode b = new RDF_BNode(node.getBlankNodeLabel()) ; |
| term.setBnode(b) ; |
| return ; |
| } |
| |
| if ( node.isURI() ) { |
| RDF_IRI iri = new RDF_IRI(node.getURI()) ; |
| term.setIri(iri) ; |
| return ; |
| } |
| |
| if ( node.isLiteral() ) { |
| // Value cases : Integer, Double, Decimal |
| if ( allowValues) { |
| boolean b = toThriftValue(node, term) ; |
| if ( b /* term.isSet() */ ) |
| return ; |
| } |
| |
| String lex = node.getLiteralLexicalForm() ; |
| String dt = node.getLiteralDatatypeURI() ; |
| String lang = node.getLiteralLanguage() ; |
| |
| // General encoding. |
| RDF_Literal literal = new RDF_Literal(lex) ; |
| if ( JenaRuntime.isRDF11 ) { |
| if ( node.getLiteralDatatype().equals(XSDDatatype.XSDstring) || |
| node.getLiteralDatatype().equals(RDFLangString.rdfLangString) ) |
| dt = null ; |
| } |
| |
| if ( dt != null ) { |
| RDF_PrefixName dtPrefixName = abbrev(dt, pmap) ; |
| if ( dtPrefixName != null ) |
| literal.setDtPrefix(dtPrefixName) ; |
| else |
| literal.setDatatype(dt) ; |
| } |
| if ( lang != null && ! lang.isEmpty() ) |
| literal.setLangtag(lang) ; |
| term.setLiteral(literal) ; |
| return ; |
| } |
| |
| if ( node.isVariable() ) { |
| RDF_VAR var = new RDF_VAR(node.getName()) ; |
| term.setVariable(var) ; |
| return ; |
| } |
| if ( Node.ANY.equals(node)) { |
| term.setAny(ANY) ; |
| return ; |
| } |
| throw new RiotThriftException("Node conversion not supported: "+node) ; |
| } |
| |
| private static final PrefixMap emptyPrefixMap = PrefixMapFactory.emptyPrefixMap() ; |
| |
| /** Build a {@link Node} from an {@link RDF_Term}. */ |
| public static Node convert(RDF_Term term) { |
| return convert(term, null) ; |
| } |
| |
| /** |
| * Build a {@link Node} from an {@link RDF_Term} using a prefix map which must agree |
| * with the map used to create the {@code RDF_Term} in the first place. |
| */ |
| public static Node convert(RDF_Term term, PrefixMap pmap) { |
| if ( term.isSetPrefixName() ) { |
| String x = expand(term.getPrefixName(), pmap) ; |
| if ( x != null ) |
| return NodeFactory.createURI(x) ; |
| throw new RiotThriftException("Failed to expand "+term) ; |
| //Log.warn(BinRDF.class, "Failed to expand "+term) ; |
| //return NodeFactory.createURI(prefix+":"+localname) ; |
| } |
| |
| if ( term.isSetIri() ) |
| return NodeFactory.createURI(term.getIri().getIri()) ; |
| |
| if ( term.isSetBnode() ) |
| return NodeFactory.createBlankNode(term.getBnode().getLabel()) ; |
| |
| if ( term.isSetLiteral() ) { |
| RDF_Literal lit = term.getLiteral() ; |
| String lex = lit.getLex() ; |
| String dtString = null ; |
| if ( lit.isSetDatatype() ) |
| dtString = lit.getDatatype() ; |
| else if ( lit.isSetDtPrefix()) { |
| String x = expand(lit.getDtPrefix(), pmap) ; |
| if ( x == null ) |
| throw new RiotThriftException("Failed to expand datatype prefix name:"+term) ; |
| dtString = x ; |
| } |
| RDFDatatype dt = NodeFactory.getType(dtString) ; |
| |
| String lang = lit.getLangtag() ; |
| return NodeFactory.createLiteral(lex, lang, dt) ; |
| } |
| |
| if ( term.isSetValInteger() ) { |
| long x = term.getValInteger() ; |
| String lex = Long.toString(x, 10) ; |
| RDFDatatype dt = XSDDatatype.XSDinteger ; |
| return NodeFactory.createLiteral(lex, dt) ; |
| } |
| |
| if ( term.isSetValDouble() ) { |
| double x = term.getValDouble() ; |
| String lex = Double.toString(x) ; |
| RDFDatatype dt = XSDDatatype.XSDdouble ; |
| return NodeFactory.createLiteral(lex, dt) ; |
| } |
| |
| if ( term.isSetValDecimal() ) { |
| long value = term.getValDecimal().getValue() ; |
| int scale = term.getValDecimal().getScale() ; |
| BigDecimal d = BigDecimal.valueOf(value, scale) ; |
| String lex = d.toPlainString() ; |
| RDFDatatype dt = XSDDatatype.XSDdecimal ; |
| return NodeFactory.createLiteral(lex, dt) ; |
| } |
| |
| if ( term.isSetVariable() ) |
| return Var.alloc(term.getVariable().getName()) ; |
| |
| if ( term.isSetAny() ) |
| return Node.ANY ; |
| |
| if ( term.isSetUndefined() ) |
| return null; |
| |
| throw new RiotThriftException("No conversion to a Node: "+term.toString()) ; |
| } |
| |
| private static String expand(RDF_PrefixName prefixName, PrefixMap pmap) { |
| if ( pmap == null ) |
| return null ; |
| |
| String prefix = prefixName.getPrefix() ; |
| String localname = prefixName.getLocalName() ; |
| String x = pmap.expand(prefix, localname) ; |
| if ( x == null ) |
| throw new RiotThriftException("Failed to expand "+prefixName) ; |
| return x ; |
| } |
| |
| |
| public static RDF_Term convert(Node node, boolean allowValues) { |
| return convert(node, null, allowValues) ; |
| } |
| |
| public static RDF_Term convert(Node node, PrefixMap pmap, boolean allowValues) { |
| RDF_Term n = new RDF_Term() ; |
| toThrift(node, pmap, n, allowValues) ; |
| return n ; |
| } |
| |
| static final BigInteger MAX_I = BigInteger.valueOf(Long.MAX_VALUE) ; |
| static final BigInteger MIN_I = BigInteger.valueOf(Long.MIN_VALUE) ; |
| |
| /** Produce a {@link RDF_PrefixName} is possible. */ |
| private static RDF_PrefixName abbrev(String uriStr, PrefixMap pmap) { |
| if ( pmap == null ) |
| return null ; |
| Pair<String, String> p = pmap.abbrev(uriStr) ; |
| if ( p == null ) |
| return null ; |
| return new RDF_PrefixName(p.getLeft(), p.getRight()) ; |
| } |
| |
| public static Triple convert(RDF_Triple triple) { |
| return convert(triple, null) ; |
| } |
| |
| public static Triple convert(RDF_Triple rt, PrefixMap pmap) { |
| Node s = convert(rt.getS(), pmap) ; |
| Node p = convert(rt.getP(), pmap) ; |
| Node o = convert(rt.getO(), pmap) ; |
| return Triple.create(s, p, o) ; |
| } |
| |
| public static RDF_Triple convert(Triple triple, boolean allowValues) { |
| return convert(triple, null, allowValues) ; |
| } |
| |
| public static RDF_Triple convert(Triple triple, PrefixMap pmap, boolean allowValues) { |
| RDF_Triple t = new RDF_Triple() ; |
| RDF_Term s = convert(triple.getSubject(), pmap, allowValues) ; |
| RDF_Term p = convert(triple.getPredicate(), pmap, allowValues) ; |
| RDF_Term o = convert(triple.getObject(), pmap, allowValues) ; |
| t.setS(s) ; |
| t.setP(p) ; |
| t.setO(o) ; |
| return t ; |
| } |
| |
| public static Quad convert(RDF_Quad quad) { |
| return convert(quad, null) ; |
| } |
| |
| public static Quad convert(RDF_Quad rq, PrefixMap pmap) { |
| Node g = (rq.isSetG() ? convert(rq.getG(), pmap) : null ) ; |
| Node s = convert(rq.getS(), pmap) ; |
| Node p = convert(rq.getP(), pmap) ; |
| Node o = convert(rq.getO(), pmap) ; |
| return Quad.create(g, s, p, o) ; |
| } |
| |
| public static RDF_Quad convert(Quad quad, boolean allowValues) { |
| return convert(quad, null, allowValues) ; |
| } |
| |
| public static RDF_Quad convert(Quad quad, PrefixMap pmap, boolean allowValues) { |
| RDF_Quad q = new RDF_Quad() ; |
| RDF_Term g = null ; |
| if ( quad.getGraph() != null ) |
| g = convert(quad.getGraph(), pmap, allowValues) ; |
| RDF_Term s = convert(quad.getSubject(), pmap, allowValues) ; |
| RDF_Term p = convert(quad.getPredicate(), pmap, allowValues) ; |
| RDF_Term o = convert(quad.getObject(), pmap, allowValues) ; |
| if ( g != null ) |
| q.setG(g) ; |
| q.setS(s) ; |
| q.setP(p) ; |
| q.setO(o) ; |
| return q ; |
| } |
| |
| /** |
| * Serialize the {@link RDF_Term} into a byte array. |
| * <p> |
| * Where possible, to is better to serialize into a stream, directly using {@code term.write(TProtocol)}. |
| */ |
| public static byte[] termToBytes(RDF_Term term) { |
| TSerializer serializer = new TSerializer(new TCompactProtocol.Factory()); |
| try { |
| return serializer.serialize(term); |
| } |
| catch (TException e) { |
| throw new RiotThriftException(e); |
| } |
| } |
| |
| /** |
| * Deserialize from a byte array into an {@link RDF_Term}. |
| * <p> |
| * Where possible, to is better to deserialize from a stream, directly using {@code term.read(TProtocol)}. |
| */ |
| public static RDF_Term termFromBytes(byte[] bytes) { |
| RDF_Term term = new RDF_Term(); |
| termFromBytes(term, bytes); |
| return term; |
| } |
| |
| /** |
| * Deserialize from a byte array into an {@link RDF_Term}. |
| * <p> |
| * Where possible, to is better to deserialize from a stream, directly using {@code term.read(TProtocol)}. |
| */ |
| public static void termFromBytes(RDF_Term term, byte[] bytes) { |
| TDeserializer deserializer = new TDeserializer(new TCompactProtocol.Factory()); |
| try { |
| deserializer.deserialize(term, bytes); |
| } |
| catch (TException e) { throw new RiotThriftException(e); } |
| } |
| |
| // RDF_Tuple => RDF_row (for result sets) or List<RDFTerm> |
| |
| // public static Tuple<Node> convert(RDF_Tuple row) { |
| // return convert(row, null) ; |
| // } |
| // |
| // public static Tuple<Node> convert(RDF_Tuple row, PrefixMap pmap) { |
| // List<RDF_Term> terms = row.getTerms() ; |
| // Node[] tuple = new Node[terms.size()] ; |
| // int idx = 0 ; |
| // for ( RDF_Term rt : terms ) { |
| // tuple[idx] = convert(rt, pmap) ; |
| // idx ++ ; |
| // } |
| |
| // return Tuple.create(tuple) ; |
| // } |
| // |
| // public static RDF_Tuple convert(Tuple<Node> tuple) { |
| // return convert(tuple, null) ; |
| // } |
| // |
| // public static RDF_Tuple convert(Tuple<Node> tuple, PrefixMap pmap) { |
| // RDF_Tuple rTuple = new RDF_Tuple() ; |
| // for ( Node n : tuple ) { |
| // RDF_Term rt = convert(n, pmap) ; |
| // rTuple.addToTerms(rt) ; |
| // } |
| // return rTuple ; |
| // } |
| } |