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.search;
017
018import java.util.HashMap;
019import java.util.Iterator;
020import java.util.LinkedList;
021import java.util.List;
022import java.util.Map;
023
024import org.apache.avalon.framework.parameters.Parameters;
025import org.apache.avalon.framework.service.ServiceException;
026import org.apache.avalon.framework.service.ServiceManager;
027import org.apache.cocoon.acting.ServiceableAction;
028import org.apache.cocoon.environment.ObjectModelHelper;
029import org.apache.cocoon.environment.Redirector;
030import org.apache.cocoon.environment.Request;
031import org.apache.cocoon.environment.SourceResolver;
032import org.slf4j.Logger;
033import org.slf4j.LoggerFactory;
034
035import org.ametys.core.cocoon.JSonReader;
036import org.ametys.core.util.JSONUtils;
037import org.ametys.core.util.ServerCommHelper;
038import org.ametys.plugins.contentio.synchronize.SynchronizableContentsCollection;
039import org.ametys.plugins.contentio.synchronize.SynchronizableContentsCollectionDAO;
040
041/**
042 * Action to search in a SCC.
043 */
044public class SCCSearchAction extends ServiceableAction
045{
046    /** Field imported to display in SCCSearchTool */
047    protected static final String FIELD_IMPORTED = "imported";
048    
049    /** SCC DAO */
050    protected SynchronizableContentsCollectionDAO _synchronizableContentsCollectionDAO;
051    /** ServerComm Helper */
052    protected ServerCommHelper _serverCommHelper;
053    /** JSON Utils */
054    protected JSONUtils _jsonUtils;
055    
056    private Logger _logger = LoggerFactory.getLogger(SCCSearchAction.class);
057    
058    @Override
059    public void service(ServiceManager smanager) throws ServiceException
060    {
061        super.service(smanager);
062        _synchronizableContentsCollectionDAO = (SynchronizableContentsCollectionDAO) smanager.lookup(SynchronizableContentsCollectionDAO.ROLE);
063        _serverCommHelper = (ServerCommHelper) smanager.lookup(ServerCommHelper.ROLE);
064        _jsonUtils = (JSONUtils) smanager.lookup(JSONUtils.ROLE);
065    }
066
067    @SuppressWarnings("unchecked")
068    @Override
069    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
070    {
071        // Get JS parameters
072        Map<String, Object> jsParameters = _serverCommHelper.getJsParameters();
073        
074        // Mask imported
075        Map<String, Object> searchParams = (Map<String, Object>) jsParameters.get("values");
076        boolean maskImported = searchParams.containsKey(SCCSearchModelConfiguration.CRITERIA_MASK_IMPORTED) && Boolean.valueOf(searchParams.get(SCCSearchModelConfiguration.CRITERIA_MASK_IMPORTED).toString());
077        searchParams.remove(SCCSearchModelConfiguration.CRITERIA_MASK_IMPORTED);
078        
079        // Get the collection for search
080        String collectionId = (String) jsParameters.get("collectionId");
081        SynchronizableContentsCollection collection = _synchronizableContentsCollectionDAO.getSynchronizableContentsCollection(collectionId);
082        
083        // Search
084        Integer offset = _getIntValue(jsParameters, "start", 0);
085        Integer limit = _getIntValue(jsParameters, "limit", Integer.MAX_VALUE);
086
087        // Construct the JSON object
088        Map<String, Object> objectToRead = new HashMap<>();
089        
090        // Different treatment if we need to mask imported or not (one is optimized)
091        if (maskImported)
092        {
093            Map<String, Map<String, Object>> results = collection.search(searchParams, 0, Integer.MAX_VALUE, _getSortList(jsParameters.get("sort")), _logger);
094            Iterator<String> resultKeys = results.keySet().iterator();
095            
096            // Skip offset
097            int currentOffset = 0;
098            while (currentOffset < offset && resultKeys.hasNext())
099            {
100                if (collection.getContent(null, resultKeys.next()) == null)
101                {
102                    currentOffset++;
103                }
104            }
105            
106            // Get the contents to the limit
107            List<Map<String, Object>> finalResults = new LinkedList<>();
108            int currentLimit = 0;
109            while (currentLimit < limit && resultKeys.hasNext())
110            {
111                String idValue = resultKeys.next();
112                Map<String, Object> contentResults = results.get(idValue);
113                if (collection.getContent(null, idValue) == null)
114                {
115                    // Add to results
116                    contentResults.put(FIELD_IMPORTED, false);
117                    finalResults.add(contentResults);
118                    currentLimit++;
119                }
120            }
121            
122            objectToRead.put("items", finalResults);
123            // FIXME CONTENTIO-87
124            objectToRead.put("total", collection.getTotalCount(searchParams, _logger));
125        }
126        else
127        {
128            Map<String, Map<String, Object>> results = collection.search(searchParams, offset, limit, _getSortList(jsParameters.get("sort")), _logger);
129
130            for (String idValue : results.keySet())
131            {
132                Map<String, Object> contentResults = results.get(idValue);
133                contentResults.put(FIELD_IMPORTED, collection.getContent(null, idValue) != null);
134                results.put(idValue, contentResults);
135            }
136            
137            objectToRead.put("items", results.values());
138            objectToRead.put("total", collection.getTotalCount(searchParams, _logger));
139        }
140        
141        // Add JSON to the request to be parsed
142        Request request = ObjectModelHelper.getRequest(objectModel);
143        request.setAttribute(JSonReader.OBJECT_TO_READ, objectToRead);
144        
145        return EMPTY_MAP;
146    }
147    
148    private int _getIntValue(Map<String, Object> values, String key, int defaultValue)
149    {
150        if (values.containsKey(key))
151        {
152            return Integer.valueOf(values.get(key).toString()).intValue();
153        }
154        
155        return defaultValue;
156    }
157    
158    private List<Object> _getSortList(Object sortValues)
159    {
160        if (sortValues != null)
161        {
162            return _jsonUtils.convertJsonToList(sortValues.toString());
163        }
164        
165        return null;
166    }
167}