001/*
002 *  Copyright 2023 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.cms.search.model;
017
018import java.util.List;
019import java.util.Locale;
020import java.util.Map;
021import java.util.Set;
022
023import org.apache.avalon.framework.component.Component;
024import org.apache.avalon.framework.configuration.Configuration;
025import org.apache.avalon.framework.configuration.ConfigurationException;
026import org.apache.avalon.framework.configuration.DefaultConfiguration;
027import org.apache.avalon.framework.service.ServiceException;
028import org.apache.avalon.framework.service.ServiceManager;
029import org.apache.avalon.framework.service.Serviceable;
030import org.apache.commons.lang3.StringUtils;
031import org.bouncycastle.util.Arrays;
032
033import org.ametys.cms.contenttype.ContentType;
034import org.ametys.cms.data.type.ModelItemTypeExtensionPoint;
035import org.ametys.cms.data.type.indexing.IndexableElementType;
036import org.ametys.cms.model.CMSDataContext;
037import org.ametys.runtime.i18n.I18nizableText;
038import org.ametys.runtime.model.exception.UnknownTypeException;
039import org.ametys.runtime.model.type.DataContext;
040import org.ametys.runtime.plugin.component.AbstractLogEnabled;
041
042/**
043 * Helper for {@link CriterionDefinition}
044 */
045public class CriterionDefinitionHelper extends AbstractLogEnabled implements Component, Serviceable
046{
047    /** The component role. */
048    public static final String ROLE = CriterionDefinitionHelper.class.getName();
049    
050    /** The extension point containing all available criterion types */
051    protected ModelItemTypeExtensionPoint _criterionTypeExtensionPoint;
052    
053    public void service(ServiceManager manager) throws ServiceException
054    {
055        _criterionTypeExtensionPoint = (ModelItemTypeExtensionPoint) manager.lookup(ModelItemTypeExtensionPoint.ROLE_CRITERION_DEFINITION);
056    }
057    
058    /**
059     * Retrieves the type implementation of the given criterion type identifier
060     * @param <T> the type of the criterion type
061     * @param criterionTypeId the criterion type identifier
062     * @return the criterion type implementation
063     */
064    @SuppressWarnings("unchecked")
065    public <T> IndexableElementType<T> getCriterionDefinitionType(String criterionTypeId)
066    {
067        if (_criterionTypeExtensionPoint.hasExtension(criterionTypeId))
068        {
069            return (IndexableElementType<T>) _criterionTypeExtensionPoint.getExtension(criterionTypeId);
070        }
071        else
072        {
073            throw new UnknownTypeException("Unable to retrieve type with identifier '" + criterionTypeId + "'. This type is not available for criteria");
074        }
075    }
076    
077    /**
078     * Retrieves the default widget for the given criterion definition 
079     * @param criterionDefinition the criterion definition
080     * @return the default widget for the given criterion definition
081     */
082    public String getCriterionDefinitionDefaultWidget(CriterionDefinition criterionDefinition)
083    {
084        String defaultWidget = criterionDefinition.getType().getDefaultCriterionWidget(CMSDataContext.newInstance()
085                                                                                                     .withModelItem(criterionDefinition));
086        
087        return "edition.textarea".equals(defaultWidget) ? null : defaultWidget;
088    }
089    
090    /**
091     * Retrieves the default widget parameters for the given criterion definition 
092     * @param criterionDefinition the criterion definition
093     * @return the default widget parameters for the given criterion definition
094     */
095    public Map<String, I18nizableText> getCriterionDefinitionDefaultWidgetParameters(CriterionDefinition criterionDefinition)
096    {
097        return criterionDefinition.getType().getDefaultCriterionWidgetParameters(CMSDataContext.newInstance()
098                                                                                               .withModelItem(criterionDefinition));
099    }
100    
101    /**
102     * Wrap the given configuration to add content types
103     * @param originalConf the configuration to wrap
104     * @param contentTypes the content types to add in wrapped configuration
105     * @return the wrapped configuration
106     * @throws ConfigurationException if an error occurs
107     */
108    public Configuration wrapCriterionConfiguration(Configuration originalConf, Set<ContentType> contentTypes) throws ConfigurationException
109    {
110        DefaultConfiguration conf = new DefaultConfiguration(originalConf);
111        
112        conf.removeChild(conf.getChild("contentTypes"));
113        
114        DefaultConfiguration contentTypesConf = new DefaultConfiguration("contentTypes");
115        for (ContentType contentType : contentTypes)
116        {
117            DefaultConfiguration contentTypeConf = new DefaultConfiguration("type");
118            contentTypeConf.setAttribute("id", contentType.getId());
119            contentTypesConf.addChild(contentTypeConf);
120        }
121        
122        conf.addChild(contentTypesConf);
123        return conf;
124    }
125    
126    /**
127     * Get the label of a facet value for the given criterion.
128     * @param <T> the type of the facet criterion
129     * @param criterion the criterion
130     * @param value the facet value.
131     * @param currentLocale the current locale
132     * @return the label, or null if the value does not exist.
133     */
134    public <T> I18nizableText getFacetLabel(CriterionDefinition<T> criterion, String value, Locale currentLocale)
135    {
136        DataContext context = DataContext.newInstance()
137                .withModelItem(criterion)
138                .withLocale(currentLocale);
139        IndexableElementType<T> type = criterion.getType();
140        return type.getFacetLabel(value, context);
141    }
142    
143    /**
144     * Check if the given query value is empty 
145     * @param value the query value to check
146     * @return <code>true</code> if the given query value is empty, <code>false</code> otherwise
147     */
148    public boolean isQueryValueEmpty(Object value)
149    {
150        return value == null
151                || value instanceof String strValue && StringUtils.isEmpty(strValue)
152                || value instanceof List listValue && listValue.isEmpty()
153                || value.getClass().isArray() && Arrays.isNullOrEmpty((Object[]) value);
154    }
155}