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.plugins.contentstree;
017
018import java.io.InputStream;
019import java.util.ArrayList;
020import java.util.Collection;
021import java.util.HashSet;
022import java.util.Set;
023
024import org.apache.avalon.framework.configuration.Configurable;
025import org.apache.avalon.framework.configuration.Configuration;
026import org.apache.avalon.framework.configuration.ConfigurationException;
027import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
028import org.apache.avalon.framework.service.ServiceException;
029import org.apache.avalon.framework.service.ServiceManager;
030import org.apache.avalon.framework.service.Serviceable;
031import org.apache.commons.lang3.StringUtils;
032import org.apache.excalibur.source.Source;
033import org.apache.excalibur.source.SourceResolver;
034import org.apache.tika.io.IOUtils;
035
036import org.ametys.cms.contenttype.ContentTypeExtensionPoint;
037import org.ametys.runtime.plugin.component.PluginAware;
038
039/**
040 * Static implementation based on configuration. See doc for template.
041 */
042public class StaticTreeConfiguration implements TreeConfiguration, Configurable, Serviceable, PluginAware
043{
044    /** The ui tool role */
045    protected String _uiToolRole;
046    /** The collection of elements */ 
047    protected Set<TreeConfigurationElements> _elements;
048    /** The content type extension point */
049    protected ContentTypeExtensionPoint _contentTypesEP;
050    /** The excalibur source resolver */
051    protected SourceResolver _sourceResolver;
052    /** The plugin name */
053    protected String _pluginName;
054    /** The extension id */
055    protected String _id;
056    
057    private Configuration _configuration;
058
059    @Override
060    public String getId()
061    {
062        return _id;
063    }
064    
065    @Override
066    public void setPluginInfo(String pluginName, String featureName, String id)
067    {
068        _pluginName = pluginName;
069    }
070    
071    @Override
072    public void service(ServiceManager manager) throws ServiceException
073    {
074        _contentTypesEP = (ContentTypeExtensionPoint) manager.lookup(ContentTypeExtensionPoint.ROLE);
075        _sourceResolver = (SourceResolver) manager.lookup(SourceResolver.ROLE);
076    }
077    
078    @Override
079    public void configure(Configuration initialConfiguration) throws ConfigurationException
080    {
081        _id = initialConfiguration.getAttribute("id");
082        
083        _configuration = _getDeportedConfiguration(initialConfiguration);
084    }
085    
086    private void _lazyConfigure()
087    {
088        if (_configuration != null)
089        {
090            try
091            {
092                _uiToolRole = _configuration.getAttribute("id");
093                _elements = new HashSet<>();
094                
095                for (Configuration contentTypeConfiguration : _configuration.getChild("elements").getChildren("content-type"))
096                {
097                    Collection<TreeConfigurationContentType> contentTypesInfo = getContentTypesInfos(contentTypeConfiguration.getChild("ids"));
098                    Collection<TreeConfigurationElementsChild> childNodes = getChildNodes(contentTypeConfiguration.getChild("children"));
099                    
100                    TreeConfigurationElements element = new TreeConfigurationElements(contentTypesInfo, childNodes);
101                    _elements.add(element);
102                }
103                
104                _configuration = null;
105            }
106            catch (ConfigurationException e)
107            {
108                throw new IllegalStateException("An error occured while configuring tree '" + _id + "'", e);
109            }
110        }
111    }
112
113    private Configuration _getDeportedConfiguration(Configuration initialConfiguration) throws ConfigurationException
114    {
115        String treeConfigURL = initialConfiguration.getChild("tree-config").getValue();
116        Source treeConfigSource = null;
117        InputStream is = null;
118        try
119        {
120            treeConfigSource = _sourceResolver.resolveURI(treeConfigURL, "plugin:" + _pluginName + "://", null);
121            is = treeConfigSource.getInputStream();
122            return new DefaultConfigurationBuilder().build(is);
123        }
124        catch (Exception e)
125        {
126            throw new ConfigurationException("Cannot open the file '" + treeConfigURL + "'", initialConfiguration, e);
127        }
128        finally
129        {
130            IOUtils.closeQuietly(is);
131            _sourceResolver.release(treeConfigSource);
132        }
133    }
134
135    /**
136     * Get the child nodes
137     * @param configuration The configuration
138     * @return the child nodes
139     * @throws ConfigurationException if configuration is invalid
140     */
141    protected Collection<TreeConfigurationElementsChild> getChildNodes(Configuration configuration) throws ConfigurationException
142    {
143        Collection<TreeConfigurationElementsChild> childnodes = new ArrayList<>();
144        
145        for (Configuration childConfiguration : configuration.getChildren())
146        {
147            if (StringUtils.equals(childConfiguration.getName(), "metadata"))
148            {
149                String id = childConfiguration.getAttribute("id");
150                
151                TreeConfigurationElementsChild child = new MetadataTreeConfigurationElementsChild(id);
152                childnodes.add(child);
153            }
154            else
155            {
156                throw new ConfigurationException("Unknown child of type '" + childConfiguration.getName() + "'", childConfiguration); 
157            }
158        }
159        
160        return childnodes;
161    }
162
163    /**
164     * Get the contents infos using a configuration
165     * @param configuration the configuration
166     * @return the contents infos using a configuration
167     * @throws ConfigurationException if an error occurred
168     */
169    protected Collection<TreeConfigurationContentType> getContentTypesInfos(Configuration configuration) throws ConfigurationException
170    {
171        Collection<TreeConfigurationContentType> contentTypesInfo = new ArrayList<>();
172
173        boolean elementsCanBeRoot = configuration.getAttributeAsBoolean("can-be-root", false);
174        boolean elementsInherit = configuration.getAttributeAsBoolean("inherit", false);
175        String elementsBusMessageType = configuration.getAttribute("bus-message-type", "content");
176        
177        for (Configuration idConfiguration : configuration.getChildren("id"))
178        {
179            TreeConfigurationContentType contentTypeInfo = getContentTypeInfo(idConfiguration, elementsCanBeRoot, elementsInherit, elementsBusMessageType);
180            contentTypesInfo.add(contentTypeInfo);
181        }
182        
183        return contentTypesInfo;
184    }
185    
186    /**
187     * Get the content info using a configuration
188     * @param configuration The configuration
189     * @param defaultCanBeRoot default value for root property
190     * @param defaultInherit default value for inherit property
191     * @param defaultBusMessageType default type of message
192     * @return the content info using a configuration 
193     * @throws ConfigurationException if the configuration is invalid
194     */
195    protected TreeConfigurationContentType getContentTypeInfo(Configuration configuration, boolean defaultCanBeRoot, boolean defaultInherit, String defaultBusMessageType) throws ConfigurationException
196    {
197        boolean canBeRoot = configuration.getAttributeAsBoolean("can-be-root", defaultCanBeRoot);
198        String mainContentTypeId = configuration.getValue();
199        
200        Collection<String> typesIds = new ArrayList<>();
201        typesIds.add(mainContentTypeId);
202        
203        boolean inherit = configuration.getAttributeAsBoolean("inherit", defaultInherit);
204        if (inherit)
205        {
206            typesIds.addAll(_contentTypesEP.getSubTypes(mainContentTypeId));
207        }
208        
209        String busMessageType = configuration.getAttribute("bus-message-type", defaultBusMessageType);
210        
211        return new TreeConfigurationContentType(canBeRoot, typesIds, busMessageType);
212    }
213    
214    @Override
215    public String getUIToolRole()
216    {
217        _lazyConfigure();
218        return _uiToolRole;
219    }
220
221    @Override
222    public Set<TreeConfigurationElements> getElements()
223    {
224        _lazyConfigure();
225        return _elements;
226    }
227
228}