src/core/net/sf/basedb/util/extensions/xml/XmlLoader.java

Code
Comments
Other
Rev Date Author Line
4163 28 Feb 08 nicklas 1 /**
4198 28 Mar 08 nicklas 2   $Id:XmlLoader.java 4187 2008-03-20 11:15:25Z nicklas $
4163 28 Feb 08 nicklas 3
4163 28 Feb 08 nicklas 4   Copyright (C) Authors contributing to this file.
4163 28 Feb 08 nicklas 5
4163 28 Feb 08 nicklas 6   This file is part of BASE - BioArray Software Environment.
4163 28 Feb 08 nicklas 7   Available at http://base.thep.lu.se/
4163 28 Feb 08 nicklas 8
4163 28 Feb 08 nicklas 9   BASE is free software; you can redistribute it and/or
4163 28 Feb 08 nicklas 10   modify it under the terms of the GNU General Public License
4479 05 Sep 08 jari 11   as published by the Free Software Foundation; either version 3
4163 28 Feb 08 nicklas 12   of the License, or (at your option) any later version.
4163 28 Feb 08 nicklas 13
4163 28 Feb 08 nicklas 14   BASE is distributed in the hope that it will be useful,
4163 28 Feb 08 nicklas 15   but WITHOUT ANY WARRANTY; without even the implied warranty of
4163 28 Feb 08 nicklas 16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
4163 28 Feb 08 nicklas 17   GNU General Public License for more details.
4163 28 Feb 08 nicklas 18
4163 28 Feb 08 nicklas 19   You should have received a copy of the GNU General Public License
4515 11 Sep 08 jari 20   along with BASE. If not, see <http://www.gnu.org/licenses/>.
4163 28 Feb 08 nicklas 21 */
4170 07 Mar 08 nicklas 22 package net.sf.basedb.util.extensions.xml;
4163 28 Feb 08 nicklas 23
4163 28 Feb 08 nicklas 24 import java.io.IOException;
4163 28 Feb 08 nicklas 25 import java.io.InputStream;
4163 28 Feb 08 nicklas 26 import java.lang.reflect.InvocationTargetException;
4163 28 Feb 08 nicklas 27 import java.lang.reflect.Method;
5519 23 Nov 10 nicklas 28 import java.util.ArrayList;
4198 28 Mar 08 nicklas 29 import java.util.HashMap;
4198 28 Mar 08 nicklas 30 import java.util.LinkedHashSet;
4163 28 Feb 08 nicklas 31 import java.util.LinkedList;
4163 28 Feb 08 nicklas 32 import java.util.List;
4198 28 Mar 08 nicklas 33 import java.util.Map;
4198 28 Mar 08 nicklas 34 import java.util.Set;
4163 28 Feb 08 nicklas 35
6473 11 Jun 14 nicklas 36 import org.jdom2.Attribute;
6473 11 Jun 14 nicklas 37 import org.jdom2.Document;
6473 11 Jun 14 nicklas 38 import org.jdom2.Element;
6473 11 Jun 14 nicklas 39 import org.jdom2.Namespace;
6473 11 Jun 14 nicklas 40 import org.jdom2.output.Format;
6473 11 Jun 14 nicklas 41 import org.jdom2.output.XMLOutputter;
4163 28 Feb 08 nicklas 42
6627 25 Nov 14 nicklas 43 import net.sf.basedb.core.Application;
5607 15 Apr 11 nicklas 44 import net.sf.basedb.core.BaseException;
4198 28 Mar 08 nicklas 45 import net.sf.basedb.core.InvalidUseOfNullException;
6627 25 Nov 14 nicklas 46 import net.sf.basedb.core.Presets.Preset;
5519 23 Nov 10 nicklas 47 import net.sf.basedb.core.StringUtil;
5607 15 Apr 11 nicklas 48 import net.sf.basedb.core.Version;
4198 28 Mar 08 nicklas 49 import net.sf.basedb.core.plugin.About;
4163 28 Feb 08 nicklas 50 import net.sf.basedb.util.ClassUtil;
4163 28 Feb 08 nicklas 51 import net.sf.basedb.util.Values;
6473 11 Jun 14 nicklas 52 import net.sf.basedb.util.XmlUtil2;
4170 07 Mar 08 nicklas 53 import net.sf.basedb.util.extensions.AboutBean;
4170 07 Mar 08 nicklas 54 import net.sf.basedb.util.extensions.Action;
4170 07 Mar 08 nicklas 55 import net.sf.basedb.util.extensions.ActionFactory;
5486 12 Nov 10 nicklas 56 import net.sf.basedb.util.extensions.ErrorHandlerFactory;
4170 07 Mar 08 nicklas 57 import net.sf.basedb.util.extensions.Extension;
4170 07 Mar 08 nicklas 58 import net.sf.basedb.util.extensions.ExtensionBean;
4170 07 Mar 08 nicklas 59 import net.sf.basedb.util.extensions.ExtensionPoint;
4170 07 Mar 08 nicklas 60 import net.sf.basedb.util.extensions.ExtensionPointBean;
4170 07 Mar 08 nicklas 61 import net.sf.basedb.util.extensions.Registry;
4170 07 Mar 08 nicklas 62 import net.sf.basedb.util.extensions.RendererFactory;
5598 30 Mar 11 nicklas 63 import net.sf.basedb.util.filter.Filter;
4163 28 Feb 08 nicklas 64
4163 28 Feb 08 nicklas 65 /**
4163 28 Feb 08 nicklas 66   Loads extension points and extensions from extension definition 
4163 28 Feb 08 nicklas 67   XML files. This class may load extensions from more than one file
4163 28 Feb 08 nicklas 68   and register or unregister all loaded extensions and extension points 
4163 28 Feb 08 nicklas 69   with a registry.
4163 28 Feb 08 nicklas 70   <p>
4163 28 Feb 08 nicklas 71   For a description of the XML file format this class can load
5519 23 Nov 10 nicklas 72   see the <a href="http://base.thep.lu.se/chrome/site/latest/html/developerdoc/extensions/extensions_developer.helloworld.html">BASE manual</a>.
4163 28 Feb 08 nicklas 73   
4163 28 Feb 08 nicklas 74   <p>
4163 28 Feb 08 nicklas 75   <b>Factory initialisation</b><br>
4163 28 Feb 08 nicklas 76   
4163 28 Feb 08 nicklas 77   This loader uses reflection to create and initialise factory
4163 28 Feb 08 nicklas 78   instances. The XML file contains the class name of the factory
4163 28 Feb 08 nicklas 79   to create. This class must declare a public no-argument
4163 28 Feb 08 nicklas 80   constructor and must also implement one of the specific
4163 28 Feb 08 nicklas 81   factory interfaces (for example {@link ActionFactory}
4163 28 Feb 08 nicklas 82   or {@link RendererFactory}). If the tag <code>&lt;parameters&gt;</code>
4163 28 Feb 08 nicklas 83   is present inside the factory declaration, the loader will
4163 28 Feb 08 nicklas 84   use reflection to find a setter method for each sub-tag inside
4163 28 Feb 08 nicklas 85   the <code>&lt;parameters&gt;</code> tag. Here is an example:
4170 07 Mar 08 nicklas 86
4163 28 Feb 08 nicklas 87   <pre class="code">
4163 28 Feb 08 nicklas 88 // XML contents
4163 28 Feb 08 nicklas 89 &lt;parameters&gt;
4163 28 Feb 08 nicklas 90    &lt;image&gt;button.png&lt;/image&gt;
4163 28 Feb 08 nicklas 91    &lt;title&gt;Click button&lt;/title&gt;
4163 28 Feb 08 nicklas 92 &lt;/parameters&gt;
4163 28 Feb 08 nicklas 93
4163 28 Feb 08 nicklas 94 // Factory initialization
4163 28 Feb 08 nicklas 95 factory.setImage("button.png");
4163 28 Feb 08 nicklas 96 factory.setTitle("Click button");
4163 28 Feb 08 nicklas 97 </pre>
4163 28 Feb 08 nicklas 98
4207 04 Apr 08 nicklas 99   The setter methods must be public and accept a single <code>String</code> 
4207 04 Apr 08 nicklas 100   parameter.
4207 04 Apr 08 nicklas 101   <p>
4207 04 Apr 08 nicklas 102   
4207 04 Apr 08 nicklas 103   The loader also looks for the <code>setParameter(String, String)</code>
4207 04 Apr 08 nicklas 104   method signature. If the method exists it will be called for each 
4207 04 Apr 08 nicklas 105   parameter found with the tagname as the first parameter and the
5519 23 Nov 10 nicklas 106   value as the second parameter. Continuing the above example:
4207 04 Apr 08 nicklas 107   
4207 04 Apr 08 nicklas 108   <pre class="code">
4207 04 Apr 08 nicklas 109 factory.setParameter("image", "button.png");
4207 04 Apr 08 nicklas 110 factory.setParameter("title", "Click button");
4207 04 Apr 08 nicklas 111 </pre>
4207 04 Apr 08 nicklas 112   
4207 04 Apr 08 nicklas 113   <p>
4163 28 Feb 08 nicklas 114   Tags that doesn't have a corresponding setter method are simply ignored.
4163 28 Feb 08 nicklas 115   The XML file format allows any tags as parameters, but only the first level
4163 28 Feb 08 nicklas 116   will be parsed.
4170 07 Mar 08 nicklas 117   
4170 07 Mar 08 nicklas 118   <p>
4170 07 Mar 08 nicklas 119   The parameter values may be subject to conversion by one or more 
4170 07 Mar 08 nicklas 120   {@link ValueConverter}:s. Converters are added to the loader
4170 07 Mar 08 nicklas 121   by {@link #addValueConverter(ValueConverter)} and are usually
4170 07 Mar 08 nicklas 122   implemented to react on method annotations. For example, the web 
4170 07 Mar 08 nicklas 123   client uses this for path and variable substitution.
4163 28 Feb 08 nicklas 124
4163 28 Feb 08 nicklas 125   @author nicklas
4163 28 Feb 08 nicklas 126   @version 2.7
4198 28 Mar 08 nicklas 127   @base.modified $Date:2008-03-20 12:15:25 +0100 (Thu, 20 Mar 2008) $
4163 28 Feb 08 nicklas 128 */
4163 28 Feb 08 nicklas 129 public class XmlLoader 
4163 28 Feb 08 nicklas 130 {
6444 09 Apr 14 nicklas 131   private static final org.slf4j.Logger log = 
6444 09 Apr 14 nicklas 132     org.slf4j.LoggerFactory.getLogger(XmlLoader.class);
4163 28 Feb 08 nicklas 133
4163 28 Feb 08 nicklas 134   /**
4163 28 Feb 08 nicklas 135     The URL pointing to the extensions.xsd schema.
4163 28 Feb 08 nicklas 136   */
5610 15 Apr 11 nicklas 137   public static final String SCHEMA_FILE_URL = 
4163 28 Feb 08 nicklas 138     XmlLoader.class.getResource("/net/sf/basedb/core/xsd/extensions.xsd").toString();
4163 28 Feb 08 nicklas 139
4163 28 Feb 08 nicklas 140   /**
4163 28 Feb 08 nicklas 141     The name of the extensions namespace.
4163 28 Feb 08 nicklas 142   */
5610 15 Apr 11 nicklas 143   public static final String NAMESPACE = "http://base.thep.lu.se/extensions.xsd";
4163 28 Feb 08 nicklas 144   
4198 28 Mar 08 nicklas 145   // To convert XML elements to a string representation
4198 28 Mar 08 nicklas 146   private XMLOutputter xmlOut;
4198 28 Mar 08 nicklas 147   
4163 28 Feb 08 nicklas 148   // All loaded extension points
5610 15 Apr 11 nicklas 149   private final List<ExtensionPoint<Action>> extensionPoints;
4163 28 Feb 08 nicklas 150   
4163 28 Feb 08 nicklas 151   // All loaded extensions
5610 15 Apr 11 nicklas 152   private final List<Extension<Action>> extensions;
4163 28 Feb 08 nicklas 153   
5610 15 Apr 11 nicklas 154   // All loaded plug-in definitions
5610 15 Apr 11 nicklas 155   private final List<PluginInfo> pluginDefinitions;
5610 15 Apr 11 nicklas 156   
4170 07 Mar 08 nicklas 157   // List of registered converters
4198 28 Mar 08 nicklas 158   private Set<ValueConverter> converters;
4170 07 Mar 08 nicklas 159   
4198 28 Mar 08 nicklas 160   // Map with XML representation of factory parameters
5610 15 Apr 11 nicklas 161   private final Map<Object, String> factoryParameters;
4198 28 Mar 08 nicklas 162   
6030 28 Mar 12 nicklas 163   // Map with classloaders
6030 28 Mar 12 nicklas 164   private final Map<String, ClassLoader> classLoaders;
6030 28 Mar 12 nicklas 165   
4202 01 Apr 08 nicklas 166   // The last document that was validated
4202 01 Apr 08 nicklas 167   private Document validatedDom;
4202 01 Apr 08 nicklas 168   
5598 30 Mar 11 nicklas 169   // The name of the last document
5598 30 Mar 11 nicklas 170   private String lastName;
5598 30 Mar 11 nicklas 171   
4163 28 Feb 08 nicklas 172   /**
4163 28 Feb 08 nicklas 173     Create a new XML loader instance.
4163 28 Feb 08 nicklas 174   */
4163 28 Feb 08 nicklas 175   public XmlLoader()
4163 28 Feb 08 nicklas 176   {
4163 28 Feb 08 nicklas 177     extensionPoints = new LinkedList<ExtensionPoint<Action>>();
4163 28 Feb 08 nicklas 178     extensions = new LinkedList<Extension<Action>>();
5610 15 Apr 11 nicklas 179     pluginDefinitions = new LinkedList<PluginInfo>();
4198 28 Mar 08 nicklas 180     factoryParameters = new HashMap<Object, String>();
6030 28 Mar 12 nicklas 181     classLoaders = new HashMap<String, ClassLoader>();
4198 28 Mar 08 nicklas 182     xmlOut = new XMLOutputter(Format.getPrettyFormat());
4163 28 Feb 08 nicklas 183   }
4163 28 Feb 08 nicklas 184   
4198 28 Mar 08 nicklas 185   /**
4198 28 Mar 08 nicklas 186     Add a value converter to this loader
4198 28 Mar 08 nicklas 187     @param converter The converter to add
4198 28 Mar 08 nicklas 188     @throws NullPointerException If the converter is null
4198 28 Mar 08 nicklas 189   */
4170 07 Mar 08 nicklas 190   public void addValueConverter(ValueConverter converter)
4170 07 Mar 08 nicklas 191   {
4198 28 Mar 08 nicklas 192     if (converter == null) throw new InvalidUseOfNullException("converter");
4198 28 Mar 08 nicklas 193     if (converters == null) converters = new LinkedHashSet<ValueConverter>();
4170 07 Mar 08 nicklas 194     converters.add(converter);
4170 07 Mar 08 nicklas 195   }
4198 28 Mar 08 nicklas 196
4198 28 Mar 08 nicklas 197   /**
4198 28 Mar 08 nicklas 198     Remove a converter.
4198 28 Mar 08 nicklas 199     @param converter The converter to remove, null values are ignored
4198 28 Mar 08 nicklas 200   */
4198 28 Mar 08 nicklas 201   public void removeValueConverter(ValueConverter converter)
4198 28 Mar 08 nicklas 202   {
4198 28 Mar 08 nicklas 203     if (converter == null || converters == null) return;
4198 28 Mar 08 nicklas 204     converters.remove(converter);
4198 28 Mar 08 nicklas 205   }
4170 07 Mar 08 nicklas 206   
4163 28 Feb 08 nicklas 207   /**
4198 28 Mar 08 nicklas 208     Remove all value converters.
4198 28 Mar 08 nicklas 209   */
4198 28 Mar 08 nicklas 210   public void clearValueConverters()
4198 28 Mar 08 nicklas 211   {
4198 28 Mar 08 nicklas 212     if (converters != null) converters.clear();
4198 28 Mar 08 nicklas 213   }
4198 28 Mar 08 nicklas 214   
5598 30 Mar 11 nicklas 215   private Filter<Element> filter;
4198 28 Mar 08 nicklas 216   /**
5615 19 Apr 11 nicklas 217     Set a filter that all &lt;extension-point&gt;, &lt;extension&gt;,
5615 19 Apr 11 nicklas 218     &lt;ref&gt; and &lt;plugin-definition&gt; tags must pass to be loaded 
5615 19 Apr 11 nicklas 219     and registered.
5598 30 Mar 11 nicklas 220     @param filter A filter or null to remove an existing filter
5598 30 Mar 11 nicklas 221     @since 3.0
5598 30 Mar 11 nicklas 222   */
5598 30 Mar 11 nicklas 223   public void setFilter(Filter<Element> filter)
5598 30 Mar 11 nicklas 224   {
7224 14 Nov 16 nicklas 225     log.debug("Using filter: " + filter);
5598 30 Mar 11 nicklas 226     this.filter = filter;
5598 30 Mar 11 nicklas 227   }
5598 30 Mar 11 nicklas 228   
5598 30 Mar 11 nicklas 229   /**
4163 28 Feb 08 nicklas 230     Load an extensions definition XML file. This method may be called 
4202 01 Apr 08 nicklas 231     multiple times with different XML files. The loading may also be
4202 01 Apr 08 nicklas 232     done by a two-step process where the first step validates the file
4202 01 Apr 08 nicklas 233     and the second step loads the extensions. See {@link 
4202 01 Apr 08 nicklas 234     #validateXmlFile(InputStream, String)} and {@link 
4202 01 Apr 08 nicklas 235     #loadLastValidatedFile(ClassLoader, boolean)}.
4163 28 Feb 08 nicklas 236
4163 28 Feb 08 nicklas 237     @param xmlFile An input stream to read the XML data from
4163 28 Feb 08 nicklas 238     @param filename The original filename the stream is coming from, or null
4163 28 Feb 08 nicklas 239       if not known. This value is only used when generating error messages
4163 28 Feb 08 nicklas 240     @param classLoader The classloader to use when loading classes that are
4163 28 Feb 08 nicklas 241       named in the XML file, or null to use the default classloader (=the same
4163 28 Feb 08 nicklas 242       class loader that loaded the BASE core classes)
4198 28 Mar 08 nicklas 243     @param clear TRUE to clear all already loaded extensions before loading 
4198 28 Mar 08 nicklas 244       the extensions in this file
4198 28 Mar 08 nicklas 245     @return Information about the extension, or null if no such information
4198 28 Mar 08 nicklas 246       is available
4163 28 Feb 08 nicklas 247     @throws IOException If there is an error reading the XML file
4163 28 Feb 08 nicklas 248     @throws ClassNotFoundException If a class named in the XML file can't
4163 28 Feb 08 nicklas 249       be found
4163 28 Feb 08 nicklas 250     @throws NoSuchMethodException If a factory class doesn't implement a
4163 28 Feb 08 nicklas 251       public, no-argument constructor
4163 28 Feb 08 nicklas 252     @throws IllegalAccessException If a factory class constructor isn't 
4163 28 Feb 08 nicklas 253       public
4163 28 Feb 08 nicklas 254     @throws InstantiationException If a factory class can't be instantiated,
4163 28 Feb 08 nicklas 255       for example, because it is an abstract class or an interface
4202 01 Apr 08 nicklas 256     @see #validateXmlFile(InputStream, String)
4202 01 Apr 08 nicklas 257     @see #loadLastValidatedFile(ClassLoader, boolean)
4163 28 Feb 08 nicklas 258   */
4198 28 Mar 08 nicklas 259   public About loadXmlFile(InputStream xmlFile, String filename,
4198 28 Mar 08 nicklas 260       ClassLoader classLoader, boolean clear)
6473 11 Jun 14 nicklas 261     throws IOException, ClassNotFoundException, NoSuchMethodException,
7513 02 Nov 18 nicklas 262       IllegalAccessException, InstantiationException, InvocationTargetException
4163 28 Feb 08 nicklas 263   {
4202 01 Apr 08 nicklas 264     validateXmlFile(xmlFile, filename);
4202 01 Apr 08 nicklas 265     return loadLastValidatedFile(classLoader, clear);
4202 01 Apr 08 nicklas 266   }
4202 01 Apr 08 nicklas 267   
4202 01 Apr 08 nicklas 268   /**
4202 01 Apr 08 nicklas 269     Validate an XML file against the extensions definition schema. If the
4202 01 Apr 08 nicklas 270     file is valid you may continue to load the extensions. See
4202 01 Apr 08 nicklas 271     {@link #loadLastValidatedFile(ClassLoader, boolean)}.
4202 01 Apr 08 nicklas 272     
4202 01 Apr 08 nicklas 273     @param xmlFile An input stream to read the XML data from
4202 01 Apr 08 nicklas 274     @param filename The original filename the stream is coming from, or null
4202 01 Apr 08 nicklas 275       if not known. This value is only used when generating error messages
4202 01 Apr 08 nicklas 276     @return Information about the extension, or null if no such information
4202 01 Apr 08 nicklas 277       is available
4202 01 Apr 08 nicklas 278     @throws IOException If there is an error reading the XML file
4202 01 Apr 08 nicklas 279   */
4202 01 Apr 08 nicklas 280   public About validateXmlFile(InputStream xmlFile, String filename)
6473 11 Jun 14 nicklas 281     throws IOException
4202 01 Apr 08 nicklas 282   {
4202 01 Apr 08 nicklas 283     validatedDom = null;
4202 01 Apr 08 nicklas 284     validatedDom = loadDocument(xmlFile, filename);
5598 30 Mar 11 nicklas 285     lastName = filename;
4202 01 Apr 08 nicklas 286     About globalAbout = loadGlobalAbout(validatedDom);
5607 15 Apr 11 nicklas 287     verifyBaseVersion(globalAbout, true);
4202 01 Apr 08 nicklas 288     return globalAbout;
4202 01 Apr 08 nicklas 289   }
4202 01 Apr 08 nicklas 290   
4202 01 Apr 08 nicklas 291   /**
4202 01 Apr 08 nicklas 292     Continue loading extensions from the last validated XML file. This is
4202 01 Apr 08 nicklas 293     the second step in a two-step process and requires that 
4202 01 Apr 08 nicklas 294     {@link #validateXmlFile(InputStream, String)} has been successfully called 
4202 01 Apr 08 nicklas 295     first.
4202 01 Apr 08 nicklas 296
4202 01 Apr 08 nicklas 297     @param classLoader The classloader to use when loading classes that are
4202 01 Apr 08 nicklas 298       named in the XML file, or null to use the default classloader (=the same
4202 01 Apr 08 nicklas 299       class loader that loaded the BASE core classes)
4202 01 Apr 08 nicklas 300     @param clear TRUE to clear all already loaded extensions before loading 
4202 01 Apr 08 nicklas 301       the extensions in this file
4202 01 Apr 08 nicklas 302     @return Information about the extension, or null if no such information
4202 01 Apr 08 nicklas 303       is available
4202 01 Apr 08 nicklas 304     @throws ClassNotFoundException If a class named in the XML file can't
4202 01 Apr 08 nicklas 305       be found
4202 01 Apr 08 nicklas 306     @throws NoSuchMethodException If a factory class doesn't implement a
4202 01 Apr 08 nicklas 307       public, no-argument constructor
4202 01 Apr 08 nicklas 308     @throws IllegalAccessException If a factory class constructor isn't 
4202 01 Apr 08 nicklas 309       public
4202 01 Apr 08 nicklas 310     @throws InstantiationException If a factory class can't be instantiated,
4202 01 Apr 08 nicklas 311       for example, because it is an abstract class or an interface
4202 01 Apr 08 nicklas 312   */
4202 01 Apr 08 nicklas 313   public About loadLastValidatedFile(ClassLoader classLoader, boolean clear)
4202 01 Apr 08 nicklas 314     throws ClassNotFoundException, IllegalAccessException, 
7513 02 Nov 18 nicklas 315     InstantiationException, NoSuchMethodException, InvocationTargetException
4202 01 Apr 08 nicklas 316   {
4202 01 Apr 08 nicklas 317     if (validatedDom == null) 
4202 01 Apr 08 nicklas 318     {
4202 01 Apr 08 nicklas 319       throw new NullPointerException("No file has been validated");
4202 01 Apr 08 nicklas 320     }
4198 28 Mar 08 nicklas 321     if (clear)
4198 28 Mar 08 nicklas 322     {
4198 28 Mar 08 nicklas 323       extensionPoints.clear();
4198 28 Mar 08 nicklas 324       extensions.clear();
5610 15 Apr 11 nicklas 325       pluginDefinitions.clear();
4198 28 Mar 08 nicklas 326     }
4202 01 Apr 08 nicklas 327     About globalAbout = loadGlobalAbout(validatedDom);
4202 01 Apr 08 nicklas 328     loadExtensionPoints(validatedDom, classLoader, globalAbout);
4202 01 Apr 08 nicklas 329     loadExtensions(validatedDom, classLoader, globalAbout);
5610 15 Apr 11 nicklas 330     loadPluginDefinitions(validatedDom, classLoader, globalAbout);
4202 01 Apr 08 nicklas 331     validatedDom = null;
4198 28 Mar 08 nicklas 332     return globalAbout;
4163 28 Feb 08 nicklas 333   }
4163 28 Feb 08 nicklas 334   
4163 28 Feb 08 nicklas 335   /**
4202 01 Apr 08 nicklas 336     Checks if an XML file has passed validation in the
4202 01 Apr 08 nicklas 337     {@link #validateXmlFile(InputStream, String)} method. If so,
4202 01 Apr 08 nicklas 338     the {@link #loadLastValidatedFile(ClassLoader, boolean)} can be called
4202 01 Apr 08 nicklas 339     to continue loading the extensions.
4202 01 Apr 08 nicklas 340     <p>
4202 01 Apr 08 nicklas 341     Note that once the file has been loaded this flag is reset to
4202 01 Apr 08 nicklas 342     FALSE.
4202 01 Apr 08 nicklas 343     
4202 01 Apr 08 nicklas 344     @return TRUE if a file has been validated, FALSE otherwise
4202 01 Apr 08 nicklas 345   */
4202 01 Apr 08 nicklas 346   public boolean hasValidFile()
4202 01 Apr 08 nicklas 347   {
4202 01 Apr 08 nicklas 348     return validatedDom != null;
4202 01 Apr 08 nicklas 349   }
4202 01 Apr 08 nicklas 350   
4202 01 Apr 08 nicklas 351   /**
4163 28 Feb 08 nicklas 352     Get a list with all loaded extension points.
4163 28 Feb 08 nicklas 353   */
4163 28 Feb 08 nicklas 354   public List<ExtensionPoint<Action>> getExtensionPoints()
4163 28 Feb 08 nicklas 355   {
4163 28 Feb 08 nicklas 356     return extensionPoints;
4163 28 Feb 08 nicklas 357   }
4163 28 Feb 08 nicklas 358   
4163 28 Feb 08 nicklas 359   /**
4163 28 Feb 08 nicklas 360     Get a list with all loaded extensions.
4163 28 Feb 08 nicklas 361   */
4163 28 Feb 08 nicklas 362   public List<Extension<Action>> getExtensions()
4163 28 Feb 08 nicklas 363   {
4163 28 Feb 08 nicklas 364     return extensions;
4163 28 Feb 08 nicklas 365   }
4163 28 Feb 08 nicklas 366   
4163 28 Feb 08 nicklas 367   /**
5610 15 Apr 11 nicklas 368     Get a list with all loaded plug-in definitions.
5610 15 Apr 11 nicklas 369     @since 3.0
5610 15 Apr 11 nicklas 370   */
5610 15 Apr 11 nicklas 371   public List<PluginInfo> getPluginDefinitions()
5610 15 Apr 11 nicklas 372   {
5610 15 Apr 11 nicklas 373     return pluginDefinitions;
5610 15 Apr 11 nicklas 374   }
5610 15 Apr 11 nicklas 375   
5610 15 Apr 11 nicklas 376   /**
4198 28 Mar 08 nicklas 377     Get a XML-like string representation of the parameters that was
4198 28 Mar 08 nicklas 378     used to initialise a factory.
4198 28 Mar 08 nicklas 379     @param factory The factory object
4198 28 Mar 08 nicklas 380     @return The parameters as found in the XML file
4198 28 Mar 08 nicklas 381   */
4198 28 Mar 08 nicklas 382   public String getFactoryParameters(Object factory)
4198 28 Mar 08 nicklas 383   {
4198 28 Mar 08 nicklas 384     return factoryParameters.get(factory);
4198 28 Mar 08 nicklas 385   }
4198 28 Mar 08 nicklas 386   
4198 28 Mar 08 nicklas 387   /**
4163 28 Feb 08 nicklas 388     Register all loaded extension points with a registry.
4163 28 Feb 08 nicklas 389     @param registry The registry
4198 28 Mar 08 nicklas 390     @param update TRUE to update already registered extension
4198 28 Mar 08 nicklas 391       points, FALSE to ignore
4198 28 Mar 08 nicklas 392     @return The number of extension points registered or updated
7703 11 Apr 19 nicklas 393     @see Registry#registerExtensionPoint(ExtensionPoint, ClassLoader)
4163 28 Feb 08 nicklas 394   */
4198 28 Mar 08 nicklas 395   public int registerExtensionPoints(Registry registry, boolean update)
4163 28 Feb 08 nicklas 396   {
4198 28 Mar 08 nicklas 397     int numRegistered = 0;
4163 28 Feb 08 nicklas 398     for (ExtensionPoint<Action> ep : extensionPoints)
4163 28 Feb 08 nicklas 399     {
4198 28 Mar 08 nicklas 400       if (update || !registry.extensionPointIsRegistered(ep.getId()))
4198 28 Mar 08 nicklas 401       {
6030 28 Mar 12 nicklas 402         registry.registerExtensionPoint(ep, classLoaders.get(ep.getId()));
4198 28 Mar 08 nicklas 403         numRegistered++;
4198 28 Mar 08 nicklas 404       }
4163 28 Feb 08 nicklas 405     }
4198 28 Mar 08 nicklas 406     return numRegistered;
4163 28 Feb 08 nicklas 407   }
4163 28 Feb 08 nicklas 408   
4163 28 Feb 08 nicklas 409   /**
4180 17 Mar 08 nicklas 410     Unregister all loaded extension points from a registry.
4198 28 Mar 08 nicklas 411     All extensions will also be unregistered.
4180 17 Mar 08 nicklas 412     @param registry The registry
4198 28 Mar 08 nicklas 413     @return The number of extension points that was unregistered
4198 28 Mar 08 nicklas 414     @see Registry#unregisterExtensionPoint(String)
4180 17 Mar 08 nicklas 415   */
4198 28 Mar 08 nicklas 416   public int unregisterExtensionPoints(Registry registry)
4180 17 Mar 08 nicklas 417   {
4180 17 Mar 08 nicklas 418     for (ExtensionPoint<Action> ep : extensionPoints)
4180 17 Mar 08 nicklas 419     {
4180 17 Mar 08 nicklas 420       registry.unregisterExtensionPoint(ep.getId());
4180 17 Mar 08 nicklas 421     }
4198 28 Mar 08 nicklas 422     return extensionPoints.size();
4180 17 Mar 08 nicklas 423   }
4180 17 Mar 08 nicklas 424   
4180 17 Mar 08 nicklas 425   /**
4163 28 Feb 08 nicklas 426     Register all loaded extensions with a registry.
4163 28 Feb 08 nicklas 427     @param registry The registry
4198 28 Mar 08 nicklas 428     @param update TRUE to update already registered extensions
4198 28 Mar 08 nicklas 429       FALSE to ignore
4198 28 Mar 08 nicklas 430     @return The number of registered or updated extensions
7703 11 Apr 19 nicklas 431     @see Registry#registerExtension(Extension, ClassLoader)
4163 28 Feb 08 nicklas 432   */
4198 28 Mar 08 nicklas 433   public int registerExtensions(Registry registry, boolean update)
4163 28 Feb 08 nicklas 434   {
4198 28 Mar 08 nicklas 435     int numRegistered = 0;
4163 28 Feb 08 nicklas 436     for (Extension<Action> ext : extensions)
4163 28 Feb 08 nicklas 437     {
4198 28 Mar 08 nicklas 438       if (update || !registry.extensionIsRegistered(ext.getId()))
4198 28 Mar 08 nicklas 439       {
6030 28 Mar 12 nicklas 440         registry.registerExtension(ext, classLoaders.get(ext.getId()));
4198 28 Mar 08 nicklas 441         numRegistered++;
4198 28 Mar 08 nicklas 442       }
4163 28 Feb 08 nicklas 443     }
4198 28 Mar 08 nicklas 444     return numRegistered;
4163 28 Feb 08 nicklas 445   }
4163 28 Feb 08 nicklas 446   
4163 28 Feb 08 nicklas 447   /**
4180 17 Mar 08 nicklas 448     Unregister all loaded extensions from a registry.
4180 17 Mar 08 nicklas 449     @param registry The registry
4198 28 Mar 08 nicklas 450     @return The number of extensions that was unregistered
4198 28 Mar 08 nicklas 451     @see Registry#unregisterExtension(String)
4180 17 Mar 08 nicklas 452   */
4198 28 Mar 08 nicklas 453   public int unregisterExtensions(Registry registry)
4180 17 Mar 08 nicklas 454   {
4180 17 Mar 08 nicklas 455     for (Extension<Action> ext : extensions)
4180 17 Mar 08 nicklas 456     {
4180 17 Mar 08 nicklas 457       registry.unregisterExtension(ext.getId());
4180 17 Mar 08 nicklas 458     }
4198 28 Mar 08 nicklas 459     return extensions.size();
4180 17 Mar 08 nicklas 460   }
4180 17 Mar 08 nicklas 461   
4180 17 Mar 08 nicklas 462   /**
4198 28 Mar 08 nicklas 463     Load and validate the XML file.
4198 28 Mar 08 nicklas 464     @return A JDOM document object
4198 28 Mar 08 nicklas 465   */
4198 28 Mar 08 nicklas 466   protected Document loadDocument(InputStream xmlFile, String filename)
6473 11 Jun 14 nicklas 467     throws IOException
4198 28 Mar 08 nicklas 468   {
5598 30 Mar 11 nicklas 469     log.debug("Validating file: " + filename);
6473 11 Jun 14 nicklas 470     return XmlUtil2.getSchemaValidatedXML(xmlFile, filename, NAMESPACE, SCHEMA_FILE_URL);
4198 28 Mar 08 nicklas 471   }
4198 28 Mar 08 nicklas 472   
5607 15 Apr 11 nicklas 473   protected boolean verifyBaseVersion(About about, boolean throwException)
5607 15 Apr 11 nicklas 474   {
5607 15 Apr 11 nicklas 475     if (about == null) return true;
5607 15 Apr 11 nicklas 476     String minVersion = about.getMinBaseVersion();
5607 15 Apr 11 nicklas 477     String maxVersion = about.getMaxBaseVersion();
5607 15 Apr 11 nicklas 478     if (minVersion != null && Version.compareWith(minVersion) > 0)
5607 15 Apr 11 nicklas 479     {
5607 15 Apr 11 nicklas 480       if (throwException)
5607 15 Apr 11 nicklas 481       {
5607 15 Apr 11 nicklas 482         throw new BaseException("BASE version "+ minVersion + " or higher is required");
5607 15 Apr 11 nicklas 483       }
5607 15 Apr 11 nicklas 484       return false;
5607 15 Apr 11 nicklas 485     }
5607 15 Apr 11 nicklas 486     if (maxVersion != null && Version.compareWith(maxVersion) <= 0)
5607 15 Apr 11 nicklas 487     {
5607 15 Apr 11 nicklas 488       if (throwException)
5607 15 Apr 11 nicklas 489       {
5607 15 Apr 11 nicklas 490         throw new BaseException("BASE version "+ maxVersion + " or higher is not supported");
5607 15 Apr 11 nicklas 491       }
5607 15 Apr 11 nicklas 492       return false;
5607 15 Apr 11 nicklas 493     }
5607 15 Apr 11 nicklas 494     return true;
5607 15 Apr 11 nicklas 495   }
5607 15 Apr 11 nicklas 496   
4198 28 Mar 08 nicklas 497   /**
4198 28 Mar 08 nicklas 498     Load the global about tag from the document.
4198 28 Mar 08 nicklas 499     @return An about bean or null if the about tag is not found.
4198 28 Mar 08 nicklas 500   */
4198 28 Mar 08 nicklas 501   protected AboutBean loadGlobalAbout(Document dom)
4198 28 Mar 08 nicklas 502   {
4198 28 Mar 08 nicklas 503     Element root = dom.getRootElement();
4198 28 Mar 08 nicklas 504     Namespace ns = root.getNamespace();
4198 28 Mar 08 nicklas 505     return loadAbout(root.getChild("about", ns));
4198 28 Mar 08 nicklas 506   }
4198 28 Mar 08 nicklas 507   
4198 28 Mar 08 nicklas 508   /**
4198 28 Mar 08 nicklas 509     Load information in an <code>&lt;about&gt;</code> tag.
4198 28 Mar 08 nicklas 510     @param aboutTag The about tag
4198 28 Mar 08 nicklas 511   */
4198 28 Mar 08 nicklas 512   protected AboutBean loadAbout(Element aboutTag)
4198 28 Mar 08 nicklas 513   {
4198 28 Mar 08 nicklas 514     AboutBean about = null;
4198 28 Mar 08 nicklas 515     if (aboutTag != null)
4198 28 Mar 08 nicklas 516     {
4198 28 Mar 08 nicklas 517       Namespace ns = aboutTag.getNamespace();
4198 28 Mar 08 nicklas 518       about = new AboutBean();
4198 28 Mar 08 nicklas 519       about.setName(Values.getStringOrNull(aboutTag.getChildText("name", ns)));
4198 28 Mar 08 nicklas 520       about.setDescription(Values.getStringOrNull(aboutTag.getChildText("description", ns)));
4198 28 Mar 08 nicklas 521       about.setVersion(Values.getStringOrNull(aboutTag.getChildText("version", ns)));
5607 15 Apr 11 nicklas 522       about.setMinBaseVersion(Values.getStringOrNull(aboutTag.getChildText("min-base-version", ns)));
5607 15 Apr 11 nicklas 523       about.setMaxBaseVersion(Values.getStringOrNull(aboutTag.getChildText("max-base-version", ns)));
4198 28 Mar 08 nicklas 524       about.setContact(Values.getStringOrNull(aboutTag.getChildText("contact", ns)));
4198 28 Mar 08 nicklas 525       about.setCopyright(Values.getStringOrNull(aboutTag.getChildText("copyright", ns)));
4198 28 Mar 08 nicklas 526       about.setEmail(Values.getStringOrNull(aboutTag.getChildText("email", ns)));
4198 28 Mar 08 nicklas 527       about.setUrl(Values.getStringOrNull(aboutTag.getChildText("url", ns)));
6408 31 Jan 14 nicklas 528       
6473 11 Jun 14 nicklas 529       List<Attribute> attributes = aboutTag.getAttributes();
6408 31 Jan 14 nicklas 530       for (Attribute a : attributes)
6408 31 Jan 14 nicklas 531       {
6408 31 Jan 14 nicklas 532         about.setAttribute(a.getName(), a.getValue());
6408 31 Jan 14 nicklas 533       }
4198 28 Mar 08 nicklas 534     }
4198 28 Mar 08 nicklas 535     return about;
4198 28 Mar 08 nicklas 536   }
4198 28 Mar 08 nicklas 537   
4198 28 Mar 08 nicklas 538   
4198 28 Mar 08 nicklas 539   /**
4163 28 Feb 08 nicklas 540     Create extension points from an XML document.
4163 28 Feb 08 nicklas 541   */
6875 20 Apr 15 nicklas 542   @SuppressWarnings({ "unchecked", "rawtypes" })
4198 28 Mar 08 nicklas 543   protected int loadExtensionPoints(Document dom, ClassLoader classLoader, About globalAbout)
4163 28 Feb 08 nicklas 544     throws ClassNotFoundException, NoSuchMethodException,
7513 02 Nov 18 nicklas 545       IllegalAccessException, InstantiationException, InvocationTargetException
4163 28 Feb 08 nicklas 546   {
5598 30 Mar 11 nicklas 547     log.debug("Loading extension points from file: " + lastName);
4198 28 Mar 08 nicklas 548     int numLoaded = 0;
4198 28 Mar 08 nicklas 549     List<ExtensionPoint<Action>> temp = new LinkedList<ExtensionPoint<Action>>();
4163 28 Feb 08 nicklas 550     /*
4163 28 Feb 08 nicklas 551       Format of an extension point is:
4163 28 Feb 08 nicklas 552       <extension-point
4163 28 Feb 08 nicklas 553         id="..."
4163 28 Feb 08 nicklas 554         >
4168 04 Mar 08 nicklas 555         <name>...</name>
4163 28 Feb 08 nicklas 556         <description>...</description>
4163 28 Feb 08 nicklas 557         <action-class>...</action-class>
4163 28 Feb 08 nicklas 558         <renderer-factory override="true|false">
4163 28 Feb 08 nicklas 559           <factory-class>...</factory-class>
4163 28 Feb 08 nicklas 560           <parameters>
4163 28 Feb 08 nicklas 561             any-tags
4163 28 Feb 08 nicklas 562           </parameters>
4163 28 Feb 08 nicklas 563         </renderer-factory>
5486 12 Nov 10 nicklas 564         <error-handler-factory>
5486 12 Nov 10 nicklas 565           <factory-class>...</factory-class>
5486 12 Nov 10 nicklas 566           <parameters>
5486 12 Nov 10 nicklas 567             any-tags
5486 12 Nov 10 nicklas 568           </parameters>
5486 12 Nov 10 nicklas 569         </error-handler-factory>
4163 28 Feb 08 nicklas 570       </extension-point>
4163 28 Feb 08 nicklas 571      */
4163 28 Feb 08 nicklas 572     Element root = dom.getRootElement();
4163 28 Feb 08 nicklas 573     Namespace ns = root.getNamespace();
4874 02 Apr 09 nicklas 574     // ID base will be prepended to extension point id:s if defined
4874 02 Apr 09 nicklas 575     String idBase = Values.getStringOrNull(root.getAttributeValue("id-base"));
4874 02 Apr 09 nicklas 576     
4163 28 Feb 08 nicklas 577     List<Element> epTags = root.getChildren("extension-point", ns);
5598 30 Mar 11 nicklas 578     log.debug("Found " + epTags.size() + " extension point(s) in file: " + lastName);
4163 28 Feb 08 nicklas 579     for (Element epTag : epTags)
4163 28 Feb 08 nicklas 580     {
4163 28 Feb 08 nicklas 581       String id = Values.getStringOrNull(epTag.getAttributeValue("id"));
4874 02 Apr 09 nicklas 582       if (idBase != null) id = idBase + id;
5598 30 Mar 11 nicklas 583
5598 30 Mar 11 nicklas 584       log.debug("Processing extension point: " + id);
4163 28 Feb 08 nicklas 585       
5598 30 Mar 11 nicklas 586       if (filter == null || filter.evaluate(epTag))
4163 28 Feb 08 nicklas 587       {
5598 30 Mar 11 nicklas 588         ExtensionPointBean<Action> extensionPoint = new ExtensionPointBean<Action>();
5598 30 Mar 11 nicklas 589         temp.add(extensionPoint);
5598 30 Mar 11 nicklas 590         numLoaded++;
5598 30 Mar 11 nicklas 591     
6030 28 Mar 12 nicklas 592         // Set ID (required) and classloader
5598 30 Mar 11 nicklas 593         extensionPoint.setId(id);
5598 30 Mar 11 nicklas 594         
5598 30 Mar 11 nicklas 595         // Set Name and Description (optional)
5598 30 Mar 11 nicklas 596         extensionPoint.setName(Values.getStringOrNull(epTag.getChildText("name", ns)));
5598 30 Mar 11 nicklas 597         extensionPoint.setDescription(Values.getStringOrNull(epTag.getChildText("description", ns)));
5598 30 Mar 11 nicklas 598         
5598 30 Mar 11 nicklas 599         // Load and set Action class (required)
5598 30 Mar 11 nicklas 600         String actionClassName = Values.getStringOrNull(epTag.getChildText("action-class", ns));
5598 30 Mar 11 nicklas 601         Class actionClass = 
5598 30 Mar 11 nicklas 602           ClassUtil.checkAndLoadClass(classLoader, actionClassName, false, Action.class);
5598 30 Mar 11 nicklas 603         extensionPoint.setActionClass(actionClass);
5598 30 Mar 11 nicklas 604         
5598 30 Mar 11 nicklas 605         // Load and set RendererFactory (optional)
5598 30 Mar 11 nicklas 606         Element rfTag = epTag.getChild("renderer-factory", ns);
5598 30 Mar 11 nicklas 607         if (rfTag != null)
5598 30 Mar 11 nicklas 608         {
6875 20 Apr 15 nicklas 609           RendererFactory<? super Action> rf = createFactory(rfTag, classLoader, RendererFactory.class, null);
5598 30 Mar 11 nicklas 610           extensionPoint.setRendererFactory(rf);
5598 30 Mar 11 nicklas 611           extensionPoint.setAllowRendererOverrider(
5598 30 Mar 11 nicklas 612             Values.getBoolean(rfTag.getAttributeValue("override")));
5598 30 Mar 11 nicklas 613         }
5598 30 Mar 11 nicklas 614         
5598 30 Mar 11 nicklas 615         Element ehfTag = epTag.getChild("error-handler-factory", ns);
5598 30 Mar 11 nicklas 616         if (ehfTag != null)
5598 30 Mar 11 nicklas 617         {
6875 20 Apr 15 nicklas 618           ErrorHandlerFactory<? super Action> ehf = createFactory(ehfTag, classLoader, ErrorHandlerFactory.class, null);
5598 30 Mar 11 nicklas 619           extensionPoint.setErrorHandlerFactory(ehf);
5598 30 Mar 11 nicklas 620         }
6030 28 Mar 12 nicklas 621         
6030 28 Mar 12 nicklas 622         if (classLoader != null) classLoaders.put(extensionPoint.getId(), classLoader);
4163 28 Feb 08 nicklas 623       }
4163 28 Feb 08 nicklas 624     }
4198 28 Mar 08 nicklas 625     extensionPoints.addAll(temp);
4198 28 Mar 08 nicklas 626     return numLoaded;
4163 28 Feb 08 nicklas 627   }
4163 28 Feb 08 nicklas 628   
4163 28 Feb 08 nicklas 629
4163 28 Feb 08 nicklas 630   /**
4163 28 Feb 08 nicklas 631     Create extensions from an XML document.
4163 28 Feb 08 nicklas 632   */
4163 28 Feb 08 nicklas 633   @SuppressWarnings("unchecked")
4198 28 Mar 08 nicklas 634   protected int loadExtensions(Document dom, ClassLoader classLoader, About globalAbout)
4163 28 Feb 08 nicklas 635     throws ClassNotFoundException, NoSuchMethodException,
7513 02 Nov 18 nicklas 636       IllegalAccessException, InstantiationException, InvocationTargetException
4163 28 Feb 08 nicklas 637   {
5598 30 Mar 11 nicklas 638     log.debug("Loading extensions from file: " + lastName);
4198 28 Mar 08 nicklas 639     int numLoaded = 0;
4198 28 Mar 08 nicklas 640     List<Extension<Action>> temp = new LinkedList<Extension<Action>>();
4163 28 Feb 08 nicklas 641     /*
4163 28 Feb 08 nicklas 642       Format of an extension is:
4163 28 Feb 08 nicklas 643       <extension
4163 28 Feb 08 nicklas 644         id="..."
4163 28 Feb 08 nicklas 645         extends="..."
4163 28 Feb 08 nicklas 646         >
5519 23 Nov 10 nicklas 647         <index>...</index>
4163 28 Feb 08 nicklas 648         <about>
4163 28 Feb 08 nicklas 649           <name>...</name>
4163 28 Feb 08 nicklas 650           <description>...</name>
4163 28 Feb 08 nicklas 651           <version>...</version>
4163 28 Feb 08 nicklas 652           <contact>...</contact>
4163 28 Feb 08 nicklas 653           <copyright>...</copyright>
4163 28 Feb 08 nicklas 654           <email>...</email>
4163 28 Feb 08 nicklas 655           <url>...</url>
4163 28 Feb 08 nicklas 656         </about>
4163 28 Feb 08 nicklas 657         <action-factory>
4163 28 Feb 08 nicklas 658           <factory-class>...</factory-class>
4163 28 Feb 08 nicklas 659           <parameters>
4163 28 Feb 08 nicklas 660             any-tags
4163 28 Feb 08 nicklas 661           </parameters>
4163 28 Feb 08 nicklas 662         </action-factory>
4163 28 Feb 08 nicklas 663         <action-factory>
4163 28 Feb 08 nicklas 664           <factory-class>...</factory-class>
4163 28 Feb 08 nicklas 665           <parameters>
4163 28 Feb 08 nicklas 666             any-tags
4163 28 Feb 08 nicklas 667           </parameters>
4163 28 Feb 08 nicklas 668         </action-factory>
4163 28 Feb 08 nicklas 669       </extension>
4163 28 Feb 08 nicklas 670     */
4163 28 Feb 08 nicklas 671
4163 28 Feb 08 nicklas 672     Element root = dom.getRootElement();
4163 28 Feb 08 nicklas 673     Namespace ns = root.getNamespace();
4874 02 Apr 09 nicklas 674     // ID base will be prepended to extension point id:s if defined
4874 02 Apr 09 nicklas 675     String idBase = Values.getStringOrNull(root.getAttributeValue("id-base"));
4874 02 Apr 09 nicklas 676
4163 28 Feb 08 nicklas 677     List<Element> epTags = root.getChildren("extension", ns);
5598 30 Mar 11 nicklas 678     log.debug("Found " + epTags.size() + " extension(s) in file: " + lastName);
4163 28 Feb 08 nicklas 679     for (Element epTag : epTags)
4163 28 Feb 08 nicklas 680     {
5519 23 Nov 10 nicklas 681       // id
4163 28 Feb 08 nicklas 682       String id = Values.getStringOrNull(epTag.getAttributeValue("id"));
4874 02 Apr 09 nicklas 683       if (idBase != null) id = idBase + id;
5598 30 Mar 11 nicklas 684       log.debug("Processing extension: " + id);
5519 23 Nov 10 nicklas 685       
5598 30 Mar 11 nicklas 686       // <index>
5598 30 Mar 11 nicklas 687       float defaultIndex = Values.getFloat(epTag.getChildText("index", ns), 999.0f);
5598 30 Mar 11 nicklas 688       
5519 23 Nov 10 nicklas 689       // Pick up the extension points + index
5519 23 Nov 10 nicklas 690       List<String> extensionPoints = new ArrayList<String>();
5519 23 Nov 10 nicklas 691       List<Float> indexes = new ArrayList<Float>();
5519 23 Nov 10 nicklas 692       // First... the extends attribute
5519 23 Nov 10 nicklas 693       String epId = Values.getStringOrNull(epTag.getAttributeValue("extends"));
5519 23 Nov 10 nicklas 694       if (epId != null)
5519 23 Nov 10 nicklas 695       {
5598 30 Mar 11 nicklas 696         if (filter == null || filter.evaluate(epTag))
5598 30 Mar 11 nicklas 697         {
5598 30 Mar 11 nicklas 698           extensionPoints.add(epId);
5598 30 Mar 11 nicklas 699           indexes.add(defaultIndex);
5598 30 Mar 11 nicklas 700         }
5519 23 Nov 10 nicklas 701       }
5598 30 Mar 11 nicklas 702       
5519 23 Nov 10 nicklas 703       // Then... any <extends>/<ref> subtags
5519 23 Nov 10 nicklas 704       Element extendsTag = epTag.getChild("extends", ns);
5519 23 Nov 10 nicklas 705       if (extendsTag != null)
5519 23 Nov 10 nicklas 706       {
6870 16 Apr 15 nicklas 707         for (Element refTag : extendsTag.getChildren("ref", ns))
5519 23 Nov 10 nicklas 708         {
5598 30 Mar 11 nicklas 709           if (filter == null || filter.evaluate(refTag))
5519 23 Nov 10 nicklas 710           {
5598 30 Mar 11 nicklas 711             epId = Values.getStringOrNull(refTag.getText());
5598 30 Mar 11 nicklas 712             if (epId != null)
5598 30 Mar 11 nicklas 713             {
5598 30 Mar 11 nicklas 714               extensionPoints.add(epId);
5598 30 Mar 11 nicklas 715               indexes.add(Values.getFloat(refTag.getAttributeValue("index"), defaultIndex));
5598 30 Mar 11 nicklas 716             }
5519 23 Nov 10 nicklas 717           }
5519 23 Nov 10 nicklas 718         }
5519 23 Nov 10 nicklas 719       }
5598 30 Mar 11 nicklas 720
5598 30 Mar 11 nicklas 721       // Any extension points remaining after filtering?
5598 30 Mar 11 nicklas 722       if (extensionPoints.size() > 0)
5519 23 Nov 10 nicklas 723       {
5598 30 Mar 11 nicklas 724   
5598 30 Mar 11 nicklas 725         // <about>
5598 30 Mar 11 nicklas 726         AboutBean about = loadAbout(epTag.getChild("about", ns));
5598 30 Mar 11 nicklas 727         if (globalAbout != null)
5598 30 Mar 11 nicklas 728         {
5598 30 Mar 11 nicklas 729           if (about == null) about = new AboutBean();
5598 30 Mar 11 nicklas 730           about.copy(globalAbout, false);
5598 30 Mar 11 nicklas 731         }
5607 15 Apr 11 nicklas 732         
5607 15 Apr 11 nicklas 733         if (!verifyBaseVersion(about, false))
5598 30 Mar 11 nicklas 734         {
5607 15 Apr 11 nicklas 735           String min = about.getMinBaseVersion();
5607 15 Apr 11 nicklas 736           String max = about.getMaxBaseVersion();
5607 15 Apr 11 nicklas 737           log.info("Extension '" + id + "' require BASE version between " + 
5607 15 Apr 11 nicklas 738             (min == null ? "*" : min) + " and " + (max == null ? "*" : max)  
5607 15 Apr 11 nicklas 739           );
5598 30 Mar 11 nicklas 740         }
5607 15 Apr 11 nicklas 741         else
5598 30 Mar 11 nicklas 742         {
5607 15 Apr 11 nicklas 743           // <action-factory>
5607 15 Apr 11 nicklas 744           Element afTag = epTag.getChild("action-factory", ns);
6875 20 Apr 15 nicklas 745           ActionFactory<? super Action> af = null;
6627 25 Nov 14 nicklas 746           Preset config = Application.getExtensionsManager().getSettings().getSettingsForExtension(id);
5607 15 Apr 11 nicklas 747           if (afTag != null)
5607 15 Apr 11 nicklas 748           {
6627 25 Nov 14 nicklas 749             af = createFactory(afTag, classLoader, ActionFactory.class, config);
5607 15 Apr 11 nicklas 750           }
5519 23 Nov 10 nicklas 751         
5607 15 Apr 11 nicklas 752           // <renderer-factory>
5607 15 Apr 11 nicklas 753           Element rfTag = epTag.getChild("renderer-factory", ns);
6875 20 Apr 15 nicklas 754           RendererFactory<? super Action> rf = null;
5607 15 Apr 11 nicklas 755           if (rfTag != null)
5607 15 Apr 11 nicklas 756           {
6627 25 Nov 14 nicklas 757             rf = createFactory(rfTag, classLoader, RendererFactory.class, config);
5607 15 Apr 11 nicklas 758           }
5598 30 Mar 11 nicklas 759           
5607 15 Apr 11 nicklas 760           String commonIdPrefix = null;
5607 15 Apr 11 nicklas 761           if (extensionPoints.size() > 1)
5598 30 Mar 11 nicklas 762           {
5607 15 Apr 11 nicklas 763             // We need to calculate the common id prefix for the extension points
5607 15 Apr 11 nicklas 764             // so that we can create unique extension id:s
5607 15 Apr 11 nicklas 765             commonIdPrefix = StringUtil.getCommonPrefix(extensionPoints);
5598 30 Mar 11 nicklas 766           }
5598 30 Mar 11 nicklas 767           
5607 15 Apr 11 nicklas 768           for (int i = 0; i < extensionPoints.size(); ++i)
5607 15 Apr 11 nicklas 769           {
5607 15 Apr 11 nicklas 770             epId = extensionPoints.get(i);
5607 15 Apr 11 nicklas 771             float index = indexes.get(i);
5607 15 Apr 11 nicklas 772             ExtensionBean<Action> ext = new ExtensionBean<Action>();
5607 15 Apr 11 nicklas 773             temp.add(ext);
5607 15 Apr 11 nicklas 774             numLoaded++;
5607 15 Apr 11 nicklas 775             
5607 15 Apr 11 nicklas 776             String thisId = id;
5607 15 Apr 11 nicklas 777             if (commonIdPrefix != null)
5607 15 Apr 11 nicklas 778             {
5607 15 Apr 11 nicklas 779               thisId += ":" + epId.substring(commonIdPrefix.length());
5607 15 Apr 11 nicklas 780             }
5607 15 Apr 11 nicklas 781             
5607 15 Apr 11 nicklas 782             ext.setId(thisId);
5607 15 Apr 11 nicklas 783             ext.setExtends(epId);
5607 15 Apr 11 nicklas 784             ext.setIndex(index);
5607 15 Apr 11 nicklas 785             ext.setAbout(about);
5607 15 Apr 11 nicklas 786             ext.setActionFactory(af);
5607 15 Apr 11 nicklas 787             ext.setRendererFactory(rf);
6030 28 Mar 12 nicklas 788             if (classLoader != null) classLoaders.put(ext.getId(), classLoader);
5607 15 Apr 11 nicklas 789           }
5598 30 Mar 11 nicklas 790         }
4163 28 Feb 08 nicklas 791       }
4163 28 Feb 08 nicklas 792     }
4198 28 Mar 08 nicklas 793     extensions.addAll(temp);
4198 28 Mar 08 nicklas 794     return numLoaded;
4163 28 Feb 08 nicklas 795   }
4163 28 Feb 08 nicklas 796   
4163 28 Feb 08 nicklas 797   /**
5610 15 Apr 11 nicklas 798     Load plug-in information from an XML document.
5610 15 Apr 11 nicklas 799     @since 3.0
5610 15 Apr 11 nicklas 800   */
5610 15 Apr 11 nicklas 801   protected int loadPluginDefinitions(Document dom, ClassLoader classLoader, About globalAbout)
5610 15 Apr 11 nicklas 802     throws ClassNotFoundException, NoSuchMethodException,
5610 15 Apr 11 nicklas 803       IllegalAccessException, InstantiationException
5610 15 Apr 11 nicklas 804   {
5610 15 Apr 11 nicklas 805     log.debug("Loading plug-in definitions from file: " + lastName);
5610 15 Apr 11 nicklas 806     int numLoaded = 0;
5610 15 Apr 11 nicklas 807     List<PluginInfo> temp = new LinkedList<PluginInfo>();
5610 15 Apr 11 nicklas 808     /*
5610 15 Apr 11 nicklas 809       Format of a plug-in definition is:
5610 15 Apr 11 nicklas 810       <plugin-definition
5610 15 Apr 11 nicklas 811         id="..."
5610 15 Apr 11 nicklas 812         >
5610 15 Apr 11 nicklas 813         <about>
5610 15 Apr 11 nicklas 814           <name>...</name>
5610 15 Apr 11 nicklas 815           <description>...</name>
5610 15 Apr 11 nicklas 816           <version>...</version>
5610 15 Apr 11 nicklas 817           <contact>...</contact>
5610 15 Apr 11 nicklas 818           <copyright>...</copyright>
5610 15 Apr 11 nicklas 819           <email>...</email>
5610 15 Apr 11 nicklas 820           <url>...</url>
5610 15 Apr 11 nicklas 821         </about>
5610 15 Apr 11 nicklas 822         <plugin-class>...</plugin-class>
5610 15 Apr 11 nicklas 823       </plugin-definition>
5610 15 Apr 11 nicklas 824     */
5610 15 Apr 11 nicklas 825   
5610 15 Apr 11 nicklas 826     Element root = dom.getRootElement();
5610 15 Apr 11 nicklas 827     Namespace ns = root.getNamespace();
5610 15 Apr 11 nicklas 828     // ID base will be prepended to extension point id:s if defined
5610 15 Apr 11 nicklas 829     String idBase = Values.getStringOrNull(root.getAttributeValue("id-base"));
5610 15 Apr 11 nicklas 830   
5610 15 Apr 11 nicklas 831     List<Element> pdTags = root.getChildren("plugin-definition", ns);
5610 15 Apr 11 nicklas 832     log.debug("Found " + pdTags.size() + " plug-in definitions in file: " + lastName);
5610 15 Apr 11 nicklas 833     for (Element pdTag : pdTags)
5610 15 Apr 11 nicklas 834     {
5610 15 Apr 11 nicklas 835       // id
5610 15 Apr 11 nicklas 836       String id = Values.getStringOrNull(pdTag.getAttributeValue("id"));
5610 15 Apr 11 nicklas 837       if (idBase != null) id = idBase + id;
5610 15 Apr 11 nicklas 838       log.debug("Processing plug-in definition: " + id);
5610 15 Apr 11 nicklas 839       
5615 19 Apr 11 nicklas 840       if (filter == null || filter.evaluate(pdTag))
5610 15 Apr 11 nicklas 841       {
5615 19 Apr 11 nicklas 842         // <about>
5615 19 Apr 11 nicklas 843         AboutBean about = loadAbout(pdTag.getChild("about", ns));
5615 19 Apr 11 nicklas 844         if (globalAbout != null)
5610 15 Apr 11 nicklas 845         {
5615 19 Apr 11 nicklas 846           if (about == null) about = new AboutBean();
5615 19 Apr 11 nicklas 847           about.copy(globalAbout, false);
5610 15 Apr 11 nicklas 848         }
5615 19 Apr 11 nicklas 849         
5615 19 Apr 11 nicklas 850         if (!verifyBaseVersion(about, false))
5615 19 Apr 11 nicklas 851         {
5615 19 Apr 11 nicklas 852           String min = about.getMinBaseVersion();
5615 19 Apr 11 nicklas 853           String max = about.getMaxBaseVersion();
5615 19 Apr 11 nicklas 854           log.info("Plug-in definition '" + id + "' require BASE version between " + 
5615 19 Apr 11 nicklas 855             (min == null ? "*" : min) + " and " + (max == null ? "*" : max)  
5615 19 Apr 11 nicklas 856           );
5615 19 Apr 11 nicklas 857         }
5615 19 Apr 11 nicklas 858         else
5615 19 Apr 11 nicklas 859         {
5616 27 Apr 11 nicklas 860           // <plugin-class>
5616 27 Apr 11 nicklas 861           String className = Values.getStringOrNull(pdTag.getChildText("plugin-class", ns));
5616 27 Apr 11 nicklas 862           
5616 27 Apr 11 nicklas 863           PluginInfo info = new PluginInfo(id);
5616 27 Apr 11 nicklas 864           info.setAbout(about);
5616 27 Apr 11 nicklas 865           info.setClassName(className);
5616 27 Apr 11 nicklas 866           
5616 27 Apr 11 nicklas 867           temp.add(info);
5616 27 Apr 11 nicklas 868           
5616 27 Apr 11 nicklas 869           // <settings>
5616 27 Apr 11 nicklas 870           Element settingsTag = pdTag.getChild("settings", ns);
5616 27 Apr 11 nicklas 871           if (settingsTag != null)
5615 19 Apr 11 nicklas 872           {
6473 11 Jun 14 nicklas 873             for (Element propertyTag : settingsTag.getChildren("property", ns))
5616 27 Apr 11 nicklas 874             {
5616 27 Apr 11 nicklas 875               String name = propertyTag.getAttributeValue("name");
5616 27 Apr 11 nicklas 876               String value = Values.getStringOrNull(propertyTag.getText());
5616 27 Apr 11 nicklas 877               info.setProperty(name, value);
5616 27 Apr 11 nicklas 878             }
5615 19 Apr 11 nicklas 879           }
5615 19 Apr 11 nicklas 880         }
5610 15 Apr 11 nicklas 881       }
5610 15 Apr 11 nicklas 882     }
5610 15 Apr 11 nicklas 883     pluginDefinitions.addAll(temp);
5610 15 Apr 11 nicklas 884     return numLoaded;
5610 15 Apr 11 nicklas 885   }
5610 15 Apr 11 nicklas 886
5610 15 Apr 11 nicklas 887   
5610 15 Apr 11 nicklas 888   /**
4163 28 Feb 08 nicklas 889     Create a new factory instance. The factory must be a class
4163 28 Feb 08 nicklas 890     with a public, no-argument constructor. The factory class name
4163 28 Feb 08 nicklas 891     is read from the &lt;factory-class&gt; tag and initialisation
4163 28 Feb 08 nicklas 892     parameters from the &lt;parameters&gt; tag.
4163 28 Feb 08 nicklas 893     
4163 28 Feb 08 nicklas 894     @param factoryTag The root tag of the factory definition
4163 28 Feb 08 nicklas 895     @param classLoader The classloader to use, or null to use
4163 28 Feb 08 nicklas 896       the BASE core classloader
4163 28 Feb 08 nicklas 897     @param factoryType Class type of the factory. The named class
4163 28 Feb 08 nicklas 898       must implement or be a subclass of this class
4163 28 Feb 08 nicklas 899     @return An initialised factory
4163 28 Feb 08 nicklas 900   */
6627 25 Nov 14 nicklas 901   protected <F> F createFactory(Element factoryTag, ClassLoader classLoader, Class<F> factoryType, Preset config)
4163 28 Feb 08 nicklas 902     throws ClassNotFoundException, NoSuchMethodException,
7513 02 Nov 18 nicklas 903       IllegalAccessException, InstantiationException, InvocationTargetException
4163 28 Feb 08 nicklas 904   {
4163 28 Feb 08 nicklas 905     Namespace ns = factoryTag.getNamespace();
4163 28 Feb 08 nicklas 906     String factoryClassName = Values.getStringOrNull(factoryTag.getChildText("factory-class", ns));
6875 20 Apr 15 nicklas 907     Class<?> factoryClass = 
4163 28 Feb 08 nicklas 908       ClassUtil.checkAndLoadClass(classLoader, factoryClassName, true, factoryType);
7513 02 Nov 18 nicklas 909     F factory = factoryType.cast(factoryClass.getDeclaredConstructor().newInstance());
6627 25 Nov 14 nicklas 910     initBeanWithReflection(factory, factoryTag.getChild("parameters", ns), config);
4163 28 Feb 08 nicklas 911     return factory;
4163 28 Feb 08 nicklas 912   }
4163 28 Feb 08 nicklas 913   
4163 28 Feb 08 nicklas 914   /**
4163 28 Feb 08 nicklas 915     Initialise a bean using reflection. For each child tag
4163 28 Feb 08 nicklas 916     of the 'root' element, this method will check if the
4163 28 Feb 08 nicklas 917     bean implements a setter method that is compatible with the
4163 28 Feb 08 nicklas 918     child tag name. The method name should start with 'set' and
4163 28 Feb 08 nicklas 919     then the tag name with the first letter capitalized. The method
4163 28 Feb 08 nicklas 920     must take a single String argument. For example, if there is
4163 28 Feb 08 nicklas 921     a child tag <code>&lt;image&gt;button.png&lt;/image&gt;</code> this will
4163 28 Feb 08 nicklas 922     be converted to the method call: <code>setImage("button.png")</code>.
4163 28 Feb 08 nicklas 923     <p>
4163 28 Feb 08 nicklas 924     Tags that has no matching public setter method are ignored.
4163 28 Feb 08 nicklas 925     
4163 28 Feb 08 nicklas 926     @param bean The bean to initialize
4163 28 Feb 08 nicklas 927     @param root The root element, if null nothing is done
4163 28 Feb 08 nicklas 928   */
6627 25 Nov 14 nicklas 929   protected void initBeanWithReflection(Object bean, Element root, Preset config)
4163 28 Feb 08 nicklas 930   {
4163 28 Feb 08 nicklas 931     if (root == null) return;
4163 28 Feb 08 nicklas 932     Class<?> beanClass = bean.getClass();
4198 28 Mar 08 nicklas 933     
6473 11 Jun 14 nicklas 934     List<Element> parameters = root.getChildren();
4198 28 Mar 08 nicklas 935     factoryParameters.put(bean, xmlOut.outputString(root));
4198 28 Mar 08 nicklas 936     
4207 04 Apr 08 nicklas 937     // Check for generic setParameter(String, String) method
4207 04 Apr 08 nicklas 938     Method setParameter = getSetParameterMethod(beanClass);
4207 04 Apr 08 nicklas 939     
4198 28 Mar 08 nicklas 940     // List all child tags
4198 28 Mar 08 nicklas 941     for (Element child : parameters)
4163 28 Feb 08 nicklas 942     {
4198 28 Mar 08 nicklas 943       // childName is the name of the setter method --> set<ChildName>
4163 28 Feb 08 nicklas 944       String childName = child.getName();
4198 28 Mar 08 nicklas 945
4198 28 Mar 08 nicklas 946       // The value passed as an argument to the setter method
6627 25 Nov 14 nicklas 947       String value = config != null ? Values.getStringOrNull(config.getSetting(childName)) : null;
6627 25 Nov 14 nicklas 948       if (value == null) 
6627 25 Nov 14 nicklas 949       {
6627 25 Nov 14 nicklas 950         value = Values.getStringOrNull(child.getText());
6627 25 Nov 14 nicklas 951       }
5014 25 Jun 09 martin 952       
5014 25 Jun 09 martin 953       if (value == null)
5014 25 Jun 09 martin 954       {
5014 25 Jun 09 martin 955         /* #### CONTINUE-STATEMENT #### */
5014 25 Jun 09 martin 956         continue;
5014 25 Jun 09 martin 957       }
4198 28 Mar 08 nicklas 958
6219 08 Jan 13 nicklas 959       Method setter = getSetterMethod(beanClass, childName);
6219 08 Jan 13 nicklas 960       if (setter != null)
4207 04 Apr 08 nicklas 961       {
6219 08 Jan 13 nicklas 962         // Specific setter method was found
6219 08 Jan 13 nicklas 963         // Convert the input value
6219 08 Jan 13 nicklas 964         String converted = getConvertedValue(value, setter);
6219 08 Jan 13 nicklas 965
6219 08 Jan 13 nicklas 966         // Invoke the setter method -- ignore all errors
6219 08 Jan 13 nicklas 967         try
6219 08 Jan 13 nicklas 968         {
6219 08 Jan 13 nicklas 969           setter.invoke(bean, converted);
6219 08 Jan 13 nicklas 970         }
6219 08 Jan 13 nicklas 971         catch (IllegalAccessException ex)
6219 08 Jan 13 nicklas 972         {}
6219 08 Jan 13 nicklas 973         catch (InvocationTargetException ex)
6219 08 Jan 13 nicklas 974         {}
6219 08 Jan 13 nicklas 975       }
6219 08 Jan 13 nicklas 976       else if (setParameter != null) 
6219 08 Jan 13 nicklas 977       {
6219 08 Jan 13 nicklas 978         // Call generic setParameter(String, String) method
4208 07 Apr 08 nicklas 979         String converted = getConvertedValue(value, setParameter);
4207 04 Apr 08 nicklas 980         try
4207 04 Apr 08 nicklas 981         {
4208 07 Apr 08 nicklas 982           setParameter.invoke(bean, childName, converted);
4207 04 Apr 08 nicklas 983         }
4207 04 Apr 08 nicklas 984         catch (IllegalAccessException ex)
4207 04 Apr 08 nicklas 985         {}
4207 04 Apr 08 nicklas 986         catch (InvocationTargetException ex)
4207 04 Apr 08 nicklas 987         {}
4207 04 Apr 08 nicklas 988       }
4163 28 Feb 08 nicklas 989     }
4163 28 Feb 08 nicklas 990   }
4163 28 Feb 08 nicklas 991   
4198 28 Mar 08 nicklas 992   /**
4198 28 Mar 08 nicklas 993     Get the setter method for a given tag name.
4198 28 Mar 08 nicklas 994     @param beanClass The class to look for the setter method in
4198 28 Mar 08 nicklas 995     @param tagName The tag name in the XML file
4207 04 Apr 08 nicklas 996     @return A Method object, or null if no method is found
4198 28 Mar 08 nicklas 997   */
4198 28 Mar 08 nicklas 998   protected Method getSetterMethod(Class<?> beanClass, String tagName)
4198 28 Mar 08 nicklas 999   {
4198 28 Mar 08 nicklas 1000     String methodName = getSetterMethodNameFromTag(tagName);
4198 28 Mar 08 nicklas 1001     Method setter = null;
4198 28 Mar 08 nicklas 1002     try
4198 28 Mar 08 nicklas 1003     {
4198 28 Mar 08 nicklas 1004       setter = beanClass.getMethod(methodName, String.class);
4198 28 Mar 08 nicklas 1005     }
4198 28 Mar 08 nicklas 1006     catch (NoSuchMethodException ex)
4198 28 Mar 08 nicklas 1007     {}
4198 28 Mar 08 nicklas 1008     return setter;
4198 28 Mar 08 nicklas 1009   }
4198 28 Mar 08 nicklas 1010   
4198 28 Mar 08 nicklas 1011   /**
4207 04 Apr 08 nicklas 1012     Check if the bean class has a <code>setParameter(String, String)</code>
4207 04 Apr 08 nicklas 1013     method and return it's reference if it has.
4207 04 Apr 08 nicklas 1014     @param beanClass The class to look for the method in
4207 04 Apr 08 nicklas 1015     @return A Method object, or null if no method is found
4207 04 Apr 08 nicklas 1016   */
4207 04 Apr 08 nicklas 1017   protected Method getSetParameterMethod(Class<?> beanClass)
4207 04 Apr 08 nicklas 1018   {
4207 04 Apr 08 nicklas 1019     Method setParameter = null;
4207 04 Apr 08 nicklas 1020     try
4207 04 Apr 08 nicklas 1021     {
4207 04 Apr 08 nicklas 1022       setParameter = beanClass.getMethod("setParameter", String.class, String.class);
4207 04 Apr 08 nicklas 1023     }
4207 04 Apr 08 nicklas 1024     catch (NoSuchMethodException ex)
4207 04 Apr 08 nicklas 1025     {}
4207 04 Apr 08 nicklas 1026     return setParameter;
4207 04 Apr 08 nicklas 1027     
4207 04 Apr 08 nicklas 1028   }
4207 04 Apr 08 nicklas 1029   
4207 04 Apr 08 nicklas 1030   /**
6219 08 Jan 13 nicklas 1031     Convert the tag name to a setter method name. The tag name is prefixed with
6219 08 Jan 13 nicklas 1032     'set', the first letter is capitalized, hyphens in the tag name are removed and
6219 08 Jan 13 nicklas 1033     the first letter after each is capitalized.
4198 28 Mar 08 nicklas 1034     @param tagName The tag name
6219 08 Jan 13 nicklas 1035     @return The converted tag name
4198 28 Mar 08 nicklas 1036   */
4198 28 Mar 08 nicklas 1037   protected String getSetterMethodNameFromTag(String tagName)
4198 28 Mar 08 nicklas 1038   {
6219 08 Jan 13 nicklas 1039     String[] parts = tagName.split("\\-");
6219 08 Jan 13 nicklas 1040     StringBuilder setterName = new StringBuilder(tagName.length()+3);
6219 08 Jan 13 nicklas 1041     setterName.append("set");
6219 08 Jan 13 nicklas 1042     for (String part : parts)
6219 08 Jan 13 nicklas 1043     {
6219 08 Jan 13 nicklas 1044       setterName.append(part.substring(0,  1).toUpperCase());
6219 08 Jan 13 nicklas 1045       setterName.append(part.substring(1));
6219 08 Jan 13 nicklas 1046     }
6219 08 Jan 13 nicklas 1047     return setterName.toString();
4198 28 Mar 08 nicklas 1048   }
4170 07 Mar 08 nicklas 1049
4208 07 Apr 08 nicklas 1050   protected String getConvertedValue(String original, Method method)
4208 07 Apr 08 nicklas 1051   {
4208 07 Apr 08 nicklas 1052     String converted = original;
4208 07 Apr 08 nicklas 1053     if (converters != null)
4208 07 Apr 08 nicklas 1054     {
4208 07 Apr 08 nicklas 1055       for (ValueConverter converter : converters)
4208 07 Apr 08 nicklas 1056       {
4208 07 Apr 08 nicklas 1057         converted = converter.convert(converted, method);
4208 07 Apr 08 nicklas 1058       }
4208 07 Apr 08 nicklas 1059     }
4208 07 Apr 08 nicklas 1060     return converted;
4208 07 Apr 08 nicklas 1061   }
4208 07 Apr 08 nicklas 1062   
4163 28 Feb 08 nicklas 1063 }