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 */
016
017package org.ametys.web.skin;
018
019import java.io.File;
020import java.io.FileInputStream;
021import java.io.IOException;
022import java.util.ArrayList;
023import java.util.Collection;
024import java.util.List;
025import java.util.Map;
026
027import org.apache.avalon.framework.configuration.Configuration;
028import org.apache.avalon.framework.configuration.ConfigurationException;
029import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
030import org.apache.avalon.framework.context.Context;
031import org.apache.avalon.framework.service.ServiceManager;
032import org.apache.cocoon.ProcessingException;
033import org.slf4j.LoggerFactory;
034import org.xml.sax.SAXException;
035
036import org.ametys.runtime.model.CategorizedElementDefinitionHelper;
037import org.ametys.runtime.model.CategorizedElementDefinitionProxy;
038import org.ametys.runtime.model.ElementDefinition;
039import org.ametys.runtime.model.Enumerator;
040import org.ametys.runtime.model.Model;
041import org.ametys.runtime.model.ModelItem;
042import org.ametys.runtime.model.exception.UndefinedItemPathException;
043import org.ametys.runtime.model.type.ElementType;
044import org.ametys.runtime.parameter.Validator;
045import org.ametys.runtime.plugin.component.AbstractThreadSafeComponentExtensionPoint;
046import org.ametys.runtime.plugin.component.ThreadSafeComponentManager;
047
048/**
049 * Model for Skin (do not mistake this with {@link SkinModel} )
050 */
051public class SkinParametersModel implements Model
052{
053    private String _skinName;
054    private File _skinDir;
055    private AbstractThreadSafeComponentExtensionPoint<? extends ElementType> _skinParameterTypeEP;
056    private Context _context;
057    private ServiceManager _manager;
058    
059    private List<ModelItem> _modelItems;
060    private Map<String, ElementDefinition> _flatDefinitions;
061    
062    private List<CategorizedElementDefinitionProxy> _skinParams;
063    
064    /**
065     * Create an instance of a model, based on the skin directory
066     * @param skinName name of the skin (must be unique)
067     * @param skinDir the skin directory where the configuration will be read
068     * @param skinParameterTypeEP the skin parameter type extension point
069     * @param context the current context
070     * @param manager the service manager
071     * @throws ProcessingException something went wrong while parsing the file
072     */
073    public SkinParametersModel(String skinName, File skinDir, SkinParameterTypeExtensionPoint skinParameterTypeEP, Context context, ServiceManager manager) throws ProcessingException
074    {
075        _skinName = skinName;
076        _skinDir = skinDir;
077        _skinParameterTypeEP = skinParameterTypeEP;
078        _context = context;
079        _manager = manager;
080        _initialize();
081    }
082    
083    public ModelItem getModelItem(String itemPath) throws UndefinedItemPathException
084    {
085        for (ModelItem modelItem : _modelItems)
086        {
087            if (modelItem.getPath().equals(itemPath))
088            {
089                return modelItem;
090            }
091        }
092
093        throw new UndefinedItemPathException("The parameter '" + itemPath + "' is not defined in skins.");
094    }
095    
096    public Collection<? extends ModelItem> getModelItems()
097    {
098        return _modelItems;
099    }
100
101    public String getId()
102    {
103        return _skinName;
104    }
105
106    public String getFamilyId()
107    {
108        return this.getClass().getName();
109    }
110    
111    /**
112     * Get a flat map of {@link ElementDefinition}
113     * @return a flat map of {@link ElementDefinition}
114     * @throws ProcessingException something went wrong when reading the configuration file
115     */
116    public Map<String, ElementDefinition> getFlatDefinitions() throws ProcessingException
117    {
118        return _flatDefinitions;
119    }
120    
121    private void _initialize() throws ProcessingException
122    {
123        List<CategorizedElementDefinitionProxy> skinParams = _getParameters();
124        _flatDefinitions = CategorizedElementDefinitionHelper.getFlatDefinitions(skinParams);
125        _modelItems = CategorizedElementDefinitionHelper.categorize(skinParams);
126    }
127    
128    /**
129     * Read the {@link CategorizedElementDefinitionProxy} from the skin dir
130     * @return a list of {@link CategorizedElementDefinitionProxy}
131     * @throws ProcessingException error while reading the XML
132     */
133    private List<CategorizedElementDefinitionProxy> _getParameters() throws ProcessingException
134    {
135        if (_skinParams != null)
136        {
137            return _skinParams;
138        }
139        List<CategorizedElementDefinitionProxy> skinParams = new ArrayList<>();
140        
141        ThreadSafeComponentManager<Validator> validatorManager = new ThreadSafeComponentManager<>();
142        ThreadSafeComponentManager<Enumerator> enumeratorManager = new ThreadSafeComponentManager<>();
143        
144        try
145        {
146            validatorManager.setLogger(LoggerFactory.getLogger(getClass()));
147            validatorManager.contextualize(_context);
148            validatorManager.service(_manager);
149            
150            enumeratorManager.setLogger(LoggerFactory.getLogger(getClass()));
151            enumeratorManager.contextualize(_context);
152            enumeratorManager.service(_manager);
153            
154            SkinParameterParser parser = new SkinParameterParser(_skinParameterTypeEP, enumeratorManager, validatorManager, new File (_skinDir, "i18n").toURI().toString());
155            Configuration configuration = _getConfigurationModel(_skinDir);
156            
157            if (configuration != null)
158            {
159                Configuration[] parameterConfigs = configuration.getChild("parameters").getChildren("parameter");
160                for (Configuration parameterConfig : parameterConfigs)
161                {
162                    CategorizedElementDefinitionProxy parameter = parser.parse(_manager, "tempskin", parameterConfig, this, null);
163                    skinParams.add(parameter);
164                }
165                
166                try
167                {
168                    parser.lookupComponents();
169                }
170                catch (Exception e)
171                {
172                    throw new ConfigurationException("Unable to lookup parameter local components", configuration, e);
173                }
174            }
175            
176            _skinParams = skinParams;
177            return _skinParams;
178        }
179        catch (ConfigurationException | SAXException | IOException e)
180        {
181            throw new ProcessingException(e);
182        }
183    }
184    
185    private Configuration _getConfigurationModel(File skinDir) throws ConfigurationException, SAXException, IOException
186    {
187        File configFile = new File(skinDir, "stylesheets" + File.separator + "config" + File.separator + "config-model.xml");
188        
189        if (configFile.exists())
190        {
191            try (FileInputStream is = new FileInputStream(configFile))
192            {
193                DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
194                return builder.build(is);
195            }
196        }
197
198        // There is no parameter
199        return null;
200    }
201}