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.solr.common.SolrInputDocument;
023
024import org.ametys.cms.data.ametysobject.ModelAwareDataAwareAmetysObject;
025import org.ametys.cms.data.type.indexing.IndexableDataContext;
026import org.ametys.runtime.i18n.I18nizableText;
027import org.ametys.runtime.model.ElementDefinition;
028import org.ametys.runtime.model.Model;
029
030/**
031 * Abstract implementation for a property referencing {@link ElementDefinition}s
032 * @param <T> Type of the property values
033 * @param <X> Type of ametys object supported by this property
034 */
035public abstract class AbstractElementsReferencingProperty<T, X extends ModelAwareDataAwareAmetysObject> extends AbstractProperty<T, X> implements ReferencingProperty<T, X>
036{
037    @Override
038    public void configure(Configuration configuration) throws ConfigurationException
039    {
040        super.configure(configuration);
041        configureReferences(configuration);
042    }
043    
044    @Override
045    protected I18nizableText _parseI18nizableText(Configuration config, String pluginName, String name) throws ConfigurationException
046    {
047        // Do not use name as default value, to use the referenced element's one if the the concerned text is not filled
048        return I18nizableText.parseI18nizableText(config.getChild(name), "plugin." + pluginName, (I18nizableText) null);
049    }
050    
051    /**
052     * Configures the references
053     * @param configuration the property configuration
054     * @throws ConfigurationException if an error occurred
055     */
056    protected abstract void configureReferences(Configuration configuration) throws ConfigurationException;
057    
058    public void initializeAfterModelItemsInitialization() throws Exception
059    {
060        for (String reference : getReferences())
061        {
062            Model model = getModel();
063            if (!model.hasModelItem(reference) || !(model.getModelItem(reference) instanceof ElementDefinition))
064            {
065                throw new ConfigurationException("'" + reference + "' is invalid for property '" + getName() + "' in model '" + model.getId() + "'. The referenced item has not been found or is a group.");
066            }
067        }
068    }
069    
070    /**
071     * Retrieves the definition of the given referenced element
072     * @param reference the reference
073     * @return the referenced element's definition
074     */
075    @SuppressWarnings("unchecked")
076    protected ElementDefinition<T> _getReferenceDefinition(String reference)
077    {
078        return (ElementDefinition<T>) getModel().getModelItem(reference);
079    } 
080    
081    @Override
082    public String getName()
083    {
084        return Optional.ofNullable(super.getName())
085                       // If no name has been configured, use the first referenced element's one
086                       .orElseGet(() -> this._getFirstReferenceDefinition().getName());
087    }
088    
089    @Override
090    public I18nizableText getLabel()
091    {
092        return Optional.ofNullable(super.getLabel())
093                       // If no label has been configured, use the first referenced element's one
094                       .orElseGet(() -> this._getFirstReferenceDefinition().getLabel());
095    }
096    
097    @Override
098    public I18nizableText getDescription()
099    {
100        return Optional.ofNullable(super.getDescription())
101                       // If no description has been configured, use the first referenced element's one
102                       .orElseGet(() -> this._getFirstReferenceDefinition().getDescription());
103    }
104    
105    public void indexValue(SolrInputDocument document, X ametysObject, IndexableDataContext context)
106    {
107        super.indexValue(document, ametysObject, context.cloneContext()
108                                                        .withModelItem(_getFirstReferenceDefinition()));
109    }
110    
111    private String _getFirstReference()
112    {
113        return getReferences().get(0);
114    }
115    
116    private ElementDefinition<T> _getFirstReferenceDefinition()
117    {
118        return _getReferenceDefinition(_getFirstReference());
119    }
120}