XMLBeanDeclaration.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.configuration2.beanutils;

  18. import java.util.ArrayList;
  19. import java.util.Collection;
  20. import java.util.HashMap;
  21. import java.util.LinkedList;
  22. import java.util.List;
  23. import java.util.Map;
  24. import java.util.Set;
  25. import java.util.function.Function;
  26. import java.util.stream.Collectors;

  27. import org.apache.commons.configuration2.BaseHierarchicalConfiguration;
  28. import org.apache.commons.configuration2.HierarchicalConfiguration;
  29. import org.apache.commons.configuration2.ex.ConfigurationRuntimeException;
  30. import org.apache.commons.configuration2.interpol.ConfigurationInterpolator;
  31. import org.apache.commons.configuration2.tree.NodeHandler;
  32. import org.apache.commons.lang3.StringUtils;

  33. /**
  34.  * <p>
  35.  * An implementation of the {@code BeanDeclaration} interface that is suitable for XML configuration files.
  36.  * </p>
  37.  * <p>
  38.  * This class defines the standard layout of a bean declaration in an XML configuration file. Such a declaration must
  39.  * look like the following example fragment:
  40.  * </p>
  41.  *
  42.  * <pre>
  43.  *   ...
  44.  *   &lt;personBean config-class=&quot;my.model.PersonBean&quot;
  45.  *       lastName=&quot;Doe&quot; firstName=&quot;John&quot;&gt;
  46.  *       &lt;config-constrarg config-value=&quot;ID03493&quot; config-type=&quot;java.lang.String&quot;/&gt;
  47.  *       &lt;address config-class=&quot;my.model.AddressBean&quot;
  48.  *           street=&quot;21st street 11&quot; zip=&quot;1234&quot;
  49.  *           city=&quot;TestCity&quot;/&gt;
  50.  *   &lt;/personBean&gt;
  51.  * </pre>
  52.  *
  53.  * <p>
  54.  * The bean declaration can be contained in an arbitrary element. Here it is the {@code personBean} element. In the
  55.  * attributes of this element there can occur some reserved attributes, which have the following meaning:
  56.  * </p>
  57.  * <dl>
  58.  * <dt>{@code config-class}</dt>
  59.  * <dd>Here the full qualified name of the bean's class can be specified. An instance of this class will be created. If
  60.  * this attribute is not specified, the bean class must be provided in another way, for example as the {@code defaultClass}
  61.  * passed to the {@code BeanHelper} class.</dd>
  62.  * <dt>{@code config-factory}</dt>
  63.  * <dd>This attribute can contain the name of the {@link BeanFactory} that should be used for creating the bean. If it
  64.  * is defined, a factory with this name must have been registered at the {@code BeanHelper} class. If this attribute is
  65.  * missing, the default bean factory will be used.</dd>
  66.  * <dt>{@code config-factoryParam}</dt>
  67.  * <dd>With this attribute a parameter can be specified that will be passed to the bean factory. This may be useful for
  68.  * custom bean factories.</dd>
  69.  * </dl>
  70.  * <p>
  71.  * All further attributes starting with the {@code config-} prefix are considered as meta data and will be ignored. All
  72.  * other attributes are treated as properties of the bean to be created, i.e. corresponding setter methods of the bean
  73.  * will be invoked with the values specified here.
  74.  * </p>
  75.  * <p>
  76.  * If the bean to be created has also some complex properties (which are itself beans), their values cannot be
  77.  * initialized from attributes. For this purpose nested elements can be used. The example listing shows how an address
  78.  * bean can be initialized. This is done in a nested element whose name must match the name of a property of the
  79.  * enclosing bean declaration. The format of this nested element is exactly the same as for the bean declaration itself,
  80.  * i.e. it can have attributes defining meta data or bean properties and even further nested elements for complex bean
  81.  * properties.
  82.  * </p>
  83.  * <p>
  84.  * If the bean should be created using a specific constructor, the constructor arguments have to be specified. This is
  85.  * done by an arbitrary number of nested {@code <config-constrarg>} elements. Each element can either have the
  86.  * {@code config-value} attribute - then it defines a simple value - or must be again a bean declaration (conforming to
  87.  * the format defined here) defining the complex value of this constructor argument.
  88.  * </p>
  89.  * <p>
  90.  * A {@code XMLBeanDeclaration} object is usually created from a {@code HierarchicalConfiguration}. From this it will
  91.  * derive a {@code SubnodeConfiguration}, which is used to access the needed properties. This subnode configuration can
  92.  * be obtained using the {@link #getConfiguration()} method. All of its properties can be accessed in the usual way. To
  93.  * ensure that the property keys used by this class are understood by the configuration, the default expression engine
  94.  * will be set.
  95.  * </p>
  96.  *
  97.  * @since 1.3
  98.  */
  99. public class XMLBeanDeclaration implements BeanDeclaration {

  100.     /**
  101.      * An internal helper class which wraps the node with the bean declaration and the corresponding node handler.
  102.      *
  103.      * @param <T> the type of the node
  104.      */
  105.     static class NodeData<T> {

  106.         /** The wrapped node. */
  107.         private final T node;

  108.         /** The node handler for interacting with this node. */
  109.         private final NodeHandler<T> nodeHandler;

  110.         /**
  111.          * Constructs a new instance of {@code NodeData}.
  112.          *
  113.          * @param node the node
  114.          * @param nodeHandler the node handler
  115.          */
  116.         NodeData(final T node, final NodeHandler<T> nodeHandler) {
  117.             this.node = node;
  118.             this.nodeHandler = nodeHandler;
  119.         }

  120.         /**
  121.          * Returns the unescaped name of the node stored in this data object. This method handles the case that the node name
  122.          * may contain reserved characters with a special meaning for the current expression engine. In this case, the
  123.          * characters affected have to be escaped accordingly.
  124.          *
  125.          * @param config the configuration
  126.          * @return the escaped node name
  127.          */
  128.         String escapedNodeName(final HierarchicalConfiguration<?> config) {
  129.             return config.getExpressionEngine().nodeKey(node, StringUtils.EMPTY, nodeHandler);
  130.         }

  131.         /**
  132.          * Gets the value of the attribute with the given name of the wrapped node.
  133.          *
  134.          * @param key the key of the attribute
  135.          * @return the value of this attribute
  136.          */
  137.         Object getAttribute(final String key) {
  138.             return nodeHandler.getAttributeValue(node, key);
  139.         }

  140.         /**
  141.          * Gets a set with the names of the attributes of the wrapped node.
  142.          *
  143.          * @return the attribute names of this node
  144.          */
  145.         Set<String> getAttributes() {
  146.             return nodeHandler.getAttributes(node);
  147.         }

  148.         /**
  149.          * Gets a list with the children of the wrapped node, again wrapped into {@code NodeData} objects.
  150.          *
  151.          * @return a list with the children
  152.          */
  153.         List<NodeData<T>> getChildren() {
  154.             return wrapInNodeData(nodeHandler.getChildren(node));
  155.         }

  156.         /**
  157.          * Gets a list with the children of the wrapped node with the given name, again wrapped into {@code NodeData}
  158.          * objects.
  159.          *
  160.          * @param name the name of the desired child nodes
  161.          * @return a list with the children with this name
  162.          */
  163.         List<NodeData<T>> getChildren(final String name) {
  164.             return wrapInNodeData(nodeHandler.getChildren(node, name));
  165.         }

  166.         /**
  167.          * Returns a flag whether the wrapped node is the root node of the passed in configuration.
  168.          *
  169.          * @param config the configuration
  170.          * @return a flag whether this node is the configuration's root node
  171.          */
  172.         boolean matchesConfigRootNode(final HierarchicalConfiguration<?> config) {
  173.             return config.getNodeModel().getNodeHandler().getRootNode().equals(node);
  174.         }

  175.         /**
  176.          * Returns the name of the wrapped node.
  177.          *
  178.          * @return the node name
  179.          */
  180.         String nodeName() {
  181.             return nodeHandler.nodeName(node);
  182.         }

  183.         /**
  184.          * Wraps the passed in list of nodes in {@code NodeData} objects.
  185.          *
  186.          * @param nodes the list with nodes
  187.          * @return the wrapped nodes
  188.          */
  189.         List<NodeData<T>> wrapInNodeData(final List<T> nodes) {
  190.             return nodes.stream().map(n -> new NodeData<>(n, nodeHandler)).collect(Collectors.toList());
  191.         }
  192.     }

  193.     /** Constant for the prefix of reserved attributes. */
  194.     public static final String RESERVED_PREFIX = "config-";

  195.     /** Constant for the prefix for reserved attributes. */
  196.     public static final String ATTR_PREFIX = "[@" + RESERVED_PREFIX;

  197.     /** Constant for the bean class attribute. */
  198.     public static final String ATTR_BEAN_CLASS = ATTR_PREFIX + "class]";

  199.     /** Constant for the bean factory attribute. */
  200.     public static final String ATTR_BEAN_FACTORY = ATTR_PREFIX + "factory]";

  201.     /** Constant for the bean factory parameter attribute. */
  202.     public static final String ATTR_FACTORY_PARAM = ATTR_PREFIX + "factoryParam]";

  203.     /** Constant for the name of the bean class attribute. */
  204.     private static final String ATTR_BEAN_CLASS_NAME = RESERVED_PREFIX + "class";

  205.     /** Constant for the name of the element for constructor arguments. */
  206.     private static final String ELEM_CTOR_ARG = RESERVED_PREFIX + "constrarg";

  207.     /**
  208.      * Constant for the name of the attribute with the value of a constructor argument.
  209.      */
  210.     private static final String ATTR_CTOR_VALUE = RESERVED_PREFIX + "value";

  211.     /**
  212.      * Constant for the name of the attribute with the data type of a constructor argument.
  213.      */
  214.     private static final String ATTR_CTOR_TYPE = RESERVED_PREFIX + "type";

  215.     /**
  216.      * Creates a {@code NodeData} object from the root node of the given configuration.
  217.      *
  218.      * @param config the configuration
  219.      * @param <T> the type of the nodes
  220.      * @return the {@code NodeData} object
  221.      */
  222.     private static <T> NodeData<T> createNodeDataFromConfiguration(final HierarchicalConfiguration<T> config) {
  223.         final NodeHandler<T> handler = config.getNodeModel().getNodeHandler();
  224.         return new NodeData<>(handler.getRootNode(), handler);
  225.     }

  226.     /**
  227.      * Tests whether the constructor argument represented by the given configuration node is a bean declaration.
  228.      *
  229.      * @param nodeData the configuration node in question
  230.      * @return a flag whether this constructor argument is a bean declaration
  231.      */
  232.     private static boolean isBeanDeclarationArgument(final NodeData<?> nodeData) {
  233.         return !nodeData.getAttributes().contains(ATTR_BEAN_CLASS_NAME);
  234.     }

  235.     /** Stores the associated configuration. */
  236.     private final HierarchicalConfiguration<?> configuration;

  237.     /** Stores the configuration node that contains the bean declaration. */
  238.     private final NodeData<?> nodeData;

  239.     /** The name of the default bean class. */
  240.     private final String defaultBeanClassName;

  241.     /**
  242.      * Constructs a new instance of {@code XMLBeanDeclaration} and initializes it with the configuration node that contains the
  243.      * bean declaration. This constructor is used internally.
  244.      *
  245.      * @param config the configuration
  246.      * @param node the node with the bean declaration.
  247.      */
  248.     XMLBeanDeclaration(final HierarchicalConfiguration<?> config, final NodeData<?> node) {
  249.         this.nodeData = node;
  250.         configuration = config;
  251.         defaultBeanClassName = null;
  252.         initSubnodeConfiguration(config);
  253.     }

  254.     /**
  255.      * Constructs a new instance of {@code XMLBeanDeclaration} and initializes it from the given configuration. The
  256.      * configuration's root node must contain the bean declaration.
  257.      *
  258.      * @param config the configuration with the bean declaration
  259.      * @param <T> the node type of the configuration
  260.      */
  261.     public <T> XMLBeanDeclaration(final HierarchicalConfiguration<T> config) {
  262.         this(config, (String) null);
  263.     }

  264.     /**
  265.      * Constructs a new instance of {@code XMLBeanDeclaration} and initializes it from the given configuration. The passed in
  266.      * key points to the bean declaration.
  267.      *
  268.      * @param config the configuration (must not be <strong>null</strong>)
  269.      * @param key the key to the bean declaration (this key must point to exactly one bean declaration or a
  270.      *        {@code IllegalArgumentException} exception will be thrown)
  271.      * @param <T> the node type of the configuration
  272.      * @throws IllegalArgumentException if required information is missing to construct the bean declaration
  273.      */
  274.     public <T> XMLBeanDeclaration(final HierarchicalConfiguration<T> config, final String key) {
  275.         this(config, key, false);
  276.     }

  277.     /**
  278.      * Constructs a new instance of {@code XMLBeanDeclaration} and initializes it from the given configuration supporting
  279.      * optional declarations.
  280.      *
  281.      * @param config the configuration (must not be <strong>null</strong>)
  282.      * @param key the key to the bean declaration
  283.      * @param optional a flag whether this declaration is optional; if set to <strong>true</strong>, no exception will be thrown if
  284.      *        the passed in key is undefined
  285.      * @param <T> the node type of the configuration
  286.      * @throws IllegalArgumentException if required information is missing to construct the bean declaration
  287.      */
  288.     public <T> XMLBeanDeclaration(final HierarchicalConfiguration<T> config, final String key, final boolean optional) {
  289.         this(config, key, optional, null);
  290.     }

  291.     /**
  292.      * Constructs a new instance of {@code XMLBeanDeclaration} and initializes it from the given configuration supporting
  293.      * optional declarations and a default bean class name. The passed in key points to the bean declaration. If the key
  294.      * does not exist and the boolean argument is <strong>true</strong>, the declaration is initialized with an empty configuration.
  295.      * It is possible to create objects from such an empty declaration if a default class is provided. If the key on the
  296.      * other hand has multiple values or is undefined and the boolean argument is <strong>false</strong>, a
  297.      * {@code IllegalArgumentException} exception will be thrown. It is possible to set a default bean class name; this name
  298.      * is used if the configuration does not contain a bean class.
  299.      *
  300.      * @param config the configuration (must not be <strong>null</strong>)
  301.      * @param key the key to the bean declaration
  302.      * @param optional a flag whether this declaration is optional; if set to <strong>true</strong>, no exception will be thrown if
  303.      *        the passed in key is undefined
  304.      * @param defBeanClsName a default bean class name
  305.      * @param <T> the node type of the configuration
  306.      * @throws IllegalArgumentException if required information is missing to construct the bean declaration
  307.      * @since 2.0
  308.      */
  309.     public <T> XMLBeanDeclaration(final HierarchicalConfiguration<T> config, final String key, final boolean optional, final String defBeanClsName) {
  310.         if (config == null) {
  311.             throw new IllegalArgumentException("Configuration must not be null!");
  312.         }

  313.         HierarchicalConfiguration<?> tmpconfiguration;
  314.         try {
  315.             tmpconfiguration = config.configurationAt(key);
  316.         } catch (final ConfigurationRuntimeException iex) {
  317.             // If we reach this block, the key does not have exactly one value
  318.             if (!optional || config.getMaxIndex(key) > 0) {
  319.                 throw iex;
  320.             }
  321.             tmpconfiguration = new BaseHierarchicalConfiguration();
  322.         }
  323.         this.nodeData = createNodeDataFromConfiguration(tmpconfiguration);
  324.         this.configuration = tmpconfiguration;
  325.         defaultBeanClassName = defBeanClsName;
  326.         initSubnodeConfiguration(getConfiguration());
  327.     }

  328.     /**
  329.      * Creates a new {@code BeanDeclaration} for a child node of the current configuration node. This method is called by
  330.      * {@code getNestedBeanDeclarations()} for all complex sub properties detected by this method. Derived classes can hook
  331.      * in if they need a specific initialization. This base implementation creates a {@code XMLBeanDeclaration} that is
  332.      * properly initialized from the passed in node.
  333.      *
  334.      * @param nodeData the child node, for which a {@code BeanDeclaration} is to be created
  335.      * @return the {@code BeanDeclaration} for this child node
  336.      */
  337.     BeanDeclaration createBeanDeclaration(final NodeData<?> nodeData) {
  338.         for (final HierarchicalConfiguration<?> config : getConfiguration().configurationsAt(nodeData.escapedNodeName(getConfiguration()))) {
  339.             if (nodeData.matchesConfigRootNode(config)) {
  340.                 return new XMLBeanDeclaration(config, nodeData);
  341.             }
  342.         }
  343.         throw new ConfigurationRuntimeException("Unable to match node for " + nodeData.nodeName());
  344.     }

  345.     /**
  346.      * Creates a {@code ConstructorArg} object for the specified configuration node.
  347.      *
  348.      * @param child the configuration node
  349.      * @return the corresponding {@code ConstructorArg} object
  350.      */
  351.     private ConstructorArg createConstructorArg(final NodeData<?> child) {
  352.         final String type = getAttribute(child, ATTR_CTOR_TYPE);
  353.         if (isBeanDeclarationArgument(child)) {
  354.             return ConstructorArg.forValue(getAttribute(child, ATTR_CTOR_VALUE), type);
  355.         }
  356.         return ConstructorArg.forBeanDeclaration(createBeanDeclaration(child), type);
  357.     }

  358.     /**
  359.      * Gets an attribute of a configuration node. This method also takes interpolation into account.
  360.      *
  361.      * @param nodeData the node
  362.      * @param attribute the name of the attribute
  363.      * @return the string value of this attribute (can be <strong>null</strong>)
  364.      */
  365.     private String getAttribute(final NodeData<?> nodeData, final String attribute) {
  366.         final Object value = nodeData.getAttribute(attribute);
  367.         return value == null ? null : String.valueOf(interpolate(value));
  368.     }

  369.     /**
  370.      * Gets a set with the names of the attributes of the configuration node holding the data of this bean declaration.
  371.      *
  372.      * @return the attribute names of the underlying configuration node
  373.      */
  374.     protected Set<String> getAttributeNames() {
  375.         return getNode().getAttributes();
  376.     }

  377.     /**
  378.      * Gets the name of the class of the bean to be created. This information is obtained from the {@code config-class}
  379.      * attribute.
  380.      *
  381.      * @return the name of the bean's class
  382.      */
  383.     @Override
  384.     public String getBeanClassName() {
  385.         return getConfiguration().getString(ATTR_BEAN_CLASS, getDefaultBeanClassName());
  386.     }

  387.     /**
  388.      * Gets the name of the bean factory. This information is fetched from the {@code config-factory} attribute.
  389.      *
  390.      * @return the name of the bean factory
  391.      */
  392.     @Override
  393.     public String getBeanFactoryName() {
  394.         return getConfiguration().getString(ATTR_BEAN_FACTORY, null);
  395.     }

  396.     /**
  397.      * Gets a parameter for the bean factory. This information is fetched from the {@code config-factoryParam} attribute.
  398.      *
  399.      * @return the parameter for the bean factory
  400.      */
  401.     @Override
  402.     public Object getBeanFactoryParameter() {
  403.         return getConfiguration().getProperty(ATTR_FACTORY_PARAM);
  404.     }

  405.     /**
  406.      * Gets a map with the bean's (simple) properties. The properties are collected from all attribute nodes, which are
  407.      * not reserved.
  408.      *
  409.      * @return a map with the bean's properties
  410.      */
  411.     @Override
  412.     public Map<String, Object> getBeanProperties() {
  413.         return getAttributeNames().stream().filter(e -> !isReservedAttributeName(e))
  414.             .collect(Collectors.toMap(Function.identity(), e -> interpolate(getNode().getAttribute(e))));
  415.     }

  416.     /**
  417.      * Gets the configuration object this bean declaration is based on.
  418.      *
  419.      * @return the associated configuration
  420.      */
  421.     public HierarchicalConfiguration<?> getConfiguration() {
  422.         return configuration;
  423.     }

  424.     /**
  425.      * {@inheritDoc} This implementation processes all child nodes with the name {@code config-constrarg}. If such a node
  426.      * has a {@code config-class} attribute, it is considered a nested bean declaration; otherwise it is interpreted as a
  427.      * simple value. If no nested constructor argument declarations are found, result is an empty collection.
  428.      */
  429.     @Override
  430.     public Collection<ConstructorArg> getConstructorArgs() {
  431.         return getNode().getChildren(ELEM_CTOR_ARG).stream().map(this::createConstructorArg).collect(Collectors.toCollection(LinkedList::new));
  432.     }

  433.     /**
  434.      * Gets the name of the default bean class. This class is used if no bean class is specified in the configuration. It
  435.      * may be <strong>null</strong> if no default class was set.
  436.      *
  437.      * @return the default bean class name
  438.      * @since 2.0
  439.      */
  440.     public String getDefaultBeanClassName() {
  441.         return defaultBeanClassName;
  442.     }

  443.     /**
  444.      * Gets a map with bean declarations for the complex properties of the bean to be created. These declarations are
  445.      * obtained from the child nodes of this declaration's root node.
  446.      *
  447.      * @return a map with bean declarations for complex properties
  448.      */
  449.     @Override
  450.     public Map<String, Object> getNestedBeanDeclarations() {
  451.         final Map<String, Object> nested = new HashMap<>();
  452.         getNode().getChildren().forEach(child -> {
  453.             if (!isReservedChildName(child.nodeName())) {
  454.                 final Object obj = nested.get(child.nodeName());
  455.                 if (obj != null) {
  456.                     final List<BeanDeclaration> list;
  457.                     if (obj instanceof List) {
  458.                         // Safe because we created the lists ourselves.
  459.                         @SuppressWarnings("unchecked")
  460.                         final List<BeanDeclaration> tmpList = (List<BeanDeclaration>) obj;
  461.                         list = tmpList;
  462.                     } else {
  463.                         list = new ArrayList<>();
  464.                         list.add((BeanDeclaration) obj);
  465.                         nested.put(child.nodeName(), list);
  466.                     }
  467.                     list.add(createBeanDeclaration(child));
  468.                 } else {
  469.                     nested.put(child.nodeName(), createBeanDeclaration(child));
  470.                 }
  471.             }
  472.         });
  473.         return nested;
  474.     }

  475.     /**
  476.      * Gets the data about the associated node.
  477.      *
  478.      * @return the node with the bean declaration
  479.      */
  480.     NodeData<?> getNode() {
  481.         return nodeData;
  482.     }

  483.     /**
  484.      * Initializes the internally managed sub configuration. This method will set some default values for some properties.
  485.      *
  486.      * @param conf the configuration to initialize
  487.      */
  488.     private void initSubnodeConfiguration(final HierarchicalConfiguration<?> conf) {
  489.         conf.setExpressionEngine(null);
  490.     }

  491.     /**
  492.      * Performs interpolation for the specified value. This implementation will interpolate against the current subnode
  493.      * configuration's parent. If sub classes need a different interpolation mechanism, they should override this method.
  494.      *
  495.      * @param value the value that is to be interpolated
  496.      * @return the interpolated value
  497.      */
  498.     protected Object interpolate(final Object value) {
  499.         final ConfigurationInterpolator interpolator = getConfiguration().getInterpolator();
  500.         return interpolator != null ? interpolator.interpolate(value) : value;
  501.     }

  502.     /**
  503.      * Tests if the specified attribute name is reserved and thus does not point to a property of the bean to be created.
  504.      * This method is called when processing the attributes of this bean declaration. It is then possible to ignore some
  505.      * attributes with a specific meaning. This implementation delegates to {@link #isReservedName(String)}.
  506.      *
  507.      * @param name the name of the attribute to be checked
  508.      * @return a flag whether this name is reserved
  509.      * @since 2.0
  510.      */
  511.     protected boolean isReservedAttributeName(final String name) {
  512.         return isReservedName(name);
  513.     }

  514.     /**
  515.      * Tests if the specified child node name is reserved and thus should be ignored. This method is called when processing
  516.      * child nodes of this bean declaration. It is then possible to ignore some nodes with a specific meaning. This
  517.      * implementation delegates to {@link #isReservedName(String)}.
  518.      *
  519.      * @param name the name of the child node to be checked
  520.      * @return a flag whether this name is reserved
  521.      * @since 2.0
  522.      */
  523.     protected boolean isReservedChildName(final String name) {
  524.         return isReservedName(name);
  525.     }

  526.     /**
  527.      * Tests if the specified name of a node or attribute is reserved and thus should be ignored. This method is called per
  528.      * default by the methods for checking attribute and child node names. It checks whether the passed in name starts with
  529.      * the reserved prefix.
  530.      *
  531.      * @param name the name to be checked
  532.      * @return a flag whether this name is reserved
  533.      */
  534.     protected boolean isReservedName(final String name) {
  535.         return name == null || name.startsWith(RESERVED_PREFIX);
  536.     }
  537. }