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.List; 022import java.util.Locale; 023import java.util.Map; 024 025import org.apache.avalon.framework.configuration.Configuration; 026import org.apache.avalon.framework.configuration.ConfigurationException; 027import org.apache.avalon.framework.service.ServiceException; 028import org.apache.avalon.framework.service.ServiceManager; 029import org.apache.cocoon.xml.AttributesImpl; 030import org.apache.cocoon.xml.XMLUtils; 031import org.xml.sax.ContentHandler; 032 033import org.ametys.cms.contenttype.ContentType; 034import org.ametys.cms.repository.Content; 035import org.ametys.cms.search.GroupSearchContentHelper; 036import org.ametys.cms.search.cocoon.GroupSearchContent; 037import org.ametys.cms.search.model.ResultField; 038import org.ametys.core.util.StringUtils; 039import org.ametys.plugins.extraction.ExtractionConstants; 040import org.ametys.plugins.extraction.execution.ExtractionExecutionContext; 041 042/** 043 * This class represents an extraction component with grouping fields 044 */ 045public abstract class AbstractGroupExtractionComponent extends AbstractSolrExtractionComponent 046{ 047 048 /** The list of grouping fields */ 049 protected List<String> _groupingFields = new ArrayList<>(); 050 051 private GroupSearchContentHelper _groupSearchContentHelper; 052 053 @Override 054 public void service(ServiceManager serviceManager) throws ServiceException 055 { 056 super.service(serviceManager); 057 _groupSearchContentHelper = (GroupSearchContentHelper) serviceManager.lookup(GroupSearchContentHelper.ROLE); 058 } 059 060 @Override 061 public void configure(Configuration configuration) throws ConfigurationException 062 { 063 super.configure(configuration); 064 065 String groupingFieldsAsString = configuration.getChild("grouping-fields").getValue(""); 066 _groupingFields.addAll(StringUtils.stringToCollection(groupingFieldsAsString)); 067 } 068 069 @Override 070 public void prepareComponentExecution(ExtractionExecutionContext context) throws Exception 071 { 072 super.prepareComponentExecution(context); 073 074 for (String groupingField : _groupingFields) 075 { 076 // get metadata types just to throw an Exception if the field is not available for content types 077 this._getMetadataType(groupingField, _contentTypes); 078 } 079 } 080 081 /** 082 * Organizes contents in groups 083 * @param contents The contents to organize 084 * @param defaultLocale The default locale for localized values. Only useful if grouping by multilingual contents or multilingual string 085 * @return groups of content 086 */ 087 protected GroupSearchContent organizeContentsInGroups(Iterable<Content> contents, Locale defaultLocale) 088 { 089 String baseContentTypeId = _contentTypesHelper.getCommonAncestor(_contentTypes); 090 ContentType baseContentType = null; 091 if (baseContentTypeId != null) 092 { 093 baseContentType = _contentTypeExtensionPoint.getExtension(baseContentTypeId); 094 } 095 GroupSearchContent rootGroup = _groupSearchContentHelper.organizeContentsInGroups(contents, _groupingFields, baseContentType, defaultLocale); 096 return rootGroup; 097 } 098 099 /** 100 * Recursive method that saxes group of contents 101 * First level (level=0) will not create a xml group (usually, 1st group is just here to handle a list) 102 * @param contentHandler result document 103 * @param group group to sax 104 * @param level current level (start with zero) 105 * @param context execution context 106 * @param resultFields result fields to sax 107 * @throws Exception if a error occurred 108 */ 109 protected void saxGroup(ContentHandler contentHandler, GroupSearchContent group, int level, ExtractionExecutionContext context, Collection< ? extends ResultField> resultFields) throws Exception 110 { 111 if (level > 0) 112 { 113 AttributesImpl attrs = new AttributesImpl(); 114 attrs.addCDATAAttribute("level", String.valueOf(level)); 115 attrs.addCDATAAttribute("fieldPath", group.getGroupFieldPath()); 116 attrs.addCDATAAttribute("value", group.getGroupName()); 117 XMLUtils.startElement(contentHandler, "group", attrs); 118 } 119 if (group.getSubList() != null) 120 { 121 for (GroupSearchContent groupSearchContent : group.getSubList()) 122 { 123 saxGroup(contentHandler, groupSearchContent, level + 1, context, resultFields); 124 } 125 } 126 if (group.getContents() != null) 127 { 128 saxContents(contentHandler, context, resultFields, group.getContents()); 129 } 130 131 if (level > 0) 132 { 133 XMLUtils.endElement(contentHandler, "group"); 134 } 135 } 136 137 /** 138 * Sax a content 139 * @param contentHandler result document 140 * @param context execution context 141 * @param resultFields result fields to sax 142 * @param contents contents to sax 143 * @throws Exception if an error occurs 144 */ 145 protected abstract void saxContents(ContentHandler contentHandler, ExtractionExecutionContext context, Collection< ? extends ResultField> resultFields, List<Content> contents) throws Exception; 146 147 @Override 148 public Map<String, Object> getComponentDetailsForTree() 149 { 150 Map<String, Object> details = super.getComponentDetailsForTree(); 151 152 @SuppressWarnings("unchecked") 153 Map<String, Object> data = (Map<String, Object>) details.get("data"); 154 data.put("groupingFields", String.join(ExtractionConstants.STRING_COLLECTIONS_INPUT_DELIMITER, this.getGroupingFields())); 155 156 return details; 157 } 158 159 /** 160 * Retrieves the component grouping fields 161 * @return The component grouping fields 162 */ 163 public List<String> getGroupingFields() 164 { 165 return _groupingFields; 166 } 167 168 /** 169 * Add grouping fields to the component 170 * @param groupingFields Array of the grouping fields to add 171 */ 172 public void addGroupingFields(String... groupingFields) 173 { 174 _groupingFields.addAll(Arrays.asList(groupingFields)); 175 } 176}