001/* 002 * Copyright 2017 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.plugins.extraction.component; 017 018import java.util.ArrayList; 019import java.util.Arrays; 020import java.util.Collection; 021import java.util.Collections; 022import java.util.HashSet; 023import java.util.LinkedHashSet; 024import java.util.List; 025import java.util.Locale; 026import java.util.Map; 027import java.util.Map.Entry; 028import java.util.Optional; 029import java.util.Set; 030import java.util.regex.Matcher; 031import java.util.regex.Pattern; 032import java.util.stream.Collectors; 033 034import org.apache.avalon.framework.configuration.Configuration; 035import org.apache.avalon.framework.configuration.ConfigurationException; 036import org.apache.avalon.framework.service.ServiceException; 037import org.apache.avalon.framework.service.ServiceManager; 038import org.apache.commons.lang3.StringUtils; 039import org.apache.solr.client.solrj.util.ClientUtils; 040import org.xml.sax.ContentHandler; 041 042import org.ametys.cms.content.ContentHelper; 043import org.ametys.cms.contenttype.ContentType; 044import org.ametys.cms.data.ContentValue; 045import org.ametys.cms.data.type.ModelItemTypeConstants; 046import org.ametys.cms.data.type.ModelItemTypeExtensionPoint; 047import org.ametys.cms.repository.Content; 048import org.ametys.cms.search.GetQueryFromJSONHelper; 049import org.ametys.cms.search.QueryBuilder; 050import org.ametys.cms.search.content.ContentSearcherFactory; 051import org.ametys.cms.search.content.ContentSearcherFactory.SimpleContentSearcher; 052import org.ametys.cms.search.model.SearchModel; 053import org.ametys.cms.search.model.SystemProperty; 054import org.ametys.cms.search.model.SystemPropertyExtensionPoint; 055import org.ametys.cms.search.query.QuerySyntaxException; 056import org.ametys.cms.search.solr.SolrContentQueryHelper; 057import org.ametys.core.util.JSONUtils; 058import org.ametys.core.util.LambdaUtils; 059import org.ametys.plugins.extraction.execution.Extraction.ClausesVariable; 060import org.ametys.plugins.extraction.execution.Extraction.ClausesVariableType; 061import org.ametys.plugins.extraction.execution.ExtractionExecutionContext; 062import org.ametys.plugins.extraction.execution.ExtractionExecutionContextHierarchyElement; 063import org.ametys.plugins.queriesdirectory.Query; 064import org.ametys.plugins.repository.AmetysObjectIterable; 065import org.ametys.plugins.repository.AmetysObjectResolver; 066import org.ametys.plugins.repository.EmptyIterable; 067import org.ametys.plugins.thesaurus.ThesaurusDAO; 068import org.ametys.runtime.model.ModelHelper; 069import org.ametys.runtime.model.ModelItem; 070import org.ametys.runtime.model.type.ElementType; 071import org.ametys.runtime.model.type.ModelItemType; 072 073/** 074 * This class represents an extraction component with a solr query 075 */ 076public abstract class AbstractSolrExtractionComponent extends AbstractExtractionComponent 077{ 078 /** Regex used to extract variables from an expression. A variable is inside a ${} */ 079 private static final String __EXTRACT_VARIABLES_REGEX = "\\$\\{([^{}]+)\\}"; 080 081 /** 082 * Regex used to check variables from a join expression: \.\.(?:\/\.\.)*(?:\/[^\/}]+)? 083 * variable starts with .. (to get the direct parent), 084 * has several /.. (to get parent of parent of (...)) 085 * and can have a /metadataName (to specify the metadata to join on) 086 */ 087 private static final String __CHECK_JOIN_VARIABLES_REGEX = "\\.\\.(?:\\/\\.\\.)*(?:\\/[^\\/}]+)?"; 088 089 /** Content types concerned by the solr search */ 090 protected Set<String> _contentTypes = new HashSet<>(); 091 092 /** Reference id of a recorded query */ 093 protected String _queryReferenceId; 094 095 /** The list of clauses */ 096 protected List<ExtractionClause> _clauses = new ArrayList<>(); 097 098 /** Helper to resolve referenced query infos */ 099 protected GetQueryFromJSONHelper _getQueryFromJSONHelper; 100 101 /** Util class to manipulate JSON String */ 102 protected JSONUtils _jsonUtils; 103 104 private AmetysObjectResolver _resolver; 105 private SystemPropertyExtensionPoint _systemPropertyExtensionPoint; 106 private ContentHelper _contentHelper; 107 private ContentSearcherFactory _contentSearcherFactory; 108 private QueryBuilder _queryBuilder; 109 private ModelItemTypeExtensionPoint _contentAttributeTypeExtensionPoint; 110 111 @Override 112 public void service(ServiceManager serviceManager) throws ServiceException 113 { 114 super.service(serviceManager); 115 _jsonUtils = (JSONUtils) serviceManager.lookup(JSONUtils.ROLE); 116 _getQueryFromJSONHelper = (GetQueryFromJSONHelper) serviceManager.lookup(GetQueryFromJSONHelper.ROLE); 117 _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE); 118 _systemPropertyExtensionPoint = (SystemPropertyExtensionPoint) serviceManager.lookup(SystemPropertyExtensionPoint.ROLE); 119 _contentHelper = (ContentHelper) serviceManager.lookup(ContentHelper.ROLE); 120 _contentSearcherFactory = (ContentSearcherFactory) serviceManager.lookup(ContentSearcherFactory.ROLE); 121 _queryBuilder = (QueryBuilder) serviceManager.lookup(QueryBuilder.ROLE); 122 _contentAttributeTypeExtensionPoint = (ModelItemTypeExtensionPoint) serviceManager.lookup(ModelItemTypeExtensionPoint.ROLE_CONTENT_ATTRIBUTE); 123 } 124 125 @Override 126 public void configure(Configuration configuration) throws ConfigurationException 127 { 128 super.configure(configuration); 129 130 Configuration clauses = configuration.getChild("clauses"); 131 for (Configuration clause : clauses.getChildren("clause")) 132 { 133 addClauses(clause.getValue()); 134 } 135 136 _contentTypes = new HashSet<>(); 137 if (Arrays.asList(configuration.getAttributeNames()).contains("ref")) 138 { 139 if (Arrays.asList(configuration.getAttributeNames()).contains("contentTypes")) 140 { 141 throw new IllegalArgumentException(getLogsPrefix() + "a component with a query reference should not specify a content type"); 142 } 143 144 _queryReferenceId = configuration.getAttribute("ref"); 145 } 146 else 147 { 148 String contentTypesString = configuration.getAttribute("contentTypes"); 149 _contentTypes.addAll(org.ametys.core.util.StringUtils.stringToCollection(contentTypesString)); 150 } 151 } 152 153 @Override 154 public void prepareComponentExecution(ExtractionExecutionContext context) throws Exception 155 { 156 super.prepareComponentExecution(context); 157 158 if (_queryReferenceId != null && !_queryReferenceId.isEmpty()) 159 { 160 Query referencedQuery = _resolver.resolveById(_queryReferenceId); 161 computeReferencedQueryInfos(referencedQuery.getContent()); 162 } 163 164 _computeClausesInfos(context); 165 } 166 167 /** 168 * Manages the stored query referenced by the component 169 * @param refQueryContent referenced query content 170 * @throws QuerySyntaxException if there is a syntax error in the referenced query 171 */ 172 @SuppressWarnings("unchecked") 173 protected void computeReferencedQueryInfos(String refQueryContent) throws QuerySyntaxException 174 { 175 Map<String, Object> contentMap = _jsonUtils.convertJsonToMap(refQueryContent); 176 Map<String, Object> exportParams = (Map<String, Object>) contentMap.get("exportParams"); 177 String modelId = (String) exportParams.get("model"); 178 179 String q; 180 if (modelId.contains("solr")) 181 { 182 Map<String, Object> values = (Map<String, Object>) exportParams.get("values"); 183 String baseQuery = (String) values.get("query"); 184 185 _contentTypes = new HashSet<>((List<String>) values.get("contentTypes")); 186 187 q = SolrContentQueryHelper.buildQuery(_queryBuilder, baseQuery, _contentTypes, Collections.emptySet()); 188 } 189 else 190 { 191 SearchModel model = _getQueryFromJSONHelper.getSearchModel(exportParams); 192 List<String> contentTypesToFill = new ArrayList<>(); 193 org.ametys.cms.search.query.Query query = _getQueryFromJSONHelper.getQueryFromModel(model, exportParams, contentTypesToFill); 194 195 q = query.build(); 196 _contentTypes = new HashSet<>(contentTypesToFill); 197 } 198 199 ExtractionClause clause = new ExtractionClause(); 200 clause.setExpression(q); 201 _clauses.add(0, clause); 202 } 203 204 private void _computeClausesInfos(ExtractionExecutionContext context) 205 { 206 for (ExtractionClause clause : _clauses) 207 { 208 String clauseExpression = clause.getExpression(); 209 clause.setExpression(clauseExpression); 210 211 List<ExtractionClauseGroup> groups = _extractGroupExpressionsFromClause(clauseExpression); 212 if (!groups.isEmpty()) 213 { 214 Collection<String> groupExpressions = groups.stream() 215 .map(ExtractionClauseGroup::getCompleteExpression) 216 .collect(Collectors.toList()); 217 if (_hasVariablesOutsideGroups(clauseExpression, groupExpressions, context.getClausesVariablesValues().keySet())) 218 { 219 throw new IllegalArgumentException(getLogsPrefix() + "if there's at least one group, every variable should be in a group."); 220 } 221 } 222 else 223 { 224 // The only group is the entire expression 225 // The complete expression is the same as the classic one (there is no characters used to identify the group) 226 ExtractionClauseGroup group = new ExtractionClauseGroup(); 227 group.setCompleteExpression(clauseExpression); 228 group.setExpression(clauseExpression); 229 groups.add(group); 230 } 231 232 for (ExtractionClauseGroup group : groups) 233 { 234 Set<String> variables = new HashSet<>(_extractVariablesFromClauseExpression(group.getExpression(), context.getClausesVariablesValues().keySet())); 235 if (!variables.isEmpty()) 236 { 237 if (variables.size() > 1) 238 { 239 throw new IllegalArgumentException(getLogsPrefix() + "only variables with same name are allowed within a single group"); 240 } 241 242 for (String variable : variables) 243 { 244 String[] pathSegments = variable.split(JOIN_HIERARCHY_SEPARATOR); 245 String fieldPath = pathSegments[pathSegments.length - 1]; 246 247 group.setVariable(variable); 248 group.setFieldPath(fieldPath); 249 } 250 } 251 252 clause.addGroup(group); 253 } 254 } 255 } 256 257 private boolean _hasVariablesOutsideGroups(String clauseExpression, Collection<String> groupExpressions, Collection<ClausesVariable> clausesVariable) 258 { 259 List<String> variablesInClause = _extractVariablesFromClauseExpression(clauseExpression, clausesVariable); 260 List<String> variablesInGroups = new ArrayList<>(); 261 for (String groupExpression : groupExpressions) 262 { 263 variablesInGroups.addAll(_extractVariablesFromClauseExpression(groupExpression, clausesVariable)); 264 } 265 return variablesInClause.size() > variablesInGroups.size(); 266 } 267 268 List<ExtractionClauseGroup> _extractGroupExpressionsFromClause(String expression) 269 { 270 List<ExtractionClauseGroup> groupExpressions = new ArrayList<>(); 271 int indexOfGroup = expression.indexOf("#{"); 272 while (indexOfGroup != -1) 273 { 274 StringBuilder currentGroupSb = new StringBuilder(); 275 int endIndex = indexOfGroup; 276 int braceLevel = 0; 277 for (int i = indexOfGroup + 2; i < expression.length(); i++) 278 { 279 endIndex = i; 280 char currentChar = expression.charAt(i); 281 if ('{' == currentChar) 282 { 283 braceLevel++; 284 } 285 else if ('}' == currentChar) 286 { 287 if (0 == braceLevel) 288 { 289 ExtractionClauseGroup group = new ExtractionClauseGroup(); 290 String currentGroup = currentGroupSb.toString(); 291 group.setCompleteExpression("#{" + currentGroup + "}"); 292 group.setExpression(currentGroup); 293 groupExpressions.add(group); 294 break; 295 } 296 braceLevel--; 297 } 298 currentGroupSb.append(currentChar); 299 } 300 301 indexOfGroup = expression.indexOf("#{", endIndex); 302 } 303 return groupExpressions; 304 } 305 306 List<String> _extractVariablesFromClauseExpression(String expression, Collection<ClausesVariable> clausesVariable) 307 { 308 List<String> variableNames = new ArrayList<>(); 309 310 Pattern variablePattern = Pattern.compile(__EXTRACT_VARIABLES_REGEX); 311 Matcher variableMatcher = variablePattern.matcher(expression); 312 313 while (variableMatcher.find()) 314 { 315 String variableName = variableMatcher.group(1); 316 Pattern joinPattern = Pattern.compile(__CHECK_JOIN_VARIABLES_REGEX); 317 Matcher joinMatcher = joinPattern.matcher(variableName); 318 if (clausesVariable.stream().map(var -> var.name()).anyMatch(variableName::equals) || joinMatcher.matches()) 319 { 320 variableNames.add(variableName); 321 } 322 } 323 324 return variableNames; 325 } 326 327 @Override 328 public void executeComponent(ContentHandler contentHandler, ExtractionExecutionContext context) throws Exception 329 { 330 Iterable<Content> contents = getContents(context); 331 processContents(contents, contentHandler, context); 332 } 333 334 List<String> _getClauseQueries(ExtractionExecutionContext context) 335 { 336 List<String> clauseQueries = new ArrayList<>(); 337 338 for (ExtractionClause clause : _clauses) 339 { 340 String expression = clause.getExpression(); 341 342 // Resolve all groups 343 for (ExtractionClauseGroup group : clause.getGroups()) 344 { 345 String variableName = group.getVariable(); 346 347 if (StringUtils.isNotEmpty(variableName)) 348 { 349 Collection<String> groupExpressions = new ArrayList<>(); 350 351 if (context.getClausesVariablesValues() 352 .keySet() 353 .stream() 354 .anyMatch(clausesVariable -> clausesVariable.name().equals(variableName))) 355 { 356 Entry<ClausesVariable, List<String>> clausesVariableValues = context.getClausesVariablesValues() 357 .entrySet() 358 .stream() 359 .filter(entry -> entry.getKey().name().equals(variableName)) 360 .findFirst() 361 .get(); 362 363 if (ClausesVariableType.SELECT_CONTENTS.equals(clausesVariableValues.getKey().type())) 364 { 365 String attributeTypeId = ModelItemTypeConstants.CONTENT_ELEMENT_TYPE_ID; 366 Collection<Object> values = clausesVariableValues.getValue() 367 .stream() 368 .map(Object.class::cast) 369 .collect(Collectors.toList()); 370 371 for (Object value : values) 372 { 373 String valueAsString = _getValueAsString(value, attributeTypeId, group.getFieldPath()); 374 groupExpressions.add("(" + group.getExpression().replace("${" + variableName + "}", valueAsString) + ")"); 375 } 376 } 377 else 378 { 379 List<String> solrRequests = clausesVariableValues.getValue(); 380 if (solrRequests.size() == 1) 381 { 382 groupExpressions.add("(" + group.getExpression().replace("${" + variableName + "}", solrRequests.get(0)) + ")"); 383 } 384 } 385 } 386 else 387 { 388 ExtractionExecutionContextHierarchyElement currentContextHierarchyElement = _getCurrentContextElementFromVariable(variableName, context.getHierarchyElements()); 389 390 ExtractionComponent contextComponent = currentContextHierarchyElement.getComponent(); 391 String attributeTypeId = _getAttributeTypeId(group.getFieldPath(), contextComponent.getContentTypes()); 392 Collection<Object> values = _getValuesFromVariable(group.getFieldPath(), attributeTypeId, currentContextHierarchyElement, context.getDefaultLocale()); 393 394 for (Object value : values) 395 { 396 String valueAsString = _getValueAsString(value, attributeTypeId, group.getFieldPath()); 397 groupExpressions.add("(" + group.getExpression().replace("${" + variableName + "}", valueAsString) + ")"); 398 } 399 } 400 401 if (groupExpressions.isEmpty()) 402 { 403 getLogger().warn(getLogsPrefix() + "no value found for field '" + group.getFieldPath() + "'. The query of this component can't be achieved"); 404 return null; 405 } 406 407 String groupReplacement = StringUtils.join(groupExpressions, " OR "); 408 expression = expression.replace(group.getCompleteExpression(), "(" + groupReplacement + ")"); 409 } 410 } 411 412 clauseQueries.add(expression); 413 } 414 415 return clauseQueries; 416 } 417 418 private ExtractionExecutionContextHierarchyElement _getCurrentContextElementFromVariable(String variable, List<ExtractionExecutionContextHierarchyElement> context) 419 { 420 int lastIndexOfSlash = variable.lastIndexOf(JOIN_HIERARCHY_SEPARATOR); 421 int indexOfCurrentContext = -1; 422 if (lastIndexOfSlash == -1) 423 { 424 indexOfCurrentContext = context.size() - 1; 425 } 426 else 427 { 428 int hierarchicalLevel = (lastIndexOfSlash + 1) / 3; 429 indexOfCurrentContext = context.size() - hierarchicalLevel; 430 if (variable.endsWith(JOIN_HIERARCHY_ELEMENT)) 431 { 432 indexOfCurrentContext--; 433 } 434 } 435 if (indexOfCurrentContext < 0 || indexOfCurrentContext >= context.size()) 436 { 437 throw new IllegalArgumentException(getLogsPrefix() + "join on '" + variable + "' does not refer to an existing parent"); 438 } 439 return context.get(indexOfCurrentContext); 440 } 441 442 /** 443 * Retrieves the field path's attribute type identifier from content types 444 * @param fieldPath the field path 445 * @param contentTypeIds the content types identifiers 446 * @return the attribute type identifier 447 */ 448 protected String _getAttributeTypeId(String fieldPath, Collection<String> contentTypeIds) 449 { 450 // Manage direct content references 451 if (JOIN_HIERARCHY_ELEMENT.equals(fieldPath)) 452 { 453 return ModelItemTypeConstants.CONTENT_ELEMENT_TYPE_ID; 454 } 455 456 // Manage System Properties 457 String[] pathSegments = fieldPath.split(EXTRACTION_ITEM_PATH_SEPARATOR); 458 String propertyName = pathSegments[pathSegments.length - 1]; 459 if (_systemPropertyExtensionPoint.hasExtension(propertyName)) 460 { 461 SystemProperty systemProperty = _systemPropertyExtensionPoint.getExtension(propertyName); 462 return systemProperty.getType().getId(); 463 } 464 465 String fieldPathWthClassicSeparator = fieldPath.replaceAll(EXTRACTION_ITEM_PATH_SEPARATOR, ModelItem.ITEM_PATH_SEPARATOR); 466 Collection<ContentType> contentTypes = contentTypeIds.stream() 467 .map(_contentTypeExtensionPoint::getExtension) 468 .collect(Collectors.toList()); 469 470 if (ModelHelper.hasModelItem(fieldPathWthClassicSeparator, contentTypes)) 471 { 472 ModelItem modelItem = ModelHelper.getModelItem(fieldPathWthClassicSeparator, contentTypes); 473 return modelItem.getType().getId(); 474 } 475 476 throw new IllegalArgumentException(getLogsPrefix() + "join on '" + fieldPath + "'. This attribute is not available"); 477 } 478 479 private Collection<Object> _getValuesFromVariable(String fieldPath, String attributeTypeId, ExtractionExecutionContextHierarchyElement contextHierarchyElement, Locale defaultLocale) 480 { 481 Collection<Object> values = new LinkedHashSet<>(); 482 483 Iterable<Content> contents = contextHierarchyElement.getContents(); 484 for (Content content: contents) 485 { 486 boolean isAutoposting = contextHierarchyElement.isAutoposting(); 487 Collection<Object> contentValues = _getContentValuesFromVariable(content, fieldPath, attributeTypeId, isAutoposting, defaultLocale); 488 values.addAll(contentValues); 489 } 490 491 return values; 492 } 493 494 private Collection<Object> _getContentValuesFromVariable(Content content, String fieldPath, String attributeTypeId, boolean isAutoposting, Locale defaultLocale) 495 { 496 Collection<Object> values = new LinkedHashSet<>(); 497 498 Object value = _getContentValue(content, fieldPath); 499 if (value == null) 500 { 501 return Collections.emptyList(); 502 } 503 504 if (value instanceof Collection<?>) 505 { 506 values.addAll((Collection<?>) value); 507 } 508 else 509 { 510 values.add(value); 511 } 512 513 Collection<Object> result = new LinkedHashSet<>(values); 514 515 if (isAutoposting && ModelItemTypeConstants.CONTENT_ELEMENT_TYPE_ID.equals(attributeTypeId)) 516 { 517 for (Object object : values) 518 { 519 Optional<? extends Content> parent = object instanceof ContentValue ? ((ContentValue) object).getContentIfExists() : Optional.of((Content) object); 520 ContentType contentType = parent.map(_contentTypesHelper::getFirstContentType) 521 .orElse(null); 522 523 // Manage autoposting only if the current value is a thesaurus term 524 if (contentType != null && Arrays.asList(_contentTypesHelper.getSupertypeIds(contentType.getId()).getLeft()).contains(ThesaurusDAO.MICROTHESAURUS_ABSTRACT_CONTENT_TYPE)) 525 { 526 AmetysObjectIterable<Content> chidren = _thesaurusDAO.getChildTerms(contentType.getId(), parent.get().getId()); 527 for (Content child : chidren) 528 { 529 Collection<Object> childValues = _getContentValuesFromVariable(child, JOIN_HIERARCHY_ELEMENT, attributeTypeId, isAutoposting, defaultLocale); 530 result.addAll(childValues); 531 } 532 } 533 } 534 } 535 536 return result; 537 } 538 539 private Object _getContentValue(Content content, String fieldPath) 540 { 541 if (JOIN_HIERARCHY_ELEMENT.equals(fieldPath)) 542 { 543 return content; 544 } 545 else 546 { 547 String fieldPathWthClassicSeparator = fieldPath.replaceAll(EXTRACTION_ITEM_PATH_SEPARATOR, ModelItem.ITEM_PATH_SEPARATOR); 548 return _contentHelper.getValue(content, fieldPathWthClassicSeparator); 549 } 550 } 551 552 private <T> String _getValueAsString(Object value, String attributeTypeId, String fieldPath) 553 { 554 ModelItemType modelItemType = _contentAttributeTypeExtensionPoint.getExtension(attributeTypeId); 555 if (modelItemType instanceof ElementType) 556 { 557 @SuppressWarnings("unchecked") 558 ElementType<T> elementType = (ElementType<T>) modelItemType; 559 T typedValue = elementType.castValue(value); 560 String valueAsString = elementType.toString(typedValue); 561 return ClientUtils.escapeQueryChars(valueAsString); 562 } 563 else 564 { 565 throw new IllegalArgumentException(getLogsPrefix() + "join on '" + fieldPath + "'. Attribute type '" + attributeTypeId + "' is not supported by extraction module"); 566 } 567 } 568 569 /** 570 * Retrieves the content searcher to use for solr search 571 * @return the content searcher 572 */ 573 protected SimpleContentSearcher getContentSearcher() 574 { 575 return _contentSearcherFactory.create(_contentTypes); 576 } 577 578 /** 579 * Gets the content results from Solr 580 * @param context component execution context 581 * @return the content results from Solr 582 * @throws Exception if an error occurs 583 */ 584 protected Iterable<Content> getContents(ExtractionExecutionContext context) throws Exception 585 { 586 List<String> clauseQueries = _getClauseQueries(context); 587 if (clauseQueries == null) 588 { 589 return new EmptyIterable<>(); 590 } 591 else 592 { 593 List<String> filterQueryStrings = clauseQueries 594 .stream() 595 .map(LambdaUtils.wrap(clauseQuery -> SolrContentQueryHelper.buildQuery(_queryBuilder, clauseQuery, Collections.emptySet(), Collections.emptySet()))) 596 .collect(Collectors.toList()); 597 return getContentSearcher() 598 .withFilterQueryStrings(filterQueryStrings) 599 .setCheckRights(false) 600 .search("*:*"); 601 } 602 } 603 604 /** 605 * Process result contents to format the result document 606 * @param contents search results 607 * @param contentHandler result document 608 * @param context component execution context 609 * @throws Exception if an error occurs 610 */ 611 protected abstract void processContents(Iterable<Content> contents, ContentHandler contentHandler, ExtractionExecutionContext context) throws Exception; 612 613 @Override 614 public Map<String, Object> getComponentDetailsForTree() 615 { 616 Map<String, Object> details = super.getComponentDetailsForTree(); 617 618 @SuppressWarnings("unchecked") 619 Map<String, Object> data = (Map<String, Object>) details.get("data"); 620 621 List<String> clauses = new ArrayList<>(); 622 for (ExtractionClause clause : this.getClauses()) 623 { 624 clauses.add(clause.getExpression()); 625 } 626 data.put("clauses", clauses); 627 628 data.put("useQueryRef", StringUtils.isNotEmpty(_queryReferenceId)); 629 data.put("contentTypes", this.getContentTypes()); 630 data.put("queryReferenceId", this.getQueryReferenceId()); 631 632 return details; 633 } 634 635 public Set<String> getContentTypes() 636 { 637 return _contentTypes; 638 } 639 640 /** 641 * Add content types to component 642 * @param contentTypes Array of content types to add 643 */ 644 public void addContentTypes(String... contentTypes) 645 { 646 _contentTypes.addAll(Arrays.asList(contentTypes)); 647 } 648 649 /** 650 * Retrieves the id of the referenced query 651 * @return the id of the referenced query 652 */ 653 public String getQueryReferenceId() 654 { 655 return _queryReferenceId; 656 } 657 658 /** 659 * Sets the id of the referenced query 660 * @param queryReferenceId The id of the referenced query to set 661 */ 662 public void setQueryReferenceId(String queryReferenceId) 663 { 664 _queryReferenceId = queryReferenceId; 665 } 666 667 /** 668 * Retrieves the component clauses 669 * @return the component clauses 670 */ 671 public List<ExtractionClause> getClauses() 672 { 673 return _clauses; 674 } 675 676 /** 677 * Add clauses to the component. Do not manage clauses' groups 678 * @param expressions Array clauses expressions to add 679 */ 680 public void addClauses(String... expressions) 681 { 682 for (String expression : expressions) 683 { 684 ExtractionClause clause = new ExtractionClause(); 685 clause.setExpression(expression); 686 _clauses.add(clause); 687 } 688 } 689}