Commit f274a6f8 authored by R.W.Majeed's avatar R.W.Majeed
Browse files

encounter inference factory added

parent 64f0841a
Loading
Loading
Loading
Loading
+57 −0
Original line number Diff line number Diff line
package de.sekmi.histream.inference;

import java.util.Iterator;
import java.util.NoSuchElementException;

public class DependencyExpandingIterator implements Iterator<String> {
	private InferenceFactory factory;
	private Iterator<String> concepts;
	/** Temporary iterator for dependencies */
	private Iterator<String> temp;
	private String next;
	
	public DependencyExpandingIterator(InferenceFactory factory, Iterator<String> concepts) {
		this.concepts = concepts;
		this.factory = factory;
	}
	
	@Override
	public boolean hasNext() {
		prefetch();
		return null != next;
	}

	private void prefetch(){
		while( next == null ){
			if( temp != null && temp.hasNext() ){
				// fetch next dependency
				next = temp.next();
			}else if( concepts.hasNext() ){
				next = concepts.next();
				// try whether this concept is inferred
				InferredConcept ic = factory.getConceptById(next);
				if( ic == null ){
					// can not infer this concept, leave as is
				}else{
					// can infer this concept
					// remove the concept id and replace with dependency IDs
					next = null;
					temp = ic.getDependencyIDs();
					// continue, fetch first dependency in next iteration
				}
			}else{
				// no more concepts
				break;
			}
		}
	}
	@Override
	public String next() {
		prefetch();
		if( next != null ){
			throw new NoSuchElementException();
		}
		return next;
	}

}
+2 −14
Original line number Diff line number Diff line
package de.sekmi.histream.inference;

import de.sekmi.histream.ObservationFactory;

public interface EncounterInferenceFactory {

	void setObservationFactory(ObservationFactory factory);
	/**
	 * Determine whether this inference factory can infer
	 * facts with the given concept id.
	 * 
	 * @param inferredConceptId inferred concept
	 * @return true if the concept can be inferred, false otherwise.
	 */
	boolean canInfer(String inferredConceptId);
public abstract class EncounterInferenceFactory extends InferenceFactory {
	
	/**
	 * Create an inference engine which infers the given concept id.
@@ -20,5 +8,5 @@ public interface EncounterInferenceFactory {
	 * @return inference engine or {@code null} if the concept is not
	 * 	supported by this factory.
	 */
	public EncounterInferenceEngine createEngine(String inferredConceptId);
	public abstract EncounterInferenceEngine createEngine(String inferredConceptId);
}
+42 −0
Original line number Diff line number Diff line
package de.sekmi.histream.inference;

import java.util.Iterator;

import de.sekmi.histream.ObservationFactory;

public abstract class InferenceFactory {
	protected ObservationFactory factory;
	
	public void setObservationFactory(ObservationFactory factory){
		this.factory = factory;
	}
	/**
	 * Determine whether this inference factory can infer
	 * facts with the given concept id.
	 * 
	 * @param inferredConceptId inferred concept
	 * @return true if the concept can be inferred, false otherwise.
	 */
	public abstract boolean canInfer(String inferredConceptId);
	
	public abstract InferredConcept getConceptById(String inferredConceptId);
	
	/**
	 * Expand a list of concept IDs with dependencies of corresponding
	 * inferred concepts. 
	 * <p>
	 * If a concept can be inferred by this inference factory, it is 
	 * replaced with its dependencies. Other concept IDs are not modified.
	 * </p>
	 * <p>
	 * This is mainly useful for generating a list of concept ids for
	 * a database query.
	 * </p>
	 * @param concepts
	 * @return iterator of concepts which replaces inferred concepts
	 *  with their corresponding dependencies.
	 */
	public Iterator<String> expandConceptDependencies(Iterator<String> concepts){
		return new DependencyExpandingIterator(this, concepts);
	}
}
+1 −0
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@ import java.util.Iterator;

public interface InferredConcept {

	// TODO need producing of multiple concepts?
	public String getConceptId();
	
	public Iterator<String> getDependencyIDs();
+73 −24
Original line number Diff line number Diff line
@@ -5,50 +5,85 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.nio.CharBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.script.Bindings;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

import de.sekmi.histream.ObservationFactory;
import de.sekmi.histream.inference.EncounterInferenceEngine;
import de.sekmi.histream.inference.EncounterInferenceFactory;
import de.sekmi.histream.inference.InferredConcept;
import jdk.nashorn.api.scripting.ScriptObjectMirror;

public class JSEncounterInferenceFactory implements EncounterInferenceFactory{
public class JSEncounterInferenceFactory extends EncounterInferenceFactory{
	private ScriptEngineManager sem;
	private ObservationFactory os;
	private List<Script> scripts;
	private Map<String, InferenceEntry> inferenceLookup;
	
	public JSEncounterInferenceFactory() {
		sem = new ScriptEngineManager();
		this.scripts = new ArrayList<>();
		this.inferenceLookup = new HashMap<>();
	}
	
	private static class Script{
		URL location;
		String charset;
		String script;
		String[] produces;
		String[] requires;
		public Script(URL location, String charset){
			this.location = location;
			this.charset = charset;
		public Script(String script){
			this.script = script;
		}
	}
	private static class InferenceEntry implements InferredConcept{
		private Script script;
		private int conceptIndex;
		public InferenceEntry(Script script, int conceptIndex){
			this.script = script;
			this.conceptIndex = conceptIndex;
		}
		@Override
		public Iterator<String> getDependencyIDs() {
			return Arrays.stream(script.produces).iterator();
		}
		@Override
		public String getConceptId() {
			return script.produces[conceptIndex];
		}
	}

	private String readReader(Reader reader) throws IOException{
		StringBuilder builder = new StringBuilder();
		CharBuffer buffer = CharBuffer.allocate(2048);
		while( reader.read(buffer) != -1 ){
			buffer.flip();
			builder.append(buffer.toString());
			buffer.clear();
		}
		return builder.toString();
	}

	public void addScript(URL scriptLocation, String charsetName) throws IOException, ScriptException{
		ScriptEngine meta = sem.getEngineByName("nashorn");
		// TODO use script context for control of error writer and bindings
		Bindings bindings = meta.createBindings();
		String source;
		try(
				InputStream in = scriptLocation.openStream();
				Reader reader = new InputStreamReader(in, charsetName)
				Reader reader = new InputStreamReader(in, charsetName);
		){
			meta.eval(reader, bindings);			
			source = readReader(reader);
			meta.eval(source, bindings);			
		}
		Script script = new Script(scriptLocation, charsetName);

		Script script = new Script(source);
		// retrieve metadata
		Object p = bindings.get("produces");
		if( p == null ){
@@ -73,24 +108,33 @@ public class JSEncounterInferenceFactory implements EncounterInferenceFactory{
		}else{
			throw new ScriptException("Unable to read this.requires of type "+p.getClass(), scriptLocation.toString(), -1);
		}
		scripts.add(script);
	}
	@Override
	public void setObservationFactory(ObservationFactory factory) {
		this.os = factory;
		addScript(script);		
	}
	
	@Override
	public boolean canInfer(String inferredConceptId) {
		// TODO use hashmap for more efficient lookups
		for( Script s : scripts ){
			for( String prod : s.produces ){
				if( prod.equals(inferredConceptId) ){
					return true;
	private void addScript(Script script){
		// verify that the produced concept ids are unique to this factory
		for( String id : script.produces ){
			if( inferenceLookup.containsKey(id) ){
				throw new IllegalStateException("Produced concept id '"+id+"' not unique to inference engine");
			}
		}
		for( int i=0; i<script.produces.length; i++ ){
			inferenceLookup.put(script.produces[i], new InferenceEntry(script,i));
		}
		scripts.add(script);
	}

	private Script getScript(String inferredConceptId){
		InferenceEntry e = inferenceLookup.get(inferredConceptId);
		if( e == null ){
			return null;
		}else{
			return e.script;
		}
	}
		return false;
	@Override
	public boolean canInfer(String inferredConceptId) {
		return inferenceLookup.containsKey(inferredConceptId);
	}

	@Override
@@ -99,4 +143,9 @@ public class JSEncounterInferenceFactory implements EncounterInferenceFactory{
		return null;
	}

	@Override
	public InferredConcept getConceptById(String inferredConceptId) {
		return inferenceLookup.get(inferredConceptId);
	}

}