/*
* Copyright (C) INRIA, 2012-2013
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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 fr.inrialpes.tyrexmo.testqc;
import java.io.IOException;
import java.io.StringReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.io.File;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Date;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.Executors;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.ExecutorService;
import java.lang.Boolean;
import java.lang.InstantiationException;
import java.lang.IllegalAccessException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import org.apache.jena.query.Query;
import org.apache.jena.query.QueryFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.PosixParser;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.HelpFormatter;
// Yes we are relying on Jena for parsing RDF
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.ModelFactory;
import org.apache.jena.rdf.model.Property;
import org.apache.jena.rdf.model.Resource;
import org.apache.jena.rdf.model.Literal;
import org.apache.jena.rdf.model.Statement;
import org.apache.jena.rdf.model.StmtIterator;
import org.apache.jena.rdf.model.RDFNode;
import org.apache.jena.rdf.model.RDFList;
import org.apache.jena.rdf.model.Container;
import org.apache.jena.rdf.model.NodeIterator;
import org.apache.jena.rdf.model.impl.RDFDefaultErrorHandler;
import org.apache.jena.vocabulary.RDF;
import org.apache.jena.vocabulary.RDFS;
import org.apache.jena.vocabulary.DC;
public class SuiteTool {
final static Logger logger = LoggerFactory.getLogger( SuiteTool.class );
protected Options options = null;
protected String testSuiteFile = null;
protected String outputType = "html";
protected String outputFile = null;
protected PrintStream stream = null;
public SuiteTool() {
options = new Options();
options.addOption( "h", "help", false, "Print this page" );
options.addOption( OptionBuilder.withLongOpt( "output" ).hasArg().withDescription( "Result FILE" ).withArgName("FILE").create( 'o' ) );
options.addOption( OptionBuilder.withLongOpt( "format" ).hasArg().withDescription( "Output format [NIY]" ).withArgName("TYPE (asc|plot)").create( 'f' ) );
// xml|csv|html
}
/**
* Usage TestContain SolverClass Q1 Q2
* Usage TestContain SolverClass -s Schema Q1 Q2
* Usage TestContain SolverClass -d testDirectory -t outputType (csv|html|xml)
* Corrently implemented:
* TestContain SolverClass Q1 Q2
*/
public static void main( String[] args ) throws Exception {
new SuiteTool().run( args );
}
public void run ( String [] args ) throws Exception, IOException {
// Read parameters
String[] argList = null;
try {
CommandLineParser parser = new PosixParser();
CommandLine line = parser.parse( options, args );
if ( line.hasOption( 'h' ) ) { usage(); System.exit( 0 ); }
if ( line.hasOption( 'f' ) ) outputType = line.getOptionValue( 'f' );
if ( line.hasOption( 'o' ) ) outputFile = line.getOptionValue( 'o' );
argList = line.getArgs();
if ( argList.length < 1 ) {
logger.error( "Usage: TestContain SolverClass Q1 Q2" );
usage();
System.exit( -1 );
}
} catch( ParseException exp ) {
logger.error( exp.getMessage() );
usage();
System.exit(-1);
}
testSuiteFile = argList[0];
// Set output file
if ( outputFile == null ) {
stream = System.out;
} else {
stream = new PrintStream( new FileOutputStream( outputFile ) );
}
render( testSuiteFile );
}
public void usage() {
Package pkg = this.getClass().getPackage();
new HelpFormatter().printHelp( 80, pkg+" [options] testSuiteFile\nRenders a test suite", "\nOptions:", options, "" );
}
File directory = null;
/**
* Display the test suite in HTML
*/
public void render( String suiteFile ) throws Exception {
directory = new File( suiteFile ).getParentFile();
//System.err.println( "DIR: "+sf );
Model suite = ModelFactory.createDefaultModel();
try {
suite.read( new FileInputStream( suiteFile ), null );
} catch (Exception ex) {
throw new Exception( "Cannot parse suite file", ex );
}
printHTMLHeader();
printHTMLContent( suite );
printHTMLFooter();
suite.close(); // JE: I am not sure that I will not have trouble with initSyntax
}
public void printHTMLHeader() {
stream.println("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">");
stream.println("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">");
stream.println("<head>");
stream.println("<title>SPARQL Containment Benchmark</title>");
stream.println("<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\" />");
stream.println("<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" media=\"screen\" />");
stream.println();
stream.println(" <link href=\"bootstrap/css/bootstrap.css\" rel=\"stylesheet\">");
stream.println(" <link href=\"bootstrap/css/bootstrap-responsive.css\" rel=\"stylesheet\">");
stream.println(" <link href=\"bootstrap/css/prettify.css\" rel=\"stylesheet\">");
stream.println(" <link href=\"bootstrap/css/docs.css\" rel=\"stylesheet\">");
stream.println();
stream.println("</head>");
stream.println("<body>");
stream.println("<div id=\"centerColumn\">");
stream.println(" <div id=\"header\">");
stream.println(" <h1>SPARQL Query Containment Benchmark</h1>");
stream.println(" <h2><a href=\"http://exmo.inrialpes.fr/\">Exmo</a> & <a href=\"http://tyrex.inria.fr/\">Tyrex</a>, <a href=\"http://www.inria.fr\">INRIA</a></h2>");
stream.println(" </div>");
stream.println(" <br />");
stream.println();
stream.println(" <hr />");
stream.println(" <div id=\"nav\">");
stream.println(" <ul>");
stream.println(" <li><a href=\"index.html\"><b>Home</b></a></li>");
stream.println(" <li><a href=\"benchmark.html\"><b>Benchmark</b></a></li>");
stream.println(" <li><a href=\"download.html\"><b>Download</b></a></li>");
stream.println(" <li><a href=\"about.html\"><b>About</b></a></li>");
stream.println(" </ul>");
stream.println(" </div>");
stream.println(" <hr/>");
stream.println();
}
public void printHTMLContent( Model suite ) throws Exception {
Resource TESTSUITE = suite.createResource( "http://sparql-qc-bench.inrialpes.fr/testsuite#TestSuite" );
Resource DIR = suite.createProperty( "http://sparql-qc-bench.inrialpes.fr/testsuite#sourceDir" );
Resource HASTEST = suite.createProperty( "http://sparql-qc-bench.inrialpes.fr/testsuite#hasTest" );
Resource CTNTTEST = suite.createResource( "http://sparql-qc-bench.inrialpes.fr/testsuite#ContainmentTest" );
Resource WARMTEST = suite.createResource( "http://sparql-qc-bench.inrialpes.fr/testsuite#WarmupContainmentTest" );
Resource SRQ = suite.createProperty( "http://sparql-qc-bench.inrialpes.fr/testsuite#sourceQuery" );
Resource TAR = suite.createProperty( "http://sparql-qc-bench.inrialpes.fr/testsuite#targetQuery" );
Resource SCH = suite.createProperty( "http://sparql-qc-bench.inrialpes.fr/testsuite#rdfSchema" );
Resource RES = suite.createProperty( "http://sparql-qc-bench.inrialpes.fr/testsuite#result" );
// Suite node
StmtIterator stmtIt = suite.listStatements(null, RDF.type, TESTSUITE);
// Take the first one if it exists
if ( !stmtIt.hasNext() ) throw new Exception("Bad test suite specification");
Resource node = stmtIt.nextStatement().getSubject();
//System.err.println( " I got the suite "+node );
String dir = directory.toString()+File.separator;
if ( node.hasProperty( (Property)DIR ) ) {
RDFNode dd = node.getProperty( (Property)DIR ).getObject();
if ( !dd.isLiteral() ) throw new Exception( "Source directory must be a directory" );
dir += ((Literal)dd).getString()+File.separator;
//System.err.println( " SRCDR="+directory );
}
String name = node.getLocalName();
String label = node.getProperty( RDFS.label ).getString();
String comment = node.getProperty( RDFS.comment ).getString();
stream.println(" <h2>"+name+" : "+label+"</h2>");
stream.println(" <p>"+comment+"</p>");
stream.println(" <div class=\"accordion\" id=\"accordion2\">" );
if ( node.hasProperty( (Property)HASTEST ) ) {
Object o = node.getProperty( (Property)HASTEST ).getObject();
//System.err.println( " Got HASTEST statement" );
if ( o instanceof Resource) {
Resource coll = (Resource)o;
if ( coll != null ) {
while ( !RDF.nil.getURI().equals( coll.getURI() ) ) {
// Do something with
Resource rr = coll.getProperty( RDF.first ).getResource();
//System.err.println( rr );
if ( !WARMTEST.equals( rr.getProperty(RDF.type).getResource() ) )
try { //sourceQuery - targetQuery - result
String tname = rr.getURI().toString();
tname = tname.substring( tname.lastIndexOf( '#' )+1 );
Statement st = rr.getProperty((Property)SRQ);
if ( st == null ) throw new Exception("Test must contain a source query");
String q1 = st.getString();
final String src = dir+q1;
st = rr.getProperty((Property)TAR);
if ( st == null ) throw new Exception("Test must contain a target query");
final String q2 = st.getString();
final String tgt = dir+q2;
st = rr.getProperty((Property)SCH);
String sch = null;
String schid = null;
if ( st != null ) {
schid = st.getString();
sch = dir+schid+".rdfs";
}
st = rr.getProperty((Property)RES);
if ( st == null ) throw new Exception("Test must contain an expected result");
final String exp = st.getString();
stream.println(" <div class=\"accordion-group\">");
stream.println(" <div class=\"accordion-heading\">");
stream.println(" <a class=\"accordion-toggle\" data-toggle=\"collapse\" data-parent=\"#accordion2\" href=\"#"+tname+"\">");
stream.print(" "+tname+" : ");
if ( sch != null ) {
// Problem with the stylesheet
//stream.print("<a href=\""+sch+"\">"+schid+"</a> ⊧ ");
stream.print(schid+" ⊧ ");
} else {
stream.print(" ");
}
stream.println( q1+" ⊑ "+q2+" : "+exp+"</a><br />");
stream.println(" </div>");
stream.println(" <div id=\""+tname+"\" class=\"accordion-body in collapse\" style=\"height: auto;\">");
stream.println(" <div class=\"accordion-inner\">");
if ( sch != null ) {
stream.print("<center><a href=\""+sch+"\">"+schid+"</a> ⊧</center>");
}
stream.println(" <table>");
stream.println(" <thead>");
stream.println(" <tr>");
stream.println(" <th style=\"width: 400px;\">"+q1+"</th>");
stream.println(" <th></th>");
stream.println(" <th style=\"width: 400px;\">"+q2+"</th>");
stream.println(" </tr>");
stream.println(" </thead>");
stream.println(" <tbody>");
stream.println(" <tr>");
stream.println(" <td>");
printHTMLQuery( src );
stream.println(" </td>");
stream.println(" <td></td>");
stream.println(" <td>");
printHTMLQuery( tgt );
stream.println(" </td>");
stream.println(" </tr>");
stream.println(" </tbody>");
stream.println(" </table>");
stream.println(" </div>");
stream.println(" </div>");
stream.println(" </div>");
} catch ( Exception ae ) {
logger.debug( "IGNORED Exception", ae );
};
coll = coll.getProperty( RDF.rest ).getResource();
}
stream.println(" </div>");
}
}
}
}
public void printHTMLQuery( String filename) {
stream.println(" <pre>");
try {
FileInputStream in = new FileInputStream( filename );
BufferedReader reader = new BufferedReader( new InputStreamReader(in) );
String line = null;
while ((line = reader.readLine()) != null) {
stream.println(line);
}
} catch (IOException ex) {
stream.println( "Cannot open query "+filename );
logger.warn( "Cannot open file", ex );
}
stream.println(" </pre>");
}
public void printHTMLFooter() {
stream.println(" <!-- footer -->");
stream.println(" <div id=\"footer\" style=\"font-size: +2\"></div>");
stream.println();
stream.println(" Generated on "+(new Date()).toString()+" with SuiteTool." );
stream.println(" <script src=\"bootstrap/js/jquery.js\" type=\"text/javascript\"></script>");
stream.println(" <script src=\"bootstrap/js/bootstrap-collapse.js\" type=\"text/javascript\"></script>");
stream.println(" <script src=\"bootstrap/js/bootstrap.min.js\" type=\"text/javascript\"></script>");
stream.println(" <script type=\"text/javascript\">");
stream.println(" $(document).ready(function() {");
stream.println(" // popover demo");
stream.println(" $(\"a[rel=popover]\")");
stream.println(" .popover()");
stream.println(" .click(function(e) {");
stream.println(" e.preventDefault()");
stream.println(" });");
stream.println(" });");
stream.println(" </script>");
stream.println();
stream.println("</body></html>");
}
}