001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.jsp.search.config.parser; 029 030import org.opencms.jsp.search.config.CmsSearchConfigurationCommon; 031import org.opencms.jsp.search.config.CmsSearchConfigurationDidYouMean; 032import org.opencms.jsp.search.config.CmsSearchConfigurationFacetField; 033import org.opencms.jsp.search.config.CmsSearchConfigurationFacetQuery; 034import org.opencms.jsp.search.config.CmsSearchConfigurationFacetQuery.CmsFacetQueryItem; 035import org.opencms.jsp.search.config.CmsSearchConfigurationFacetRange; 036import org.opencms.jsp.search.config.CmsSearchConfigurationHighlighting; 037import org.opencms.jsp.search.config.CmsSearchConfigurationPagination; 038import org.opencms.jsp.search.config.CmsSearchConfigurationSortOption; 039import org.opencms.jsp.search.config.CmsSearchConfigurationSorting; 040import org.opencms.jsp.search.config.I_CmsSearchConfigurationCommon; 041import org.opencms.jsp.search.config.I_CmsSearchConfigurationDidYouMean; 042import org.opencms.jsp.search.config.I_CmsSearchConfigurationFacet; 043import org.opencms.jsp.search.config.I_CmsSearchConfigurationFacetField; 044import org.opencms.jsp.search.config.I_CmsSearchConfigurationFacetQuery; 045import org.opencms.jsp.search.config.I_CmsSearchConfigurationFacetQuery.I_CmsFacetQueryItem; 046import org.opencms.jsp.search.config.I_CmsSearchConfigurationFacetRange; 047import org.opencms.jsp.search.config.I_CmsSearchConfigurationHighlighting; 048import org.opencms.jsp.search.config.I_CmsSearchConfigurationPagination; 049import org.opencms.jsp.search.config.I_CmsSearchConfigurationSortOption; 050import org.opencms.jsp.search.config.I_CmsSearchConfigurationSorting; 051import org.opencms.main.CmsLog; 052import org.opencms.xml.content.CmsXmlContent; 053import org.opencms.xml.content.CmsXmlContentValueSequence; 054import org.opencms.xml.types.I_CmsXmlContentValue; 055 056import java.util.ArrayList; 057import java.util.Arrays; 058import java.util.HashMap; 059import java.util.LinkedHashMap; 060import java.util.List; 061import java.util.Locale; 062import java.util.Map; 063 064import org.apache.commons.logging.Log; 065 066/** Search configuration parser reading XML. */ 067public class CmsXMLSearchConfigurationParser implements I_CmsSearchConfigurationParser { 068 069 /** Logger for the class. */ 070 protected static final Log LOG = CmsLog.getLog(CmsXMLSearchConfigurationParser.class); 071 072 /** The element names of the xml content. */ 073 /** Elements for common options. */ 074 /** XML element name. */ 075 private static final String XML_ELEMENT_QUERYPARAM = "QueryParam"; 076 /** XML element name. */ 077 private static final String XML_ELEMENT_LAST_QUERYPARAM = "LastQueryParam"; 078 /** XML element name. */ 079 private static final String XML_ELEMENT_ESCAPE_QUERY_CHARACTERS = "EscapeQueryCharacters"; 080 /** XML element name. */ 081 private static final String XML_ELEMENT_RELOADED_PARAM = "ReloadedParam"; 082 /** XML element name. */ 083 private static final String XML_ELEMENT_SEARCH_FOR_EMPTY_QUERY = "SearchForEmptyQuery"; 084 /** XML element name. */ 085 private static final String XML_ELEMENT_IGNORE_QUERY = "IgnoreQuery"; 086 /** XML element name. */ 087 private static final String XML_ELEMENT_IGNORE_RELEASE_DATE = "IgnoreReleaseDate"; 088 /** XML element name. */ 089 private static final String XML_ELEMENT_IGNORE_EXPIRATION_DATE = "IgnoreExpirationDate"; 090 /** XML element name. */ 091 private static final String XML_ELEMENT_QUERY_MODIFIER = "QueryModifier"; 092 /** XML element name. */ 093 private static final String XML_ELEMENT_PAGEPARAM = "PageParam"; 094 /** XML element name. */ 095 private static final String XML_ELEMENT_INDEX = "Index"; 096 /** XML element name. */ 097 private static final String XML_ELEMENT_CORE = "Core"; 098 /** XML element name. */ 099 private static final String XML_ELEMENT_EXTRASOLRPARAMS = "ExtraSolrParams"; 100 /** XML element name. */ 101 private static final String XML_ELEMENT_ADDITIONAL_PARAMETERS = "AdditionalRequestParams"; 102 /** XML element name. */ 103 private static final String XML_ELEMENT_ADDITIONAL_PARAMETERS_PARAM = "Param"; 104 /** XML element name. */ 105 private static final String XML_ELEMENT_ADDITIONAL_PARAMETERS_SOLRQUERY = "SolrQuery"; 106 /** XML element name. */ 107 private static final String XML_ELEMENT_PAGESIZE = "PageSize"; 108 /** XML element name. */ 109 private static final String XML_ELEMENT_PAGENAVLENGTH = "PageNavLength"; 110 111 /** XML element names for facet configuration. */ 112 /** XML element name for the root element of a field facet configuration. */ 113 private static final String XML_ELEMENT_FIELD_FACETS = "FieldFacet"; 114 /** XML element name for the root element of the query facet configuration. */ 115 private static final String XML_ELEMENT_QUERY_FACET = "QueryFacet"; 116 /** XML element names for facet options. */ 117 /** XML element name. */ 118 private static final String XML_ELEMENT_FACET_LIMIT = "Limit"; 119 /** XML element name. */ 120 private static final String XML_ELEMENT_FACET_MINCOUNT = "MinCount"; 121 /** XML element name. */ 122 private static final String XML_ELEMENT_FACET_LABEL = "Label"; 123 /** XML element name. */ 124 private static final String XML_ELEMENT_FACET_FIELD = "Field"; 125 /** XML element name. */ 126 private static final String XML_ELEMENT_FACET_NAME = "Name"; 127 /** XML element name. */ 128 private static final String XML_ELEMENT_FACET_PREFIX = "Prefix"; 129 /** XML element name. */ 130 private static final String XML_ELEMENT_FACET_ORDER = "Order"; 131 /** XML element name. */ 132 private static final String XML_ELEMENT_FACET_FILTERQUERYMODIFIER = "FilterQueryModifier"; 133 /** XML element name. */ 134 private static final String XML_ELEMENT_FACET_ISANDFACET = "IsAndFacet"; 135 /** XML element name. */ 136 private static final String XML_ELEMENT_FACET_PRESELECTION = "PreSelection"; 137 /** XML element name. */ 138 private static final String XML_ELEMENT_FACET_IGNOREALLFACETFILTERS = "IgnoreAllFacetFilters"; 139 /** XML element name. */ 140 private static final String XML_ELEMENT_QUERY_FACET_QUERY = "QueryItem"; 141 /** XML element name. */ 142 private static final String XML_ELEMENT_QUERY_FACET_QUERY_QUERY = "Query"; 143 /** XML element name. */ 144 private static final String XML_ELEMENT_QUERY_FACET_QUERY_LABEL = "Label"; 145 146 /** XML element names for sort options. */ 147 /** XML element name. */ 148 private static final String XML_ELEMENT_SORTPARAM = "SortParam"; 149 /** XML element name for the root element for sort options. */ 150 private static final String XML_ELEMENT_SORTOPTIONS = "SortOption"; 151 /** XML element names for a single search option. */ 152 private static final String XML_ELEMENT_SORTOPTION_LABEL = "Label"; 153 /** XML element name. */ 154 private static final String XML_ELEMENT_SORTOPTION_PARAMVALUE = "ParamValue"; 155 /** XML element name. */ 156 private static final String XML_ELEMENT_SORTOPTION_SOLRVALUE = "SolrValue"; 157 /** XML element name for the root element for the highlighting configuration. */ 158 private static final String XML_ELEMENT_HIGHLIGHTER = "Highlighting"; 159 /** XML elements for the highlighting configuration. */ 160 /** XML element name. */ 161 private static final String XML_ELEMENT_HIGHLIGHTER_FIELD = "Field"; 162 /** XML element name. */ 163 private static final String XML_ELEMENT_HIGHLIGHTER_SNIPPETS = "Snippets"; 164 /** XML element name. */ 165 private static final String XML_ELEMENT_HIGHLIGHTER_FRAGSIZE = "FragSize"; 166 /** XML element name. */ 167 private static final String XML_ELEMENT_HIGHLIGHTER_ALTERNATE_FIELD = "AlternateField"; 168 /** XML element name. */ 169 private static final String XML_ELEMENT_HIGHLIGHTER_MAX_LENGTH_ALTERNATE_FIELD = "MaxAlternateFieldLength"; 170 /** XML element name. */ 171 private static final String XML_ELEMENT_HIGHLIGHTER_SIMPLE_PRE = "SimplePre"; 172 /** XML element name. */ 173 private static final String XML_ELEMENT_HIGHLIGHTER_SIMPLE_POST = "SimplePost"; 174 /** XML element name. */ 175 private static final String XML_ELEMENT_HIGHLIGHTER_FORMATTER = "Formatter"; 176 /** XML element name. */ 177 private static final String XML_ELEMENT_HIGHLIGHTER_FRAGMENTER = "Fragmenter"; 178 /** XML element name. */ 179 private static final String XML_ELEMENT_HIGHLIGHTER_FASTVECTORHIGHLIGHTING = "UseFastVectorHighlighting"; 180 181 /** XML element names for "Did you mean ...?". */ 182 /** XML element name. */ 183 private static final String XML_ELEMENT_DIDYOUMEAN = "DidYouMean"; 184 /** XML elements for the "Did you mean ...?" configuration. */ 185 /** XML element name. */ 186 private static final String XML_ELEMENT_DIDYOUMEAN_QUERYPARAM = "QueryParam"; 187 /** XML element name. */ 188 private static final String XML_ELEMENT_DIDYOUMEAN_COLLATE = "Collate"; 189 /** XML element name. */ 190 private static final String XML_ELEMENT_DIDYOUMEAN_COUNT = "Count"; 191 /** XML element name. */ 192 private static final String XML_ELEMENT_RANGE_FACETS = "RangeFacet"; 193 /** XML element name. */ 194 private static final String XML_ELEMENT_RANGE_FACET_RANGE = "Range"; 195 /** XML element name. */ 196 private static final String XML_ELEMENT_RANGE_FACET_START = "Start"; 197 /** XML element name. */ 198 private static final String XML_ELEMENT_RANGE_FACET_END = "End"; 199 /** XML element name. */ 200 private static final String XML_ELEMENT_RANGE_FACET_GAP = "Gap"; 201 /** XML element name. */ 202 private static final String XML_ELEMENT_RANGE_FACET_OTHER = "Other"; 203 /** XML element name. */ 204 private static final String XML_ELEMENT_RANGE_FACET_HARDEND = "HardEnd"; 205 206 /** Default value. */ 207 private static final String DEFAULT_QUERY_PARAM = "q"; 208 /** Default value. */ 209 private static final String DEFAULT_LAST_QUERY_PARAM = "lq"; 210 /** Default value. */ 211 private static final String DEFAULT_RELOADED_PARAM = "reloaded"; 212 213 /** The XML content that contains the configuration. */ 214 CmsXmlContent m_xml; 215 /** The locale in which the configuration should be read. */ 216 Locale m_locale; 217 218 /** Constructor taking the XML content that should be read and the locale in which it should be read. 219 * @param xml The XML content that should be read for the configuration. 220 * @param locale The locale in which the content should be read. 221 */ 222 public CmsXMLSearchConfigurationParser(final CmsXmlContent xml, final Locale locale) { 223 224 m_xml = xml; 225 m_locale = locale; 226 } 227 228 /** 229 * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parseCommon() 230 */ 231 @Override 232 public I_CmsSearchConfigurationCommon parseCommon() { 233 234 return new CmsSearchConfigurationCommon( 235 getQueryParam(), 236 getLastQueryParam(), 237 parseOptionalBooleanValue(XML_ELEMENT_ESCAPE_QUERY_CHARACTERS), 238 getFirstCallParam(), 239 getSearchForEmtpyQuery(), 240 getIgnoreQuery(), 241 getQueryModifier(), 242 getIndex(), 243 getCore(), 244 getExtraSolrParams(), 245 getAdditionalRequestParameters(), 246 getIgnoreReleaseDate(), 247 getIgnoreExpirationDate()); 248 } 249 250 /** 251 * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parseDidYouMean() 252 */ 253 public I_CmsSearchConfigurationDidYouMean parseDidYouMean() { 254 255 final I_CmsXmlContentValue didYouMean = m_xml.getValue(XML_ELEMENT_DIDYOUMEAN, m_locale); 256 if (didYouMean == null) { 257 return null; 258 } else { 259 final String pathPrefix = didYouMean.getPath() + "/"; 260 String param = parseOptionalStringValue(pathPrefix + XML_ELEMENT_DIDYOUMEAN_QUERYPARAM); 261 if (null == param) { 262 param = getQueryParam(); 263 } 264 Boolean collate = parseOptionalBooleanValue(pathPrefix + XML_ELEMENT_DIDYOUMEAN_COLLATE); 265 Integer count = parseOptionalIntValue(pathPrefix + XML_ELEMENT_DIDYOUMEAN_COUNT); 266 return new CmsSearchConfigurationDidYouMean(param, collate, count); 267 } 268 } 269 270 /** 271 * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parseFieldFacets() 272 */ 273 @Override 274 public Map<String, I_CmsSearchConfigurationFacetField> parseFieldFacets() { 275 276 final Map<String, I_CmsSearchConfigurationFacetField> facetConfigs = new LinkedHashMap<String, I_CmsSearchConfigurationFacetField>(); 277 final CmsXmlContentValueSequence fieldFacets = m_xml.getValueSequence(XML_ELEMENT_FIELD_FACETS, m_locale); 278 if (fieldFacets != null) { 279 for (int i = 0; i < fieldFacets.getElementCount(); i++) { 280 281 final I_CmsSearchConfigurationFacetField config = parseFieldFacet( 282 fieldFacets.getValue(i).getPath() + "/"); 283 if (config != null) { 284 facetConfigs.put(config.getName(), config); 285 } 286 } 287 } 288 return facetConfigs; 289 } 290 291 /** 292 * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parseHighlighter() 293 */ 294 @Override 295 public I_CmsSearchConfigurationHighlighting parseHighlighter() { 296 297 final I_CmsXmlContentValue highlighter = m_xml.getValue(XML_ELEMENT_HIGHLIGHTER, m_locale); 298 if (highlighter == null) { 299 return null; 300 } else { 301 try { 302 final String pathPrefix = highlighter.getPath() + "/"; 303 final String field = parseMandatoryStringValue(pathPrefix + XML_ELEMENT_HIGHLIGHTER_FIELD); 304 final Integer snippets = parseOptionalIntValue(pathPrefix + XML_ELEMENT_HIGHLIGHTER_SNIPPETS); 305 final Integer fragsize = parseOptionalIntValue(pathPrefix + XML_ELEMENT_HIGHLIGHTER_FRAGSIZE); 306 final String alternateField = parseOptionalStringValue( 307 pathPrefix + XML_ELEMENT_HIGHLIGHTER_ALTERNATE_FIELD); 308 final Integer maxAlternateFieldLength = parseOptionalIntValue( 309 pathPrefix + XML_ELEMENT_HIGHLIGHTER_MAX_LENGTH_ALTERNATE_FIELD); 310 final String pre = parseOptionalStringValue(pathPrefix + XML_ELEMENT_HIGHLIGHTER_SIMPLE_PRE); 311 final String post = parseOptionalStringValue(pathPrefix + XML_ELEMENT_HIGHLIGHTER_SIMPLE_POST); 312 final String formatter = parseOptionalStringValue(pathPrefix + XML_ELEMENT_HIGHLIGHTER_FORMATTER); 313 final String fragmenter = parseOptionalStringValue(pathPrefix + XML_ELEMENT_HIGHLIGHTER_FRAGMENTER); 314 final Boolean useFastVectorHighlighting = parseOptionalBooleanValue( 315 pathPrefix + XML_ELEMENT_HIGHLIGHTER_FASTVECTORHIGHLIGHTING); 316 return new CmsSearchConfigurationHighlighting( 317 field, 318 snippets, 319 fragsize, 320 alternateField, 321 maxAlternateFieldLength, 322 pre, 323 post, 324 formatter, 325 fragmenter, 326 useFastVectorHighlighting); 327 } catch (final Exception e) { 328 LOG.error(Messages.get().getBundle().key(Messages.ERR_MANDATORY_HIGHLIGHTING_FIELD_MISSING_0), e); 329 return null; 330 } 331 } 332 } 333 334 /** 335 * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parsePagination() 336 */ 337 @Override 338 public I_CmsSearchConfigurationPagination parsePagination() { 339 340 return CmsSearchConfigurationPagination.create(getPageParam(), getPageSizes(), getPageNavLength()); 341 } 342 343 /** 344 * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parseQueryFacet() 345 */ 346 @Override 347 public I_CmsSearchConfigurationFacetQuery parseQueryFacet() { 348 349 final String pathPrefix = XML_ELEMENT_QUERY_FACET + "/"; 350 try { 351 final List<I_CmsFacetQueryItem> queries = parseFacetQueryItems(pathPrefix + XML_ELEMENT_QUERY_FACET_QUERY); 352 final String label = parseOptionalStringValue(pathPrefix + XML_ELEMENT_FACET_LABEL); 353 final Boolean isAndFacet = parseOptionalBooleanValue(pathPrefix + XML_ELEMENT_FACET_ISANDFACET); 354 final List<String> preselection = parseOptionalStringValues(pathPrefix + XML_ELEMENT_FACET_PRESELECTION); 355 final Boolean ignoreAllFacetFilters = parseOptionalBooleanValue( 356 pathPrefix + XML_ELEMENT_FACET_IGNOREALLFACETFILTERS); 357 return new CmsSearchConfigurationFacetQuery( 358 queries, 359 label, 360 isAndFacet, 361 preselection, 362 ignoreAllFacetFilters); 363 } catch (final Exception e) { 364 LOG.error( 365 Messages.get().getBundle().key( 366 Messages.ERR_QUERY_FACET_MANDATORY_KEY_MISSING_1, 367 XML_ELEMENT_QUERY_FACET_QUERY), 368 e); 369 return null; 370 } 371 } 372 373 /** 374 * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parseRangeFacets() 375 */ 376 public Map<String, I_CmsSearchConfigurationFacetRange> parseRangeFacets() { 377 378 final Map<String, I_CmsSearchConfigurationFacetRange> facetConfigs = new LinkedHashMap<String, I_CmsSearchConfigurationFacetRange>(); 379 final CmsXmlContentValueSequence rangeFacets = m_xml.getValueSequence(XML_ELEMENT_RANGE_FACETS, m_locale); 380 if (rangeFacets != null) { 381 for (int i = 0; i < rangeFacets.getElementCount(); i++) { 382 383 final I_CmsSearchConfigurationFacetRange config = parseRangeFacet( 384 rangeFacets.getValue(i).getPath() + "/"); 385 if (config != null) { 386 facetConfigs.put(config.getName(), config); 387 } 388 } 389 } 390 return facetConfigs; 391 } 392 393 /** 394 * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parseSorting() 395 */ 396 @Override 397 public I_CmsSearchConfigurationSorting parseSorting() { 398 399 List<I_CmsSearchConfigurationSortOption> options = getSortOptions(); 400 I_CmsSearchConfigurationSortOption defaultOption = (options != null) && !options.isEmpty() 401 ? options.get(0) 402 : null; 403 return CmsSearchConfigurationSorting.create(getSortParam(), options, defaultOption); 404 } 405 406 /** Helper to read a mandatory String value list. 407 * @param path The XML path of the element to read. 408 * @return The String list stored in the XML, or <code>null</code> if the value could not be read. 409 * @throws Exception thrown if the list of String values can not be read. 410 */ 411 protected List<I_CmsFacetQueryItem> parseFacetQueryItems(final String path) throws Exception { 412 413 final List<I_CmsXmlContentValue> values = m_xml.getValues(path, m_locale); 414 if (values == null) { 415 return null; 416 } else { 417 List<I_CmsFacetQueryItem> parsedItems = new ArrayList<I_CmsFacetQueryItem>(values.size()); 418 for (I_CmsXmlContentValue value : values) { 419 I_CmsFacetQueryItem item = parseFacetQueryItem(value.getPath() + "/"); 420 if (null != item) { 421 parsedItems.add(item); 422 } else { 423 // TODO: log 424 } 425 } 426 return parsedItems; 427 } 428 } 429 430 /** Reads the configuration of a field facet. 431 * @param pathPrefix The XML Path that leads to the field facet configuration, or <code>null</code> if the XML was not correctly structured. 432 * @return The read configuration, or <code>null</code> if the XML was not correctly structured. 433 */ 434 protected I_CmsSearchConfigurationFacetField parseFieldFacet(final String pathPrefix) { 435 436 try { 437 final String field = parseMandatoryStringValue(pathPrefix + XML_ELEMENT_FACET_FIELD); 438 final String name = parseOptionalStringValue(pathPrefix + XML_ELEMENT_FACET_NAME); 439 final String label = parseOptionalStringValue(pathPrefix + XML_ELEMENT_FACET_LABEL); 440 final Integer minCount = parseOptionalIntValue(pathPrefix + XML_ELEMENT_FACET_MINCOUNT); 441 final Integer limit = parseOptionalIntValue(pathPrefix + XML_ELEMENT_FACET_LIMIT); 442 final String prefix = parseOptionalStringValue(pathPrefix + XML_ELEMENT_FACET_PREFIX); 443 final String sorder = parseOptionalStringValue(pathPrefix + XML_ELEMENT_FACET_ORDER); 444 I_CmsSearchConfigurationFacet.SortOrder order; 445 try { 446 order = I_CmsSearchConfigurationFacet.SortOrder.valueOf(sorder); 447 } catch (@SuppressWarnings("unused") final Exception e) { 448 order = null; 449 } 450 final String filterQueryModifier = parseOptionalStringValue( 451 pathPrefix + XML_ELEMENT_FACET_FILTERQUERYMODIFIER); 452 final Boolean isAndFacet = parseOptionalBooleanValue(pathPrefix + XML_ELEMENT_FACET_ISANDFACET); 453 final List<String> preselection = parseOptionalStringValues(pathPrefix + XML_ELEMENT_FACET_PRESELECTION); 454 final Boolean ignoreAllFacetFilters = parseOptionalBooleanValue( 455 pathPrefix + XML_ELEMENT_FACET_IGNOREALLFACETFILTERS); 456 return new CmsSearchConfigurationFacetField( 457 field, 458 name, 459 minCount, 460 limit, 461 prefix, 462 label, 463 order, 464 filterQueryModifier, 465 isAndFacet, 466 preselection, 467 ignoreAllFacetFilters); 468 } catch (final Exception e) { 469 LOG.error( 470 Messages.get().getBundle().key( 471 Messages.ERR_FIELD_FACET_MANDATORY_KEY_MISSING_1, 472 XML_ELEMENT_FACET_FIELD), 473 e); 474 return null; 475 } 476 } 477 478 /** Helper to read an optional Boolean value. 479 * @param path The XML path of the element to read. 480 * @return The Boolean value stored in the XML, or <code>null</code> if the value could not be read. 481 */ 482 protected Boolean parseOptionalBooleanValue(final String path) { 483 484 final I_CmsXmlContentValue value = m_xml.getValue(path, m_locale); 485 if (value == null) { 486 return null; 487 } else { 488 final String stringValue = value.getStringValue(null); 489 try { 490 final Boolean boolValue = Boolean.valueOf(stringValue); 491 return boolValue; 492 } catch (final NumberFormatException e) { 493 LOG.info(Messages.get().getBundle().key(Messages.LOG_OPTIONAL_BOOLEAN_MISSING_1, path), e); 494 return null; 495 } 496 } 497 } 498 499 /** Helper to read an optional Integer value. 500 * @param path The XML path of the element to read. 501 * @return The Integer value stored in the XML, or <code>null</code> if the value could not be read. 502 */ 503 protected Integer parseOptionalIntValue(final String path) { 504 505 final I_CmsXmlContentValue value = m_xml.getValue(path, m_locale); 506 if (value == null) { 507 return null; 508 } else { 509 final String stringValue = value.getStringValue(null); 510 try { 511 final Integer intValue = Integer.valueOf(stringValue); 512 return intValue; 513 } catch (final NumberFormatException e) { 514 LOG.info(Messages.get().getBundle().key(Messages.LOG_OPTIONAL_INTEGER_MISSING_1, path), e); 515 return null; 516 } 517 } 518 } 519 520 /** Helper to read an optional String value. 521 * @param path The XML path of the element to read. 522 * @return The String value stored in the XML, or <code>null</code> if the value could not be read. 523 */ 524 protected String parseOptionalStringValue(final String path) { 525 526 final I_CmsXmlContentValue value = m_xml.getValue(path, m_locale); 527 if (value == null) { 528 return null; 529 } else { 530 return value.getStringValue(null); 531 } 532 } 533 534 /** Helper to read an optional String value list. 535 * @param path The XML path of the element to read. 536 * @return The String list stored in the XML, or <code>null</code> if the value could not be read. 537 */ 538 protected List<String> parseOptionalStringValues(final String path) { 539 540 final List<I_CmsXmlContentValue> values = m_xml.getValues(path, m_locale); 541 if (values == null) { 542 return null; 543 } else { 544 List<String> stringValues = new ArrayList<String>(values.size()); 545 for (I_CmsXmlContentValue value : values) { 546 stringValues.add(value.getStringValue(null)); 547 } 548 return stringValues; 549 } 550 } 551 552 /** Reads the configuration of a range facet. 553 * @param pathPrefix The XML Path that leads to the range facet configuration, or <code>null</code> if the XML was not correctly structured. 554 * @return The read configuration, or <code>null</code> if the XML was not correctly structured. 555 */ 556 protected I_CmsSearchConfigurationFacetRange parseRangeFacet(String pathPrefix) { 557 558 try { 559 final String range = parseMandatoryStringValue(pathPrefix + XML_ELEMENT_RANGE_FACET_RANGE); 560 final String name = parseOptionalStringValue(pathPrefix + XML_ELEMENT_FACET_NAME); 561 final String label = parseOptionalStringValue(pathPrefix + XML_ELEMENT_FACET_LABEL); 562 final Integer minCount = parseOptionalIntValue(pathPrefix + XML_ELEMENT_FACET_MINCOUNT); 563 final String start = parseMandatoryStringValue(pathPrefix + XML_ELEMENT_RANGE_FACET_START); 564 final String end = parseMandatoryStringValue(pathPrefix + XML_ELEMENT_RANGE_FACET_END); 565 final String gap = parseMandatoryStringValue(pathPrefix + XML_ELEMENT_RANGE_FACET_GAP); 566 final String sother = parseOptionalStringValue(pathPrefix + XML_ELEMENT_RANGE_FACET_OTHER); 567 List<I_CmsSearchConfigurationFacetRange.Other> other = null; 568 if (sother != null) { 569 final List<String> sothers = Arrays.asList(sother.split(",")); 570 other = new ArrayList<I_CmsSearchConfigurationFacetRange.Other>(sothers.size()); 571 for (String so : sothers) { 572 try { 573 I_CmsSearchConfigurationFacetRange.Other o = I_CmsSearchConfigurationFacetRange.Other.valueOf( 574 so); 575 other.add(o); 576 } catch (final Exception e) { 577 LOG.error(Messages.get().getBundle().key(Messages.ERR_INVALID_OTHER_OPTION_1, so), e); 578 } 579 } 580 } 581 final Boolean hardEnd = parseOptionalBooleanValue(pathPrefix + XML_ELEMENT_RANGE_FACET_HARDEND); 582 final Boolean isAndFacet = parseOptionalBooleanValue(pathPrefix + XML_ELEMENT_FACET_ISANDFACET); 583 final List<String> preselection = parseOptionalStringValues(pathPrefix + XML_ELEMENT_FACET_PRESELECTION); 584 final Boolean ignoreAllFacetFilters = parseOptionalBooleanValue( 585 pathPrefix + XML_ELEMENT_FACET_IGNOREALLFACETFILTERS); 586 return new CmsSearchConfigurationFacetRange( 587 range, 588 start, 589 end, 590 gap, 591 other, 592 hardEnd, 593 name, 594 minCount, 595 label, 596 isAndFacet, 597 preselection, 598 ignoreAllFacetFilters); 599 } catch (final Exception e) { 600 LOG.error( 601 Messages.get().getBundle().key( 602 Messages.ERR_RANGE_FACET_MANDATORY_KEY_MISSING_1, 603 XML_ELEMENT_RANGE_FACET_RANGE 604 + ", " 605 + XML_ELEMENT_RANGE_FACET_START 606 + ", " 607 + XML_ELEMENT_RANGE_FACET_END 608 + ", " 609 + XML_ELEMENT_RANGE_FACET_GAP), 610 e); 611 return null; 612 } 613 614 } 615 616 /** Returns a map with additional request parameters, mapping the parameter names to Solr query parts. 617 * @return A map with additional request parameters, mapping the parameter names to Solr query parts. 618 */ 619 private Map<String, String> getAdditionalRequestParameters() { 620 621 List<I_CmsXmlContentValue> parametersToParse = m_xml.getValues(XML_ELEMENT_ADDITIONAL_PARAMETERS, m_locale); 622 Map<String, String> result = new HashMap<String, String>(parametersToParse.size()); 623 for (I_CmsXmlContentValue additionalParam : parametersToParse) { 624 String param = m_xml.getValue( 625 additionalParam.getPath() + "/" + XML_ELEMENT_ADDITIONAL_PARAMETERS_PARAM, 626 m_locale).getStringValue(null); 627 String solrQuery = m_xml.hasValue( 628 additionalParam.getPath() + "/" + XML_ELEMENT_ADDITIONAL_PARAMETERS_SOLRQUERY, 629 m_locale) 630 ? m_xml.getValue( 631 additionalParam.getPath() + "/" + XML_ELEMENT_ADDITIONAL_PARAMETERS_SOLRQUERY, 632 m_locale).getStringValue(null) 633 : null; 634 result.put(param, solrQuery); 635 } 636 return result; 637 } 638 639 /** Returns the configured Solr core, or <code>null</code> if the core is not specified. 640 * @return The configured Solr core, or <code>null</code> if the core is not specified. 641 */ 642 private String getCore() { 643 644 try { 645 return parseMandatoryStringValue(XML_ELEMENT_CORE); 646 } catch (final Exception e) { 647 LOG.info(Messages.get().getBundle().key(Messages.LOG_NO_CORE_SPECIFIED_0), e); 648 return null; 649 } 650 } 651 652 /** Returns the extra Solr parameters specified in the configuration, or the empty string if no extra parameters are configured. 653 * @return The extra Solr parameters specified in the configuration, or the empty string if no extra parameters are configured. 654 */ 655 private String getExtraSolrParams() { 656 657 return parseOptionalStringValue(XML_ELEMENT_EXTRASOLRPARAMS); 658 } 659 660 /** Returns the configured request parameter for the last query, or the default parameter if the core is not specified. 661 * @return The configured request parameter for the last query, or the default parameter if the core is not specified. 662 */ 663 private String getFirstCallParam() { 664 665 final String param = parseOptionalStringValue(XML_ELEMENT_RELOADED_PARAM); 666 if (param == null) { 667 return DEFAULT_RELOADED_PARAM; 668 } else { 669 return param; 670 } 671 } 672 673 /** Returns a flag indicating if also expired resources should be found. 674 * @return A flag indicating if also expired resources should be found. 675 */ 676 private Boolean getIgnoreExpirationDate() { 677 678 return parseOptionalBooleanValue(XML_ELEMENT_IGNORE_EXPIRATION_DATE); 679 } 680 681 /** Returns a flag, indicating if the query and lastquery parameters should be ignored. E.g., if only the additional parameters should be used for the search. 682 * @return A flag, indicating if the query and lastquery parameters should be ignored. 683 */ 684 private Boolean getIgnoreQuery() { 685 686 return parseOptionalBooleanValue(XML_ELEMENT_IGNORE_QUERY); 687 } 688 689 /** Returns a flag indicating if also unreleased resources should be found. 690 * @return A flag indicating if also unreleased resources should be found. 691 */ 692 private Boolean getIgnoreReleaseDate() { 693 694 return parseOptionalBooleanValue(XML_ELEMENT_IGNORE_RELEASE_DATE); 695 } 696 697 /** Returns the configured Solr index, or <code>null</code> if the core is not specified. 698 * @return The configured Solr index, or <code>null</code> if the core is not specified. 699 */ 700 private String getIndex() { 701 702 try { 703 return parseMandatoryStringValue(XML_ELEMENT_INDEX); 704 } catch (final Exception e) { 705 LOG.info(Messages.get().getBundle().key(Messages.LOG_NO_INDEX_SPECIFIED_0), e); 706 return null; 707 } 708 } 709 710 /** Returns the configured request parameter for the last query, or the default parameter if the core is not specified. 711 * @return The configured request parameter for the last query, or the default parameter if the core is not specified. 712 */ 713 private String getLastQueryParam() { 714 715 final String param = parseOptionalStringValue(XML_ELEMENT_LAST_QUERYPARAM); 716 if (param == null) { 717 return DEFAULT_LAST_QUERY_PARAM; 718 } else { 719 return param; 720 } 721 } 722 723 /** Returns the configured length of the "Google"-like page navigation, or the default length if it is not configured. 724 * @return The configured length of the "Google"-like page navigation, or the default length if it is not configured. 725 */ 726 private Integer getPageNavLength() { 727 728 return parseOptionalIntValue(XML_ELEMENT_PAGENAVLENGTH); 729 } 730 731 /** Returns the configured request parameter for the current page, or the default parameter if the core is not specified. 732 * @return The configured request parameter for the current page, or the default parameter if the core is not specified. 733 */ 734 private String getPageParam() { 735 736 return parseOptionalStringValue(XML_ELEMENT_PAGEPARAM); 737 } 738 739 /** Returns the configured page size, or the default page size if it is not configured. 740 * @return The configured page size, or the default page size if it is not configured. 741 */ 742 private List<Integer> getPageSizes() { 743 744 final String pageSizes = parseOptionalStringValue(XML_ELEMENT_PAGESIZE); 745 if (pageSizes != null) { 746 String[] pageSizesArray = pageSizes.split("-"); 747 if (pageSizesArray.length > 0) { 748 try { 749 List<Integer> result = new ArrayList<>(pageSizesArray.length); 750 for (int i = 0; i < pageSizesArray.length; i++) { 751 result.add(Integer.valueOf(pageSizesArray[i])); 752 } 753 return result; 754 } catch (NumberFormatException e) { 755 LOG.warn(Messages.get().getBundle().key(Messages.LOG_PARSING_PAGE_SIZES_FAILED_1, pageSizes), e); 756 } 757 } 758 } 759 return null; 760 } 761 762 /** Returns the optional query modifier. 763 * @return the optional query modifier. 764 */ 765 private String getQueryModifier() { 766 767 return parseOptionalStringValue(XML_ELEMENT_QUERY_MODIFIER); 768 } 769 770 /** Returns the configured request parameter for the current query string, or the default parameter if the core is not specified. 771 * @return The configured request parameter for the current query string, or the default parameter if the core is not specified. 772 */ 773 private String getQueryParam() { 774 775 final String param = parseOptionalStringValue(XML_ELEMENT_QUERYPARAM); 776 if (param == null) { 777 return DEFAULT_QUERY_PARAM; 778 } else { 779 return param; 780 } 781 } 782 783 /** Returns a flag, indicating if search should be performed using a wildcard if the empty query is given. 784 * @return A flag, indicating if search should be performed using a wildcard if the empty query is given. 785 */ 786 private Boolean getSearchForEmtpyQuery() { 787 788 return parseOptionalBooleanValue(XML_ELEMENT_SEARCH_FOR_EMPTY_QUERY); 789 } 790 791 /** Returns the configured sort options, or the empty list if no such options are configured. 792 * @return The configured sort options, or the empty list if no such options are configured. 793 */ 794 private List<I_CmsSearchConfigurationSortOption> getSortOptions() { 795 796 final List<I_CmsSearchConfigurationSortOption> options = new ArrayList<I_CmsSearchConfigurationSortOption>(); 797 final CmsXmlContentValueSequence sortOptions = m_xml.getValueSequence(XML_ELEMENT_SORTOPTIONS, m_locale); 798 if (sortOptions == null) { 799 return null; 800 } else { 801 for (int i = 0; i < sortOptions.getElementCount(); i++) { 802 final I_CmsSearchConfigurationSortOption option = parseSortOption( 803 sortOptions.getValue(i).getPath() + "/"); 804 if (option != null) { 805 options.add(option); 806 } 807 } 808 return options; 809 } 810 } 811 812 /** Returns the configured request parameter for the current sort option, or the default parameter if the core is not specified. 813 * @return The configured request parameter for the current sort option, or the default parameter if the core is not specified. 814 */ 815 private String getSortParam() { 816 817 return parseOptionalStringValue(XML_ELEMENT_SORTPARAM); 818 } 819 820 /** Parses a single query facet item with query and label. 821 * @param prefix path to the query facet item (with trailing '/'). 822 * @return the query facet item. 823 */ 824 private I_CmsFacetQueryItem parseFacetQueryItem(final String prefix) { 825 826 I_CmsXmlContentValue query = m_xml.getValue(prefix + XML_ELEMENT_QUERY_FACET_QUERY_QUERY, m_locale); 827 if (null != query) { 828 String queryString = query.getStringValue(null); 829 I_CmsXmlContentValue label = m_xml.getValue(prefix + XML_ELEMENT_QUERY_FACET_QUERY_LABEL, m_locale); 830 String labelString = null != label ? label.getStringValue(null) : null; 831 return new CmsFacetQueryItem(queryString, labelString); 832 } else { 833 return null; 834 } 835 } 836 837 /** Helper to read a mandatory String value. 838 * @param path The XML path of the element to read. 839 * @return The String value stored in the XML. 840 * @throws Exception thrown if the value could not be read. 841 */ 842 private String parseMandatoryStringValue(final String path) throws Exception { 843 844 final String value = parseOptionalStringValue(path); 845 if (value == null) { 846 throw new Exception(); 847 } 848 return value; 849 } 850 851 /** Returns the configuration of a single sort option, or <code>null</code> if the XML cannot be read. 852 * @param pathPrefix The XML path to the root node of the sort option's configuration. 853 * @return The configuration of a single sort option, or <code>null</code> if the XML cannot be read. 854 */ 855 private I_CmsSearchConfigurationSortOption parseSortOption(final String pathPrefix) { 856 857 try { 858 final String solrValue = parseMandatoryStringValue(pathPrefix + XML_ELEMENT_SORTOPTION_SOLRVALUE); 859 String paramValue = parseOptionalStringValue(pathPrefix + XML_ELEMENT_SORTOPTION_PARAMVALUE); 860 paramValue = (paramValue == null) ? solrValue : paramValue; 861 String label = parseOptionalStringValue(pathPrefix + XML_ELEMENT_SORTOPTION_LABEL); 862 label = (label == null) ? paramValue : label; 863 return new CmsSearchConfigurationSortOption(label, paramValue, solrValue); 864 } catch (final Exception e) { 865 LOG.error( 866 Messages.get().getBundle().key( 867 Messages.ERR_SORT_OPTION_NOT_PARSABLE_1, 868 XML_ELEMENT_SORTOPTION_SOLRVALUE), 869 e); 870 return null; 871 } 872 } 873}