/* * Copyright 2009 Fedora Commons, Inc. * * Licensed 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.mulgara.protocol.http; import java.net.URI; import java.net.URISyntaxException; import java.util.Collections; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jrdf.graph.Literal; import org.jrdf.graph.ObjectNode; import org.jrdf.graph.PredicateNode; import org.jrdf.graph.SubjectNode; import org.jrdf.graph.Triple; import org.mulgara.query.rdf.BlankNodeImpl; import org.mulgara.query.rdf.LiteralImpl; import org.mulgara.query.rdf.URIReferenceImpl; import org.mulgara.util.StringUtil; /** * Represents a triple to be created or removed. Null values indicate blank nodes. * * @created Feb 15, 2009 * @author Paula Gearon * @copyright © 2008 <a href="http://www.topazproject.org/">The Topaz Project</a> * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a> */ class LocalTriple implements Triple { /** Generated serialization ID */ private static final long serialVersionUID = -2922482409580801899L; final URI subject; final URI predicate; final Object object; final boolean hasLiteral; public LocalTriple(String s, String p, String o) throws ServletException { this(s, p, o, false); } /** * Creates a new triple. * @param s The subject. * @param p The predicate. * @param o The object. */ public LocalTriple(String s, String p, String o, boolean validate) throws ServletException { if (validate) { if (s == null || s.length() == 0) throw new BadRequestException("Blank subject node not permitted in this operation"); if (o == null || o.length() == 0) throw new BadRequestException("Blank object node not permitted in this operation"); } try { subject = s == null ? null : new URI(s); } catch (URISyntaxException e) { throw new BadRequestException("Invalid subject URI: " + s); } if (p == null) throw new BadRequestException("Illegal triple presented. Predicate cannot be blank."); try { predicate = new URI(p); } catch (URISyntaxException e) { throw new BadRequestException("Invalid predicate URI: " + p); } // temporary variables because the compiler doesn't understand code paths when assigning finals Object tmpo; boolean tmpl; try { tmpl = false; if (o == null || o.length() == 0) { // no object, so it's blank tmpo = null; } else { char zc = o.charAt(0); if (zc == '\'' || zc == '"') { // starts with quote, so treat it as a literal tmpo = new LocalLiteral(o); tmpl = true; } else { // attempt to load as a URI tmpo = new URI(o); } } } catch (URISyntaxException e) { tmpo = new LocalLiteral(o); tmpl = true; } object = tmpo; hasLiteral = tmpl; } /** @return the subject. <code>null</code> indicates a blank node. */ public SubjectNode getSubject() { return subject == null ? new BlankNodeImpl() : new URIReferenceImpl(subject); } /** @return the predicate. */ public PredicateNode getPredicate() { return new URIReferenceImpl(predicate); } /** * Get the object. <code>null</code> indicates a blank node. * The resulting type is either a URI or a Literal. * @return the object. */ public ObjectNode getObject() { return object == null ? new BlankNodeImpl() : (hasLiteral ? ((LocalLiteral)object).toJRDFLiteral() : new URIReferenceImpl((URI)object, false)); } /** * Present this object as a singleton set. * @return A set containing only this triple. */ public Set<Triple> toSet() { return Collections.singleton((Triple)this); } /** * Represents a literal node found in a request. */ public class LocalLiteral { String text; URI type; String lang; LocalLiteral(String s) { type = null; lang = null; String quot = null; char c = s.charAt(0); if (c == '\'') { quot = s.charAt(1) == '\'' ? "'''" : "'"; } else if (c == '"') { quot = s.charAt(1) == '"' ? "\"\"\"" : "\""; } text = parseQuoted(quot, s); } /** * Construct a JRDF literal for use in the query system. * @return A new Literal object with the established parameters. */ public Literal toJRDFLiteral() { if (type != null) return new LiteralImpl(text, type); if (lang != null) return new LiteralImpl(text, lang); return new LiteralImpl(text); } /** * Pull a literal apart into its constituents. If any part is not valid, then the entire * string is treated as a single literal. This method sets the lang or uri sections of * the literal. * @param q The quote character used to wrap the string * @param n The string for the literal node. * @return The text portion of the literal. */ private String parseQuoted(String q, String n) { // unquoted, so return entire string if (q == null) return n; Pattern p = Pattern.compile(q + "(.*)" + q + "((@([a-zA-Z\\-]+))|(\\^\\^((.+))))?"); Matcher m = p.matcher(n); // no match, so entire thing is literal if (!m.find()) return n; // partial match, so return entire string if (m.end() != n.length()) return n; // test for unquoted characters String g1 = m.group(1).replaceAll("\\\\\\\\", ""); if (containsUnquoted(g1, q.substring(0, 1))) return n; // get the language lang = m.group(4); if (lang == null) { // no language, so get the type URI String t = m.group(6); if (t != null) { try { type = new URI(t); } catch (URISyntaxException e) { // invalid URI, so return the entire string as a literal return n; } } } return StringUtil.unescapeJavaString(m.group(1)); } /** * Tests the contents of a string for unescaped quote characters. * @param str The string to test. * @param q The type of quote character to look for. * @return <code>true</code> if the string contains an unescaped quote. */ private boolean containsUnquoted(String str, String q) { Matcher m = Pattern.compile(q).matcher(str); while (m.find()) { int s = m.start(); if (s == 0 || str.charAt(s - 1) != '\\') return true; } return false; } } }