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