001/*
002 *  Copyright 2024 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.impl;
017
018import java.util.Map;
019import java.util.Optional;
020import java.util.Set;
021
022import org.apache.cocoon.xml.AttributesImpl;
023import org.apache.cocoon.xml.XMLUtils;
024import org.xml.sax.ContentHandler;
025import org.xml.sax.SAXException;
026
027import org.ametys.cms.data.type.indexing.IndexableElementType;
028import org.ametys.cms.model.CMSDataContext;
029import org.ametys.cms.search.model.CriterionDefinitionAwareElementDefinition;
030import org.ametys.cms.search.model.SearchModel;
031import org.ametys.cms.search.model.SearchModelCriterionDefinition;
032import org.ametys.cms.search.query.Query;
033import org.ametys.cms.search.query.Query.LogicalOperator;
034import org.ametys.cms.search.query.Query.Operator;
035import org.ametys.runtime.model.DefinitionContext;
036import org.ametys.runtime.model.ElementDefinition;
037
038/**
039 * Default implementation for {@link SearchModelCriterionDefinition} searching on a model item.
040 * @param <T> Type of the criterion value
041 */
042public class ReferencingSearchModelCriterionDefinition<T> extends ReferencingCriterionDefinition<T> implements SearchModelCriterionDefinition<T>
043{
044    /** Prefix for name of referencing criterion definitions */
045    public static final String CRITERION_DEFINITION_PREFIX = "reference-";
046    
047    /** The criterion name */
048    protected String _name;
049    /** The criterion operator */
050    protected Operator _operator;
051    
052    /**
053     * Default constructor.
054     */
055    public ReferencingSearchModelCriterionDefinition()
056    {
057        // Nothing to do
058    }
059    
060    /**
061     * Constructor used to create a BO criterion definition on a referenced item
062     * @param reference the item referenced by this criterion
063     * @param referencePath the path of the criterion's reference
064     */
065    public ReferencingSearchModelCriterionDefinition(ElementDefinition reference, String referencePath)
066    {
067        super(reference, referencePath);
068    }
069    
070    @Override
071    public Set<String> getContentTypeIds(Map<String, Object> contextualParameters)
072    {
073        return getModel().getContentTypes(contextualParameters);
074    }
075    
076    @Override
077    public String getName()
078    {
079        return _name != null ? _name : _getDefaultName();
080    }
081    
082    private String _getDefaultName()
083    {
084        return CRITERION_DEFINITION_PREFIX + getReferencePath() + "-" + getOperator().getName();
085    }
086    
087    @Override
088    public void setName(String name)
089    {
090        _name = name;
091    }
092    
093    @Override
094    public Query getQuery(Object value, Operator customOperator, Map<String, Object> allValues, String language, Map<String, Object> contextualParameters)
095    {
096        Operator operator = customOperator != null ? customOperator : getOperator();
097        return super.getQuery(value, operator, allValues, language, contextualParameters);
098    }
099    
100    public Operator getOperator()
101    {
102        return Optional.ofNullable(_operator)
103                       .orElseGet(this::_getDefaultOperator);
104    }
105    
106    /**
107     * Retrieves the default operator for the criterion
108     * @return the default operator for the criterion
109     */
110    protected Operator _getDefaultOperator()
111    {
112        if (getEnumerator() != null)
113        {
114            return Operator.EQ;
115        }
116        else
117        {
118            ElementDefinition reference = getReference();
119            return reference instanceof CriterionDefinitionAwareElementDefinition criterionDefinitionAwareReference
120                    ? criterionDefinitionAwareReference.getDefaultCriterionOperator()
121                    : ((IndexableElementType) reference.getType()).getDefaultCriterionOperator(CMSDataContext.newInstance()
122                                                                                                             .withModelItem(reference));
123        }
124    }
125    
126    public void setOperator(Operator operator)
127    {
128        _operator = operator;
129    }
130    
131    public void setMultipleOperandOperator(LogicalOperator multipleOperand)
132    {
133        _multipleOperandOperator = multipleOperand;
134    }
135
136    @Override
137    protected Map<String, Object> _toJSON(DefinitionContext context)
138    {
139        Map<String, Object> json = super._toJSON(context);
140        json.put("criterionOperator", getOperator().toString().toLowerCase());
141        json.put("criterionMultipleOperand", getMultipleOperandOperator().name());
142        json.put("criterionProperty", getReferencePath());
143        return json;
144    }
145    
146    @Override
147    public void toSAX(ContentHandler contentHandler, DefinitionContext context) throws SAXException
148    {
149        super.toSAX(contentHandler, context);
150        
151        XMLUtils.createElement(contentHandler, "test-operator", getOperator().toString().toLowerCase());
152        XMLUtils.createElement(contentHandler, "multiple-operand", getMultipleOperandOperator().name());
153
154        AttributesImpl referenceAttributes = new AttributesImpl();
155        referenceAttributes.addCDATAAttribute("ref", getReferencePath());
156        XMLUtils.createElement(contentHandler, "item", referenceAttributes);
157    }
158    
159    @SuppressWarnings("unchecked")
160    public void copyTo(SearchModelCriterionDefinition<T> criterion, Map<String, Object> contextualParameters)
161    {
162        criterion.setModel(getModel());
163        
164        criterion.setName(getName());
165        criterion.setPluginName(getPluginName());
166        criterion.setLabel(getLabel());
167        criterion.setDescription(getDescription());
168        criterion.setWidget(getWidget());
169        criterion.setWidgetParameters(getWidgetParameters());
170        
171        criterion.setType(getType());
172        criterion.setMultiple(isMultiple());
173        
174        criterion.setEnumerator(getEnumerator());
175        criterion.setCustomEnumerator(getCustomEnumerator());
176        criterion.setEnumeratorConfiguration(getEnumeratorConfiguration());
177        
178        criterion.setValidator(getValidator());
179        criterion.setCustomValidator(getCustomValidator());
180        criterion.setValidatorConfiguration(getValidatorConfiguration());
181        
182        criterion.setParsedDefaultValues(getParsedDefaultValues());
183        
184        criterion.setOperator(getOperator());
185        criterion.setMultipleOperandOperator(getMultipleOperandOperator());
186        
187        if (criterion instanceof ReferencingSearchModelCriterionDefinition referencingCriterion)
188        {
189            referencingCriterion.setSolrFacetFieldName(getSolrFacetFieldName(contextualParameters));
190            referencingCriterion.setJoinedPaths(getJoinedPaths(contextualParameters));
191            
192            referencingCriterion.setReference(getReference());
193            referencingCriterion.setReferencePath(getReferencePath());
194            
195            referencingCriterion.setContentTypeIds(getContentTypeIds(contextualParameters));
196        }
197    }
198    
199    @Override
200    public SearchModel getModel()
201    {
202        return (SearchModel) super.getModel();
203    }
204}