KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > info > magnolia > cms > taglibs > util > SimpleSearchTag


1 /**
2  *
3  * Magnolia and its source-code is licensed under the LGPL.
4  * You may copy, adapt, and redistribute this file for commercial or non-commercial use.
5  * When copying, adapting, or redistributing this document in keeping with the guidelines above,
6  * you are required to provide proper attribution to obinary.
7  * If you reproduce or distribute the document without making any substantive modifications to its content,
8  * please use the following attribution line:
9  *
10  * Copyright 1993-2006 obinary Ltd. (http://www.obinary.com) All rights reserved.
11  *
12  */

13 package info.magnolia.cms.taglibs.util;
14
15 import info.magnolia.cms.beans.config.ContentRepository;
16 import info.magnolia.cms.core.Content;
17 import info.magnolia.cms.core.search.Query;
18 import info.magnolia.cms.core.search.QueryResult;
19 import info.magnolia.cms.util.Resource;
20 import info.magnolia.context.MgnlContext;
21
22 import java.text.MessageFormat JavaDoc;
23
24 import javax.jcr.RepositoryException;
25 import javax.servlet.http.HttpServletRequest JavaDoc;
26 import javax.servlet.jsp.JspException JavaDoc;
27 import javax.servlet.jsp.PageContext JavaDoc;
28 import javax.servlet.jsp.tagext.TagSupport JavaDoc;
29
30 import org.apache.commons.lang.ArrayUtils;
31 import org.apache.commons.lang.StringUtils;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35
36 /**
37  * <p/> A simple tag which allows searching in all the site content with a "natural language" query. It simply strips
38  * all the reserved chars from input string, build an xpath query and feed Magnolia QueryManager.
39  * </p>
40  * <p/> By defaults search terms are ANDed, but it also supports using the AND or OR keywords in the query string.
41  * Search is not case sensitive and it's performed on any non-binary property.
42  * </p>
43  * <p/> A collection on Content (page) objects is added to the specified scope with the specified name.
44  * </p>
45  * <p/> Tipical usage:
46  * </p>
47  * <p/>
48  *
49  * <pre>
50  * &lt;cmsu:simplesearch query="${param.search}" startLevel="3" var="results" />
51  * &lt;c:forEach items="${results}">
52  * &lt;a HREF="${pageContext.request.contextPath}${node.handle}.html">${node.title}&lt;/a>
53  * &lt;/c:forEach>
54  * </pre>
55  *
56  * @author Fabrizio Giustina
57  * @version $Revision: 6341 $ ($Author: philipp $)
58  */

59 public class SimpleSearchTag extends TagSupport JavaDoc {
60
61     /**
62      * Stable serialVersionUID.
63      */

64     private static final long serialVersionUID = 222L;
65
66     /**
67      * Reserved chars, stripped from query.
68      */

69     private static final String JavaDoc RESERVED_CHARS = "()[]{}<>:/\\@*?=\"'&"; //$NON-NLS-1$
70

71     /**
72      * keywords.
73      */

74     static final String JavaDoc[] KEYWORDS = new String JavaDoc[]{"and", "or"}; //$NON-NLS-1$ //$NON-NLS-2$
75

76     /**
77      * Logger.
78      */

79     private static Logger log = LoggerFactory.getLogger(SimpleSearchTag.class);
80
81     /**
82      * Start level.
83      */

84     private int startLevel;
85
86     /**
87      * Query, natural language.
88      */

89     private String JavaDoc query;
90
91     /**
92      * Variable name for results.
93      */

94     private String JavaDoc var;
95
96     /**
97      * Tag attribute. Scope for the declared variable. Can be <code>page</code>, <code>request</code>,
98      * <code>session</code> or <code>application</code><code></code>.
99      */

100     private int scope = PageContext.PAGE_SCOPE;
101
102     /**
103      * Setter for <code>query</code>.
104      * @param query The query to set.
105      */

106     public void setQuery(String JavaDoc query) {
107         this.query = query;
108     }
109
110     /**
111      * Setter for <code>var</code>.
112      * @param var The var to set.
113      */

114     public void setVar(String JavaDoc var) {
115         this.var = var;
116     }
117
118     /**
119      * Setter for <code>scope</code>.
120      * @param scope The scope to set.
121      */

122     public void setScope(String JavaDoc scope) {
123         if ("request".equalsIgnoreCase(scope)) { //$NON-NLS-1$
124
this.scope = PageContext.REQUEST_SCOPE;
125         }
126         else if ("session".equalsIgnoreCase(scope)) { //$NON-NLS-1$
127
this.scope = PageContext.SESSION_SCOPE;
128         }
129         else if ("application".equalsIgnoreCase(scope)) { //$NON-NLS-1$
130
this.scope = PageContext.APPLICATION_SCOPE;
131         }
132         else {
133             // default
134
this.scope = PageContext.PAGE_SCOPE;
135         }
136     }
137
138     /**
139      * Setter for <code>startLevel</code>.
140      * @param startLevel The startLevel to set.
141      */

142     public void setStartLevel(int startLevel) {
143         this.startLevel = startLevel;
144     }
145
146     /**
147      * @see javax.servlet.jsp.tagext.TagSupport#doStartTag()
148      */

149     public int doStartTag() throws JspException JavaDoc {
150
151         String JavaDoc queryString = generateXPathQuery();
152
153         if (queryString == null) {
154             if (log.isDebugEnabled()) {
155                 log.debug("A valid query could not be built, skipping"); //$NON-NLS-1$
156
}
157             return EVAL_PAGE;
158         }
159
160         if (log.isDebugEnabled()) {
161             log.debug("Executing xpath query " + queryString); //$NON-NLS-1$
162
}
163
164         Query q;
165         try {
166             q = MgnlContext.getQueryManager(ContentRepository.WEBSITE).createQuery(queryString, "xpath"); //$NON-NLS-1$
167

168             QueryResult result = q.execute();
169
170             pageContext.setAttribute(var, result.getContent(), scope);
171         }
172         catch (Exception JavaDoc e) {
173             log.error(MessageFormat.format(
174                 "{0} caught while parsing query for search term [{1}] - query is [{2}]: {3}", //$NON-NLS-1$
175
new Object JavaDoc[]{e.getClass().getName(), this.query, queryString, e.getMessage()}), e);
176         }
177
178         return EVAL_PAGE;
179     }
180
181     /**
182      * Split search terms and build an xpath query in the form:
183      * <code>//*[@jcr:primaryType='mgnl:content']/\*\/\*[jcr:contains(., 'first') or jcr:contains(., 'second')]</code>
184      * @return valid xpath expression or null if the given query doesn't contain at least one valid search term
185      */

186     protected String JavaDoc generateXPathQuery() {
187
188         String JavaDoc startPath = null;
189
190         // search only in a specific subtree
191
if (this.startLevel != 0) {
192             try {
193                 Content activePage = Resource.getActivePage((HttpServletRequest JavaDoc) this.pageContext.getRequest());
194                 if (activePage != null) {
195                     startPath = StringUtils.strip(activePage.getAncestor(this.startLevel).getHandle(), "/"); //$NON-NLS-1$
196
}
197             }
198             catch (RepositoryException e) {
199                 log.error(e.getMessage(), e);
200             }
201         }
202
203         // strip reserved chars and split
204
String JavaDoc[] tokens = StringUtils.split(StringUtils.lowerCase(StringUtils.replaceChars(
205             this.query,
206             RESERVED_CHARS,
207             null)));
208
209         // null input string?
210
if (tokens == null) {
211             return null;
212         }
213
214         StringBuffer JavaDoc xpath = new StringBuffer JavaDoc(tokens.length * 20);
215         if (StringUtils.isNotEmpty(startPath)) {
216             xpath.append(startPath);
217         }
218         xpath.append("//*[@jcr:primaryType=\'mgnl:content\']/*/*["); //$NON-NLS-1$
219

220         String JavaDoc joinOperator = "and"; //$NON-NLS-1$
221
boolean emptyQuery = true;
222
223         for (int j = 0; j < tokens.length; j++) {
224             String JavaDoc tkn = tokens[j];
225             if (ArrayUtils.contains(KEYWORDS, tkn)) {
226                 joinOperator = tkn;
227             }
228             else {
229                 if (!emptyQuery) {
230                     xpath.append(" "); //$NON-NLS-1$
231
xpath.append(joinOperator);
232                     xpath.append(" "); //$NON-NLS-1$
233
}
234                 xpath.append("jcr:contains(., '"); //$NON-NLS-1$
235
xpath.append(tkn);
236                 xpath.append("')"); //$NON-NLS-1$
237
emptyQuery = false;
238             }
239
240         }
241
242         xpath.append("]"); //$NON-NLS-1$
243

244         // if no valid search terms are added don't return a catch-all query
245
if (emptyQuery) {
246             return null;
247         }
248
249         return xpath.toString();
250     }
251
252     /**
253      * @see javax.servlet.jsp.tagext.TagSupport#release()
254      */

255     public void release() {
256         this.query = null;
257         this.var = null;
258         this.scope = PageContext.PAGE_SCOPE;
259         this.startLevel = 0;
260         super.release();
261     }
262
263 }
264
Popular Tags