KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > alfresco > web > bean > SearchContext


1 /*
2  * Copyright (C) 2005 Alfresco, Inc.
3  *
4  * Licensed under the Mozilla Public License version 1.1
5  * with a permitted attribution clause. You may obtain a
6  * copy of the License at
7  *
8  * http://www.alfresco.org/legal/license.txt
9  *
10  * Unless required by applicable law or agreed to in writing,
11  * software distributed under the License is distributed on an
12  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13  * either express or implied. See the License for the specific
14  * language governing permissions and limitations under the
15  * License.
16  */

17 package org.alfresco.web.bean;
18
19 import java.io.Serializable JavaDoc;
20 import java.io.StringReader JavaDoc;
21 import java.io.StringWriter JavaDoc;
22 import java.util.ArrayList JavaDoc;
23 import java.util.Collection JavaDoc;
24 import java.util.HashMap JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import java.util.List JavaDoc;
27 import java.util.Map JavaDoc;
28 import java.util.StringTokenizer JavaDoc;
29
30 import javax.faces.context.FacesContext;
31
32 import org.alfresco.error.AlfrescoRuntimeException;
33 import org.alfresco.model.ContentModel;
34 import org.alfresco.repo.search.impl.lucene.QueryParser;
35 import org.alfresco.service.cmr.repository.ChildAssociationRef;
36 import org.alfresco.service.cmr.repository.NodeRef;
37 import org.alfresco.service.cmr.repository.Path;
38 import org.alfresco.service.namespace.NamespaceService;
39 import org.alfresco.service.namespace.QName;
40 import org.alfresco.util.ISO9075;
41 import org.alfresco.web.bean.repository.Repository;
42 import org.apache.commons.logging.Log;
43 import org.apache.commons.logging.LogFactory;
44 import org.dom4j.Document;
45 import org.dom4j.DocumentHelper;
46 import org.dom4j.Element;
47 import org.dom4j.io.OutputFormat;
48 import org.dom4j.io.SAXReader;
49 import org.dom4j.io.XMLWriter;
50
51 /**
52  * Holds the context required to build a search query and can return the populated query.
53  * <p>
54  * Builds a lucene format search string from each of the supplied attributes and terms.
55  * Can be serialized to and from XML format for saving and restoring of previous searches.
56  *
57  * @author Kevin Roast
58  */

59 public final class SearchContext implements Serializable JavaDoc
60 {
61    private static final long serialVersionUID = 6730844584074229969L;
62    
63    /** XML serialization elements */
64    private static final String JavaDoc ELEMENT_VALUE = "value";
65    private static final String JavaDoc ELEMENT_FIXED_VALUES = "fixed-values";
66    private static final String JavaDoc ELEMENT_INCLUSIVE = "inclusive";
67    private static final String JavaDoc ELEMENT_UPPER = "upper";
68    private static final String JavaDoc ELEMENT_LOWER = "lower";
69    private static final String JavaDoc ELEMENT_RANGE = "range";
70    private static final String JavaDoc ELEMENT_RANGES = "ranges";
71    private static final String JavaDoc ELEMENT_NAME = "name";
72    private static final String JavaDoc ELEMENT_ATTRIBUTE = "attribute";
73    private static final String JavaDoc ELEMENT_ATTRIBUTES = "attributes";
74    private static final String JavaDoc ELEMENT_MIMETYPE = "mimetype";
75    private static final String JavaDoc ELEMENT_CONTENT_TYPE = "content-type";
76    private static final String JavaDoc ELEMENT_CATEGORY = "category";
77    private static final String JavaDoc ELEMENT_CATEGORIES = "categories";
78    private static final String JavaDoc ELEMENT_LOCATION = "location";
79    private static final String JavaDoc ELEMENT_MODE = "mode";
80    private static final String JavaDoc ELEMENT_TEXT = "text";
81    private static final String JavaDoc ELEMENT_SEARCH = "search";
82    private static final String JavaDoc ELEMENT_QUERY = "query";
83    
84    /** advanced search term operators */
85    private static final char OP_WILDCARD = '*';
86    private static final char OP_AND = '+';
87    private static final char OP_NOT = '-';
88    
89    /** Search mode constants */
90    public final static int SEARCH_ALL = 0;
91    public final static int SEARCH_FILE_NAMES_CONTENTS = 1;
92    public final static int SEARCH_FILE_NAMES = 2;
93    public final static int SEARCH_SPACE_NAMES = 3;
94    
95    /** the search text string */
96    private String JavaDoc text = "";
97    
98    /** mode for the search */
99    private int mode = SearchContext.SEARCH_ALL;
100    
101    /** folder XPath location for the search */
102    private String JavaDoc location = null;
103    
104    /** categories to add to the search */
105    private String JavaDoc[] categories = new String JavaDoc[0];
106    
107    /** content type to restrict search against */
108    private String JavaDoc contentType = null;
109    
110    /** content mimetype to restrict search against */
111    private String JavaDoc mimeType = null;
112    
113    /** any extra query attributes to add to the search */
114    private Map JavaDoc<QName, String JavaDoc> queryAttributes = new HashMap JavaDoc<QName, String JavaDoc>(5, 1.0f);
115    
116    /** any additional range attribute to add to the search */
117    private Map JavaDoc<QName, RangeProperties> rangeAttributes = new HashMap JavaDoc<QName, RangeProperties>(5, 1.0f);
118    
119    /** any additional fixed value attributes to add to the search, such as boolean or noderef */
120    private Map JavaDoc<QName, String JavaDoc> queryFixedValues = new HashMap JavaDoc<QName, String JavaDoc>(5, 1.0f);
121    
122    /** set true to force the use of AND between text terms */
123    private boolean forceAndTerms = false;
124    
125    /** logger */
126    private static Log logger = LogFactory.getLog(SearchContext.class);
127    
128    
129    /**
130     * Build the search query string based on the current search context members.
131     *
132     * @param minimum small possible textual string used for a match
133     * this does not effect fixed values searches (e.g. boolean, int values) or date ranges
134     *
135     * @return prepared search query string
136     */

137    public String JavaDoc buildQuery(int minimum)
138    {
139       String JavaDoc query;
140       boolean validQuery = false;
141       
142       // the QName for the well known "name" attribute
143
String JavaDoc nameAttr = Repository.escapeQName(QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, ELEMENT_NAME));
144       
145       // match against content text
146
String JavaDoc text = this.text.trim();
147       
148       StringBuilder JavaDoc fullTextBuf = new StringBuilder JavaDoc(64);
149       StringBuilder JavaDoc nameAttrBuf = new StringBuilder JavaDoc(64);
150       
151       if (text.length() != 0 && text.length() >= minimum)
152       {
153          if (text.indexOf(' ') == -1 && text.charAt(0) != '"')
154          {
155             // check for existance of a special operator
156
boolean operatorAND = (text.charAt(0) == OP_AND);
157             boolean operatorNOT = (text.charAt(0) == OP_NOT);
158             // strip operator from term if one was found
159
if (operatorAND || operatorNOT)
160             {
161                text = text.substring(1);
162             }
163             
164             // prepend NOT operator if supplied
165
if (operatorNOT)
166             {
167                fullTextBuf.append(OP_NOT);
168                nameAttrBuf.append(OP_NOT);
169             }
170             
171             // simple single word text search
172
if (text.charAt(0) != OP_WILDCARD)
173             {
174                // escape characters and append the wildcard character
175
String JavaDoc safeText = QueryParser.escape(text);
176                fullTextBuf.append("TEXT:").append(safeText).append(OP_WILDCARD);
177                nameAttrBuf.append("@").append(nameAttr).append(":").append(safeText).append(OP_WILDCARD);
178             }
179             else
180             {
181                // found a leading wildcard - prepend it again after escaping the other characters
182
String JavaDoc safeText = QueryParser.escape(text.substring(1));
183                fullTextBuf.append("TEXT:*").append(safeText).append(OP_WILDCARD);
184                nameAttrBuf.append("@").append(nameAttr).append(":*").append(safeText).append(OP_WILDCARD);
185             }
186          }
187          else
188          {
189             // multiple word search
190
if (text.charAt(0) == '"' && text.charAt(text.length() - 1) == '"')
191             {
192                // as a single quoted phrase
193
String JavaDoc quotedSafeText = '"' + QueryParser.escape(text.substring(1, text.length() - 1)) + '"';
194                fullTextBuf.append("TEXT:").append(quotedSafeText);
195                nameAttrBuf.append("@").append(nameAttr).append(":").append(quotedSafeText);
196             }
197             else
198             {
199                // as individual search terms
200
StringTokenizer JavaDoc t = new StringTokenizer JavaDoc(text, " ");
201                
202                fullTextBuf.append('(');
203                nameAttrBuf.append('(');
204                int tokenCount = t.countTokens();
205                for (int i=0; i<tokenCount; i++)
206                {
207                   String JavaDoc term = t.nextToken();
208                   
209                   // check for existance of a special operator
210
boolean operatorAND = (term.charAt(0) == OP_AND);
211                   boolean operatorNOT = (term.charAt(0) == OP_NOT);
212                   // strip operator from term if one was found
213
if (operatorAND || operatorNOT)
214                   {
215                      term = term.substring(1);
216                   }
217                   
218                   // special case for AND all terms if set (apply after operator character removed)
219
operatorAND = operatorAND | this.forceAndTerms;
220                   
221                   if (term.length() != 0)
222                   {
223                      // operators such as AND and OR are only make sense for full text searching
224
if (i != 0 && !operatorAND)
225                      {
226                         fullTextBuf.append("OR ");
227                         nameAttrBuf.append("OR ");
228                      }
229                      
230                      // prepend NOT operator if supplied
231
if (operatorNOT)
232                      {
233                         fullTextBuf.append(OP_NOT);
234                         nameAttrBuf.append(OP_NOT);
235                      }
236                      // prepend AND operator if supplied
237
if (operatorAND)
238                      {
239                         fullTextBuf.append(OP_AND);
240                         nameAttrBuf.append(OP_AND);
241                      }
242                      
243                      if (term.charAt(0) != OP_WILDCARD)
244                      {
245                         String JavaDoc safeTerm = QueryParser.escape(term);
246                         fullTextBuf.append("TEXT:").append(safeTerm).append(OP_WILDCARD);
247                         nameAttrBuf.append("@").append(nameAttr).append(":").append(safeTerm).append(OP_WILDCARD);
248                      }
249                      else
250                      {
251                         String JavaDoc safeTerm = QueryParser.escape(term.substring(1));
252                         fullTextBuf.append("TEXT:*").append(safeTerm).append(OP_WILDCARD);
253                         nameAttrBuf.append("@").append(nameAttr).append(":*").append(safeTerm).append(OP_WILDCARD);
254                      }
255                      fullTextBuf.append(' ');
256                      nameAttrBuf.append(' ');
257                   }
258                }
259                fullTextBuf.append(')');
260                nameAttrBuf.append(')');
261             }
262          }
263          
264          validQuery = true;
265       }
266       
267       // match a specific PATH for space location or categories
268
StringBuilder JavaDoc pathQuery = null;
269       if (location != null || (categories != null && categories.length !=0))
270       {
271          pathQuery = new StringBuilder JavaDoc(128);
272          if (location != null)
273          {
274             pathQuery.append(" PATH:\"").append(location).append("\" ");
275             if (categories != null && categories.length != 0)
276             {
277                pathQuery.append("AND (");
278             }
279          }
280          if (categories != null && categories.length != 0)
281          {
282             for (int i=0; i<categories.length; i++)
283             {
284                if (i > 0)
285                {
286                   pathQuery.append("OR");
287                }
288                pathQuery.append(" PATH:\"").append(categories[i]).append("\" ");
289             }
290             if (location != null)
291             {
292                pathQuery.append(") ");
293             }
294          }
295       }
296       
297       // match any extra query attribute values specified
298
StringBuilder JavaDoc attributeQuery = null;
299       if (queryAttributes.size() != 0)
300       {
301          attributeQuery = new StringBuilder JavaDoc(queryAttributes.size() << 6);
302          for (QName qname : queryAttributes.keySet())
303          {
304             String JavaDoc value = queryAttributes.get(qname).trim();
305             if (value.length() != 0 && value.length() >= minimum)
306             {
307                String JavaDoc escapedName = Repository.escapeQName(qname);
308                attributeQuery.append(" +@").append(escapedName)
309                              .append(":").append(QueryParser.escape(value)).append(OP_WILDCARD);
310             }
311          }
312          
313          // handle the case where we did not add any attributes due to minimum length restrictions
314
if (attributeQuery.length() == 0)
315          {
316             attributeQuery = null;
317          }
318       }
319       
320       // match any extra fixed value attributes specified
321
if (queryFixedValues.size() != 0)
322       {
323          if (attributeQuery == null)
324          {
325             attributeQuery = new StringBuilder JavaDoc(queryFixedValues.size() << 6);
326          }
327          for (QName qname : queryFixedValues.keySet())
328          {
329             String JavaDoc escapedName = Repository.escapeQName(qname);
330             String JavaDoc value = queryFixedValues.get(qname);
331             attributeQuery.append(" +@").append(escapedName)
332                           .append(":\"").append(value).append('"');
333          }
334       }
335       
336       // range attributes are a special case also
337
if (rangeAttributes.size() != 0)
338       {
339          if (attributeQuery == null)
340          {
341             attributeQuery = new StringBuilder JavaDoc(rangeAttributes.size() << 6);
342          }
343          for (QName qname : rangeAttributes.keySet())
344          {
345             String JavaDoc escapedName = Repository.escapeQName(qname);
346             RangeProperties rp = rangeAttributes.get(qname);
347             String JavaDoc value1 = QueryParser.escape(rp.lower);
348             String JavaDoc value2 = QueryParser.escape(rp.upper);
349             attributeQuery.append(" +@").append(escapedName)
350                           .append(":").append(rp.inclusive ? "[" : "{").append(value1)
351                           .append(" TO ").append(value2).append(rp.inclusive ? "]" : "}");
352          }
353       }
354       
355       // mimetype is a special case - it is indexed as a special attribute it comes from the combined
356
// ContentData attribute of cm:content - ContentData string cannot be searched directly
357
if (mimeType != null && mimeType.length() != 0)
358       {
359          if (attributeQuery == null)
360          {
361             attributeQuery = new StringBuilder JavaDoc(64);
362          }
363          String JavaDoc escapedName = Repository.escapeQName(QName.createQName(ContentModel.PROP_CONTENT + ".mimetype"));
364          attributeQuery.append(" +@").append(escapedName)
365                        .append(":").append(mimeType);
366       }
367       
368       // match against appropriate content type
369
String JavaDoc fileTypeQuery;
370       if (contentType != null)
371       {
372          fileTypeQuery = " TYPE:\"" + contentType + "\" ";
373       }
374       else
375       {
376          // default to cm:content
377
fileTypeQuery = " TYPE:\"{" + NamespaceService.CONTENT_MODEL_1_0_URI + "}content\" ";
378       }
379       
380       // match against FOLDER type
381
String JavaDoc folderTypeQuery = " TYPE:\"{" + NamespaceService.CONTENT_MODEL_1_0_URI + "}folder\" ";
382       
383       String JavaDoc fullTextQuery = fullTextBuf.toString();
384       String JavaDoc nameAttrQuery = nameAttrBuf.toString();
385       if (text.length() != 0 && text.length() >= minimum)
386       {
387          // text query for name and/or full text specified
388
switch (mode)
389          {
390             case SearchContext.SEARCH_ALL:
391                query = '(' + fileTypeQuery + " AND " + '(' + nameAttrQuery + ' ' + fullTextQuery + ')' + ')' + " OR " +
392                        '(' + folderTypeQuery + " AND " + nameAttrQuery + ')';
393                break;
394             
395             case SearchContext.SEARCH_FILE_NAMES:
396                query = fileTypeQuery + " AND " + nameAttrQuery;
397                break;
398             
399             case SearchContext.SEARCH_FILE_NAMES_CONTENTS:
400                query = fileTypeQuery + " AND " + '(' + nameAttrQuery + ' ' + fullTextQuery + ')';
401                break;
402             
403             case SearchContext.SEARCH_SPACE_NAMES:
404                query = folderTypeQuery + " AND " + nameAttrQuery;
405                break;
406             
407             default:
408                throw new IllegalStateException JavaDoc("Unknown search mode specified: " + mode);
409          }
410       }
411       else
412       {
413          // no text query specified - must be an attribute/value query only
414
switch (mode)
415          {
416             case SearchContext.SEARCH_ALL:
417                query = '(' + fileTypeQuery + " OR " + folderTypeQuery + ')';
418                break;
419             
420             case SearchContext.SEARCH_FILE_NAMES:
421             case SearchContext.SEARCH_FILE_NAMES_CONTENTS:
422                query = fileTypeQuery;
423                break;
424             
425             case SearchContext.SEARCH_SPACE_NAMES:
426                query = folderTypeQuery;
427                break;
428             
429             default:
430               throw new IllegalStateException JavaDoc("Unknown search mode specified: " + mode);
431          }
432       }
433       
434       // match entire query against any additional attributes specified
435
if (attributeQuery != null)
436       {
437          query = attributeQuery + " AND (" + query + ')';
438       }
439       
440       // match entire query against any specified paths
441
if (pathQuery != null)
442       {
443          query = "(" + pathQuery + ") AND (" + query + ')';
444       }
445       
446       // check that we have a query worth executing - if we have no attributes, paths or text/name search
447
// then we'll only have a search against files/type TYPE which does nothing by itself!
448
validQuery = validQuery | (attributeQuery != null) | (pathQuery != null);
449       if (validQuery == false)
450       {
451          query = null;
452       }
453       
454       if (logger.isDebugEnabled())
455          logger.debug("Query: " + query);
456       
457       return query;
458    }
459    
460    /**
461     * Generate a search XPATH pointing to the specified node, optionally return an XPATH
462     * that includes the child nodes.
463     *
464     * @param id Of the node to generate path too
465     * @param children Whether to include children of the node
466     *
467     * @return the path
468     */

469    /*package*/ static String JavaDoc getPathFromSpaceRef(NodeRef ref, boolean children)
470    {
471       FacesContext context = FacesContext.getCurrentInstance();
472       Path path = Repository.getServiceRegistry(context).getNodeService().getPath(ref);
473       NamespaceService ns = Repository.getServiceRegistry(context).getNamespaceService();
474       StringBuilder JavaDoc buf = new StringBuilder JavaDoc(64);
475       for (int i=0; i<path.size(); i++)
476       {
477          String JavaDoc elementString = "";
478          Path.Element element = path.get(i);
479          if (element instanceof Path.ChildAssocElement)
480          {
481             ChildAssociationRef elementRef = ((Path.ChildAssocElement)element).getRef();
482             if (elementRef.getParentRef() != null)
483             {
484                Collection JavaDoc prefixes = ns.getPrefixes(elementRef.getQName().getNamespaceURI());
485                if (prefixes.size() >0)
486                {
487                   elementString = '/' + (String JavaDoc)prefixes.iterator().next() + ':' + ISO9075.encode(elementRef.getQName().getLocalName());
488                }
489             }
490          }
491          
492          buf.append(elementString);
493       }
494       if (children == true)
495       {
496          // append syntax to get all children of the path
497
buf.append("//*");
498       }
499       else
500       {
501          // append syntax to just represent the path, not the children
502
buf.append("/*");
503       }
504       
505       return buf.toString();
506    }
507    
508    /**
509     * @return Returns the categories to use for the search
510     */

511    public String JavaDoc[] getCategories()
512    {
513       return this.categories;
514    }
515    
516    /**
517     * @param categories The categories to set as a list of search XPATHs
518     */

519    public void setCategories(String JavaDoc[] categories)
520    {
521       if (categories != null)
522       {
523          this.categories = categories;
524       }
525    }
526    
527    /**
528     * @return Returns the node XPath to search in or null for all.
529     */

530    public String JavaDoc getLocation()
531    {
532       return this.location;
533    }
534    
535    /**
536     * @param location The node XPATH to search from or null for all..
537     */

538    public void setLocation(String JavaDoc location)
539    {
540       this.location = location;
541    }
542    
543    /**
544     * @return Returns the mode to use during the search (see constants)
545     */

546    public int getMode()
547    {
548       return this.mode;
549    }
550    
551    /**
552     * @param mode The mode to use during the search (see constants)
553     */

554    public void setMode(int mode)
555    {
556       this.mode = mode;
557    }
558    
559    /**
560     * @return Returns the search text string.
561     */

562    public String JavaDoc getText()
563    {
564       return this.text;
565    }
566    
567    /**
568     * @param text The search text string.
569     */

570    public void setText(String JavaDoc text)
571    {
572       this.text = text;
573    }
574
575    /**
576     * @return Returns the contentType.
577     */

578    public String JavaDoc getContentType()
579    {
580       return this.contentType;
581    }
582
583    /**
584     * @param contentType The content type to restrict attribute search against.
585     */

586    public void setContentType(String JavaDoc contentType)
587    {
588       this.contentType = contentType;
589    }
590    
591    /**
592     * @return Returns the mimeType.
593     */

594    public String JavaDoc getMimeType()
595    {
596       return this.mimeType;
597    }
598    /**
599     * @param mimeType The mimeType to set.
600     */

601    public void setMimeType(String JavaDoc mimeType)
602    {
603       this.mimeType = mimeType;
604    }
605    
606    /**
607     * Add an additional attribute to search against
608     *
609     * @param qname QName of the attribute to search against
610     * @param value Value of the attribute to use
611     */

612    public void addAttributeQuery(QName qname, String JavaDoc value)
613    {
614       this.queryAttributes.put(qname, value);
615    }
616    
617    public String JavaDoc getAttributeQuery(QName qname)
618    {
619       return this.queryAttributes.get(qname);
620    }
621    
622    /**
623     * Add an additional range attribute to search against
624     *
625     * @param qname QName of the attribute to search against
626     * @param lower Lower value for range
627     * @param upper Upper value for range
628     * @param inclusive True for inclusive within the range, false otherwise
629     */

630    public void addRangeQuery(QName qname, String JavaDoc lower, String JavaDoc upper, boolean inclusive)
631    {
632       this.rangeAttributes.put(qname, new RangeProperties(qname, lower, upper, inclusive));
633    }
634    
635    public RangeProperties getRangeProperty(QName qname)
636    {
637       return this.rangeAttributes.get(qname);
638    }
639    
640    /**
641     * Add an additional fixed value attribute to search against
642     *
643     * @param qname QName of the attribute to search against
644     * @param value Fixed value of the attribute to use
645     */

646    public void addFixedValueQuery(QName qname, String JavaDoc value)
647    {
648       this.queryFixedValues.put(qname, value);
649    }
650    
651    public String JavaDoc getFixedValueQuery(QName qname)
652    {
653       return this.queryFixedValues.get(qname);
654    }
655    
656    /**
657     * @return Returns if AND is forced between text terms. False (OR terms) is the default.
658     */

659    public boolean getForceAndTerms()
660    {
661       return this.forceAndTerms;
662    }
663
664    /**
665     * @param forceAndTerms Set true to force AND between text terms. Otherwise OR is the default.
666     */

667    public void setForceAndTerms(boolean forceAndTerms)
668    {
669       this.forceAndTerms = forceAndTerms;
670    }
671
672    /**
673     * @return this SearchContext as XML
674     *
675     * Example:
676     * <code>
677     * <?xml version="1.0" encoding="UTF-8"?>
678     * <search>
679     * <text>CDATA</text>
680     * <mode>int</mode>
681     * <location>XPath</location>
682     * <categories>
683     * <category>XPath</category>
684     * </categories>
685     * <content-type>String</content-type>
686     * <mimetype>String</mimetype>
687     * <attributes>
688     * <attribute name="String">String</attribute>
689     * </attributes>
690     * <ranges>
691     * <range name="String">
692     * <lower>String</lower>
693     * <upper>String</upper>
694     * <inclusive>boolean</inclusive>
695     * </range>
696     * </ranges>
697     * <fixed-values>
698     * <value name="String">String</value>
699     * </fixed-values>
700     * <query>CDATA</query>
701     * </search>
702     * </code>
703     */

704    public String JavaDoc toXML()
705    {
706       try
707       {
708          NamespaceService ns = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getNamespaceService();
709          
710          Document doc = DocumentHelper.createDocument();
711          
712          Element root = doc.addElement(ELEMENT_SEARCH);
713          
714          root.addElement(ELEMENT_TEXT).addCDATA(this.text);
715          root.addElement(ELEMENT_MODE).addText(Integer.toString(this.mode));
716          if (this.location != null)
717          {
718             root.addElement(ELEMENT_LOCATION).addText(this.location);
719          }
720          
721          Element categories = root.addElement(ELEMENT_CATEGORIES);
722          for (String JavaDoc path : this.categories)
723          {
724             categories.addElement(ELEMENT_CATEGORY).addText(path);
725          }
726          
727          if (this.contentType != null)
728          {
729             root.addElement(ELEMENT_CONTENT_TYPE).addText(this.contentType);
730          }
731          if (this.mimeType != null && this.mimeType.length() != 0)
732          {
733             root.addElement(ELEMENT_MIMETYPE).addText(this.mimeType);
734          }
735          
736          Element attributes = root.addElement(ELEMENT_ATTRIBUTES);
737          for (QName attrName : this.queryAttributes.keySet())
738          {
739             attributes.addElement(ELEMENT_ATTRIBUTE)
740                       .addAttribute(ELEMENT_NAME, attrName.toPrefixString(ns))
741                       .addCDATA(this.queryAttributes.get(attrName));
742          }
743          
744          Element ranges = root.addElement(ELEMENT_RANGES);
745          for (QName rangeName : this.rangeAttributes.keySet())
746          {
747             RangeProperties rangeProps = this.rangeAttributes.get(rangeName);
748             Element range = ranges.addElement(ELEMENT_RANGE);
749             range.addAttribute(ELEMENT_NAME, rangeName.toPrefixString(ns));
750             range.addElement(ELEMENT_LOWER).addText(rangeProps.lower);
751             range.addElement(ELEMENT_UPPER).addText(rangeProps.upper);
752             range.addElement(ELEMENT_INCLUSIVE).addText(Boolean.toString(rangeProps.inclusive));
753          }
754          
755          Element values = root.addElement(ELEMENT_FIXED_VALUES);
756          for (QName valueName : this.queryFixedValues.keySet())
757          {
758             values.addElement(ELEMENT_VALUE)
759                   .addAttribute(ELEMENT_NAME, valueName.toPrefixString(ns))
760                   .addCDATA(this.queryFixedValues.get(valueName));
761          }
762          
763          // outputing the full lucene query may be useful for some situations
764
Element query = root.addElement(ELEMENT_QUERY);
765          String JavaDoc queryString = buildQuery(0);
766          if (queryString != null)
767          {
768             query.addCDATA(queryString);
769          }
770          
771          StringWriter JavaDoc out = new StringWriter JavaDoc(1024);
772          XMLWriter writer = new XMLWriter(OutputFormat.createPrettyPrint());
773          writer.setWriter(out);
774          writer.write(doc);
775          
776          return out.toString();
777       }
778       catch (Throwable JavaDoc err)
779       {
780          throw new AlfrescoRuntimeException("Failed to export SearchContext to XML.", err);
781       }
782    }
783    
784    /**
785     * Restore a SearchContext from an XML definition
786     *
787     * @param xml XML format SearchContext @see #toXML()
788     */

789    public SearchContext fromXML(String JavaDoc xml)
790    {
791       try
792       {
793          NamespaceService ns = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getNamespaceService();
794          
795          // get the root element
796
SAXReader reader = new SAXReader();
797          Document document = reader.read(new StringReader JavaDoc(xml));
798          Element rootElement = document.getRootElement();
799          Element textElement = rootElement.element(ELEMENT_TEXT);
800          if (textElement != null)
801          {
802             this.text = textElement.getText();
803          }
804          Element modeElement = rootElement.element(ELEMENT_MODE);
805          if (modeElement != null)
806          {
807             this.mode = Integer.parseInt(modeElement.getText());
808          }
809          Element locationElement = rootElement.element(ELEMENT_LOCATION);
810          if (locationElement != null)
811          {
812             this.location = locationElement.getText();
813          }
814          Element categoriesElement = rootElement.element(ELEMENT_CATEGORIES);
815          if (categoriesElement != null)
816          {
817             List JavaDoc<String JavaDoc> categories = new ArrayList JavaDoc<String JavaDoc>(4);
818             for (Iterator JavaDoc i=categoriesElement.elementIterator(ELEMENT_CATEGORY); i.hasNext(); /**/)
819             {
820                Element categoryElement = (Element)i.next();
821                categories.add(categoryElement.getText());
822             }
823             this.categories = categories.toArray(this.categories);
824          }
825          Element contentTypeElement = rootElement.element(ELEMENT_CONTENT_TYPE);
826          if (contentTypeElement != null)
827          {
828             this.contentType = contentTypeElement.getText();
829          }
830          Element mimetypeElement = rootElement.element(ELEMENT_MIMETYPE);
831          if (mimetypeElement != null)
832          {
833             this.mimeType = mimetypeElement.getText();
834          }
835          Element attributesElement = rootElement.element(ELEMENT_ATTRIBUTES);
836          if (attributesElement != null)
837          {
838             for (Iterator JavaDoc i=attributesElement.elementIterator(ELEMENT_ATTRIBUTE); i.hasNext(); /**/)
839             {
840                Element attrElement = (Element)i.next();
841                QName qname = QName.createQName(attrElement.attributeValue(ELEMENT_NAME), ns);
842                addAttributeQuery(qname, attrElement.getText());
843             }
844          }
845          Element rangesElement = rootElement.element(ELEMENT_RANGES);
846          if (rangesElement != null)
847          {
848             for (Iterator JavaDoc i=rangesElement.elementIterator(ELEMENT_RANGE); i.hasNext(); /**/)
849             {
850                Element rangeElement = (Element)i.next();
851                Element lowerElement = rangeElement.element(ELEMENT_LOWER);
852                Element upperElement = rangeElement.element(ELEMENT_UPPER);
853                Element incElement = rangeElement.element(ELEMENT_INCLUSIVE);
854                if (lowerElement != null && upperElement != null && incElement != null)
855                {
856                   QName qname = QName.createQName(rangeElement.attributeValue(ELEMENT_NAME), ns);
857                   addRangeQuery(qname,
858                         lowerElement.getText(), upperElement.getText(),
859                         Boolean.parseBoolean(incElement.getText()));
860                }
861             }
862          }
863          
864          Element valuesElement = rootElement.element(ELEMENT_FIXED_VALUES);
865          if (valuesElement != null)
866          {
867             for (Iterator JavaDoc i=valuesElement.elementIterator(ELEMENT_VALUE); i.hasNext(); /**/)
868             {
869                Element valueElement = (Element)i.next();
870                QName qname = QName.createQName(valueElement.attributeValue(ELEMENT_NAME), ns);
871                addFixedValueQuery(qname, valueElement.getText());
872             }
873          }
874       }
875       catch (Throwable JavaDoc err)
876       {
877          throw new AlfrescoRuntimeException("Failed to import SearchContext from XML.", err);
878       }
879       return this;
880    }
881    
882    /**
883     * Simple wrapper class for range query attribute properties
884     */

885    static class RangeProperties
886    {
887       QName qname;
888       String JavaDoc lower;
889       String JavaDoc upper;
890       boolean inclusive;
891       
892       RangeProperties(QName qname, String JavaDoc lower, String JavaDoc upper, boolean inclusive)
893       {
894          this.qname = qname;
895          this.lower = lower;
896          this.upper = upper;
897          this.inclusive = inclusive;
898       }
899    }
900 }
901
Popular Tags