/** * License Agreement for OpenSearchServer * * Copyright (C)2011-2012 Emmanuel Keller / Jaeksoft * * http://www.open-search-server.com * * This file is part of OpenSearchServer. * * OpenSearchServer is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenSearchServer 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenSearchServer. * If not, see <http://www.gnu.org/licenses/>. **/ package com.jaeksoft.searchlib.render; /** * @author naveen * */ import com.jaeksoft.searchlib.SearchLibException; import com.jaeksoft.searchlib.facet.Facet; import com.jaeksoft.searchlib.facet.FacetCounter; import com.jaeksoft.searchlib.facet.FacetField; import com.jaeksoft.searchlib.facet.FacetList; import com.jaeksoft.searchlib.function.expression.SyntaxError; import com.jaeksoft.searchlib.query.ParseException; import com.jaeksoft.searchlib.request.AbstractSearchRequest; import com.jaeksoft.searchlib.request.ReturnField; import com.jaeksoft.searchlib.result.AbstractResultSearch; import com.jaeksoft.searchlib.result.ResultDocument; import com.jaeksoft.searchlib.schema.FieldValueItem; import com.jaeksoft.searchlib.snippet.SnippetField; import com.jaeksoft.searchlib.web.ServletTransaction; import org.apache.commons.lang3.StringEscapeUtils; import org.xml.sax.SAXException; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerConfigurationException; import javax.xml.xpath.XPathExpressionException; import java.io.IOException; import java.io.PrintWriter; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; public class RenderOpenSearch implements Render { private PrintWriter writer; private AbstractResultSearch<?> result; private AbstractSearchRequest searchRequest; private Matcher controlMatcher; private String serverURL; private String outputEncoding; public RenderOpenSearch(AbstractResultSearch<?> result, String serverURL, String outputEncoding) { this.result = result; this.searchRequest = result.getRequest(); this.serverURL = serverURL; Pattern p = Pattern.compile("\\p{Cntrl}"); controlMatcher = p.matcher(""); this.outputEncoding = outputEncoding; } private String xmlTextRender(String text) { controlMatcher.reset(text); return StringEscapeUtils.escapeXml11(controlMatcher.replaceAll("")); } private void renderPrefix() throws ParseException, SyntaxError, SearchLibException, IOException { String encoding = null; if (outputEncoding != null && !outputEncoding.equals("")) encoding = outputEncoding; else encoding = "UTF-8"; writer.println("<?xml version=\"1.0\" encoding=\"" + encoding + "\"?>"); writer.println("<rss version=\"2.0\" " + " xmlns:OpenSearchServer=\"http://www.open-search-server.com/opensearch/1.0/\" " + " xmlns:opensearch=\"http://a9.com/-/spec/opensearch/1.1/\" " + " xmlns:atom=\"http://www.w3.org/2005/Atom\">"); writer.println("<channel>"); writer.println("\t<title>"); writer.print("OpenSearchServer: "); writer.print(StringEscapeUtils.escapeXml11(searchRequest.getQueryString())); writer.print("\t</title>"); writer.println("\t<description>"); writer.print("Search results for "); writer.print("\""); writer.print(StringEscapeUtils.escapeXml11(searchRequest.getQueryString())); writer.print("\""); writer.print("\t</description>"); writer.println("\t<link>"); writer.print(StringEscapeUtils.escapeXml11(serverURL)); writer.print("\t</link>"); } private void renderSuffix() { writer.println("</channel>"); writer.println("</rss>"); } private void renderDocuments() throws IOException, ParseException, SyntaxError, XPathExpressionException, ParserConfigurationException, SAXException, SearchLibException, TransformerConfigurationException { AbstractSearchRequest searchRequest = result.getRequest(); int start = searchRequest.getStart(); int end = result.getDocumentCount() + searchRequest.getStart(); writer.println("\t<opensearch:totalResults>"); writer.print(result.getNumFound()); writer.print("\t</opensearch:totalResults>"); writer.println("\t<opensearch:startIndex>"); writer.print(searchRequest.getStart()); writer.print("\t</opensearch:startIndex>"); writer.println("\t<opensearch:itemsPerPage>"); writer.print(searchRequest.getRows()); writer.print("\t</opensearch:itemsPerPage>"); writer.println("\t<opensearch:Query role=\"request\""); writer.print(" searchTerms=\"" + searchRequest.getQueryString()); writer.print("\"/>"); for (int i = start; i < end; i++) this.renderDocument(i); } private void renderDocument(int pos) throws IOException, ParseException, SyntaxError, XPathExpressionException, ParserConfigurationException, SAXException, SearchLibException, TransformerConfigurationException { writer.println("<item>"); ResultDocument doc = result.getDocument(pos, null); for (SnippetField field : searchRequest.getSnippetFieldList()) renderSnippetValue(doc, field); for (ReturnField field : searchRequest.getReturnFieldList()) renderField(doc, field); int cc = result.getCollapseCount(pos); if (cc > 0) { writer.print("\t\t<OpenSearchServer:collapseCount>"); writer.print(cc); writer.println("</OpenSearchServer:collapseCount>"); } writer.println("\t</item>"); } private void renderField(ResultDocument doc, ReturnField field) throws IOException, XPathExpressionException, ParserConfigurationException, SAXException, TransformerConfigurationException, SearchLibException { String fieldName = field.getName(); List<FieldValueItem> values = doc.getValues(field); String openSearchtitleField = getFieldMap("opensearch", "title"); String openSearchDescriptionField = getFieldMap("opensearch", "description"); String openSearchUrlField = getFieldMap("opensearch", "url"); if (values != null) for (FieldValueItem v : values) { if (openSearchtitleField != null && openSearchtitleField.equalsIgnoreCase(fieldName)) { writer.print("\t<title>"); writer.print(StringEscapeUtils.escapeXml11(v.getValue())); writer.println("</title>"); } else if (openSearchDescriptionField != null && openSearchDescriptionField.equalsIgnoreCase(fieldName)) { writer.print("\t<description>"); writer.print(StringEscapeUtils.escapeXml11(v.getValue())); writer.println("</description>"); } else if (openSearchUrlField != null && openSearchUrlField.equalsIgnoreCase(fieldName)) { writer.print("\t<link>"); writer.print(StringEscapeUtils.escapeXml11(v.getValue())); writer.println("</link>"); } else { writer.print("\t\t<OpenSearchServer:"); writer.print(fieldName); writer.print('>'); writer.print(xmlTextRender(v.getValue())); writer.print("</OpenSearchServer:"); writer.print(fieldName); writer.print('>'); } } } private String getFieldMap(String apiName, String field) throws XPathExpressionException, ParserConfigurationException, SAXException, IOException, TransformerConfigurationException, SearchLibException { return searchRequest.getConfig().getApiManager().getMappedValue(apiName, field); } private void renderSnippetValue(ResultDocument doc, SnippetField field) throws IOException, XPathExpressionException, ParserConfigurationException, SAXException, TransformerConfigurationException, SearchLibException { String fieldName = field.getName(); List<FieldValueItem> snippets = doc.getSnippetValues(field); if (snippets == null) return; String openSearchtitleField = getFieldMap("opensearch", "title"); String openSearchDescriptionField = getFieldMap("opensearch", "description"); String openSearchUrlField = getFieldMap("opensearch", "url"); for (FieldValueItem snippet : snippets) { if (openSearchtitleField != null && openSearchtitleField.equalsIgnoreCase(fieldName)) { writer.print("\t<title>"); writer.print(StringEscapeUtils.escapeXml11(snippet.getValue())); writer.println("</title>"); } else if (openSearchDescriptionField != null && openSearchDescriptionField.equalsIgnoreCase(fieldName)) { writer.print("\t<description>"); writer.print(StringEscapeUtils.escapeXml11(snippet.getValue())); writer.println("</description>"); } else if (openSearchUrlField != null && openSearchUrlField.equalsIgnoreCase(fieldName)) { writer.print("\t<link>"); writer.print(StringEscapeUtils.escapeXml11(snippet.getValue())); writer.println("</link>"); } else { writer.print("\t\t<OpenSearchServer:"); writer.print(fieldName); writer.print('>'); writer.print(xmlTextRender(snippet.getValue())); writer.print("</OpenSearchServer:"); writer.print(fieldName); writer.print('>'); } } } private void renderFacet(Facet facet) throws Exception { FacetField facetField = facet.getFacetField(); writer.print("\t\t<OpenSearchServer:"); writer.print(facetField.getName()); writer.println(">"); for (Map.Entry<String, FacetCounter> facetItem : facet) { writer.print("\t\t<OpenSearchServer:"); writer.print(StringEscapeUtils.escapeXml11(facetItem.getKey())); writer.print(">"); writer.print(facetItem.getValue().count); writer.print("</OpenSearchServer:"); writer.print(StringEscapeUtils.escapeXml11(facetItem.getKey())); writer.print(">"); } writer.print("</OpenSearchServer:"); writer.print(facetField.getName()); writer.println(">"); } private void renderFacets() throws Exception { FacetList facetList = result.getFacetList(); if (facetList == null) return; writer.println("\t\t\t<OpenSearchServer:faceting>"); for (Facet facet : facetList) renderFacet(facet); writer.println("</OpenSearchServer:faceting>"); } // private void renderSpellCheck(SpellCheck spellCheck) throws Exception { // String fieldName = spellCheck.getFieldName(); // writer.print("\t\t<OpenSearchServer:"); // writer.print(fieldName); // writer.println(">"); // for (SpellCheckItem spellCheckItem : spellCheck) { // writer.print("\t\t\t<OpenSearchServer:"); // writer.print(StringEscapeUtils.escapeXml(spellCheckItem.getWord())); // writer.println(">"); // for (SuggestionItem suggest : spellCheckItem.getSuggestions()) { // writer.print("\t\t\t\t<OpenSearchServer:suggest>"); // writer.print("\t\t\t\t\t<OpenSearchServer:term>"); // writer.print(StringEscapeUtils.escapeXml(suggest.getTerm())); // writer.println("</OpenSearchServer:term>"); // writer.print("\t\t\t\t\t<OpenSearchServer:freq>"); // writer.print(suggest.getFreq()); // writer.println("</OpenSearchServer:freq>"); // writer.println("</OpenSearchServer:suggest>"); // } // writer.print("</OpenSearchServer:"); // writer.print(StringEscapeUtils.escapeXml(spellCheckItem.getWord())); // writer.println(">"); // } // writer.print("\t\t</OpenSearchServer:"); // writer.print(fieldName); // writer.println(">"); // } // private void renderSpellChecks() throws Exception { // List<SpellCheck> spellChecklist = result.getSpellCheckList(); // if (spellChecklist == null) // return; // writer.println("\t\t\t<OpenSearchServer:spellcheck>"); // for (SpellCheck spellCheck : spellChecklist) // renderSpellCheck(spellCheck); // writer.println("</OpenSearchServer:spellcheck>"); // } public void render(PrintWriter writer) throws Exception { this.writer = writer; renderPrefix(); renderDocuments(); renderFacets(); // renderSpellChecks(); renderSuffix(); } @Override public void render(ServletTransaction servletTransaction) throws Exception { servletTransaction.setResponseContentType("text/xml"); if (outputEncoding != null && !outputEncoding.equals("")) render(servletTransaction.getWriter(outputEncoding)); else render(servletTransaction.getWriter("UTF-8")); } }