/* * The contents of this file are subject to the Mozilla Public License * Version 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is the Kowari Metadata Store. * * The Initial Developer of the Original Code is Plugged In Software Pty * Ltd (http://www.pisoftware.com, mailto:info@pisoftware.com). Portions * created by Plugged In Software Pty Ltd are Copyright (C) 2001,2002 * Northrop Grumman Corporation. All Rights Reserved. * * This file is an original work and contains no Original Code. It was * developed by Netymon Pty Ltd under contract to the Australian * Commonwealth Government, Defense Science and Technology Organisation * under contract #4500507038 and is contributed back to the Kowari/Mulgara * Project as per clauses 4.1.3 and 4.1.4 of the above contract. * * Contributor(s): N/A. * * Copyright: * The copyright on this file is held by: * The Australian Commonwealth Government * Department of Defense * Developed by Netymon Pty Ltd * Copyright (C) 2006 * The Australian Commonwealth Government * Department of Defense * * [NOTE: The text of this Exhibit A may differ slightly from the text * of the notices in the Source Code files of the Original Code. You * should use the text of this Exhibit A rather than the text found in the * Original Code Source Code for Your Modifications.] * */ package org.mulgara.resolver.relational; import java.util.Set; import java.util.List; import java.util.HashSet; import java.util.ArrayList; import java.util.Iterator; import java.util.Map; import java.util.HashMap; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.net.URLEncoder; import java.sql.ResultSet; import java.sql.SQLException; import org.apache.log4j.Logger; import org.jrdf.graph.Node; import org.jrdf.graph.URIReference; import org.jrdf.graph.Literal; import org.mulgara.query.TuplesException; import org.mulgara.query.rdf.URIReferenceImpl; import org.mulgara.query.rdf.LiteralImpl; import org.mulgara.resolver.relational.d2rq.ClassMapElem; import org.mulgara.resolver.relational.d2rq.DatatypePropertyBridgeElem; import org.mulgara.resolver.relational.d2rq.ObjectPropertyBridgeElem; import org.mulgara.resolver.relational.d2rq.TranslationTableElem; import org.mulgara.resolver.relational.d2rq.DatabaseElem.DBType; public class PatternDesc extends VariableDesc { private enum UriEncoding { NONE, URLENCODING, URLIFY } @SuppressWarnings("unused") private static final Logger logger = Logger.getLogger(PatternDesc.class); private Set<String> tables; private Set<String> columns; private List<String> pattern; // Order is str : column : str : column : .... private List<UriEncoding> patternEncoding; // Matches 1-1 with pattern list private Class<? extends Node> anticipClass; private URI datatype; private String lang; private Map<String,Integer> columnIndices; private TranslationTableElem ttable; private final DBType dbType; public PatternDesc(ClassMapElem cmap, DBType dbType) { super(cmap); init(cmap.uriPattern, cmap.translateWith, URIReference.class); this.dbType = dbType; } public PatternDesc(ObjectPropertyBridgeElem bridge, DBType dbType) { super(bridge); init(bridge.pattern, bridge.translateWith, URIReference.class); this.dbType = dbType; } public PatternDesc(DatatypePropertyBridgeElem bridge, DBType dbType) { super(bridge); init(bridge.pattern, bridge.translateWith, Literal.class); if (bridge.datatype != null) { try { datatype = new URI(bridge.datatype); } catch (URISyntaxException eu) { datatype = null; } } else if (bridge.lang != null) { lang = bridge.lang; } this.dbType = dbType; } private void init(String rawPattern, TranslationTableElem ttable, Class<? extends Node> anticipatedClass) { if (rawPattern == null) { throw new IllegalArgumentException("Attempt to create PatternDesc with null pattern"); } this.ttable = ttable; anticipClass = anticipatedClass; String[] split = rawPattern.split("@@"); tables = new HashSet<String>(); columns = new HashSet<String>(); pattern = new ArrayList<String>(); patternEncoding = new ArrayList<UriEncoding>(); columnIndices = new HashMap<String,Integer>(); boolean isColumn = true; for (int i = 0; i < split.length; i++) { isColumn = !isColumn; // Note this makes the initial state *false* String patternPart = split[i]; if (isColumn) { if(patternPart.endsWith("|urlify")) { patternPart = patternPart.substring(0, patternPart.length()-("|urlify".length())); patternEncoding.add(UriEncoding.URLIFY); } else if(patternPart.endsWith("|urlencoding")) { patternPart = patternPart.substring(0, patternPart.length()-("|urlencoding".length())); patternEncoding.add(UriEncoding.URLENCODING); } else { patternEncoding.add(UriEncoding.NONE); } tables.add(RelationalResolver.parseTableFromColumn(patternPart)); columns.add(patternPart); } else { patternEncoding.add(UriEncoding.NONE); } pattern.add(patternPart); } } public void assignColumnIndex(String column, int index) { columnIndices.put(column, new Integer(index)); } public Node getNode(ResultSet resultSet) throws SQLException, TuplesException { StringBuffer buff = new StringBuffer(); boolean isColumn = true; for (int patternIndex = 0; patternIndex < pattern.size(); patternIndex++) { String p = pattern.get(patternIndex); UriEncoding encoding = patternEncoding.get(patternIndex); isColumn = !isColumn; // Note this makes the initial state *false* if (isColumn) { int index = columnIndices.get(p).intValue(); String colValue = resultSet.getString(index + 1); // For URLIFY, replace spaces with _ if(encoding == UriEncoding.URLIFY) { colValue = colValue.replace(' ', '_'); } // For URLENCODING and URLIFY, apply URL encoding if(encoding != UriEncoding.NONE) { try { colValue = URLEncoder.encode(colValue, "UTF-8"); } catch(UnsupportedEncodingException e) { // Should never happen - UTF-8 must be supported in all Java impls assert false : "Received unexpected encoding exception for UTF-8."; } } buff.append(colValue); } else { buff.append(p); } } String value; if (ttable != null) { value = (String)ttable.db2rdf.get(buff.toString()); if (value == null) { value = buff.toString(); } } else { value = buff.toString(); } if (anticipClass == URIReference.class) { try { return new URIReferenceImpl(new URI(value)); } catch (URISyntaxException eu) { return new LiteralImpl(value); } } else if (anticipClass == Literal.class) { if (datatype != null) { return new LiteralImpl(value, datatype); } else if (lang != null) { return new LiteralImpl(value, lang); } else { return new LiteralImpl(value); } } else { throw new TuplesException("Unknown expected node type: " + anticipClass); } } public Set<String> getTables() { return tables; } public Set<String> getColumns() { return columns; } private class EscapeContinuation extends Exception { private static final long serialVersionUID = -7899849917859481249L; } public String restrict(String rdfValue) { // Note: There are two possible approaches to this function. The first (simpler) approach is to // use concatenation within sql to assemble the pattern within the query itself. This works, but // as it subverts indexing by combining all columns into a single opaque function in the query. // The result is that this reduces to a massive filter on a possibly enormous result-set, with // the ensuing performance problems. // The alternative is to decompose the literal against the pattern, into a conjunction of // independent terms. The resulting query can therefore be run against indicies where available, // and is amenable to optimisation by the sql engine. try { List<String> terms = new ArrayList<String>(); String value = rdfValue; Iterator<String> i = pattern.iterator(); String str = (String)i.next(); if (!value.startsWith(str)) { throw new EscapeContinuation(); } value = value.substring(str.length()); String column = null; while (i.hasNext()) { str = (String)i.next(); if (column == null) { column = str; } else { int index = value.indexOf(str); if (index == -1) { throw new EscapeContinuation(); } terms.add(column + " = '" + value.substring(0, index - 1) + "'"); value = value.substring(index); column = null; } } // If column != null then the final pattern term was a column, and should match the remainder // of the value. if (column != null) { terms.add(column + " = " + encode(column, value, dbType)); } String result = RelationalQuery.toList(terms, " AND "); return result; } catch (EscapeContinuation ec) { return "1 = 2"; // Simple definition of 'false' } } }