001/* 002 * Copyright 2013 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.skinfactory.model; 017 018import java.io.InputStream; 019import java.nio.file.Files; 020import java.nio.file.Path; 021import java.util.HashMap; 022import java.util.Map; 023 024import org.apache.avalon.framework.component.Component; 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.logger.AbstractLogEnabled; 029import org.apache.avalon.framework.service.ServiceException; 030import org.apache.avalon.framework.service.ServiceManager; 031import org.apache.avalon.framework.service.Serviceable; 032import org.apache.avalon.framework.thread.ThreadSafe; 033 034import org.ametys.runtime.i18n.I18nizableText; 035import org.ametys.skinfactory.SkinFactoryComponent; 036import org.ametys.skinfactory.parameters.AbstractSkinParameter; 037import org.ametys.skinfactory.parameters.I18nizableTextParameter; 038import org.ametys.skinfactory.parameters.ImageParameter; 039import org.ametys.web.skin.SkinModel; 040import org.ametys.web.skin.SkinModelsManager; 041 042/** 043 * Manages the design conceptions of a model 044 */ 045public class ModelDesignsManager extends AbstractLogEnabled implements ThreadSafe, Serviceable, Component 046{ 047 /** The avalon role name */ 048 public static final String ROLE = ModelDesignsManager.class.getName(); 049 050 private SkinModelsManager _modelsManager; 051 private SkinFactoryComponent _skinFactoryManager; 052 053 private Map<String, Map<String, Design>> _designsCache = new HashMap<>(); 054 055 @Override 056 public void service(ServiceManager smanager) throws ServiceException 057 { 058 _modelsManager = (SkinModelsManager) smanager.lookup(SkinModelsManager.ROLE); 059 _skinFactoryManager = (SkinFactoryComponent) smanager.lookup(SkinFactoryComponent.ROLE); 060 } 061 062 /** 063 * Get all design instances for given model 064 * @param modelName The model name 065 * @return all design instances for given model 066 */ 067 public Map<String, Design> getDesigns (String modelName) 068 { 069 if (!_designsCache.containsKey(modelName)) 070 { 071 _configureDesigns (modelName); 072 } 073 return _designsCache.get(modelName); 074 } 075 076 /** 077 * Get design instance of given id and model name 078 * @param modelName The model name 079 * @param id The id 080 * @return design instance 081 */ 082 public Design getDesign (String modelName, String id) 083 { 084 if (!_designsCache.containsKey(modelName)) 085 { 086 _configureDesigns (modelName); 087 } 088 return _designsCache.get(modelName).get(id); 089 } 090 091 /** 092 * Apply a design 093 * @param modelName The model name 094 * @param id Id of design 095 * @param skinDir The skin directory (could be temp, work or skins) 096 */ 097 public void applyDesign (String modelName, String id, Path skinDir) 098 { 099 SkinModel model = _modelsManager.getModel(modelName); 100 101 Path file = model.getPath().resolve("model/designs/" + id + ".xml"); 102 if (Files.exists(file)) 103 { 104 // Apply color theme 105 String themeId = _getColorTheme(file); 106 if (themeId != null) 107 { 108 _skinFactoryManager.saveColorTheme(skinDir, themeId); 109 } 110 111 // Apply values 112 Map<String, Object> values = _getParameterValues (modelName, file); 113 _skinFactoryManager.applyModelParameters(modelName, skinDir, values); 114 } 115 } 116 117 118 private void _configureDesigns (String modelName) 119 { 120 SkinModel model = _modelsManager.getModel(modelName); 121 122 _designsCache.put(modelName, new HashMap<String, Design>()); 123 Map<String, Design> designs = _designsCache.get(modelName); 124 125 Path designDir = model.getPath().resolve("model/designs"); 126 if (Files.exists(designDir)) 127 { 128 try 129 { 130 Files.walk(designDir, 1) 131 .filter(f -> f.getFileName().toString().toLowerCase().endsWith(".xml")) 132 .map(f -> _configureDesign(modelName, f)) 133 .filter(design -> design != null) 134 .forEach(design -> designs.put(design.getId(), design)); 135 } 136 catch (Exception e) 137 { 138 throw new RuntimeException("Cannot read the configuration file model/designs for the model " + modelName); 139 } 140 } 141 } 142 143 144 private Design _configureDesign (String modelName, Path configurationFile) 145 { 146 try (InputStream is = Files.newInputStream(configurationFile)) 147 { 148 String fileName = configurationFile.getFileName().toString(); 149 String id = fileName.substring(0, fileName.lastIndexOf(".")); 150 151 Configuration configuration = new DefaultConfigurationBuilder().build(is); 152 I18nizableText label = _configureI18nizableText(configuration.getChild("label", false), new I18nizableText(id), modelName); 153 I18nizableText description = _configureI18nizableText(configuration.getChild("description", false), new I18nizableText(id), modelName); 154 155 String iconName = id + ".png"; 156 String icon = "/plugins/skinfactory/resources/img/actions/designs_32.png"; 157 Path iconFile = configurationFile.getParent().resolve(iconName); 158 if (Files.exists(iconFile)) 159 { 160 icon = "/plugins/skinfactory/" + modelName + "/_thumbnail/32/32/model/designs/" + iconName; 161 } 162 163 return new Design(id, label, description, icon); 164 } 165 catch (Exception e) 166 { 167 if (getLogger().isWarnEnabled()) 168 { 169 getLogger().warn("Cannot read the configuration file model/designs/" + configurationFile.getFileName().toString() + " for the model '" + modelName + "'. Continue as if file was not existing", e); 170 } 171 return null; 172 } 173 174 } 175 176 private String _getColorTheme(Path file) 177 { 178 try (InputStream is = Files.newInputStream(file)) 179 { 180 181 Configuration configuration = new DefaultConfigurationBuilder(true).build(is); 182 return configuration.getChild("color-theme").getValue(null); 183 } 184 catch (Exception e) 185 { 186 getLogger().error("Unable to get color theme", e); 187 return null; 188 } 189 } 190 191 private Map<String, Object> _getParameterValues (String modelName, Path file) 192 { 193 Map<String, Object> values = new HashMap<>(); 194 195 try (InputStream is = Files.newInputStream(file)) 196 { 197 198 Configuration configuration = new DefaultConfigurationBuilder(true).build(is); 199 Configuration[] parametersConf = configuration.getChild("parameters").getChildren("parameter"); 200 201 Map<String, AbstractSkinParameter> modelParameters = _skinFactoryManager.getModelParameters(modelName); 202 203 for (Configuration paramConf : parametersConf) 204 { 205 String id = paramConf.getAttribute("id"); 206 AbstractSkinParameter modelParam = modelParameters.get(id); 207 if (modelParam != null) 208 { 209 if (modelParam instanceof I18nizableTextParameter) 210 { 211 Configuration[] children = paramConf.getChildren(); 212 Map<String, String> langValues = new HashMap<>(); 213 for (Configuration langConfig : children) 214 { 215 langValues.put(langConfig.getName(), langConfig.getValue("")); 216 } 217 values.put(id, langValues); 218 } 219 else if (modelParam instanceof ImageParameter) 220 { 221 values.put(id, new ImageParameter.FileValue(paramConf.getValue(""), false)); 222 } 223 else 224 { 225 values.put(id, paramConf.getValue("")); 226 } 227 } 228 } 229 230 return values; 231 } 232 catch (Exception e) 233 { 234 getLogger().error("Unable to get values of all parameters", e); 235 return new HashMap<>(); 236 } 237 } 238 239 240 private I18nizableText _configureI18nizableText(Configuration configuration, I18nizableText defaultValue, String modelName) throws ConfigurationException 241 { 242 if (configuration != null) 243 { 244 boolean i18nSupported = configuration.getAttributeAsBoolean("i18n", false); 245 if (i18nSupported) 246 { 247 String catalogue = configuration.getAttribute("catalogue", null); 248 if (catalogue == null) 249 { 250 catalogue = "model." + modelName; 251 } 252 253 return new I18nizableText(catalogue, configuration.getValue()); 254 } 255 else 256 { 257 return new I18nizableText(configuration.getValue("")); 258 } 259 } 260 else 261 { 262 return defaultValue; 263 } 264 265 } 266 267 /** 268 * Bean representing a model design 269 * 270 */ 271 public class Design 272 { 273 private String _id; 274 private I18nizableText _label; 275 private I18nizableText _description; 276 private String _icon; 277 278 /** 279 * Constructor 280 * @param id the theme id 281 * @param label the theme's label 282 * @param description the theme's description 283 * @param icon the icon 284 */ 285 public Design (String id, I18nizableText label, I18nizableText description, String icon) 286 { 287 _id = id; 288 _label = label; 289 _description = description; 290 _icon = icon; 291 } 292 293 /** 294 * Get the id 295 * @return the id 296 */ 297 public String getId () 298 { 299 return _id; 300 } 301 302 /** 303 * Get the label 304 * @return the label 305 */ 306 public I18nizableText getLabel () 307 { 308 return _label; 309 } 310 311 /** 312 * Get the description 313 * @return the description 314 */ 315 public I18nizableText getDescription () 316 { 317 return _description; 318 } 319 320 /** 321 * Get the icon 322 * @return the icon 323 */ 324 public String getIcon () 325 { 326 return _icon; 327 } 328 } 329 330}