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.contentio.synchronize;
017
018import java.util.ArrayList;
019import java.util.Collection;
020import java.util.HashMap;
021import java.util.LinkedHashMap;
022import java.util.List;
023import java.util.Map;
024import java.util.Objects;
025import java.util.stream.Collectors;
026import java.util.stream.Stream;
027
028import javax.jcr.Node;
029import javax.jcr.RepositoryException;
030import javax.jcr.Value;
031
032import org.apache.avalon.framework.component.Component;
033import org.apache.avalon.framework.service.ServiceException;
034import org.apache.avalon.framework.service.ServiceManager;
035import org.apache.avalon.framework.service.Serviceable;
036
037import org.ametys.cms.repository.Content;
038import org.ametys.cms.repository.DefaultContent;
039import org.ametys.plugins.repository.AmetysRepositoryException;
040import org.ametys.runtime.plugin.component.AbstractLogEnabled;
041
042/**
043 * Helper for Synchronizable Contents Collections.
044 */
045public class SynchronizableContentsCollectionHelper extends AbstractLogEnabled implements Serviceable, Component
046{
047    /** The Avalon Role */
048    public static final String ROLE = SynchronizableContentsCollectionHelper.class.getName();
049    
050    /** SCC DAO */
051    protected SynchronizableContentsCollectionDAO _sccDAO;
052    
053    @Override
054    public void service(ServiceManager smanager) throws ServiceException
055    {
056        _sccDAO = (SynchronizableContentsCollectionDAO) smanager.lookup(SynchronizableContentsCollectionDAO.ROLE);
057    }
058    
059    /**
060     * Get the first {@link SynchronizableContentsCollection} found for the given SCC model id.
061     * @param modelId Id of the SCC model
062     * @return The first SCC found or null
063     */
064    public SynchronizableContentsCollection getSCCFromModelId(String modelId)
065    {
066        SynchronizableContentsCollection collection = null;
067
068        // Get the first collection corresponding to the SCC model
069        for (SynchronizableContentsCollection scc : _sccDAO.getSynchronizableContentsCollections())
070        {
071            if (scc.getSynchronizeCollectionModelId().equals(modelId))
072            {
073                collection = scc;
074                break;
075            }
076        }
077        
078        return collection;
079    }
080
081    /**
082     * Transform results to be organized by metadata, and remove the null values.
083     * @param searchResult Remote values from a search by content and column or attribute
084     * @param mapping Mapping between metadata and columns/attributes
085     * @return A {@link Map} of possible metadata values organized by content synchronization key and metadata name
086     */
087    public Map<String, Map<String, List<Object>>> organizeRemoteValuesByMetadata(Map<String, Map<String, Object>> searchResult, Map<String, List<String>> mapping)
088    {
089        Map<String, Map<String, List<Object>>> result = new LinkedHashMap<>();
090        
091        // For each searchResult line (1 line = 1 content)
092        for (String resultKey : searchResult.keySet())
093        {
094            Map<String, Object> searchItem = searchResult.get(resultKey);
095            Map<String, List<Object>> contentResult = new HashMap<>();
096            
097            // For each metadata in the mapping
098            for (String metadataRef : mapping.keySet())
099            {
100                List<String> columns = mapping.get(metadataRef); // Get the columns for the current metadata
101                List<Object> values = columns.stream() // For each column corresponding to the metadata
102                        .map(searchItem::get) // Map the values
103                        .flatMap(o ->
104                        {
105                            if (o instanceof Collection<?>)
106                            {
107                                return ((Collection<?>) o).stream();
108                            }
109                            return Stream.of(o);
110                        }) // If it's a list of objects, get a flat stream
111                        .filter(Objects::nonNull) // Remove null values
112                        .collect(Collectors.toList()); // Collect it into a List
113                contentResult.put(metadataRef, values); // Add the retrieved metadata values list to the contentResult
114            }
115            
116            result.put(resultKey, contentResult);
117        }
118        
119        return result;
120    }
121    
122    /**
123     * Retrieves the synchronizable collection identifiers
124     * @param content the content
125     * @return the synchronizable collection identifiers
126     * @throws AmetysRepositoryException if an error occurs while reading SCC info on the given content
127     */
128    public List<String> getSynchronizableCollectionIds(Content content) throws AmetysRepositoryException
129    {
130        List<String> collectionIds = new ArrayList<>();
131        
132        if (content instanceof DefaultContent)
133        {
134            try
135            {
136                Node node = ((DefaultContent) content).getNode();
137                if (node.hasProperty(SynchronizableContentsCollection.COLLECTION_ID_PROPERTY))
138                {
139                    Value[] values = node.getProperty(SynchronizableContentsCollection.COLLECTION_ID_PROPERTY).getValues();
140                    for (Value value : values)
141                    {
142                        collectionIds.add(value.getString());
143                    }
144                }
145            }
146            catch (RepositoryException e)
147            {
148                getLogger().error("Failed to get linked synchronizable collections for content {}", content.getId(), e);
149                throw new AmetysRepositoryException("Failed to get linked synchronizable collections for content " + content.getId(), e);
150            }
151        }
152        
153        return collectionIds;
154    }
155}