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.model.properties;
017
018import java.util.Optional;
019
020import org.apache.avalon.framework.configuration.Configuration;
021import org.apache.avalon.framework.configuration.ConfigurationException;
022import org.apache.avalon.framework.service.ServiceException;
023import org.apache.avalon.framework.service.ServiceManager;
024
025import org.ametys.cms.data.ametysobject.ModelAwareDataAwareAmetysObject;
026import org.ametys.cms.data.type.indexing.IndexableElementType;
027import org.ametys.cms.model.CMSDataContext;
028import org.ametys.cms.search.model.CriterionDefinitionAwareElementDefinition;
029import org.ametys.cms.search.model.CriterionDefinitionHelper;
030import org.ametys.cms.search.model.IndexationAwareElementDefinition;
031import org.ametys.cms.search.model.IndexationAwareElementDefinitionHelper;
032import org.ametys.runtime.i18n.I18nizableText;
033import org.ametys.runtime.model.ElementDefinition;
034import org.ametys.runtime.model.ItemParserHelper;
035import org.ametys.runtime.model.ItemParserHelper.ConfigurationAndPluginName;
036import org.ametys.runtime.model.Model;
037
038/**
039 * Abstract implementation for a property referencing {@link ElementDefinition}s
040 * @param <T> Type of the property values
041 * @param <X> Type of ametys object supported by this property
042 */
043public abstract class AbstractElementsReferencingProperty<T, X extends ModelAwareDataAwareAmetysObject> extends AbstractProperty<T, X> implements ReferencingProperty<T, X>, CriterionDefinitionAwareElementDefinition<T>, IndexationAwareElementDefinition<T, X>
044{
045    /** The criterion definition helper */
046    protected CriterionDefinitionHelper _criterionDefinitionHelper;
047    /** The indexation aware element definition helper */
048    protected IndexationAwareElementDefinitionHelper _indexationAwareElementDefinitionHelper;
049    
050    @Override
051    public void service(ServiceManager manager) throws ServiceException
052    {
053        super.service(manager);
054        _criterionDefinitionHelper = (CriterionDefinitionHelper) manager.lookup(CriterionDefinitionHelper.ROLE);
055        _indexationAwareElementDefinitionHelper = (IndexationAwareElementDefinitionHelper) manager.lookup(IndexationAwareElementDefinitionHelper.ROLE);
056    }
057    
058    @Override
059    public void configure(Configuration configuration) throws ConfigurationException
060    {
061        super.configure(configuration);
062        configureReferences(configuration);
063    }
064    
065    @Override
066    protected I18nizableText _parseLabel(Configuration configuration) throws ConfigurationException
067    {
068        // Do not use name as default value, to use the referenced element's one if the the concerned text is not filled
069        return ItemParserHelper.parseI18nizableText(new ConfigurationAndPluginName(configuration, getPluginName()), "label", (I18nizableText) null);
070    }
071    
072    @Override
073    protected I18nizableText _parseDescription(Configuration configuration) throws ConfigurationException
074    {
075        // Do not use empty string as default value, to use the referenced element's one if the the concerned text is not filled
076        return ItemParserHelper.parseI18nizableText(new ConfigurationAndPluginName(configuration, getPluginName()), "description", (I18nizableText) null);
077    }
078    
079    /**
080     * Configures the references
081     * @param configuration the property configuration
082     * @throws ConfigurationException if an error occurred
083     */
084    protected abstract void configureReferences(Configuration configuration) throws ConfigurationException;
085    
086    public void initializeAfterModelItemsInitialization() throws Exception
087    {
088        for (String reference : getReferences())
089        {
090            Model model = getModel();
091            if (!model.hasModelItem(reference) || !(model.getModelItem(reference) instanceof ElementDefinition))
092            {
093                throw new ConfigurationException("'" + reference + "' is invalid for property '" + getName() + "' in model '" + model.getId() + "'. The referenced item has not been found or is a group.");
094            }
095        }
096    }
097    
098    /**
099     * Retrieves the definition of the given referenced element
100     * @param reference the reference
101     * @return the referenced element's definition
102     */
103    @SuppressWarnings("unchecked")
104    protected ElementDefinition<T> _getReferenceDefinition(String reference)
105    {
106        return (ElementDefinition<T>) getModel().getModelItem(reference);
107    } 
108    
109    @Override
110    public String getName()
111    {
112        return Optional.ofNullable(super.getName())
113                       // If no name has been configured, use the first referenced element's one
114                       .orElseGet(() -> this._getFirstReferenceDefinition().getName());
115    }
116    
117    @Override
118    public I18nizableText getLabel()
119    {
120        return Optional.ofNullable(super.getLabel())
121                       // If no label has been configured, use the first referenced element's one
122                       .orElseGet(() -> this._getFirstReferenceDefinition().getLabel());
123    }
124    
125    @Override
126    public I18nizableText getDescription()
127    {
128        return Optional.ofNullable(super.getDescription())
129                       // If no description has been configured, use the first referenced element's one
130                       .orElseGet(() -> this._getFirstReferenceDefinition().getDescription());
131    }
132    
133    private String _getFirstReference()
134    {
135        return getReferences().get(0);
136    }
137    
138    private ElementDefinition<T> _getFirstReferenceDefinition()
139    {
140        return _getReferenceDefinition(_getFirstReference());
141    }
142    
143    public IndexableElementType getDefaultCriterionType()
144    {
145        CMSDataContext context = CMSDataContext.newInstance()
146                                               .withModelItem(this);
147        
148        String typeId = getType().getDefaultCriterionTypeId(context);
149        return _criterionDefinitionHelper.getCriterionDefinitionType(typeId);
150    }
151    
152    public String getSolrSortFieldName()
153    {
154        return _indexationAwareElementDefinitionHelper.getDefaultSolrSortFieldName(this);
155    }
156}