001/*
002 *  Copyright 2016 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.cms.search.cocoon;
017
018import java.io.IOException;
019import java.util.ArrayList;
020import java.util.Collection;
021import java.util.HashMap;
022import java.util.List;
023import java.util.Locale;
024import java.util.Map;
025
026import org.apache.avalon.framework.component.Component;
027import org.apache.commons.lang3.StringUtils;
028import org.xml.sax.ContentHandler;
029import org.xml.sax.SAXException;
030
031import org.ametys.cms.repository.Content;
032import org.ametys.cms.search.model.MetadataResultField;
033import org.ametys.cms.search.model.ResultField;
034import org.ametys.cms.search.model.SystemProperty;
035import org.ametys.plugins.repository.AmetysRepositoryException;
036import org.ametys.runtime.model.Model;
037import org.ametys.runtime.model.ModelItem;
038import org.ametys.runtime.model.View;
039import org.ametys.runtime.model.ViewElement;
040import org.ametys.runtime.model.ViewHelper;
041import org.ametys.runtime.model.ViewItemAccessor;
042import org.ametys.runtime.model.type.DataContext;
043
044/**
045 * Helper to genrate SAX event for result of a search based on the requested result fields.
046 */
047public class ContentResultSetHelper implements Component
048{
049    /** Avalon Role. */
050    public static final String ROLE = ContentResultSetHelper.class.getName();
051    
052    /**
053     * Generates SAX events for the items in the content's view. This method does not check rights.
054     * @param contentHandler the content handler where to SAX into.
055     * @param content the content.
056     * @param view the view.
057     * @param defaultLocale The locale to use to sax localized values such as multilingual content or multilingual string. 
058     * Only to be valued if initial content's language is null, otherwise set this parameter to null.
059     * @throws AmetysRepositoryException if an error occurs.
060     * @throws SAXException if an error occurs.
061     * @throws IOException if an error occurs.
062     */
063    public void viewToSAX(ContentHandler contentHandler, Content content, View view, Locale defaultLocale) throws AmetysRepositoryException, SAXException, IOException
064    {
065        DataContext context = DataContext.newInstance()
066                                         .withLocale(defaultLocale)
067                                         .withEmptyValues(false);
068        
069        content.dataToSAX(contentHandler, view, context);
070    }
071    
072    /**
073     * Generates SAX events for the result fields of a content. This method does not check rights.
074     * @param contentHandler the content handler where to SAX into.
075     * @param content the content.
076     * @param fields the result fields.
077     * @param defaultLocale The locale to use to sax localized values such as multilingual content or multilingual string. 
078     * Only to be valued if initial content's language is null, otherwise set this parameter to null.
079     * @throws AmetysRepositoryException if an error occurs.
080     * @throws SAXException if an error occurs.
081     * @throws IOException if an error occurs.
082     * @deprecated use {@link #viewToSAX(ContentHandler, Content, View, Locale)} instead
083     */
084    @Deprecated
085    public void saxResultFields(ContentHandler contentHandler, Content content, Collection<? extends ResultField> fields, Locale defaultLocale) throws AmetysRepositoryException, SAXException, IOException
086    {
087        View view  = _buildViewFromResultFields(fields, content);
088        viewToSAX(contentHandler, content, view, defaultLocale);
089    }
090    
091    private View _buildViewFromResultFields(Collection<? extends ResultField> fields, Content content)
092    {
093        List<String> modelItemPaths = new ArrayList<>();
094        Map<String, SystemProperty> systemPropertiesByPaths = new HashMap<>();
095        
096        // Get all model items from result fields
097        for (ResultField field : fields)
098        {
099            String fieldPath = field instanceof MetadataResultField ? ((MetadataResultField) field).getFieldPath() : field.getId();
100            ModelItem modelItem = content.getDefinition(fieldPath);
101            if (modelItem instanceof SystemProperty systemProperty)
102            {
103                // System properties are not part of the content model, so they have to be added to the view separately
104                systemPropertiesByPaths.put(fieldPath, systemProperty);
105            }
106            else
107            {
108                modelItemPaths.add(fieldPath);
109            }
110        }
111        
112        // Create view with all attributes and properties (not system)
113        View view =  View.of(content.getModel(), modelItemPaths.toArray(String[]::new));
114        
115        // Add view items for system properties
116        for (String systemPropertyPath : systemPropertiesByPaths.keySet())
117        {
118            // Create view items for parents
119            // Created view items respect the model hierarchy
120            int lastIndexOfItemPathSeparator = systemPropertyPath.lastIndexOf(ModelItem.ITEM_PATH_SEPARATOR);
121            String parentPath = lastIndexOfItemPathSeparator > -1 ? systemPropertyPath.substring(0, lastIndexOfItemPathSeparator) : StringUtils.EMPTY;
122            ViewItemAccessor viewItemAccessor = StringUtils.isEmpty(parentPath) ? view : (ViewItemAccessor) ViewHelper.addViewItem(parentPath, view, content.getModel().toArray(Model[]::new));
123            
124            // Create a view element for system property
125            ViewElement systemPropertyViewItem = new ViewElement();
126            systemPropertyViewItem.setDefinition(systemPropertiesByPaths.get(systemPropertyPath));
127            
128            // Add the view element to the parent view item accessor
129            viewItemAccessor.addViewItem(systemPropertyViewItem);
130        }
131        
132        return view;
133    }
134}