001/* 002 * Copyright 2010 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.service; 018 019import java.io.IOException; 020import java.io.InputStream; 021import java.util.ArrayList; 022import java.util.Collection; 023import java.util.Collections; 024import java.util.HashMap; 025import java.util.HashSet; 026import java.util.List; 027import java.util.Map; 028import java.util.Set; 029import java.util.regex.Pattern; 030 031import org.apache.avalon.framework.component.Component; 032import org.apache.avalon.framework.configuration.Configurable; 033import org.apache.avalon.framework.configuration.Configuration; 034import org.apache.avalon.framework.configuration.ConfigurationException; 035import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder; 036import org.apache.avalon.framework.context.Context; 037import org.apache.avalon.framework.context.ContextException; 038import org.apache.avalon.framework.context.Contextualizable; 039import org.apache.avalon.framework.logger.AbstractLogEnabled; 040import org.apache.avalon.framework.service.ServiceException; 041import org.apache.avalon.framework.service.ServiceManager; 042import org.apache.avalon.framework.service.Serviceable; 043import org.apache.cocoon.components.ContextHelper; 044import org.apache.commons.lang.StringUtils; 045import org.apache.excalibur.source.Source; 046import org.apache.excalibur.source.SourceResolver; 047import org.apache.excalibur.source.TraversableSource; 048 049import org.ametys.runtime.i18n.I18nizableText; 050import org.ametys.runtime.model.Enumerator; 051import org.ametys.runtime.plugin.component.PluginAware; 052import org.ametys.web.source.ServiceSourceFactory; 053 054/** 055 * This enuerator return the list of available files for services. 056 * The files are searched in the skin and in the plugin 057 * Configuration is 058 * <path> to specify the path where file are stored 059 * <file> an optionnal regexp to list files in the path (default is ^.*\.xsl$ to list all xsl files) 060 */ 061public class ServiceXSLTEnumerator extends AbstractLogEnabled implements Enumerator<String>, org.ametys.runtime.parameter.Enumerator, Component, Configurable, PluginAware, Serviceable, Contextualizable 062{ 063 /** The relative path to search */ 064 protected String _path; 065 066 /** The file pattern */ 067 protected Pattern _fileFilter; 068 /** The plugin declaring the enumerator */ 069 protected String _pluginName; 070 /** The feature declaring the enumerator */ 071 protected String _featureName; 072 /** The excalibur source resolver */ 073 protected SourceResolver _resolver; 074 /** The default value of the xsl to use. Can be null. */ 075 protected String _defaultValue; 076 /** The plugin xsl to use. Can be empty. */ 077 protected Set<String> _values; 078 /** The avalon context */ 079 protected Context _context; 080 081 @Override 082 public void configure(Configuration configuration) throws ConfigurationException 083 { 084 _defaultValue = configuration.getChild("default-value").getValue(null); 085 086 Configuration enumConf = configuration.getChild("enumeration").getChild("custom-enumerator"); 087 088 _values = _getPluginOtherXSL (enumConf); 089 090 _path = enumConf.getChild("path").getValue(""); 091 if (StringUtils.isBlank(_path)) 092 { 093 throw new ConfigurationException("The configuration of the path is mandatory", configuration); 094 } 095 096 String regexp = enumConf.getChild("file").getValue("^.*\\.xsl$"); 097 _fileFilter = Pattern.compile(regexp); 098 } 099 100 @Override 101 public void contextualize(Context context) throws ContextException 102 { 103 _context = context; 104 } 105 106 @Override 107 public void service(ServiceManager manager) throws ServiceException 108 { 109 _resolver = (SourceResolver) manager.lookup(SourceResolver.ROLE); 110 } 111 112 @Override 113 public void setPluginInfo(String pluginName, String featureName, String id) 114 { 115 _pluginName = pluginName; 116 _featureName = featureName; 117 } 118 119 @Override 120 public Map<String, Object> getConfiguration() 121 { 122 Map<String, Object> config = new HashMap<>(); 123 124 config.put("defaultValue", _defaultValue); 125 config.put("values", _values); 126 config.put("path", _path); 127 config.put("fileFilter", _fileFilter.pattern()); 128 129 return config; 130 } 131 132 /** 133 * Filter a list of sources to return thoses matching the _filter 134 * @param files A non null list of files to filter 135 * @return The non null filtered list of names 136 */ 137 protected List<String> _filterNames(Collection<TraversableSource> files) 138 { 139 List<String> matched = new ArrayList<>(); 140 141 for (TraversableSource f : files) 142 { 143 if (!f.isCollection() && _fileFilter.matcher(f.getName()).matches()) 144 { 145 matched.add(f.getName()); 146 } 147 } 148 149 return matched; 150 } 151 152 public Map<String, I18nizableText> getTypedEntries() throws Exception 153 { 154 // 1 - Compute the list of files 155 Set<String> files = new HashSet<>(); 156 157 TraversableSource source = null; 158 try 159 { 160 source = (TraversableSource) _resolver.resolveURI("skin://services/" + _pluginName + "/" + _path); 161 if (source.exists()) 162 { 163 if (source.isCollection()) 164 { 165 @SuppressWarnings("unchecked") 166 List<String> sourceFiles = _filterNames(source.getChildren()); 167 files.addAll(sourceFiles); 168 } 169 else 170 { 171 getLogger().warn("Can not enumerate files for service defined in '" + _pluginName + "/" + _featureName + "' on the path '" + _path + "', because it exists on the skin but is not a directory in the skin"); 172 } 173 } 174 } 175 finally 176 { 177 _resolver.release(source); 178 } 179 180 if (_defaultValue != null) 181 { 182 Source pluginSource = null; 183 try 184 { 185 pluginSource = _resolver.resolveURI("plugin:" + _pluginName + "://" + _defaultValue); 186 if (pluginSource.exists()) 187 { 188 int i = _defaultValue.lastIndexOf('/'); 189 String name = _defaultValue; 190 if (i > 0) 191 { 192 name = _defaultValue.substring(i + 1); 193 } 194 files.add(name); 195 } 196 } 197 finally 198 { 199 _resolver.release(pluginSource); 200 } 201 } 202 203 if (_values != null) 204 { 205 for (String value : _values) 206 { 207 Source pluginSource = null; 208 try 209 { 210 pluginSource = _resolver.resolveURI("plugin:" + _pluginName + "://" + value); 211 if (pluginSource.exists()) 212 { 213 int i = value.lastIndexOf('/'); 214 String name = value; 215 if (i > 0) 216 { 217 name = value.substring(i + 1); 218 } 219 files.add(name); 220 } 221 } 222 finally 223 { 224 _resolver.release(pluginSource); 225 } 226 } 227 } 228 229 // 2 - Convert the files into entries 230 Map<String, I18nizableText> entries = new HashMap<>(); 231 for (String filename : files) 232 { 233 String s = _path + "/" + filename; 234 235 I18nizableText i18ntext = getEntry(s); 236 if (i18ntext != null) 237 { 238 entries.put(s, i18ntext); 239 } 240 } 241 242 return entries; 243 } 244 245 @Override 246 // TODO NEWATTRIBUTEAPI: remove this method when org.ametys.runtime.parameter.Enumerator will be removed 247 public Map<Object, I18nizableText> getEntries() throws Exception 248 { 249 Map<Object, I18nizableText> result = new HashMap<>(); 250 for (Map.Entry<String, I18nizableText> entry : getTypedEntries().entrySet()) 251 { 252 result.put(entry.getKey(), entry.getValue()); 253 } 254 return Collections.unmodifiableMap(result); 255 } 256 257 @Override 258 public I18nizableText getEntry(String value) throws Exception 259 { 260 String valueWithNoExtension = value.substring(0, value.length() - 4); 261 String url = "service:" + _pluginName + "://" + valueWithNoExtension + ".xml"; 262 263 Source source = null; 264 try 265 { 266 source = _resolver.resolveURI(url); 267 if (source.exists()) 268 { 269 String defaultCatalogue = "plugin." + _pluginName; 270 271 if (source instanceof ServiceSourceFactory.ServiceSource) 272 { 273 if (((ServiceSourceFactory.ServiceSource) source).getSourceType() != ServiceSourceFactory.SourceType.PLUGIN) 274 { 275 String skinName = (String) ContextHelper.getRequest(_context).getAttribute("skin"); 276 defaultCatalogue = "skin." + skinName; 277 } 278 } 279 280 try (InputStream inputStream = source.getInputStream()) 281 { 282 Configuration conf = new DefaultConfigurationBuilder().build(inputStream); 283 284 if (!conf.getAttributeAsBoolean("exclude", false)) 285 { 286 Configuration node = conf.getChild("label"); 287 return I18nizableText.parseI18nizableText(node, defaultCatalogue); 288 } 289 else 290 { 291 return null; 292 } 293 } 294 } 295 } 296 catch (IOException e) 297 { 298 // Nothing 299 } 300 finally 301 { 302 _resolver.release(source); 303 } 304 305 int i = valueWithNoExtension.lastIndexOf('/'); 306 String shortFilename = valueWithNoExtension.substring(i + 1); 307 return new I18nizableText(shortFilename); 308 } 309 310 private Set<String> _getPluginOtherXSL (Configuration configuration) 311 { 312 Set<String> otherFiles = new HashSet<>(); 313 314 Configuration[] filesConf = configuration.getChild("values", true).getChildren("value"); 315 for (Configuration fileConf : filesConf) 316 { 317 otherFiles.add(fileConf.getValue(null)); 318 } 319 320 return otherFiles; 321 } 322 323}