/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.lucene.queryparser.xml; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.*; import javax.xml.transform.dom.DOMResult; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import java.io.StringWriter; import java.io.IOException; import java.io.InputStream; import java.util.Enumeration; import java.util.HashMap; import java.util.Properties; /** * Provides utilities for turning query form input (such as from a web page or Swing gui) into * Lucene XML queries by using XSL templates. This approach offers a convenient way of externalizing * and changing how user input is turned into Lucene queries. * Database applications often adopt similar practices by externalizing SQL in template files that can * be easily changed/optimized by a DBA. * The static methods can be used on their own or by creating an instance of this class you can store and * re-use compiled stylesheets for fast use (e.g. in a server environment) */ public class QueryTemplateManager { static final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); static final TransformerFactory tFactory = TransformerFactory.newInstance(); HashMap<String, Templates> compiledTemplatesCache = new HashMap<>(); Templates defaultCompiledTemplates = null; public QueryTemplateManager() { } public QueryTemplateManager(InputStream xslIs) throws TransformerConfigurationException, ParserConfigurationException, SAXException, IOException { addDefaultQueryTemplate(xslIs); } public void addDefaultQueryTemplate(InputStream xslIs) throws TransformerConfigurationException, ParserConfigurationException, SAXException, IOException { defaultCompiledTemplates = getTemplates(xslIs); } public void addQueryTemplate(String name, InputStream xslIs) throws TransformerConfigurationException, ParserConfigurationException, SAXException, IOException { compiledTemplatesCache.put(name, getTemplates(xslIs)); } public String getQueryAsXmlString(Properties formProperties, String queryTemplateName) throws SAXException, IOException, ParserConfigurationException, TransformerException { Templates ts = compiledTemplatesCache.get(queryTemplateName); return getQueryAsXmlString(formProperties, ts); } public Document getQueryAsDOM(Properties formProperties, String queryTemplateName) throws SAXException, IOException, ParserConfigurationException, TransformerException { Templates ts = compiledTemplatesCache.get(queryTemplateName); return getQueryAsDOM(formProperties, ts); } public String getQueryAsXmlString(Properties formProperties) throws SAXException, IOException, ParserConfigurationException, TransformerException { return getQueryAsXmlString(formProperties, defaultCompiledTemplates); } public Document getQueryAsDOM(Properties formProperties) throws SAXException, IOException, ParserConfigurationException, TransformerException { return getQueryAsDOM(formProperties, defaultCompiledTemplates); } /** * Fast means of constructing query using a precompiled stylesheet */ public static String getQueryAsXmlString(Properties formProperties, Templates template) throws ParserConfigurationException, TransformerException { // TODO: Suppress XML header with encoding (as Strings have no encoding) StringWriter writer = new StringWriter(); StreamResult result = new StreamResult(writer); transformCriteria(formProperties, template, result); return writer.toString(); } /** * Slow means of constructing query parsing a stylesheet from an input stream */ public static String getQueryAsXmlString(Properties formProperties, InputStream xslIs) throws SAXException, IOException, ParserConfigurationException, TransformerException { // TODO: Suppress XML header with encoding (as Strings have no encoding) StringWriter writer = new StringWriter(); StreamResult result = new StreamResult(writer); transformCriteria(formProperties, xslIs, result); return writer.toString(); } /** * Fast means of constructing query using a cached,precompiled stylesheet */ public static Document getQueryAsDOM(Properties formProperties, Templates template) throws ParserConfigurationException, TransformerException { DOMResult result = new DOMResult(); transformCriteria(formProperties, template, result); return (Document) result.getNode(); } /** * Slow means of constructing query - parses stylesheet from input stream */ public static Document getQueryAsDOM(Properties formProperties, InputStream xslIs) throws SAXException, IOException, ParserConfigurationException, TransformerException { DOMResult result = new DOMResult(); transformCriteria(formProperties, xslIs, result); return (Document) result.getNode(); } /** * Slower transformation using an uncompiled stylesheet (suitable for development environment) */ public static void transformCriteria(Properties formProperties, InputStream xslIs, Result result) throws SAXException, IOException, ParserConfigurationException, TransformerException { dbf.setNamespaceAware(true); DocumentBuilder builder = dbf.newDocumentBuilder(); org.w3c.dom.Document xslDoc = builder.parse(xslIs); DOMSource ds = new DOMSource(xslDoc); Transformer transformer = null; synchronized (tFactory) { transformer = tFactory.newTransformer(ds); } transformCriteria(formProperties, transformer, result); } /** * Fast transformation using a pre-compiled stylesheet (suitable for production environments) */ public static void transformCriteria(Properties formProperties, Templates template, Result result) throws ParserConfigurationException, TransformerException { transformCriteria(formProperties, template.newTransformer(), result); } public static void transformCriteria(Properties formProperties, Transformer transformer, Result result) throws ParserConfigurationException, TransformerException { dbf.setNamespaceAware(true); //Create an XML document representing the search index document. DocumentBuilder db = dbf.newDocumentBuilder(); org.w3c.dom.Document doc = db.newDocument(); Element root = doc.createElement("Document"); doc.appendChild(root); Enumeration<?> keysEnum = formProperties.propertyNames(); while (keysEnum.hasMoreElements()) { String propName = keysEnum.nextElement().toString(); String value = formProperties.getProperty(propName); if ((value != null) && (value.length() > 0)) { DOMUtils.insertChild(root, propName, value); } } //Use XSLT to to transform into an XML query string using the queryTemplate DOMSource xml = new DOMSource(doc); transformer.transform(xml, result); } /** * Parses a query stylesheet for repeated use */ public static Templates getTemplates(InputStream xslIs) throws ParserConfigurationException, SAXException, IOException, TransformerConfigurationException { dbf.setNamespaceAware(true); DocumentBuilder builder = dbf.newDocumentBuilder(); org.w3c.dom.Document xslDoc = builder.parse(xslIs); DOMSource ds = new DOMSource(xslDoc); return tFactory.newTemplates(ds); } }