001/*
002 *  Copyright 2015 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.plugin;
017
018import java.util.ArrayList;
019import java.util.Arrays;
020import java.util.Collection;
021import java.util.Collections;
022import java.util.HashMap;
023import java.util.LinkedHashMap;
024import java.util.List;
025import java.util.Map;
026import java.util.stream.Collectors;
027
028import org.apache.avalon.framework.configuration.Configuration;
029import org.apache.commons.lang3.StringUtils;
030
031import org.ametys.runtime.config.ConfigParameterInfo;
032
033/**
034 * A plugin is composed by features, containing components definitions and extensions.
035 */
036public class Feature
037{
038    private String _pluginName;
039    private String _featureName;
040    private Configuration _configuration;
041    private boolean _safe;
042    private boolean _passive;
043    private Collection<String> _dependencies = new ArrayList<>();
044    private Collection<String> _deactivations = new ArrayList<>();
045
046    private Map<String, ConfigParameterInfo> _configParameters = new LinkedHashMap<>();
047    private Collection<String> _configParametersRefs = new ArrayList<>();
048    private Map<String, ConfigParameterInfo> _paramCheckers = new LinkedHashMap<>();
049    private Map<String, Map<String, ExtensionDefinition>> _extensions = new LinkedHashMap<>();
050    private Map<String, ComponentDefinition> _components = new LinkedHashMap<>();
051
052    Feature(String pluginName, String featureName)
053    {
054        _pluginName = pluginName;
055        _featureName = featureName;
056    }
057
058    /**
059     * Returns the declaring plugin name
060     * @return the declaring plugin name
061     */
062    public String getPluginName()
063    {
064        return _pluginName;
065    }
066    
067    /**
068     * Returns this feature name
069     * @return this feature name
070     */
071    public String getFeatureName()
072    {
073        return _featureName;
074    }
075    
076    /**
077     * Returns the feature id, ie. <code>getPluginName() + '/' + getFeatureName()</code>
078     * @return the feature id.
079     */
080    public String getFeatureId()
081    {
082        return _pluginName + PluginsManager.FEATURE_ID_SEPARATOR + _featureName;
083    }
084    
085    /**
086     * Returns true if this feature is passive.
087     * @return true if this feature is passive.
088     */
089    public boolean isPassive()
090    {
091        return _passive;
092    }
093    
094    /**
095     * Returns true if this feature is declared as safe.
096     * @return true if this feature is declared as safe.
097     */
098    public boolean isSafe()
099    {
100        return _safe;
101    }
102    
103    /**
104     * Returns the extensions declared within this feature, grouped by extension point.
105     * @return the extensions declared within this feature, grouped by extension point.
106     */
107    public Map<String, Collection<String>> getExtensionsIds()
108    {
109        Map<String, Collection<String>> result = new LinkedHashMap<>();
110        
111        for (String point : _extensions.keySet())
112        {
113            result.put(point, _extensions.get(point).keySet());
114        }
115        
116        return Collections.unmodifiableMap(result);
117    }
118    
119    /**
120     * Returns the components declared within this feature, stored by role.
121     * @return the components declared within this feature, stored by role.
122     */
123    public Map<String, String> getComponentsIds()
124    {
125        Map<String, String> result = new LinkedHashMap<>();
126        
127        for (String role : _components.keySet())
128        {
129            result.put(role, _components.get(role).getId());
130        }
131        
132        return Collections.unmodifiableMap(result);
133    }
134    
135    Configuration getConfiguration()
136    {
137        return _configuration;
138    }
139    
140    Collection<String> getDependencies()
141    {
142        return _dependencies;
143    }
144    
145    Collection<String> getDeactivations()
146    {
147        return _deactivations;
148    }
149    
150    Map<String, Map<String, ExtensionDefinition>> getExtensions()
151    {
152        return _extensions;
153    }
154    
155    Map<String, ComponentDefinition> getComponents()
156    {
157        return _components;
158    }
159    
160    Map<String, ConfigParameterInfo> getConfigParameters()
161    {
162        return _configParameters;
163    }
164    
165    Collection<String> getConfigParametersReferences()
166    {
167        return _configParametersRefs;
168    }
169    
170    Map<String, ConfigParameterInfo> getParameterCheckers()
171    {
172        return _paramCheckers;
173    }
174    
175    void configure(Configuration configuration)
176    {
177        _configuration = configuration;
178        _passive = configuration.getAttributeAsBoolean("passive", false);
179        _safe = configuration.getAttributeAsBoolean("safe", false);
180        
181        _configureDependencies();
182        _configureDeactivations();
183        
184        _configureExtensions();
185        _configureComponents();
186
187        Configuration configConfiguration = configuration.getChild("config");
188
189        _configureConfigParameters(configConfiguration);
190        _configureConfigParameterReferences(configConfiguration);
191        _configureParametersCheckers(configConfiguration);
192    }
193    
194    private void _configureDependencies()
195    {
196        String depends = _configuration.getAttribute("depends", null);
197        
198        if (depends != null)
199        {
200            List<String> dependencies = Arrays.stream(StringUtils.split(depends, ','))
201                                              .map(String::trim)
202                                              .filter(StringUtils::isNotEmpty)
203                                              .collect(Collectors.toList());
204            
205            for (String dependency : dependencies)
206            {
207                String dependingFeatureId = dependency;
208                
209                int i = dependency.indexOf('/');
210                if (i == -1)
211                {
212                    dependingFeatureId = _pluginName + PluginsManager.FEATURE_ID_SEPARATOR + dependency;
213                }
214                
215                _dependencies.add(dependingFeatureId);
216            }
217        }
218    }
219    
220    private void _configureDeactivations()
221    {
222        String deactivates = _configuration.getAttribute("deactivates", null);
223        
224        if (deactivates != null)
225        {
226            List<String> deactivations = Arrays.stream(StringUtils.split(deactivates, ','))
227                                               .map(String::trim)
228                                               .filter(StringUtils::isNotEmpty)
229                                               .collect(Collectors.toList());
230
231            for (String deactivation : deactivations)
232            {
233                String deactivatedFeatureId = deactivation;
234                
235                int i = deactivation.indexOf('/');
236                if (i == -1)
237                {
238                    deactivatedFeatureId = _pluginName + PluginsManager.FEATURE_ID_SEPARATOR + deactivation;
239                }
240                
241                _deactivations.add(deactivatedFeatureId);
242            }
243        }
244    }
245    
246    private void _configureConfigParameters(Configuration configConfiguration)
247    {
248        Configuration[] parameterConfigurations = configConfiguration.getChildren("param");
249        for (Configuration parameterConfiguration : parameterConfigurations)
250        {
251            String id = parameterConfiguration.getAttribute("id", null);
252            
253            // Add the new parameter to the list of declared parameters
254            _configParameters.put(id, new ConfigParameterInfo(id, _pluginName, parameterConfiguration));
255        }            
256    }
257    
258    private void _configureConfigParameterReferences(Configuration configConfiguration)
259    {
260        Configuration[] parameterConfigurations = configConfiguration.getChildren("param-ref");
261        for (Configuration parameterConfiguration : parameterConfigurations)
262        {
263            String id = parameterConfiguration.getAttribute("id", null);
264            _configParametersRefs.add(id);
265        }            
266    }
267    
268    private void _configureParametersCheckers(Configuration configConfiguration)
269    {
270        Configuration[] parameterConfigurations = configConfiguration.getChildren("param-checker");
271        for (Configuration parameterConfiguration : parameterConfigurations)
272        {
273            String id = parameterConfiguration.getAttribute("id", null);
274            
275            // Add the new parameter to the list of declared parameters
276            _paramCheckers.put(id, new ConfigParameterInfo(id, _pluginName, parameterConfiguration));
277        }            
278    }
279    
280    private void _configureExtensions()
281    {
282        Configuration[] extsConf = _configuration.getChild("extensions").getChildren("extension");
283        for (Configuration extConf : extsConf)
284        {
285            // XML schema requires attributes id and point and enforces that the combination (id,point) is unique
286            String id = extConf.getAttribute("id", null);
287            String point = extConf.getAttribute("point", null);
288            
289            Map<String, ExtensionDefinition> confs = _extensions.get(point);
290            if (confs == null)
291            {
292                confs = new HashMap<>();
293                _extensions.put(point, confs);
294            }
295            
296            confs.put(id, new ExtensionDefinition(id, point, _pluginName, _featureName, extConf));
297        }
298    }
299    
300    private void _configureComponents()
301    {
302        Configuration[] componentsConf = _configuration.getChild("components").getChildren("component");
303        for (Configuration componentConf : componentsConf)
304        {
305            // XML schema requires attributes id and role
306            String id = componentConf.getAttribute("id", null);
307            String role = componentConf.getAttribute("role", null);
308            
309            _components.put(role, new ComponentDefinition(id, role, _pluginName, _featureName, componentConf));
310        }
311    }
312}