de.faustedition.reasoning.InscriptionPrecedenceResource.java Source code

Java tutorial

Introduction

Here is the source code for de.faustedition.reasoning.InscriptionPrecedenceResource.java

Source

/*
 * Copyright (c) 2014 Faust Edition development team.
 *
 * This file is part of the Faust Edition.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package de.faustedition.reasoning;

import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Ordering;
import com.google.common.collect.Sets;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closeables;
import com.google.common.io.FileBackedOutputStream;
import de.faustedition.FaustURI;
import de.faustedition.document.Document;
import de.faustedition.document.MaterialUnit;
import de.faustedition.genesis.dating.GeneticSource;
import de.faustedition.genesis.lines.GraphVerseInterval;
import de.faustedition.genesis.lines.VerseInterval;
import de.faustedition.genesis.lines.VerseManager;
import de.faustedition.graph.FaustGraph;
import de.faustedition.reasoning.PremiseBasedRelation.Premise;
import de.faustedition.transcript.TranscriptManager;
import edu.bath.transitivityutils.ImmutableRelation;
import edu.bath.transitivityutils.Relation;
import edu.bath.transitivityutils.Relations;
import eu.interedition.text.neo4j.Neo4jTextRepository;
import org.hibernate.SessionFactory;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.restlet.data.MediaType;
import org.restlet.representation.OutputRepresentation;
import org.restlet.representation.Representation;
import org.restlet.representation.StringRepresentation;
import org.restlet.resource.Get;
import org.restlet.resource.ResourceException;
import org.restlet.resource.ServerResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

import javax.annotation.Nullable;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.*;

/**
 * @author <a href="http://gregor.middell.net/" title="Homepage">Gregor Middell</a>
 */
@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class InscriptionPrecedenceResource extends ServerResource {

    @Autowired
    private GraphDatabaseService graphDb;

    @Autowired
    private SessionFactory sessionFactory;

    @Autowired
    private Environment environment;

    @Autowired
    private FaustGraph faustGraph;

    @Autowired
    private TranscriptManager transcriptManager;

    @Autowired
    private VerseManager verseManager;

    @Autowired
    private Neo4jTextRepository textRepo;

    @Autowired
    private org.slf4j.Logger logger;

    final private Map<Inscription, Node> nodeMap = new HashMap<Inscription, Node>();

    @Override
    protected void doInit() throws ResourceException {
        super.doInit();

        final VerseInterval verseInterval = VerseManager.fromRequestAttibutes(getRequestAttributes());

        final Multimap<String, GraphVerseInterval> intervalIndex = Multimaps
                .index(verseManager.forInterval(verseInterval), new Function<GraphVerseInterval, String>() {
                    @Override
                    public String apply(@Nullable GraphVerseInterval input) {

                        //final String sigil = transcriptManager.materialUnitForTranscript(input.getTranscript(textRepo)).toString();
                        final String sigil = ((Document) (transcriptManager
                                .materialUnitForTranscript(input.getTranscript(textRepo)))).getSource().toString();
                        return sigil;
                    }

                });

        inscriptions = Sets.newHashSet();
        for (String sigil : Ordering.natural().immutableSortedCopy(intervalIndex.keySet())) {
            final Inscription inscription = new Inscription(sigil);
            for (VerseInterval interval : intervalIndex.get(sigil)) {
                inscription.addInterval(interval.getStart(), interval.getEnd());
            }
            Preconditions.checkState(!inscription.isEmpty());
            inscriptions.add(inscription);
            //long materialUnitId = intervalIndex.get(sigil).iterator().next().getTranscript(textRepo).getMaterialUnitId();
            //Node node = graphDb.getNodeById(materialUnitId);
            Node node = transcriptManager.materialUnitForTranscript(
                    intervalIndex.get(sigil).iterator().next().getTranscript(textRepo)).node;
            nodeMap.put(inscription, node);

        }
        for (Inscription subject : inscriptions) {
            for (Inscription object : inscriptions) {
                if (InscriptionRelations.syntagmaticallyPrecedesByFirstLine(subject, object)) {
                    //            if (InscriptionRelations.syntagmaticallyPrecedesByAverage(subject, object)) {

                    syntagmaticPrecedence.relate(subject, object);
                }
                if (InscriptionRelations.exclusivelyContains(subject, object)) {
                    exclusiveContainment.relate(subject, object);
                }
                if (InscriptionRelations.paradigmaticallyContains(subject, object)) {
                    paradigmaticContainment.relate(subject, object);
                }

            }
        }
        try {
            explicitPrecedence = new GraphBasedRelation<Inscription>(nodeMap,
                    new FaustURI(new URI("faust://secondary/gruss2011")));
        } catch (URISyntaxException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        List<Premise<Inscription>> premises = new ArrayList<Premise<Inscription>>();
        //      premises.addAll(premisesFromGeneticSources());
        premises.addAll(premisesFromInference());

        precedence = new PremiseBasedRelation<Inscription>(premises);
        //      precedence = new LastPremiseRelation<Inscription> (premises);

        Relation<Inscription> test = Util
                .wrapTransitive(new PremiseBasedRelation<Inscription>(premisesFromInference()), inscriptions);

        Relation<Inscription> check = Util
                .wrapTransitive(new PremiseBasedRelation<Inscription>(premisesFromGeneticSources()), inscriptions);

        logger.info("Genetic graph statistics: ");
        logger.info("  Coverage: " + Statistics.completeness(precedence, check, inscriptions) * 100 + ", Recall: "
                + Statistics.recall(precedence, check, inscriptions) * 100 + ", Accuracy : "
                + Statistics.correctness(precedence, check, inscriptions) * 100

        );

    }

    @Get("txt")
    public Representation dot() {
        return new StringRepresentation(asDot());
    }

    @Get("svg|html")
    public Representation svg() throws IOException, ExecutionException, InterruptedException {
        final ExecutorService executorService = Executors.newCachedThreadPool();
        final Process tred = new ProcessBuilder(environment.getRequiredProperty("graphviz.tred.path")).start();
        final Process dot = new ProcessBuilder(environment.getRequiredProperty("graphviz.dot.path"), "-Tsvg")
                .start();

        executorService.submit(new Callable<Void>() {
            @Override
            public Void call() throws Exception {
                InputStream dataStream = null;
                OutputStream tredStream = null;
                try {
                    ByteStreams.copy(
                            dataStream = new ByteArrayInputStream(asDot().getBytes(Charset.forName("UTF-8"))),
                            tredStream = tred.getOutputStream());
                } finally {
                    Closeables.close(dataStream, false);
                    Closeables.close(tredStream, false);
                }
                return null;
            }
        });

        executorService.submit(new Callable<Void>() {
            @Override
            public Void call() throws Exception {
                InputStream tredStream = null;
                OutputStream dotStream = null;
                try {
                    ByteStreams.copy(tredStream = tred.getInputStream(), dotStream = dot.getOutputStream());
                } finally {
                    Closeables.close(tredStream, false);
                    Closeables.close(dotStream, false);
                }
                return null;
            }
        });

        final Future<FileBackedOutputStream> dotFuture = executorService
                .submit(new Callable<FileBackedOutputStream>() {
                    @Override
                    public FileBackedOutputStream call() throws Exception {
                        final FileBackedOutputStream buf = new FileBackedOutputStream(102400);
                        InputStream dotStream = null;
                        try {
                            ByteStreams.copy(dotStream = dot.getInputStream(), buf);
                        } finally {
                            Closeables.close(dotStream, false);
                            Closeables.close(buf, false);
                        }
                        return buf;
                    }
                });

        Preconditions.checkState(tred.waitFor() == 0);
        Preconditions.checkState(dot.waitFor() == 0);

        final FileBackedOutputStream resultBuf = dotFuture.get();

        return new OutputRepresentation(MediaType.IMAGE_SVG) {
            @Override
            public void write(OutputStream outputStream) throws IOException {
                ByteStreams.copy(resultBuf.getSupplier(), outputStream);
                resultBuf.reset();
            }
        };
    }

    private String asDot() {
        final StringBuilder dot = new StringBuilder("digraph genetic_graph {\n");

        for (Inscription inscription : inscriptions) {
            dot.append(toLabel(inscription));

            dot.append(" [label=\"");
            dot.append(toLabel(inscription));
            dot.append("\\n");
            dot.append(inscription.first());
            dot.append("...");
            dot.append(inscription.last());
            dot.append("\"]");

            dot.append(";\n");

        }

        dot.append(" edge [");
        dot.append(" color=").append("black");
        dot.append(" fontcolor=").append("black");
        dot.append(" weight=").append("1");
        dot.append(" ];\n");

        for (Inscription i : inscriptions) {
            for (Inscription j : inscriptions) {
                if (precedence.areRelated(i, j)) {
                    final String premise = precedence.findRelevantPremise(i, j).getName();

                    dot.append(toLabel(i));
                    dot.append(" -> ");
                    dot.append(toLabel(j));
                    dot.append(" [ ");
                    dot.append(" label=").append(premise);
                    dot.append(" color=").append("r_syn".equals(premise) ? "grey" : "black");
                    dot.append(" weight=").append("r_syn".equals(premise) ? "1" : "1");
                    dot.append(" ];\n");
                }
            }
        }

        dot.append("}\n");
        return dot.toString();
    }

    private String toLabel(Inscription inscription) {
        return inscription.getName().replaceAll("[ ,:\\(\\)\\-\\.\\{\\}/]", "_");
    }

    private Set<Inscription> inscriptions;
    private Relation<Inscription> syntagmaticPrecedence = Relations.newTransitiveRelation();
    private Relation<Inscription> exclusiveContainment = Relations.newTransitiveRelation();//MultimapBasedRelation.create();
    private Relation<Inscription> paradigmaticContainment = Relations.newTransitiveRelation(); //MultimapBasedRelation.create();
    private ImmutableRelation<Inscription> explicitPrecedence;

    private List<Premise<Inscription>> premisesFromGeneticSources() {
        List<Premise<Inscription>> result = new ArrayList<Premise<Inscription>>();
        for (final GeneticSource geneticSource : faustGraph.getGeneticSources()) {
            final GraphBasedRelation<Inscription> gbr = new GraphBasedRelation<Inscription>(nodeMap,
                    geneticSource.getUri());
            Premise<Inscription> premise = new Premise<Inscription>() {
                @Override
                public String getName() {
                    String name;
                    try {
                        name = geneticSource.getUri().toString().substring("faust://secondary/".length());
                    } catch (Exception e) {
                        name = geneticSource.getUri().toString();
                    }
                    return name.replaceAll("[:/]", "_");
                }

                public boolean applies(Inscription i, Inscription j) {

                    return gbr.areRelated(i, j);
                }
            };
            result.add(premise);
        }
        return result;
    }

    private List<Premise<Inscription>> premisesFromInference() {
        ArrayList<Premise<Inscription>> result = new ArrayList<Premise<Inscription>>();

        Premise<Inscription> rEcon = new Premise<Inscription>() {
            @Override
            public String getName() {
                return "r_econ";
            }

            @Override
            public boolean applies(Inscription i, Inscription j) {
                return exclusiveContainment.areRelated(i, j);
            }
        };

        Premise<Inscription> rPcon = new PremiseBasedRelation.Premise<Inscription>() {
            @Override
            public String getName() {
                return "r_pcon";
            }

            @Override
            public boolean applies(Inscription i, Inscription j) {
                return paradigmaticContainment.areRelated(j, i);
            }
        };

        Premise<Inscription> rSyn = new PremiseBasedRelation.Premise<Inscription>() {
            @Override
            public String getName() {
                return "r_syn";
            }

            @Override
            public boolean applies(Inscription i, Inscription j) {
                return syntagmaticPrecedence.areRelated(i, j);
            }
        };

        result.add(rPcon);
        result.add(rEcon);
        result.add(rSyn);

        return result;
    }

    public PremiseBasedRelation<Inscription> precedence;

    /**
     * @param relA
     * @param relB is supposed to be the overriding or "stronger" relation
     * @return A relation result where result(i, j) if relA(i,j) && relB(j,i)
     */
    public Relation<Inscription> contradictions(ImmutableRelation<Inscription> relA,
            ImmutableRelation<Inscription> relB) {
        Relation<Inscription> result = MultimapBasedRelation.create();
        for (Inscription i : inscriptions)
            for (Inscription j : inscriptions) {
                if (relA.areRelated(i, j) && relB.areRelated(j, i)) {
                    result.relate(i, j);
                }
            }
        return result;
    }

}