blob: 8226e6d1774483785e0386281741db6fdb5658cc [file] [log] [blame]
/**
* 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 ;
// }
}