001/*
002 *  Copyright 2020 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.web.frontoffice.search.metamodel.impl;
017
018import java.util.ArrayList;
019import java.util.Collection;
020import java.util.HashMap;
021import java.util.List;
022import java.util.Map;
023import java.util.Objects;
024import java.util.Optional;
025import java.util.stream.Collectors;
026
027import org.ametys.cms.contenttype.MetadataType;
028import org.ametys.cms.search.query.NotQuery;
029import org.ametys.cms.search.query.OrQuery;
030import org.ametys.cms.search.query.Query;
031import org.ametys.cms.search.query.Query.Operator;
032import org.ametys.plugins.repository.AmetysObjectResolver;
033import org.ametys.plugins.repository.UnknownAmetysObjectException;
034import org.ametys.runtime.i18n.I18nizableText;
035import org.ametys.web.frontoffice.search.metamodel.EnumeratedValues;
036import org.ametys.web.frontoffice.search.metamodel.SearchCriterionDefinition;
037import org.ametys.web.frontoffice.search.metamodel.Searchable;
038import org.ametys.web.repository.page.Page;
039import org.ametys.web.search.query.PageQuery;
040
041/**
042 * {@link SearchCriterionDefinition} proposing a search criterion on the page of the indexed document.
043 */
044public class PageSearchCriterionDefinition extends AbstractDefaultSearchCriterionDefinition
045{
046    /** The ametys object resolver */
047    private AmetysObjectResolver _resolver;
048    
049    /**
050     * Default constructor
051     * @param id The id
052     * @param pluginName The plugin name
053     * @param label The label
054     * @param resolver the {@link AmetysObjectResolver}
055     * @param searchable The {@link Searchable}
056     */
057    public PageSearchCriterionDefinition(
058            String id, 
059            String pluginName, 
060            I18nizableText label, 
061            AmetysObjectResolver resolver,
062            Optional<Searchable> searchable)
063    {
064        super(id, pluginName, label, MetadataType.STRING, _widget(), _widgetParameters(), Optional.empty(), Optional.empty(), Optional.empty(), searchable);
065        this._resolver = resolver;
066    }
067
068    @Override
069    public boolean isEnumerated()
070    {
071        return true;
072    }
073    
074    @Override
075    public boolean isTooBigForStaticEnumerator() 
076    {
077        return true;
078    }
079    
080    @Override
081    public Optional<EnumeratedValues> getEnumeratedValues(Map<String, Object> contextualParameters)
082    {
083        return Optional.of(new PageEnumeratedValues(this._resolver));
084    }
085    
086    private static Optional<String> _widget()
087    {
088        return Optional.of("edition.select-page");
089    }
090    
091    private static Optional<Map<String, I18nizableText>> _widgetParameters()
092    {
093        Map<String, I18nizableText> parameters = new HashMap<>();
094        parameters.put("siteContext", new I18nizableText("all"));
095        
096        return Optional.of(parameters);
097    }
098    
099    @Override
100    public Query getQuery(Object value, Operator operator, String language, Map<String, Object> contextualParameters)
101    {
102        String[] pageIds;
103        if (value instanceof String)
104        {
105            pageIds = ((String) value).split(",");
106        }
107        else if (value instanceof Collection<?>)
108        {
109            pageIds = ((Collection<?>) value)
110                    .stream()
111                    .filter(String.class::isInstance)
112                    .map(String.class::cast)
113                    .toArray(String[]::new);
114        }
115        else
116        {
117            throw new IllegalArgumentException("Not handled type of value: '" + value + "'");
118        }
119        
120        List<Query> queries = new ArrayList<>();
121        for (String pageId : pageIds)
122        {
123            queries.add(new PageQuery(pageId, true));
124        }
125        
126        OrQuery orQuery = new OrQuery(queries);
127        return operator == Operator.NE ? new NotQuery(orQuery) : orQuery;
128    }
129    
130    static class PageEnumeratedValues implements EnumeratedValues
131    {
132        /** The ametys object resolver */
133        private AmetysObjectResolver _resolver;
134        
135        public PageEnumeratedValues(AmetysObjectResolver resolver)
136        {
137            this._resolver = resolver;
138        }
139        
140        public Map<Object, I18nizableText> getAllValues()
141        {
142            // Never calculate all values because too many page can be display
143            return new HashMap<>();
144        }
145
146        public RestrictedValues getRestrictedValuesFor(List<Object> objs)
147        {
148            return new PageRestrictedValues(objs, this._resolver);
149        }
150        
151        static class PageRestrictedValues implements RestrictedValues
152        {
153            private List<Object> _forObjs;
154            private AmetysObjectResolver _resolver;
155            
156            PageRestrictedValues(List<Object> forObjs, AmetysObjectResolver resolver)
157            {
158                this._forObjs = forObjs;
159                this._resolver = resolver;
160            }
161            
162            @Override
163            public Map<Object, I18nizableText> values()
164            {
165                return this._forObjs.stream()
166                    .filter(String.class::isInstance)
167                    .map(String.class::cast)
168                    .map(this::_getPage)
169                    .filter(Objects::nonNull)
170                    .collect(Collectors.toMap(Page::getId, page -> new I18nizableText(page.getTitle())));
171            }
172            
173            private Page _getPage(String pageId)
174            {
175                try
176                {
177                    return this._resolver.resolveById(pageId);
178                }
179                catch (UnknownAmetysObjectException e) 
180                {
181                    return null;
182                }
183            }
184        }
185    }
186}