001/*
002 *  Copyright 2022 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 org.apache.avalon.framework.configuration.Configurable;
019import org.apache.avalon.framework.configuration.Configuration;
020import org.apache.avalon.framework.configuration.ConfigurationException;
021import org.apache.avalon.framework.context.Context;
022import org.apache.avalon.framework.context.ContextException;
023import org.apache.avalon.framework.context.Contextualizable;
024import org.apache.avalon.framework.service.ServiceException;
025import org.apache.avalon.framework.service.ServiceManager;
026import org.apache.avalon.framework.service.Serviceable;
027
028import org.ametys.cms.data.ametysobject.ModelAwareDataAwareAmetysObject;
029import org.ametys.cms.data.type.indexing.IndexableElementType;
030import org.ametys.runtime.i18n.I18nizableText;
031import org.ametys.runtime.model.DefaultElementDefinition;
032import org.ametys.runtime.model.Enumerator;
033import org.ametys.runtime.model.ItemParserHelper;
034import org.ametys.runtime.model.ItemParserHelper.ConfigurationAndPluginName;
035import org.ametys.runtime.model.StaticEnumerator;
036import org.ametys.runtime.model.type.ModelItemType;
037import org.ametys.runtime.plugin.ExtensionPoint;
038import org.ametys.runtime.plugin.component.PluginAware;
039import org.ametys.runtime.plugin.component.ThreadSafeComponentManager;
040
041/**
042 * Abstract class for single property
043 * @param <T> type of the property values
044 * @param <X> type of ametys object supported by this property
045 */
046public abstract class AbstractProperty<T, X extends ModelAwareDataAwareAmetysObject> extends DefaultElementDefinition<T> implements Property<T, X>, Configurable, PluginAware, Serviceable, Contextualizable
047{
048    /** The service manager */
049    protected ServiceManager _manager;
050    
051    /** The avalon context */
052    protected Context _context;
053    
054    /** extension point containing the types available for this property */
055    protected ExtensionPoint<ModelItemType> _availableTypesExtensionPoint;
056    
057    public void service(ServiceManager manager) throws ServiceException
058    {
059        _manager = manager;
060    }
061    
062    public void contextualize(Context context) throws ContextException
063    {
064        _context = context;
065    }
066    
067    public void configure(Configuration configuration) throws ConfigurationException
068    {
069        setName(_parseName(configuration));
070        setLabel(_parseLabel(configuration));
071        setDescription(_parseDescription(configuration));
072        _parseAndSetEnumerator(configuration, getPluginName());
073        setMultiple(configuration.getAttributeAsBoolean("multiple", false));
074    }
075    
076    /**
077     * Parses the name of the property
078     * @param configuration the property configuration to use.
079     * @return the name of the property.
080     * @throws ConfigurationException if the configuration is not valid.
081     */
082    protected String _parseName(Configuration configuration) throws ConfigurationException
083    {
084        return ItemParserHelper.parseName(configuration, _getNameConfigurationAttribute());
085    }
086    
087    /**
088     * Retrieves the name of the configuration attribute that contains the name of the property
089     * @return the name of the configuration attribute that contains the name of the property
090     */
091    protected String _getNameConfigurationAttribute()
092    {
093        return "name";
094    }
095    
096    /**
097     * Parses the label of the property
098     * @param configuration the property configuration to use.
099     * @return the label of the property.
100     * @throws ConfigurationException if the configuration is not valid.
101     */
102    protected I18nizableText _parseLabel(Configuration configuration) throws ConfigurationException
103    {
104        return ItemParserHelper.parseI18nizableText(new ConfigurationAndPluginName(configuration, getPluginName()), "label", getName());
105    }
106    
107    /**
108     * Parses the description of the property
109     * @param configuration the property configuration to use.
110     * @return the description of the property.
111     * @throws ConfigurationException if the configuration is not valid.
112     */
113    protected I18nizableText _parseDescription(Configuration configuration) throws ConfigurationException
114    {
115        return ItemParserHelper.parseI18nizableText(new ConfigurationAndPluginName(configuration, getPluginName()), "description");
116    }
117
118    /**
119     * Parse the configuration to set the enumerator.
120     * @param configuration The configuration of the property
121     * @param pluginName The plugin name
122     * @throws ConfigurationException if an error occurs
123     */
124    protected void _parseAndSetEnumerator(Configuration configuration, String pluginName) throws ConfigurationException
125    {
126        ThreadSafeComponentManager<Enumerator> enumeratorManager = new ThreadSafeComponentManager<>();
127        try
128        {
129            enumeratorManager.setLogger(_logger);
130            enumeratorManager.contextualize(_context);
131            enumeratorManager.service(_manager);
132            
133
134            Configuration enumeratorConfig = configuration.getChild("enumeration", false);
135            if (enumeratorConfig != null)
136            {
137                Configuration customEnumerator = enumeratorConfig.getChild("custom-enumerator", false);
138                if (customEnumerator != null)
139                {
140                    String enumeratorClassName = customEnumerator.getAttribute("class");
141                    String role = "enumerator";
142                    
143                    try
144                    {
145                        @SuppressWarnings("unchecked")
146                        Class<Enumerator<T>> enumeratorClass = (Class<Enumerator<T>>) Class.forName(enumeratorClassName);
147                        enumeratorManager.addComponent(pluginName, null, role, enumeratorClass, configuration);
148                    }
149                    catch (Exception e)
150                    {
151                        throw new ConfigurationException("Unable to instantiate enumerator for class: " + enumeratorClassName, e);
152                    }
153                    
154                    enumeratorManager.initialize();
155                    setEnumerator(enumeratorManager.lookup(role));
156                    
157                    // Add the custom enumerator information to the element definition
158                    setCustomEnumerator(enumeratorClassName);
159                    setEnumeratorConfiguration(customEnumerator);
160                }
161                else
162                {
163                    StaticEnumerator<T> staticEnumerator = new StaticEnumerator<>();
164                    
165                    for (Configuration entryConfig : enumeratorConfig.getChildren("entry"))
166                    {
167                        Configuration valueConfiguration = entryConfig.getChild("value");
168                        T value = getType().parseConfiguration(valueConfiguration);
169                        I18nizableText label = ItemParserHelper.parseI18nizableText(new ConfigurationAndPluginName(entryConfig, pluginName), "label", (I18nizableText) null);
170                        staticEnumerator.add(label, value);
171                    }
172                    
173                    setEnumerator(staticEnumerator);
174                }
175            }
176        }
177        catch (Exception e)
178        {
179            throw new ConfigurationException("An error occured while configuring the enumerator of the property " + getName(), e);
180        }
181        finally
182        {
183            enumeratorManager.dispose();
184        }
185    }
186    
187    @Override
188    public void setPluginInfo(String pluginName, String featureName, String id)
189    {
190        setPluginName(pluginName);
191    }
192    
193    public boolean isEditable()
194    {
195        return false;
196    }
197    
198    public void setAvailableTypeExtensionPoint(ExtensionPoint<ModelItemType> availableTypesExtensionPoint)
199    {
200        _availableTypesExtensionPoint = availableTypesExtensionPoint;
201    }
202    
203    @SuppressWarnings("unchecked")
204    @Override
205    public IndexableElementType<T> getType()
206    {
207        return (IndexableElementType<T>) _availableTypesExtensionPoint.getExtension(getTypeId());
208    }
209    
210    /**
211     * Retrieves the id of  the property's type
212     * @return the id of  the property's type
213     */
214    protected abstract String getTypeId();
215}