/**
 * Copyright (c) 2017 Eclipse RDF4J contributors.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Distribution License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 */
package org.eclipse.rdf4j.lucene.spin;

import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.function.BiConsumer;
import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.Statement;
import org.eclipse.rdf4j.model.ValueFactory;
import org.eclipse.rdf4j.sail.NotifyingSailConnection;
import org.eclipse.rdf4j.sail.SailException;
import org.eclipse.rdf4j.sail.evaluation.TupleFunctionEvaluationMode;
import org.eclipse.rdf4j.sail.helpers.NotifyingSailWrapper;
import org.eclipse.rdf4j.sail.lucene.LuceneSail;
import static org.eclipse.rdf4j.sail.lucene.LuceneSail.INDEXEDFIELDS;
import org.eclipse.rdf4j.sail.lucene.SearchIndex;
import org.eclipse.rdf4j.sail.lucene.SearchIndexQueryContextInitializer;
import org.eclipse.rdf4j.sail.lucene.util.SearchIndexUtils;
import org.eclipse.rdf4j.sail.spin.SpinSail;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Activates support of {@link SearchIndex} feature inside {@link SpinSail} and manages the index during
 * statements adding/removing. Technically this sail binds {@link SearchIndex} using
 * .addQueryContextInitializer and wraps connection from baseSail by wrapped which modify SearchIndex.
 *
 * @author jacek grzebyta
 */
public class LuceneSpinSail extends NotifyingSailWrapper {

	private final Logger log = LoggerFactory.getLogger(LuceneSpinSail.class);

	private SearchIndex si;

	private Properties parameters = new Properties();

	private Set<IRI> indexedFields;

	private Map<IRI, IRI> indexedFieldsMapping;

	public LuceneSpinSail() {
	}

	public LuceneSpinSail(SpinSail baseSail) {
		super(baseSail);
	}

	public Properties getParameters() {
		return parameters;
	}

	/**
	 * Replaces existing parameters.
	 * <p>
	 * By default parameters field is instantiated in constructor. Using this method replaces the existing
	 * field. If you wish only add missing parameters use {@link #addAbsentParameters(java.util.Properties)}.
	 * </p>
	 * 
	 * @param parameters
	 */
	public void setParameters(Properties parameters) {
		this.parameters = parameters;
	}

	/**
	 * Add only absent parameters from argument.
	 * 
	 * @see Properties#putIfAbsent(java.lang.Object, java.lang.Object)
	 * @param parameters
	 */
	public void addAbsentParameters(Properties parameters) {
		parameters.forEach((Object k, Object v) -> {
			LuceneSpinSail.this.parameters.putIfAbsent(k, v);
		});
	}

	/**
	 * @throws SailException
	 */
	@Override
	public void initialize()
		throws SailException
	{
		// Add support for indexed fields
		if (parameters.containsKey(INDEXEDFIELDS)) {
			String indexedfieldsString = parameters.getProperty(INDEXEDFIELDS);
			Properties prop = new Properties();
			try (Reader reader = new StringReader(indexedfieldsString)) {
				prop.load(reader);
			}
			catch (IOException e) {
				throw new SailException("Could read " + INDEXEDFIELDS + ": " + indexedfieldsString, e);
			}
			ValueFactory vf = getValueFactory();
			indexedFields = new HashSet<>();
			indexedFieldsMapping = new HashMap<>();
			for (Object key : prop.keySet()) {
				String keyStr = key.toString();
				if (keyStr.startsWith("index.")) {
					indexedFields.add(vf.createIRI(prop.getProperty(keyStr)));
				}
				else {
					indexedFieldsMapping.put(vf.createIRI(keyStr), vf.createIRI(prop.getProperty(keyStr)));
				}
			}
		}

		((SpinSail)getBaseSail()).setEvaluationMode(TupleFunctionEvaluationMode.TRIPLE_SOURCE);
		parameters.setProperty(LuceneSail.INDEX_CLASS_KEY,
				getParameters().getProperty(LuceneSail.INDEX_CLASS_KEY, LuceneSail.DEFAULT_INDEX_CLASS));
		log.debug("index location: {}", parameters.getProperty(LuceneSail.LUCENE_DIR_KEY));
		try {
			si = SearchIndexUtils.createSearchIndex(parameters);
			// bind index to SpinSail
			((SpinSail)getBaseSail()).addQueryContextInitializer(new SearchIndexQueryContextInitializer(si));
		}
		catch (Exception ex) {
			log.warn("error occured during set up of the search index. It might affect functionality.");
			throw new SailException(ex);
		}

		super.initialize();
	}

	@Override
	public void setDataDir(File dataDir) {
		super.setDataDir(dataDir);
		while (!parameters.containsKey(LuceneSail.LUCENE_DIR_KEY)) {
			parameters.setProperty(LuceneSail.LUCENE_DIR_KEY, dataDir.getAbsolutePath() + "/index");
		}
	}

	/**
	 * @return @throws SailException
	 */
	@Override
	public NotifyingSailConnection getConnection()
		throws SailException
	{
		if (si == null) {
			throw new SailException("Index is not created");
		}
		// the connection from the super is created only when the index exists
		return new LuceneSpinSailConnection(super.getConnection(), si, this);
	}

	/**
	 * Copy of {@link LuceneSail#mapStatement(org.eclipse.rdf4j.model.Statement) }
	 * 
	 * @param statement
	 * @return
	 */
	public Statement mapStatement(Statement statement) {
		IRI p = statement.getPredicate();
		boolean predicateChanged = false;
		Map<IRI, IRI> nextIndexedFieldsMapping = indexedFieldsMapping;
		if (nextIndexedFieldsMapping != null) {
			IRI res = nextIndexedFieldsMapping.get(p);
			if (res != null) {
				p = res;
				predicateChanged = true;
			}
		}
		Set<IRI> nextIndexedFields = indexedFields;
		if (nextIndexedFields != null && !nextIndexedFields.contains(p)) {
			return null;
		}

		if (predicateChanged) {
			return getValueFactory().createStatement(statement.getSubject(), p, statement.getObject(),
					statement.getContext());
		}
		else {
			return statement;
		}
	}

}

@@ -444,32 +444,87 @@
 o.StringReader;%0A
+import java.nio.file.Path;%0Aimport java.nio.file.Paths;%0A
 import java.util
@@ -636,46 +636,8 @@
 et;%0A
-import java.util.function.BiConsumer;%0A
 impo
@@ -4571,37 +4571,8 @@
 ) %7B%0A
-%09%09super.setDataDir(dataDir);%0A
 %09%09wh
@@ -4722,24 +4722,157 @@
 ndex%22);%0A%09%09%7D%0A
+%09%09Path indexPath = Paths.get(parameters.getProperty(LuceneSail.LUCENE_DIR_KEY));%0A%09%09super.setDataDir(indexPath.getParent().toFile());%0A
 %09%7D%0A%0A%09/**%0A%09 *

Use LUCENE_DIR_KEY over setDataDir