001/*
002 *  Copyright 2018 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.Collections;
019import java.util.List;
020import java.util.Locale;
021import java.util.Map;
022import java.util.Optional;
023import java.util.Set;
024
025import org.apache.avalon.framework.service.ServiceException;
026import org.apache.commons.lang3.LocaleUtils;
027
028import org.ametys.cms.contenttype.ContentType;
029import org.ametys.cms.search.model.impl.ReferencingCriterionDefinition;
030import org.ametys.cms.search.query.NotQuery;
031import org.ametys.cms.search.query.Query;
032import org.ametys.cms.search.query.Query.Operator;
033import org.ametys.runtime.i18n.I18nizableText;
034import org.ametys.runtime.model.DefinitionContext;
035import org.ametys.runtime.model.ElementDefinition;
036import org.ametys.runtime.model.Enumerator;
037import org.ametys.runtime.parameter.Validator;
038import org.ametys.web.frontoffice.search.metamodel.RestrictedEnumerator;
039import org.ametys.web.frontoffice.search.metamodel.SearchServiceCriterionDefinition;
040import org.ametys.web.frontoffice.search.metamodel.SearchServiceCriterionDefinitionHelper;
041import org.ametys.web.frontoffice.search.metamodel.Searchable;
042
043/**
044 * {@link SearchServiceCriterionDefinition} for {@link ContentSearchable} searching on a model item.
045 * @param <T> Type of the criterion value
046 */
047public class ReferencingSearchServiceCriterionDefinition<T> extends ReferencingCriterionDefinition<T> implements SearchServiceCriterionDefinition<T>
048{
049    /** The search service criterion definition helper */
050    protected SearchServiceCriterionDefinitionHelper _searchServiceCriterionDefinitionHelper;
051
052    private Optional<Searchable> _searchable = Optional.empty();
053    
054    /**
055     * Constructor used to create a FO criterion definition on a referenced item
056     * @param reference the item referenced by this criterion
057     * @param referencePath the path of the criterion's reference
058     * @param baseContentType the content type defining the reference
059     */
060    public ReferencingSearchServiceCriterionDefinition(ElementDefinition reference, String referencePath, ContentType baseContentType)
061    {
062        super(reference, referencePath);
063        
064        if (baseContentType != null)
065        {
066            setContentTypeIds(Set.of(baseContentType.getId()));
067        }
068    }
069    
070    public Optional<Searchable> getSearchable()
071    {
072        return _searchable;
073    }
074    
075    public void setSearchable(Searchable searchable)
076    {
077        _searchable = Optional.ofNullable(searchable);
078    }
079    
080    /**
081     * Get the label of a facet value.
082     * @param value the facet value.
083     * @param contextualParameters the contextual parameters
084     * @return the label, or null if the value does not exist.
085     */
086    public Optional<I18nizableText> getFacetLabel(String value, Map<String, Object> contextualParameters)
087    {
088        Locale currentLocale = LocaleUtils.toLocale((String) contextualParameters.get("lang"));
089        return Optional.ofNullable(_getCriterionDefinitionHelper().getFacetLabel(this, value, currentLocale))
090                       .or(() -> Optional.of(new I18nizableText(value)));
091    }
092    
093    @Override
094    public List<I18nizableText> getContextPrefixLabels()
095    {
096        return Collections.singletonList(
097                Optional.of(getReference())
098                        .map(ElementDefinition::getModel)
099                        .filter(ContentType.class::isInstance)
100                        .map(ContentType.class::cast)
101                        .map(ContentType::getLabel)
102                        .orElse(new I18nizableText("plugin.web", "PLUGINS_WEB_SERVICE_SEARCH_SEARCHABLE_CONTENT_ALL_PREFIX_LABEL")));
103    }
104    
105    @Override
106    public boolean isMultiple()
107    {
108        return isEnumerated();
109    }
110    
111    @Override
112    public Validator getValidator()
113    {
114        ElementDefinition reference = getReference();
115        return reference.isEditable() && reference.getType().getId().equals(getType().getId())
116                ? reference.getValidator()
117                : super.getValidator();
118    }
119    
120    @Override
121    public Query getEmptyValueQuery(String language, Map<String, Object> contextualParameters)
122    {
123        Query existQuery = getQuery(null, Operator.EXISTS, Collections.EMPTY_MAP, language, contextualParameters);
124        return new NotQuery(existQuery);
125    }
126    
127    public boolean isEnumerated()
128    {
129        return getEnumerator() != null;
130    }
131    
132    /**
133     * Determines if the current criterion definition is enumerated but can contain too much data
134     * @return <code>true</code> if the criterion definition can contain too much data, <code>false</code> otherwise
135     */
136    protected boolean isTooBigForStaticEnumerator() 
137    {
138        return false;
139    }
140    
141    public RestrictedEnumerator<T> getRestrictedEnumerator(Map<String, Object> contextualParameters)
142    {
143        Enumerator<T> enumerator = getEnumerator();
144        return enumerator != null
145                ? new RestrictedWrappedEnumerator<>(enumerator, getName())
146                : null;
147    }
148    
149    @Override
150    protected Map<String, Object> _toJSON(DefinitionContext context)
151    {
152        Map<String, Object> result = super._toJSON(context);
153        result.putAll(_getSearchServiceCriterionDefinitionHelper().getDefaultSearchServiceCriterionDefinitionJSON(this, isTooBigForStaticEnumerator()));
154        return result;
155    }
156    
157    /**
158     * Retrieves the search service criterion definition helper
159     * @return the search service criterion definition helper
160     */
161    protected SearchServiceCriterionDefinitionHelper _getSearchServiceCriterionDefinitionHelper()
162    {
163        if (_searchServiceCriterionDefinitionHelper == null)
164        {
165            try
166            {
167                _searchServiceCriterionDefinitionHelper = (SearchServiceCriterionDefinitionHelper) __serviceManager.lookup(SearchServiceCriterionDefinitionHelper.ROLE);
168            }
169            catch (ServiceException e)
170            {
171                throw new RuntimeException("Unable to lookup after the search service criterion definition helper", e);
172            }
173        }
174        
175        return _searchServiceCriterionDefinitionHelper;
176    }
177}