/*
* eXist Open Source Native XML Database
* Copyright (C) 2008-2009 The eXist Project
* http://exist-db.org
*
* 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
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package org.exist.xslt;
import static org.junit.Assert.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.exist.storage.BrokerPool;
import org.exist.storage.DBBroker;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.xmldb.api.DatabaseManager;
import org.xmldb.api.base.Collection;
import org.xmldb.api.base.Database;
import org.xmldb.api.base.ResourceSet;
import org.xmldb.api.base.XMLDBException;
import org.xmldb.api.modules.CollectionManagementService;
import org.xmldb.api.modules.XPathQueryService;
/**
* @author <a href="mailto:shabanovd@gmail.com">Dmitriy Shabanov</a>
*
*/
public class XSLTestCase {
private final static String URI = "xmldb:exist://" + DBBroker.ROOT_COLLECTION;
private final static String DRIVER = "org.exist.xmldb.DatabaseImpl";
private final static String XSLT_COLLECTION = "xslt_tests";
static File existDir = new File(".");
private Collection col = null;
/**
* @throws java.lang.Exception
*/
@BeforeClass
public static void setUpBeforeClass() throws Exception {
}
/**
* @throws java.lang.Exception
*/
@AfterClass
public static void tearDownAfterClass() throws Exception {
}
/**
* @throws java.lang.Exception
*/
@Before
public void setUp() throws Exception {
try {
Class<?> cl = Class.forName(DRIVER);
Database database = (Database) cl.newInstance();
database.setProperty("create-database", "true");
DatabaseManager.registerDatabase(database);
col = DatabaseManager.getCollection(URI + "/" + XSLT_COLLECTION);
if (col == null) {
Collection root = DatabaseManager.getCollection(URI);
CollectionManagementService mgtService =
(CollectionManagementService) root.getService(
"CollectionManagementService",
"1.0");
col = mgtService.createCollection(XSLT_COLLECTION);
System.out.println("collection created.");
}
BrokerPool.getInstance().getConfiguration().setProperty(
TransformerFactoryAllocator.PROPERTY_TRANSFORMER_CLASS,
"org.exist.xslt.TransformerFactoryImpl");
loadBench("test/src/org/exist/xslt/test/bench/v1_0", bench);
} catch (Exception e) {
e.printStackTrace();
}
}
private Map<String, Map<String, String>> bench = new TreeMap<String, Map<String, String>>();
private void loadBench(String benchLocation, Map<String, Map<String, String>> bench) throws Exception {
File testConf = new File(benchLocation+"/default.conf");
if (testConf.canRead()) {
// Open the file and then get a channel from the stream
FileInputStream fis = new FileInputStream(testConf);
FileChannel fc = fis.getChannel();
// Get the file's size and then map it into memory
int sz = (int)fc.size();
MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, sz);
// Charset and decoder for ISO-8859-15
Charset charset = Charset.forName("ISO-8859-15");
CharsetDecoder decoder = charset.newDecoder();
// Decode the file into a char buffer
CharBuffer cb = decoder.decode(bb);
// Perform the search
loadBench(testConf, cb, bench);
// Close the channel and the stream
fc.close();
}
}
private void loadBench(File testConf, CharBuffer cb, Map<String, Map<String, String>> bench) {
// Pattern used to parse lines
Pattern linePattern = Pattern.compile(".*\r?\n");
String testName = null;
Map<String, String> testInfo = null;
int position;
Matcher lm = linePattern.matcher(cb); // Line matcher
int lines = 0;
while (lm.find()) {
lines++;
CharSequence cs = lm.group(); // The current line
String str = cs.toString();
if (cs.charAt(0) == (char)0x005B) {
position = str.indexOf("]");
testName = str.substring(1, position);
if (bench.containsKey(testName)) {
testInfo = bench.get(testName);
} else {
testInfo = new HashMap<String, String>();
bench.put(testName, testInfo);
}
} else if (testName != null){
position = str.indexOf("=");
if (position != -1) {
String key = str.substring(0, position).trim();
String value = str.substring(position+1).trim();
testInfo.put(key, value);
}
}
if (lm.end() == cb.limit())
break;
}
}
/**
* @throws java.lang.Exception
*/
@After
public void tearDown() throws Exception {
}
@Test
public void testSimpleTransform() {
try {
XPathQueryService service = (XPathQueryService) col.getService("XPathQueryService", "1.0");
String query =
"xquery version \"1.0\";\n" +
"declare namespace transform=\"http://exist-db.org/xquery/transform\";\n" +
"declare variable $xml {\n" +
" <node xmlns=\"http://www.w3.org/1999/xhtml\">text</node>\n" +
"};\n" +
"declare variable $xslt {\n" +
" <xsl:stylesheet xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"2.0\">\n" +
" <xsl:template match=\"node\">\n" +
" <div><xsl:value-of select=\".\"/></div>\n" +
" </xsl:template>\n" +
" </xsl:stylesheet>\n" +
"};\n" +
"<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" +
" <body>\n" +
" {transform:transform($xml, $xslt, ())}\n" +
" </body>\n" +
"</html>";
ResourceSet result = service.query(query);
//check there is one result
assertEquals(1, result.getSize());
String content = (String) result.getResource(0).getContent();
// System.out.println(content);
//check the namespace
assertTrue(content.startsWith("<html xmlns=\"http://www.w3.org/1999/xhtml\">"));
//check the content
assertTrue(content.indexOf("<div>text</div>") > -1);
} catch (XMLDBException e) {
// fail(e.getMessage());
throw new RuntimeException(e);
}
}
@Test
public void testComplexTransform() throws Exception {
try {
XPathQueryService service = (XPathQueryService) col.getService("XPathQueryService", "1.0");
String query =
"xquery version \"1.0\";\n" +
"declare namespace transform=\"http://exist-db.org/xquery/transform\";\n" +
"declare variable $xml {\n" +
"<salesdata>\n" +
" <year>\n" +
" <year>1997</year>\n" +
" <region>\n" +
" <name>west</name>\n" +
" <sales unit=\"millions\">32</sales>\n" +
" </region>\n" +
" <region>\n" +
" <name>central</name>\n" +
" <sales unit=\"millions\">11</sales>\n" +
" </region>\n" +
" <region>\n" +
" <name>east</name>\n" +
" <sales unit=\"millions\">19</sales>\n" +
" </region>\n" +
" </year>\n" +
" <year>\n" +
" <year>1998</year>\n" +
" <region>\n" +
" <name>west</name>\n" +
" <sales unit=\"millions\">35</sales>\n" +
" </region>\n" +
" <region>\n" +
" <name>central</name>\n" +
" <sales unit=\"millions\">12</sales>\n" +
" </region>\n" +
" <region>\n" +
" <name>east</name>\n" +
" <sales unit=\"millions\">25</sales>\n" +
" </region>\n" +
" </year>\n" +
" <year>\n" +
" <year>1999</year>\n" +
" <region>\n" +
" <name>west</name>\n" +
" <sales unit=\"millions\">36</sales>\n" +
" </region>\n" +
" <region>\n" +
" <name>central</name>\n" +
" <sales unit=\"millions\">12</sales>\n" +
" </region>\n" +
" <region>\n" +
" <name>east</name>\n" +
" <sales unit=\"millions\">31</sales>\n" +
" </region>\n" +
" </year>\n" +
" <year>\n" +
" <year>2000</year>\n" +
" <region>\n" +
" <name>west</name>\n" +
" <sales unit=\"millions\">37</sales>\n" +
" </region>\n" +
" <region>\n" +
" <name>central</name>\n" +
" <sales unit=\"millions\">11</sales>\n" +
" </region>\n" +
" <region>\n" +
" <name>east</name>\n" +
" <sales unit=\"millions\">40</sales>\n" +
" </region>\n" +
" </year>\n" +
"</salesdata>\n" +
"};\n" +
"declare variable $xslt {\n" +
"<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n" +
"<xsl:output method=\"html\" encoding=\"utf-8\"/>\n" +
"<xsl:template match=\"/\">\n" +
" <html>\n" +
" <table border=\"1\">\n" +
" <tr>\n" +
" <td colspan=\"2\">Total Sales</td>\n" +
" </tr>\n" +
" <xsl:for-each select=\"salesdata/year\">\n" +
" <tr>\n" +
" <td>\n" +
" <xsl:value-of select=\"year\"/>\n" +
" </td>\n" +
" <td align=\"right\">\n" +
" <xsl:value-of select=\"sum(region/sales)\"/>\n" +
" </td>\n" +
" </tr>\n" +
" </xsl:for-each>\n" +
" <tr>\n" +
" <td>Grand Total</td>\n" +
" <td align=\"right\">\n" +
" <xsl:value-of select=\"sum(salesdata/year/region/sales)\"/>\n" +
" </td>\n" +
" </tr>\n" +
" </table>\n" +
" </html>\n" +
"</xsl:template>\n" +
"</xsl:stylesheet>\n" +
"};\n" +
"transform:transform($xml, $xslt, ())";
ResourceSet result = service.query(query);
//check there is one result
assertEquals(1, result.getSize());
String content = (String) result.getResource(0).getContent();
// System.out.println(content);
//check the content
assertTrue(checkResult("total.ref", content));
} catch (XMLDBException e) {
// fail(e.getMessage());
throw new RuntimeException(e);
}
}
private boolean checkResult(String file, String result) throws Exception {
int tokenCount = 0;
String ref = loadFile(file);
ref = ref.replaceAll("\\n", " ");
ref = ref.replaceAll("<dgnorm_document>", "");
ref = ref.replaceAll("</dgnorm_document>", "");
String delim = " \t\n\r\f<>";
StringTokenizer refTokenizer = new StringTokenizer(ref, delim);
StringTokenizer resTokenizer = new StringTokenizer(result, delim);
while (refTokenizer.hasMoreTokens()) {
tokenCount++;
String refToken = refTokenizer.nextToken();
if (!resTokenizer.hasMoreTokens()) {
System.out.println(ref);
throw new Exception("result should have: "+refToken+", but get EOF (at "+tokenCount+")");
}
String resToken = resTokenizer.nextToken();
if (!refToken.equals(resToken)) {
System.out.println(ref);
throw new Exception("result should have: "+refToken+", but get "+resToken+" (at "+tokenCount+")");
}
}
if (resTokenizer.hasMoreTokens()) {
String resToken = resTokenizer.nextToken();
System.out.println(ref);
throw new Exception("result should have nothing, but get "+resToken+" (at "+tokenCount+")");
}
return true;
}
@Test
public void testBench() throws Exception {
long start_time;
long end_time;
String query = null;
String content = null;
int passed = 0;
boolean passing;
Map<String, String> testInfo;
String reqTest = null;//"avts";
for (String testName : bench.keySet()) {
if ((reqTest != null) && (!testName.equals(reqTest)))
continue;
passing = true;
query = null;
content = null;
testInfo = bench.get(testName);
System.out.print(testName+": ");
if (testInfo.containsKey("storeBeforeTest")) {
System.out.print("skipping");
if (testInfo.containsKey("comment"))
System.out.print(" ("+testInfo.get("comment")+")");
System.out.println();
continue;
}
String input = loadFile(testInfo.get("input"));
String stylesheet = loadFile(testInfo.get("stylesheet"));
try {
start_time = System.currentTimeMillis();
XPathQueryService service = (XPathQueryService) col.getService("XPathQueryService", "1.0");
query = "xquery version \"1.0\";\n" +
"declare namespace transform=\"http://exist-db.org/xquery/transform\";\n" +
"declare variable $xml {"+input+"};\n" +
"declare variable $xslt {"+stylesheet+"};\n" +
"transform:transform($xml, $xslt, ())\n";
ResourceSet result = service.query(query);
end_time = System.currentTimeMillis();
//check there is one result
// assertEquals(1, result.getSize());
content = "";
for (int i = 0; i < result.getSize(); i++)
content = content + (String) result.getResource(i).getContent();
//check the content
assertTrue(checkResult(testInfo.get("reference"), content));
} catch (Exception e) {
// System.out.println("************************************** query ******************************");
// System.out.println(query);
System.out.println();
System.out.println("************************************* content ******************************");
System.out.println(content);
passing = false;
throw new RuntimeException(e);
}
if (passing) {
end_time = end_time - start_time;
System.out.println("pass ("+end_time+" ms)");
passed++;
} else
System.out.println("faild");
}
System.out.println(" "+passed+" of "+bench.keySet().size());
}
//TODO: test <!-- reassembles an xml tree in reverse order -->
private String loadFile(String string) throws IOException {
String result = null;
File file = new File("test/src/org/exist/xslt/test/bench/v1_0/"+string);
if (!file.canRead()) {
throw new IOException("can load information.");
} else {
// Open the file and then get a channel from the stream
FileInputStream fis = new FileInputStream(file);
FileChannel fc = fis.getChannel();
// Get the file's size and then map it into memory
int sz = (int)fc.size();
MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, sz);
// Charset and decoder for ISO-8859-15
Charset charset = Charset.forName("ISO-8859-15");
CharsetDecoder decoder = charset.newDecoder();
// Decode the file into a char buffer
CharBuffer cb = decoder.decode(bb);
result = cb.toString();
//TODO: rewrite to handle <?xml*?>
if (result.startsWith("<?xml version=\"1.0\"?>"))
result = result.substring("<?xml version=\"1.0\"?>".length());
if (result.startsWith("<?xml version=\"1.0\" encoding=\"utf-8\"?>"))
result = result.substring("<?xml version=\"1.0\" encoding=\"utf-8\"?>".length());
//XXX: rethink: prexslt query processing
// result = result.replaceAll("{", "{{");
// result = result.replaceAll("}", "}}");
// Close the channel and the stream
fc.close();
}
return result;
}
}