Clover coverage report -
Coverage timestamp: Thu Jul 5 2007 20:02:32 EDT
file stats: LOC: 675   Methods: 17
NCLOC: 529   Classes: 2
 
 Source file Conditionals Statements Methods TOTAL
XmlConfigurationParser.java 74% 87.1% 100% 84.2%
coverage coverage
 1    /*
 2    * JBoss, Home of Professional Open Source
 3    *
 4    * Distributable under LGPL license.
 5    * See terms of license at gnu.org.
 6    */
 7    package org.jboss.cache.factories;
 8   
 9    import org.apache.commons.logging.Log;
 10    import org.apache.commons.logging.LogFactory;
 11    import org.jboss.cache.buddyreplication.NextMemberBuddyLocator;
 12    import org.jboss.cache.config.BuddyReplicationConfig;
 13    import org.jboss.cache.config.BuddyReplicationConfig.BuddyLocatorConfig;
 14    import org.jboss.cache.config.CacheLoaderConfig;
 15    import org.jboss.cache.config.Configuration;
 16    import org.jboss.cache.config.ConfigurationException;
 17    import org.jboss.cache.config.EvictionConfig;
 18    import org.jboss.cache.config.EvictionPolicyConfig;
 19    import org.jboss.cache.config.EvictionRegionConfig;
 20    import org.jboss.cache.config.MissingPolicyException;
 21    import org.jboss.cache.eviction.EvictionPolicy;
 22    import org.jboss.cache.util.Util;
 23    import org.jboss.cache.xml.XmlHelper;
 24    import org.w3c.dom.Attr;
 25    import org.w3c.dom.Element;
 26    import org.w3c.dom.NamedNodeMap;
 27    import org.w3c.dom.Node;
 28    import org.w3c.dom.NodeList;
 29   
 30    import java.beans.PropertyEditor;
 31    import java.beans.PropertyEditorManager;
 32    import java.io.FileInputStream;
 33    import java.io.FileNotFoundException;
 34    import java.io.IOException;
 35    import java.io.InputStream;
 36    import java.lang.reflect.Method;
 37    import java.util.ArrayList;
 38    import java.util.HashMap;
 39    import java.util.List;
 40    import java.util.Locale;
 41    import java.util.Map;
 42    import java.util.Map.Entry;
 43    import java.util.Properties;
 44   
 45    /**
 46    * Reads in XMLconfiguration files and spits out a {@link org.jboss.cache.config.Configuration} object. When deployed as a
 47    * JBoss MBean, this role is performed by the JBoss Microcontainer. This class is only used internally in unit tests
 48    * or within {@link org.jboss.cache.CacheFactory} implementations for standalone JBoss Cache usage.
 49    *
 50    * @author <a href="mailto:manik@jboss.org">Manik Surtani (manik@jboss.org)</a>
 51    */
 52    public class XmlConfigurationParser
 53    {
 54    private static Log log = LogFactory.getLog(XmlConfigurationParser.class);
 55   
 56    public static final String ATTR = "attribute";
 57    public static final String NAME = "name";
 58   
 59    /**
 60    * Parses an XML file and returns a new configuration.
 61    */
 62  357 public Configuration parseFile(String filename)
 63    {
 64  357 InputStream is = getAsInputStreamFromClassLoader(filename);
 65  357 if (is == null)
 66    {
 67  0 if (log.isDebugEnabled())
 68  0 log.debug("Unable to find configuration file " + filename + " in classpath; searching for this file on the filesystem instead.");
 69  0 try
 70    {
 71  0 is = new FileInputStream(filename);
 72    }
 73    catch (FileNotFoundException e)
 74    {
 75  0 throw new ConfigurationException("Unable to find config file " + filename + " either in classpath or on the filesystem!", e);
 76    }
 77    }
 78   
 79  357 return parseStream(is);
 80    }
 81   
 82    /**
 83    * Parses an input stream containing XML text and returns a new configuration.
 84    */
 85  357 protected Configuration parseStream(InputStream stream)
 86    {
 87    // loop through all elements in XML.
 88  357 Element root = XmlHelper.getDocumentRoot(stream);
 89  357 Element mbeanElement = getMBeanElement(root);
 90   
 91  357 ParsedAttributes attributes = extractAttributes(mbeanElement);
 92   
 93    // Deal with legacy attributes we no longer support
 94  357 handleRemovedAttributes(attributes);
 95   
 96    // Deal with legacy attributes that we renamed or otherwise altered
 97  357 handleRenamedAttributes(attributes);
 98   
 99  357 Configuration c = new Configuration();
 100  357 setValues(c, attributes.stringAttribs, false);
 101    // Special handling for XML elements -- we hard code the parsing
 102  357 setXmlValues(c, attributes.xmlAttribs);
 103   
 104  357 return c;
 105    }
 106   
 107    /**
 108    * Check for and remove any attributes that were supported in the
 109    * 1.x releases and no longer are. Log a WARN or throw a
 110    * {@link ConfigurationException} if any are found. Which is done depends
 111    * on the attribute:
 112    * <p/>
 113    * <ul>
 114    * <li><i>MultiplexerService</i> -- throws an Exception</li>
 115    * <li><i>ServiceName</i> -- logs a WARN</li>
 116    * </ul>
 117    *
 118    * @param attributes
 119    */
 120  357 protected void handleRemovedAttributes(ParsedAttributes attributes)
 121    {
 122  357 String evictionPolicy = attributes.stringAttribs.remove("EvictionPolicyClass");
 123  357 if (evictionPolicy != null)
 124    {
 125  0 throw new ConfigurationException("XmlConfigurationParser does not " +
 126    "support the JBC 1.x attribute EvictionPolicyClass. Set the default " +
 127    "eviction policy via the policyClass element in the EvictionConfig section");
 128    }
 129  357 String multiplexerService = attributes.stringAttribs.remove("MultiplexerService");
 130  357 if (multiplexerService != null)
 131    {
 132  0 throw new ConfigurationException("XmlConfigurationParser does not " +
 133    "support the JBC 1.x attribute MultiplexerService. Inject the " +
 134    "multiplexer directly using Configuration.getRuntimeConfig().setMuxChannelFactory()");
 135    }
 136  357 String serviceName = attributes.stringAttribs.remove("ServiceName");
 137  357 if (serviceName != null)
 138    {
 139  0 log.warn("XmlConfigurationParser does not support the deprecated " +
 140    "attribute ServiceName. If JMX registration is needed, " +
 141    "register a CacheJmxWrapper or PojoCacheJmxWrapper in " +
 142    "JMX with the desired name");
 143    }
 144    }
 145   
 146    /**
 147    * Check for any attributes that were supported in the
 148    * 1.x releases but whose name has changed. Log a WARN if any are found, but
 149    * convert the attribute to the new name.
 150    * <p/>
 151    * <ul>
 152    * <li><i>UseMbean</i> becomes <i>ExposeManagementStatistics</i></li>
 153    * </ul>
 154    *
 155    * @param attributes
 156    */
 157  357 private void handleRenamedAttributes(ParsedAttributes attributes)
 158    {
 159  357 String keepStats = attributes.stringAttribs.remove("UseInterceptorMbeans");
 160  357 if (keepStats != null && attributes.stringAttribs.get("ExposeManagementStatistics") == null)
 161    {
 162  4 log.warn("Found non-existent JBC 1.x attribute 'UseInterceptorMbeans' and replaced " +
 163    "with 'ExposeManagementStatistics'. Please update your config " +
 164    "to use the new attribute name");
 165  4 attributes.stringAttribs.put("ExposeManagementStatistics", keepStats);
 166    }
 167  357 Element clc = attributes.xmlAttribs.remove("CacheLoaderConfiguration");
 168  357 if (clc != null && attributes.xmlAttribs.get("CacheLoaderConfig") == null)
 169    {
 170  2 log.warn("Found non-existent JBC 1.x attribute 'CacheLoaderConfiguration' and replaced " +
 171    "with 'CacheLoaderConfig'. Please update your config " +
 172    "to use the new attribute name");
 173  2 attributes.xmlAttribs.put("CacheLoaderConfig", clc);
 174    }
 175    }
 176   
 177  1017 protected InputStream getAsInputStreamFromClassLoader(String filename)
 178    {
 179  1017 InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(filename);
 180  1017 if (is == null)
 181    {
 182    // check system class loader
 183  0 is = getClass().getClassLoader().getResourceAsStream(filename);
 184    }
 185  1017 return is;
 186    }
 187   
 188  1017 protected Element getMBeanElement(Element root)
 189    {
 190    // This is following JBoss convention.
 191  1017 NodeList list = root.getElementsByTagName(XmlHelper.ROOT);
 192  0 if (list == null) throw new ConfigurationException("Can't find " + XmlHelper.ROOT + " tag");
 193   
 194  0 if (list.getLength() > 1) throw new ConfigurationException("Has multiple " + XmlHelper.ROOT + " tag");
 195   
 196  1017 Node node = list.item(0);
 197  1017 Element element = null;
 198  1017 if (node.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE)
 199    {
 200  1017 element = (Element) node;
 201    }
 202    else
 203    {
 204  0 throw new ConfigurationException("Can't find " + XmlHelper.ROOT + " element");
 205    }
 206  1017 return element;
 207    }
 208   
 209  7123 protected static void setValues(Object target, Map<?, ?> attribs, boolean isXmlAttribs)
 210    {
 211  7123 Class objectClass = target.getClass();
 212   
 213    // go thru simple string setters first.
 214  7123 for (Entry entry : attribs.entrySet())
 215    {
 216  21203 String propName = (String) entry.getKey();
 217  21203 String setter = getSetterName(propName);
 218  21203 Method method = null;
 219   
 220  21203 try
 221    {
 222  21203 if (isXmlAttribs)
 223    {
 224  0 method = objectClass.getMethod(setter, new Class[]{Element.class});
 225  0 method.invoke(target, new Object[]{entry.getValue()});
 226    }
 227    else
 228    {
 229  21203 method = objectClass.getMethod(setter, new Class[]{String.class});
 230  4072 method.invoke(target, new Object[]{entry.getValue()});
 231    }
 232   
 233  4072 continue;
 234    }
 235    catch (NoSuchMethodException me)
 236    {
 237    // this is ok
 238    //log.debug("Unable to find String setter for " + setter);
 239    }
 240    catch (Exception e)
 241    {
 242  0 throw new ConfigurationException("Unable to invoke setter " + setter + " on " + objectClass, e);
 243    }
 244   
 245    // if we get here, we could not find a String or Element setter.
 246  17131 for (Method m : objectClass.getMethods())
 247    {
 248  1100935 if (setter.equals(m.getName()))
 249    {
 250  12752 Class paramTypes[] = m.getParameterTypes();
 251  12752 if (paramTypes.length != 1)
 252    {
 253  0 throw new ConfigurationException("Setter " + setter + " does not contain the expected number of params. Has " + paramTypes.length + " instead of just 1.");
 254    }
 255   
 256  12752 Class parameterType = paramTypes[0];
 257  12752 PropertyEditor editor = PropertyEditorManager.findEditor(parameterType);
 258  12752 if (editor == null)
 259    {
 260  0 throw new ConfigurationException("Couldn't find a property editor for parameter type " + parameterType);
 261    }
 262   
 263  12752 editor.setAsText((String) attribs.get(propName));
 264   
 265  12752 Object parameter = editor.getValue();
 266    //if (log.isDebugEnabled()) log.debug("Invoking setter method: " + setter + " with parameter \"" + parameter + "\" of type " + parameter.getClass());
 267   
 268  12752 try
 269    {
 270  12752 m.invoke(target, new Object[]{parameter});
 271    }
 272    catch (Exception e)
 273    {
 274  0 throw new ConfigurationException("Unable to invoke setter " + setter + " on " + objectClass, e);
 275    }
 276    }
 277    }
 278    }
 279    }
 280   
 281  1017 protected void setXmlValues(Configuration conf, Map<String, Element> attribs)
 282    {
 283  1017 for (Entry<String, Element> entry : attribs.entrySet())
 284    {
 285  1721 String propname = entry.getKey();
 286  1721 if ("BuddyReplicationConfiguration".equals(propname)
 287    || "BuddyReplicationConfig".equals(propname))
 288    {
 289  1 BuddyReplicationConfig brc = parseBuddyReplicationConfig(entry.getValue());
 290  1 conf.setBuddyReplicationConfig(brc);
 291    }
 292  1720 else if ("CacheLoaderConfiguration".equals(propname)
 293    || "CacheLoaderConfig".equals(propname))
 294    {
 295  685 CacheLoaderConfig clc = parseCacheLoaderConfig(entry.getValue());
 296  685 conf.setCacheLoaderConfig(clc);
 297    }
 298  1035 else if ("EvictionPolicyConfiguration".equals(propname)
 299    || "EvictionPolicyConfig".equals(propname))
 300    {
 301  1007 EvictionConfig ec = parseEvictionConfig(entry.getValue());
 302  1007 conf.setEvictionConfig(ec);
 303    }
 304  28 else if ("ClusterConfig".equals(propname))
 305    {
 306  28 String jgc = parseClusterConfigXml(entry.getValue());
 307  28 conf.setClusterConfig(jgc);
 308    }
 309    else
 310    {
 311  0 throw new ConfigurationException("Unknown configuration element " + propname);
 312    }
 313    }
 314    }
 315   
 316  168 public static BuddyReplicationConfig parseBuddyReplicationConfig(Element element)
 317    {
 318  168 BuddyReplicationConfig brc = new BuddyReplicationConfig();
 319  168 brc.setEnabled(XmlHelper.readBooleanContents(element, "buddyReplicationEnabled"));
 320  168 brc.setDataGravitationRemoveOnFind(XmlHelper.readBooleanContents(element, "dataGravitationRemoveOnFind", true));
 321  168 brc.setDataGravitationSearchBackupTrees(XmlHelper.readBooleanContents(element, "dataGravitationSearchBackupTrees", true));
 322  168 brc.setAutoDataGravitation(brc.isEnabled() && XmlHelper.readBooleanContents(element, "autoDataGravitation", false));
 323   
 324  168 String strBuddyCommunicationTimeout = XmlHelper.readStringContents(element, "buddyCommunicationTimeout");
 325  168 try
 326    {
 327  168 brc.setBuddyCommunicationTimeout(Integer.parseInt(strBuddyCommunicationTimeout));
 328    }
 329    catch (Exception e)
 330    {
 331    }
 332    finally
 333    {
 334  168 if (log.isDebugEnabled())
 335    {
 336  0 log.debug("Using buddy communication timeout of " + brc.getBuddyCommunicationTimeout() + " millis");
 337    }
 338    }
 339  168 String buddyPoolName = XmlHelper.readStringContents(element, "buddyPoolName");
 340  168 if ("".equals(buddyPoolName))
 341    {
 342  129 buddyPoolName = null;
 343    }
 344   
 345  168 brc.setBuddyPoolName(buddyPoolName);
 346   
 347    // now read the buddy locator details
 348   
 349  168 String buddyLocatorClass = XmlHelper.readStringContents(element, "buddyLocatorClass");
 350  168 if (buddyLocatorClass == null || buddyLocatorClass.length() == 0)
 351    {
 352  24 buddyLocatorClass = NextMemberBuddyLocator.class.getName();
 353    }
 354  168 Properties props = null;
 355  168 try
 356    {
 357  168 props = XmlHelper.readPropertiesContents(element, "buddyLocatorProperties");
 358    }
 359    catch (IOException e)
 360    {
 361  0 log.warn("Caught exception reading buddyLocatorProperties", e);
 362  0 log.error("Unable to read buddyLocatorProperties specified! Using defaults for [" + buddyLocatorClass + "]");
 363    }
 364  168 BuddyLocatorConfig blc = new BuddyLocatorConfig();
 365  168 blc.setBuddyLocatorClass(buddyLocatorClass);
 366  168 blc.setBuddyLocatorProperties(props);
 367  168 brc.setBuddyLocatorConfig(blc);
 368   
 369  168 return brc;
 370    }
 371   
 372  1724 public static CacheLoaderConfig parseCacheLoaderConfig(Element element)
 373    {
 374  1724 CacheLoaderConfig clc = new CacheLoaderConfig();
 375  1724 clc.setPassivation(XmlHelper.readBooleanContents(element, "passivation"));
 376  1724 clc.setPreload(XmlHelper.readStringContents(element, "preload"));
 377  1724 clc.setShared(XmlHelper.readBooleanContents(element, "shared"));
 378   
 379  1724 NodeList cacheLoaderNodes = element.getElementsByTagName("cacheloader");
 380  1724 for (int i = 0; i < cacheLoaderNodes.getLength(); i++)
 381    {
 382  1801 Node node = cacheLoaderNodes.item(i);
 383  1801 if (node.getNodeType() == Node.ELEMENT_NODE)
 384    {
 385  1801 Element indivElement = (Element) node;
 386  1801 CacheLoaderConfig.IndividualCacheLoaderConfig iclc = new CacheLoaderConfig.IndividualCacheLoaderConfig();
 387  1801 iclc.setAsync(XmlHelper.readBooleanContents(indivElement, "async", false));
 388  1801 iclc.setIgnoreModifications(XmlHelper.readBooleanContents(indivElement, "ignoreModifications", false));
 389  1801 iclc.setFetchPersistentState(XmlHelper.readBooleanContents(indivElement, "fetchPersistentState", false));
 390  1801 iclc.setPurgeOnStartup(XmlHelper.readBooleanContents(indivElement, "purgeOnStartup", false));
 391  1801 iclc.setClassName(XmlHelper.readStringContents(indivElement, "class"));
 392  1801 try
 393    {
 394  1801 iclc.setProperties(XmlHelper.readPropertiesContents(indivElement, "properties"));
 395    }
 396    catch (IOException e)
 397    {
 398  0 throw new ConfigurationException("Problem loader cache loader properties", e);
 399    }
 400  1801 iclc.setSingletonStore(XmlHelper.readBooleanContents(indivElement, "singletonStore", false));
 401  1801 iclc.setPushStateWhenCoordinator(XmlHelper.readBooleanAttribute(indivElement, "singletonStore", "pushStateWhenCoordinator", false));
 402  1801 clc.addIndividualCacheLoaderConfig(iclc);
 403    }
 404    }
 405   
 406  1724 return clc;
 407    }
 408   
 409  1009 public static EvictionConfig parseEvictionConfig(Element element)
 410    {
 411  1009 EvictionConfig ec = new EvictionConfig();
 412   
 413  1009 if (element != null)
 414    {
 415    // If they set the default eviction policy in the element, use that
 416    // in preference to the external attribute
 417  1009 String temp = XmlHelper.getTagContents(element,
 418    EvictionConfig.EVICTION_POLICY_CLASS, ATTR, NAME);
 419  1009 if (temp != null && temp.length() > 0)
 420    {
 421  1004 ec.setDefaultEvictionPolicyClass(temp);
 422    }
 423   
 424  1009 temp = XmlHelper.getTagContents(element,
 425    EvictionConfig.WAKEUP_INTERVAL_SECONDS, ATTR, NAME);
 426   
 427  1009 int wakeupIntervalSeconds = 0;
 428  1009 if (temp != null)
 429    {
 430  1009 wakeupIntervalSeconds = Integer.parseInt(temp);
 431    }
 432   
 433  1009 if (wakeupIntervalSeconds <= 0)
 434    {
 435  0 wakeupIntervalSeconds = EvictionConfig.WAKEUP_DEFAULT;
 436    }
 437   
 438  1009 ec.setWakeupIntervalSeconds(wakeupIntervalSeconds);
 439   
 440  1009 int eventQueueSize = 0;
 441  1009 temp = XmlHelper.getTagContents(element,
 442    EvictionConfig.EVENT_QUEUE_SIZE, ATTR, NAME);
 443   
 444  1009 if (temp != null)
 445    {
 446  344 eventQueueSize = Integer.parseInt(temp);
 447    }
 448   
 449  1009 if (eventQueueSize <= 0)
 450    {
 451  665 eventQueueSize = EvictionConfig.EVENT_QUEUE_SIZE_DEFAULT;
 452    }
 453   
 454  1009 ec.setDefaultEventQueueSize(eventQueueSize);
 455   
 456  1009 NodeList list = element.getElementsByTagName(EvictionRegionConfig.REGION);
 457  1009 if (list != null && list.getLength() > 0)
 458    {
 459  1009 List regionConfigs = new ArrayList(list.getLength());
 460  1009 for (int i = 0; i < list.getLength(); i++)
 461    {
 462  3031 org.w3c.dom.Node node = list.item(i);
 463  3031 if (node.getNodeType() != org.w3c.dom.Node.ELEMENT_NODE)
 464    {
 465  0 continue;
 466    }
 467  3031 try
 468    {
 469  3031 regionConfigs.add(parseEvictionRegionConfig((Element) node, ec.getDefaultEvictionPolicyClass(), eventQueueSize));
 470    }
 471    catch (MissingPolicyException ignored)
 472    {
 473    // Just log a warning and continue
 474    // TODO (BES Oct-2006) I did it this way as that is how it worked
 475    // before, but why not just throw the exception?
 476  0 LogFactory.getLog(EvictionConfig.class).warn(ignored.getLocalizedMessage());
 477    }
 478    }
 479   
 480  1009 ec.setEvictionRegionConfigs(regionConfigs);
 481    }
 482    }
 483   
 484  1009 return ec;
 485   
 486    }
 487   
 488  3036 public static EvictionRegionConfig parseEvictionRegionConfig(Element element,
 489    String defaultEvictionClass,
 490    int defaultQueueCapacity)
 491    {
 492  3036 EvictionRegionConfig erc = new EvictionRegionConfig();
 493   
 494  3036 erc.setRegionName(element.getAttribute(EvictionRegionConfig.NAME));
 495   
 496  3036 String temp = element.getAttribute(EvictionRegionConfig.EVENT_QUEUE_SIZE);
 497  3036 if (temp != null && temp.length() > 0)
 498    {
 499  2 erc.setEventQueueSize(Integer.parseInt(temp));
 500    }
 501    else
 502    {
 503  3034 erc.setEventQueueSize(defaultQueueCapacity);
 504    }
 505   
 506  3036 String evictionClass = element.getAttribute(EvictionRegionConfig.REGION_POLICY_CLASS);
 507  3036 if (evictionClass == null || evictionClass.length() == 0)
 508    {
 509  3012 evictionClass = defaultEvictionClass;
 510    // if it's still null... what do we setCache?
 511  3012 if (evictionClass == null || evictionClass.length() == 0)
 512    {
 513  0 throw new MissingPolicyException(
 514    "There is no Eviction Policy Class specified on the region or for the entire cache!");
 515    }
 516    }
 517   
 518  3036 EvictionPolicy policy = null;
 519  3036 try
 520    {
 521  3036 policy = (EvictionPolicy) Util.loadClass(evictionClass).newInstance();
 522    }
 523    catch (RuntimeException e)
 524    {
 525  0 throw e;
 526    }
 527    catch (Exception e)
 528    {
 529  0 throw new RuntimeException("Eviction class is not properly loaded in classloader", e);
 530    }
 531   
 532  3036 EvictionPolicyConfig epc = null;
 533  3036 try
 534    {
 535  3036 epc = policy.getEvictionConfigurationClass().newInstance();
 536    }
 537    catch (RuntimeException e)
 538    {
 539  0 throw e;
 540    }
 541    catch (Exception e)
 542    {
 543  0 throw new RuntimeException("Failed to instantiate eviction configuration of class " +
 544    policy.getEvictionConfigurationClass(), e);
 545    }
 546   
 547  3036 parseEvictionPolicyConfig(element, epc);
 548   
 549  3036 erc.setEvictionPolicyConfig(epc);
 550   
 551  3036 return erc;
 552    }
 553   
 554  3053 public static void parseEvictionPolicyConfig(Element element, EvictionPolicyConfig target)
 555    {
 556  3053 target.reset();
 557  3053 ParsedAttributes attributes = extractAttributes(element);
 558  3053 setValues(target, attributes.stringAttribs, false);
 559  3053 setValues(target, attributes.xmlAttribs, true);
 560  3053 target.validate();
 561    }
 562   
 563    /**
 564    * Parses the cluster config which is used to start a JGroups channel
 565    *
 566    * @param config an old-style JGroups protocol config String
 567    */
 568  688 public static String parseClusterConfigXml(Element config)
 569    {
 570  688 StringBuffer buffer = new StringBuffer();
 571  688 NodeList stack = config.getChildNodes();
 572  688 int length = stack.getLength();
 573   
 574  688 for (int s = 0; s < length; s++)
 575    {
 576  25566 org.w3c.dom.Node node = stack.item(s);
 577  25566 if (node.getNodeType() != org.w3c.dom.Node.ELEMENT_NODE)
 578    {
 579  16445 continue;
 580    }
 581   
 582  9121 Element tag = (Element) node;
 583  9121 String protocol = tag.getTagName();
 584  9121 buffer.append(protocol);
 585  9121 NamedNodeMap attrs = tag.getAttributes();
 586  9121 int attrLength = attrs.getLength();
 587  9121 if (attrLength > 0)
 588    {
 589  8433 buffer.append('(');
 590    }
 591  9121 for (int a = 0; a < attrLength; a++)
 592    {
 593  39570 Attr attr = (Attr) attrs.item(a);
 594  39570 String name = attr.getName();
 595  39570 String value = attr.getValue();
 596  39570 buffer.append(name);
 597  39570 buffer.append('=');
 598  39570 buffer.append(value);
 599  39570 if (a < attrLength - 1)
 600    {
 601  31137 buffer.append(';');
 602    }
 603    }
 604  9121 if (attrLength > 0)
 605    {
 606  8433 buffer.append(')');
 607    }
 608  9121 buffer.append(':');
 609    }
 610    // Remove the trailing ':'
 611  688 buffer.setLength(buffer.length() - 1);
 612  688 return buffer.toString();
 613    }
 614   
 615  4070 protected static ParsedAttributes extractAttributes(Element source)
 616    {
 617  4070 Map<String, String> stringAttribs = new HashMap<String, String>();
 618  4070 Map<String, Element> xmlAttribs = new HashMap<String, Element>();
 619  4070 NodeList list = source.getElementsByTagName(XmlHelper.ATTR);
 620  0 if (log.isDebugEnabled()) log.debug("Attribute size: " + list.getLength());
 621   
 622    // loop through attributes
 623  4070 for (int loop = 0; loop < list.getLength(); loop++)
 624    {
 625  26931 Node node = list.item(loop);
 626  0 if (node.getNodeType() != org.w3c.dom.Node.ELEMENT_NODE) continue;
 627   
 628    // for each element (attribute) ...
 629  26931 Element element = (Element) node;
 630  26931 String name = element.getAttribute(XmlHelper.NAME);
 631  26931 String valueStr = XmlHelper.getElementContent(element, true);
 632   
 633  26931 Element valueXml = null;
 634  26931 if (valueStr.length() == 0)
 635    {
 636    // This may be an XML element ...
 637  1721 valueXml = XmlHelper.getConfigSubElement(element);
 638    }
 639   
 640    // add these to the maps.
 641   
 642  25210 if (valueStr.length() > 0) stringAttribs.put(name, valueStr);
 643  1721 if (valueXml != null) xmlAttribs.put(name, valueXml);
 644    }
 645   
 646  4070 return new ParsedAttributes(stringAttribs, xmlAttribs);
 647    }
 648   
 649   
 650  21203 private static String getSetterName(String propName)
 651    {
 652  21203 StringBuffer sb = new StringBuffer("set");
 653  21203 if (propName != null && propName.length() > 0)
 654    {
 655  21203 sb.append(propName.substring(0, 1).toUpperCase(Locale.ENGLISH));
 656  21203 if (propName.length() > 1)
 657    {
 658  21203 sb.append(propName.substring(1));
 659    }
 660    }
 661  21203 return sb.toString();
 662    }
 663   
 664    static class ParsedAttributes
 665    {
 666    Map<String, String> stringAttribs;
 667    Map<String, Element> xmlAttribs;
 668   
 669  4070 ParsedAttributes(Map strings, Map elements)
 670    {
 671  4070 this.stringAttribs = strings;
 672  4070 this.xmlAttribs = elements;
 673    }
 674    }
 675    }