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