001/*
002 *  Copyright 2018 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.runtime.model;
017
018import org.apache.avalon.framework.configuration.Configuration;
019import org.apache.avalon.framework.configuration.ConfigurationException;
020import org.apache.avalon.framework.service.ServiceManager;
021import org.apache.commons.lang3.StringUtils;
022
023import org.ametys.runtime.i18n.I18nizableText;
024import org.ametys.runtime.model.exception.UnknownTypeException;
025import org.ametys.runtime.model.type.ModelItemType;
026import org.ametys.runtime.plugin.component.AbstractThreadSafeComponentExtensionPoint;
027
028/**
029 * {@link ModelItem} parser from an XML configuration.
030 */
031public abstract class AbstractModelItemParser
032{
033    /** The extension point to use to get available model items types */
034    protected AbstractThreadSafeComponentExtensionPoint<? extends ModelItemType> _modelItemTypeExtensionPoint;
035    
036    /**
037     * Creates a model item parser.
038     * @param modelItemTypeExtensionPoint the extension point to use to get available model items types
039     */
040    public AbstractModelItemParser(AbstractThreadSafeComponentExtensionPoint<? extends ModelItemType> modelItemTypeExtensionPoint)
041    {
042        _modelItemTypeExtensionPoint = modelItemTypeExtensionPoint;
043    }
044    
045    /**
046     * Parses an element definition from a XML configuration.
047     * @param serviceManager the service manager
048     * @param pluginName the plugin name declaring this item.
049     * @param itemConfig the XML configuration of the model item.
050     * @param model the model which defines the model item
051     * @param parent the parent of the model item to create. Can be null if the model item to parse has no parent
052     * @return the parsed model item.
053     * @throws ConfigurationException if the configuration is not valid.
054     */
055    public ModelItem parse(ServiceManager serviceManager, String pluginName, Configuration itemConfig, Model model, ModelItemGroup parent) throws ConfigurationException
056    {
057        ModelItem modelItem = _createModelItem(itemConfig);
058        
059        modelItem.setModel(model);
060        if (parent != null)
061        {
062            parent.addChild(modelItem);
063        }
064        
065        modelItem.setName(_parseName(itemConfig));
066        modelItem.setLabel(_parseI18nizableText(itemConfig, pluginName, "label"));
067        modelItem.setDescription(_parseI18nizableText(itemConfig, pluginName, "description"));
068        modelItem.setType(_parseType(itemConfig));
069        
070        return modelItem;
071    }
072    
073    /**
074     * Create the model item to populate it.
075     * @param itemConfig the model item configuration to use.
076     * @return the item instantiated.
077     * @throws ConfigurationException if the configuration is not valid.
078     */
079    protected abstract ModelItem _createModelItem(Configuration itemConfig) throws ConfigurationException;
080
081    /**
082     * Parses the name of the model item.
083     * @param itemConfig the model item configuration to use.
084     * @return the name of the model item.
085     * @throws ConfigurationException if the configuration is not valid.
086     */
087    protected String _parseName(Configuration itemConfig) throws ConfigurationException
088    {
089        String itemName = itemConfig.getAttribute(_getNameConfigurationAttribute());
090        
091        if (!itemName.matches("^[a-zA-Z]((?!__)[a-zA-Z0-9_\\.-])*$"))
092        {
093            throw new ConfigurationException("Invalid model item name: " + itemName + ". The item name must start with a letter and must contain only letters, digits, underscore, dot or dash characters.", itemConfig);
094        }
095        
096        return itemName;
097    }
098    
099    /**
100     * Retrieves the name of the configuration attribute that contains the name of the model item
101     * @return the name of the configuration attribute that contains the name of the model item
102     */
103    protected String _getNameConfigurationAttribute()
104    {
105        return "name";
106    }
107    
108    /**
109     * Parses an i18n text.
110     * @param config the configuration to use.
111     * @param pluginName the current plugin name.
112     * @param name the child name.
113     * @return the i18n text.
114     * @throws ConfigurationException if the configuration is not valid.
115     */
116    protected I18nizableText _parseI18nizableText(Configuration config, String pluginName, String name) throws ConfigurationException
117    {
118        return I18nizableText.parseI18nizableText(config.getChild(name), "plugin." + pluginName);
119    }
120    
121    /**
122     * Parses the type.
123     * @param config the model item configuration to use.
124     * @return the type.
125     * @throws ConfigurationException if the configuration is not valid.
126     */
127    protected ModelItemType _parseType(Configuration config) throws ConfigurationException
128    {
129        String typeId = config.getAttribute("type");
130        
131        if (!_modelItemTypeExtensionPoint.hasExtension(typeId))
132        {
133            String modelItemName = _parseName(config);
134            String availableTypes = StringUtils.join(_modelItemTypeExtensionPoint.getExtensionsIds(), ", ");
135            UnknownTypeException ute = new UnknownTypeException("The type '" + typeId + "' is not available for the extension point '" + _modelItemTypeExtensionPoint + "'. Available types are: '" + availableTypes + "'.");
136            throw new ConfigurationException("Unable to find the type '" + typeId + "' defined on the item '" + modelItemName + "'.", ute);
137        }
138        
139        return _modelItemTypeExtensionPoint.getExtension(typeId);
140    }
141}