/** Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved. Contact: SYSTAP, LLC DBA Blazegraph 2501 Calvert ST NW #106 Washington, DC 20008 licenses@blazegraph.com 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 */ package com.bigdata.rdf.internal; import java.util.Map; import java.util.TreeMap; import org.openrdf.model.URI; import com.bigdata.rdf.internal.impl.literal.AbstractLiteralIV; import com.bigdata.rdf.internal.impl.uri.URIExtensionIV; import com.bigdata.rdf.model.BigdataLiteral; import com.bigdata.rdf.vocab.Vocabulary; /** * Default implementation of {@link IInlineURIFactory} that comes pre-loaded * with two handlers: * <ul> * <li>IPv4 ({@link InlineIPv4URIHandler})</li> * <li>UUID ({@link InlineUUIDURIHandler}</li> * </ul> * You MAY declare additional handlers. This MUST be done before the triple * store instance has been created. Once the triple store exists, the * translation imposed by the lexicon configuration and the lexicon indices must * be stable. * * @see BLZG-1507 (Implement support for DTE extension types for URIs) */ public class InlineURIFactory implements IInlineURIFactory { // private final List<InlineURIHandler> handlers = new LinkedList<InlineURIHandler>(); /** * The set of declared {@link InlineURIHandler}s, with an index on the * prefix for the handler. * <p> * Note: I've removed the separate linked list of the handlers and changed * this to a tree map. This provides lookup by prefix and the longest prefix * match is now used rather than visiting all registered handlers. * <p> * This map should be populated in the constructor for this class and * the subclasses in order to guarantee that the changes are visible * once we leave the scope of the constructor. */ private final TreeMap<String, InlineURIHandler> handlersByNamespace = new TreeMap<String, InlineURIHandler>(); /** * By default, handle IPv4 and UUID. */ public InlineURIFactory() { addHandler(new InlineUUIDURIHandler(InlineUUIDURIHandler.NAMESPACE)); addHandler(new InlineIPv4URIHandler(InlineIPv4URIHandler.NAMESPACE)); } /** * Declare a handler. This must be invoked in the constructor for this class * or the constructor of a subclass. The set of registered handlers must not * be changed outside of the constructor scope both for reasons of * visibility of the changes (thread-safety) and stability of the mapping of * {@link URI}s onto {@link IV}s. * * @param handler */ protected void addHandler(final InlineURIHandler handler) { // this.handlers.add(handler); getHandlersByNamespace().put(handler.getNamespace(), handler); } @Override public void init(final Vocabulary vocab) { for (InlineURIHandler handler : getHandlersByNamespace().values()) { handler.init(vocab); } } @Override @SuppressWarnings({ "unchecked", "rawtypes" }) public URIExtensionIV createInlineURIIV(final URI uri) { final String str = uri.stringValue(); // Find handler with longest prefix match LTE the given URI. final Map.Entry<String, InlineURIHandler> floorEntry = getHandlersByNamespace().floorEntry(str); if (floorEntry == null) { // No potentially suitable handler. return null; } final String prefix = floorEntry.getKey(); /* * Note: the floorEntry is NOT guaranteed to be a prefix. It can also be * strictly LT the probe key. Therefore we must additionally verify here * that the prefix under which the URI handler was registered is a * prefix of the URI before invoking that handler. */ if (str.startsWith(prefix)) { final InlineURIHandler handler = floorEntry.getValue(); final URIExtensionIV iv = handler.createInlineIV(uri); if (iv != null) { return iv; } } return null; /* * Note: This code checks each handler. This is presumably being done * because the ipv4 syntax can include "/" characters (for the optional * netmask) so the URI namespace (as defined by openrdf) is not matching * the prefix under which the handler is registered. */ // for (InlineURIHandler handler : handlersByNamespace.values()) { // final URIExtensionIV iv = handler.createInlineIV(uri); // if (iv != null) { // return iv; // } // } // return null; } @Override public String getLocalNameFromDelegate(final URI namespace, final AbstractLiteralIV<BigdataLiteral, ?> delegate) { final InlineURIHandler handler = getHandlersByNamespace().get(namespace.stringValue()); if (handler == null) { throw new IllegalArgumentException( "Can't resolve uri handler for \"" + namespace + "\". Maybe its be deregistered?"); } return handler.getLocalNameFromDelegate(delegate); } private TreeMap<String, InlineURIHandler> getHandlersByNamespace() { return handlersByNamespace; } }