com.github.thesmartenergy.sparql.generate.jena.engine.impl.SourcePlanImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.github.thesmartenergy.sparql.generate.jena.engine.impl.SourcePlanImpl.java

Source

/*
 * Copyright 2016 The Apache Software Foundation.
 *
 * 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 com.github.thesmartenergy.sparql.generate.jena.engine.impl;

import com.github.thesmartenergy.sparql.generate.jena.LocatorURLAccept;
import com.github.thesmartenergy.sparql.generate.jena.SPARQLGenerateException;
import static com.github.thesmartenergy.sparql.generate.jena.engine.impl.SourcePlanImpl.localCache;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.UnaryOperator;
import org.apache.commons.io.IOUtils;
import org.apache.jena.datatypes.RDFDatatype;
import org.apache.jena.datatypes.TypeMapper;
import org.apache.jena.graph.Node;
import org.apache.jena.graph.NodeFactory;
import org.apache.jena.sparql.core.Var;
import org.apache.jena.util.FileManager;
import org.apache.jena.util.Locator;
import org.apache.jena.util.LocatorURL;
import org.apache.jena.util.TypedStream;
import org.apache.log4j.Logger;
import com.github.thesmartenergy.sparql.generate.jena.engine.IteratorOrSourcePlan;

/**
 * Executes a <code>{@code SOURCE <node> ACCEPT <mime> AS <var>}</code> clause.
 *
 * @author Maxime Lefranois <maxime.lefrancois at emse.fr>
 */
public class SourcePlanImpl extends PlanBase implements IteratorOrSourcePlan {

    /**
     * The logger.
     */
    private static final Logger LOG = Logger.getLogger(SourcePlanImpl.class.getName());

    /**
     * The source node. A uri or a variable.
     */
    private final Node node;

    /**
     * The accept node. A uri or a variable.
     */
    private final Node accept;

    /**
     * The bound variable.
     */
    private final Var var;

    /**
     * The file manager.
     */
    private final FileManager fileManager;

    /** map uri --> node. */
    final static Map<String, Node> localCache = new HashMap<>();
    /** map uri --> accept uri --> node. */
    final static Map<String, Map<String, Node>> distantCache = new HashMap<>();
    /** type mapper. */
    final static TypeMapper tm = TypeMapper.getInstance();

    /**
     * The generation plan of a <code>{@code SOURCE <node> ACCEPT <mime> AS
     * <var>}</code> clause.
     *
     * @param node0 The IRI or the Variable node where a GET must be operated.
     * Must not be null.
     * @param accept0 The IRI or the Variable node that represent the accepted
     * Internet Media Type. May be null.
     * @param var0 The variable to bound the potentially retrieved document.
     * Must not be null.
     * @param fileManager0 The file manager to use to fetch a local file.
     */
    public SourcePlanImpl(final Node node0, final Node accept0, final Var var0, final FileManager fileManager0) {
        checkNotNull(node0, "Node must not be null");
        checkNotNull(var0, "Var must not be null");
        checkNotNull(fileManager0, "FileManager must not be null");
        if (!node0.isURI() && !node0.isVariable()) {
            throw new IllegalArgumentException("Source node must be a IRI or a" + " Variable. got " + node0);
        }
        this.node = node0;
        this.accept = accept0;
        this.var = var0;
        this.fileManager = fileManager0;
    }

    /**
     * {@inheritDoc}
     */
    final public void exec(final List<Var> variables, final List<BindingHashMapOverwrite> values) {
        LOG.debug("exec");
        boolean added = variables.add(var);
        if (!added) {
            throw new SPARQLGenerateException("Variable " + var + " is already" + " bound !");
        }
        // ensure we shunt the LocatorURL
        Set<Locator> toRemove = new HashSet<>();
        for (Iterator<Locator> it = fileManager.locators(); it.hasNext();) {
            Locator loc = it.next();
            if (loc instanceof LocatorURL) {
                toRemove.add(loc);
            }
        }
        for (Locator loc : toRemove) {
            fileManager.remove(loc);
        }
        ensureNotEmpty(variables, values);
        values.replaceAll(new Replacer());
    }

    /**
     *
     * @param binding -
     * @return the actual URI that represents the location of the query to be
     * fetched.
     */
    private String getActualSource(final BindingHashMapOverwrite binding) {
        if (node.isVariable()) {
            Node actualSource = binding.get((Var) node);
            if (actualSource == null) {
                return null;
            }
            if (!actualSource.isURI()) {
                throw new SPARQLGenerateException("Variable " + node.getName()
                        + " must be bound to a IRI that represents the location" + " of the query to be fetched.");
            }
            return actualSource.getURI();
        } else {
            if (!node.isURI()) {
                throw new SPARQLGenerateException("The source must be a IRI"
                        + " that represents the location of the query to be" + " fetched. Got " + node.getURI());
            }
            return node.getURI();
        }
    }

    /**
     * returns the accept header computed from ACCEPT clause.
     *
     * @param binding -
     * @return the actual accept header to use.
     */
    private String getAcceptHeader(final BindingHashMapOverwrite binding) {
        Node actualAccept = accept;
        if (accept == null) {
            return "*/*";
        }
        if (accept.isVariable()) {
            actualAccept = binding.get((Var) accept);
            if (accept == null) {
                return "*/*";
            }
        }
        if (!actualAccept.isURI()) {
            throw new SPARQLGenerateException(
                    "Variable " + node.getName() + " must be bound to a IRI that represents the internet"
                            + " media type of the source to be fetched. For"
                            + " instance, <urn:iana:mime:application/xml>.");
        }
        if (!actualAccept.getURI().startsWith("urn:iana:mime:")) {
            throw new SPARQLGenerateException(
                    "Variable " + node.getName() + " must be bound to a IANA MIME URN (RFC to be"
                            + " written). For instance," + " <urn:iana:mime:application/xml>.");
        }
        return actualAccept.getURI();

    }

    private class Replacer implements UnaryOperator<BindingHashMapOverwrite> {

        @Override
        public BindingHashMapOverwrite apply(BindingHashMapOverwrite value) {
            String literal = null;
            String datatypeURI = null;

            // generate the source URI.
            final String sourceUri = getActualSource(value);
            if (sourceUri == null) {
                LOG.debug("No source for " + node);
                return new BindingHashMapOverwrite(value, var, null);
            }

            // check local
            if (localCache.containsKey(sourceUri)) {
                Node n = localCache.get(sourceUri);
                LOG.debug("Found in local cache: " + var + "=" + n);
                return new BindingHashMapOverwrite(value, var, n);
            }
            try {
                literal = IOUtils.toString(fileManager.open(sourceUri));
                datatypeURI = "http://www.w3.org/2001/XMLSchema#string";
                RDFDatatype dt = tm.getSafeTypeByName(datatypeURI);
                final Node n = NodeFactory.createLiteral(literal, dt);
                LOG.debug("Found local: " + var + "=" + n);
                localCache.put(sourceUri, n);
                return new BindingHashMapOverwrite(value, var, n);
            } catch (Exception ex) {
                LOG.debug("Not found locally: " + node);
            }

            // check distant
            String acceptHeader = getAcceptHeader(value);
            if (distantCache.containsKey(sourceUri) && distantCache.get(sourceUri).containsKey(acceptHeader)) {
                Node n = distantCache.get(sourceUri).get(acceptHeader);
                LOG.debug("Found in distant cache: " + var + "=" + n);
                return new BindingHashMapOverwrite(value, var, n);
            }
            try {
                Locator loc = new LocatorURLAccept(acceptHeader);
                TypedStream stream = loc.open(sourceUri);
                //TODO check charset --> UTF-8 ok. else, base64
                literal = IOUtils.toString(stream.getInput());
                datatypeURI = "urn:iana:mime:" + stream.getMimeType();
                RDFDatatype dt = tm.getSafeTypeByName(datatypeURI);
                final Node n = NodeFactory.createLiteral(literal, dt);
                if (!distantCache.containsKey(sourceUri)) {
                    distantCache.put(sourceUri, new HashMap<String, Node>());
                }
                distantCache.get(sourceUri).put(acceptHeader, n);
                LOG.debug("Found distant: " + var + "=" + n);
                return new BindingHashMapOverwrite(value, var, n);
            } catch (Exception ex) {
                LOG.debug("Not found distant file." + node);
                if (!distantCache.containsKey(sourceUri)) {
                    distantCache.put(sourceUri, new HashMap<String, Node>());
                }
                distantCache.get(sourceUri).put(acceptHeader, null);
                return new BindingHashMapOverwrite(value, var, null);
            }
        }
    }

}