com.bigdata.rdf.sail.webapp.client.EncodeDecodeValue Maven / Gradle / Ivy
/**
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
[email protected]
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Created on Oct 13, 2011
*/
package com.bigdata.rdf.sail.webapp.client;
import org.openrdf.model.BNode;
import org.openrdf.model.Literal;
import org.openrdf.model.Resource;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.openrdf.model.impl.LiteralImpl;
import org.openrdf.model.impl.URIImpl;
/**
* Utility class to encode/decode RDF {@link Value}s for interchange with the
* REST API.
*
* @author Bryan Thompson
* @version $Id$
*/
public class EncodeDecodeValue {
// /*
// * Note: The decode logic was derived from the JavaCharStream file generated
// * by JavaCC.
// */
//
// private static final int hexval(char c) {
// switch (c) {
// case '0':
// return 0;
// case '1':
// return 1;
// case '2':
// return 2;
// case '3':
// return 3;
// case '4':
// return 4;
// case '5':
// return 5;
// case '6':
// return 6;
// case '7':
// return 7;
// case '8':
// return 8;
// case '9':
// return 9;
//
// case 'a':
// case 'A':
// return 10;
// case 'b':
// case 'B':
// return 11;
// case 'c':
// case 'C':
// return 12;
// case 'd':
// case 'D':
// return 13;
// case 'e':
// case 'E':
// return 14;
// case 'f':
// case 'F':
// return 15;
// }
//
// throw new AssertionError();
// }
//
// private static class DecodeString {
// private final StringBuilder sb = new StringBuilder();
// private final String src;
// private int srcpos = 0;
// DecodeString(final String s) {
// this.src = s;
// }
//
// private char ReadByte() {
// return src.charAt(srcpos++);
// }
//
// private void backup(final int n) {
//
// sb.setLength(sb.length() - n);
//
// }
//
// /**
// * Read a character.
// *
// * TODO Does not handle the 8 character escape code sequences (but
// * neither does the SPARQL parser!)
// */
// private char readChar() throws java.io.IOException {
// char c;
//
// sb.append(c = ReadByte());
//
// if (c == '\\') {
//
// int backSlashCnt = 1;
//
// for (;;) // Read all the backslashes
// {
//
// try {
// sb.append(c=ReadByte());
// if (c != '\\') {
// // found a non-backslash char.
// if ((c == 'u') && ((backSlashCnt & 1) == 1)) {
// if (--bufpos < 0)
// bufpos = bufsize - 1;
//
// break;
// }
//
// backup(backSlashCnt);
// return '\\';
// }
// } catch (java.io.IOException e) {
// // We are returning one backslash so we should only
// // backup (count-1)
// if (backSlashCnt > 1)
// backup(backSlashCnt - 1);
//
// return '\\';
// }
//
// backSlashCnt++;
// }
//
// // Here, we have seen an odd number of backslash's followed by a
// // 'u'
// try {
// while ((c = ReadByte()) == 'u') {}
//
// // Decode the code sequence.
// c = (char) (hexval(c) << 12 | hexval(ReadByte()) << 8
// | hexval(ReadByte()) << 4 | hexval(ReadByte()));
//
// sb.append(c);
//
// } catch (java.io.IOException e) {
//
// throw new Error("Invalid escape character");
//
// }
//
// if (backSlashCnt == 1)
// return c;
// else {
// backup(backSlashCnt - 1);
// return '\\';
// }
// } else {
// return c;
// }
// }
//
// }
//
// /**
// * Apply code point escape sequences for anything that we need to escape.
// * For our purposes, this is just "
and >
.
// * @param s
// * @return
// *
// * @see http://www.w3.org/TR/sparql11-query/#codepointEscape
// */
// static String encodeEscapeSequences(final String s) {
//
// return s;
//
// }
//
// /**
// * Decode all code point escape sequences. Note that we need to decode more
// * than we encode since we are not responsible for the encoding when it
// * comes to the REST API, just the decoding.
// *
// * @param s
// * The string, which may have escape sequences encoded.
// *
// * @return The string with escape sequences decoded.
// *
// * @throws IllegalArgumentException
// * if the argument is null
.
// * @throws IllegalArgumentException
// * if the argument is contains an ill-formed escape code
// * sequence.
// *
// * @see http://www.w3.org/TR/sparql11-query/#codepointEscape
// *
// */
// static String decodeEscapeSequences(final String s) {
//
//// // Remove any escape sequences.
//// final StringBuilder sb = new StringBuilder();
//// for (int i = 0; i < slen; i++) {
//// char ch = s.charAt(i);
//// if (ch == '\\') {
//// if (i + 1 == slen)
//// throw new IllegalArgumentException(s);
//// ch = s.charAt(i);
//// }
//// sb.append(ch);
//// }
//// final String t = sb.toString();
//
// return s;
//
// }
/**
* Decode a URI or Literal.
*
* @param s
* The value to be decoded.
*
* @return The URI or literal -or- null
if the argument was
* null
.
*
* @throws IllegalArgumentException
* if the request parameter could not be decoded as an RDF
* {@link Value}.
*/
public static Value decodeValue(final String s) {
if(s == null)
return null;
// final String s = decodeEscapeSequences(ss);
final int slen = s.length();
if (slen == 0)
throw new IllegalArgumentException("");
final char ch = s.charAt(0);
if(ch == '\"' || ch == '\'') {
/*
* Literal.
*/
final int closeQuotePos = s.lastIndexOf(ch);
if (closeQuotePos == 0)
throw new IllegalArgumentException(s);
final String label = s.substring(1, closeQuotePos);
if (slen == closeQuotePos + 1) {
/*
* Plain literal.
*/
return new LiteralImpl(label);
}
final char ch2 = s.charAt(closeQuotePos + 1);
if (ch2 == '@') {
/*
* Language code literal.
*/
final String languageCode = s.substring(closeQuotePos + 2);
return new LiteralImpl(label, languageCode);
} else if (ch2 == '^') {
/*
* Datatype literal.
*/
if (slen <= closeQuotePos + 2)
throw new IllegalArgumentException(s);
if (s.charAt(closeQuotePos + 2) != '^')
throw new IllegalArgumentException(s);
final String datatypeStr = s.substring(closeQuotePos + 3);
final URI datatypeURI = decodeURI(datatypeStr);
return new LiteralImpl(label,datatypeURI);
} else {
throw new IllegalArgumentException(s);
}
} else if (ch == '<') {
/*
* URI
*/
if (s.charAt(slen - 1) != '>')
throw new IllegalArgumentException(s);
final String uriStr = s.substring(1, slen - 1);
return new URIImpl(uriStr);
} else {
throw new IllegalArgumentException(s);
}
}
/**
* Type safe variant for a {@link Resource}.
*
* @param param
* The encoded value.
*
* @return The Resource -or- null
if the argument was
* null
.
*/
public static Resource decodeResource(final String param) {
final Value v = decodeValue(param);
if (v == null || v instanceof Resource)
return (Resource) v;
throw new IllegalArgumentException("Not a Resource: '" + param + "'");
}
/**
* Type safe variant for a {@link URI}.
*
* @param param
* The encoded value.
*
* @return The URI -or- null
if the argument was
* null
.
*/
public static URI decodeURI(final String param) {
final Value v = decodeValue(param);
if (v == null || v instanceof URI)
return (URI) v;
throw new IllegalArgumentException("Not an URI: '" + param + "'");
}
/**
* Encode an RDF {@link Value} as it should appear if used in a SPARQL
* query. E.g., a literal will look like "abc"
,
* "abc"@en
or
* "3"^^xsd:int. A URI will look like <http://www.bigdata.com/>
* .
*
* @param v
* The value (optional).
*
* @return The encoded value -or- null
if the argument is
* null
.
*
* @throws IllegalArgumentException
* if the argument is a {@link BNode}.
*/
public static String encodeValue(final Value v) {
if(v == null)
return null;
if (v instanceof BNode)
throw new IllegalArgumentException();
if (v instanceof URI) {
return "<" + v.stringValue() + ">";
}
if (v instanceof Literal) {
final Literal lit = (Literal) v;
final StringBuilder sb = new StringBuilder();
sb.append("\"");
sb.append(lit.getLabel());
sb.append("\"");
if (lit.getLanguage() != null) {
sb.append("@");
sb.append(lit.getLanguage());
}
if (lit.getDatatype() != null) {
sb.append("^^");
sb.append(encodeValue(lit.getDatatype()));
}
return sb.toString();
}
throw new AssertionError();
}
/**
* Decode an array of named graph contexts.
*
* @param strings
* An array of encoded named graph contexts (required).
*
* @return An array of decoded resources that is 1:1 with the original array.
*
* @throws IllegalArgumentException
* if the argument is null
.
*
* @see com.bigdata.rdf.store.BD;#NULL_GRAPH
*
* @see Resource... contexts
* not encoded/decoded according to openrdf semantics (REST API)
*/
public static Resource[] decodeContexts(final String[] strings) {
if (strings == null) {
// Not allowed per the openrdf pattern.
throw new IllegalArgumentException();
}
if (strings.length == 0) {
// Allowed. Corresponds to all named graphs.
return EMPTY_RESOURCE_ARRAY;
}
final Resource[] tmp = new Resource[strings.length];
for (int i = 0; i < strings.length; i++) {
final String s = strings[i];
if (s == null || s.length() == 0) {
/*
* Allowed. This is a reference to the openrdf "null" graph.
*
* Note: "c=" reports a zero length string so we need to decode that
* to a null reference.
*/
tmp[i] = null;
} else {
tmp[i] = decodeResource(s);
}
}
return tmp;
}
/**
* Encode each Resource.
*
* Note: When generating URL parameters, we will have zero or more
* &c=...
instances. These are turned into a Resource[]. If
* there are no such arguments, then the array needs to be turned into
* Resource[0] on receipt by the webapp in order to be correctly decoded by
* {@link #decodeContexts(String[])}.
*
* @param values
* An array of RDF {@link Value}s.
*
* @return An array of encoded RDF values that is 1:1 with the original
* array.
*
* @throws IllegalArgumentException
* if the argument is null
.
*
* @see com.bigdata.rdf.store.BD;#NULL_GRAPH
*
* @see Resource... contexts
* not encoded/decoded according to openrdf semantics (REST API)
*/
public static String[] encodeContexts(final Resource[] values) {
if (values == null) {
// Not allowed per the openrdf patterns.
throw new IllegalArgumentException();
}
if (values.length == 0) {
// Allowed. Corresponds to all named graphs.
return EMPTY_STRING_ARRAY;
}
final String[] strings = new String[values.length];
for (int i = 0; i < values.length; i++) {
final Value val = values[i];
if (val == null) {
strings[i] = null; // i.e., an openrdf null graph reference.
} else {
strings[i] = encodeValue(val);
}
}
return strings;
}
/** An empty String[] for encode. */
static private final String[] EMPTY_STRING_ARRAY = new String[0];
/** An empty Resource[] for decode. */
static private final Resource[] EMPTY_RESOURCE_ARRAY = new Resource[0];
}