001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.configuration2;
019
020import org.apache.commons.configuration2.tree.ConfigurationNodeVisitorAdapter;
021import org.apache.commons.configuration2.tree.NodeHandler;
022import org.apache.commons.configuration2.tree.NodeTreeWalker;
023import org.xml.sax.Attributes;
024import org.xml.sax.helpers.AttributesImpl;
025
026/**
027 * <p>
028 * A specialized SAX2 XML parser that "parses" hierarchical configuration
029 * objects.
030 * </p>
031 * <p>
032 * This class mimics to be a SAX conform XML parser. Instead of parsing XML
033 * documents it processes a {@code Configuration} object and generates SAX
034 * events for the single properties defined there. This enables the whole world
035 * of XML processing for configuration objects.
036 * </p>
037 * <p>
038 * The {@code HierarchicalConfiguration} object to be parsed can be specified
039 * using a constructor or the {@code setConfiguration()} method. This object
040 * will be processed by the {@code parse()} methods. Note that these methods
041 * ignore their argument.
042 * </p>
043 *
044 * @param <T> the type of the nodes supported by this reader
045 */
046public class HierarchicalConfigurationXMLReader<T> extends
047        ConfigurationXMLReader
048{
049    /** Stores the configuration object to be parsed. */
050    private HierarchicalConfiguration<T> configuration;
051
052    /**
053     * Creates a new instance of {@code HierarchicalConfigurationXMLReader}.
054     */
055    public HierarchicalConfigurationXMLReader()
056    {
057        super();
058    }
059
060    /**
061     * Creates a new instance of {@code HierarchicalConfigurationXMLReader} and
062     * sets the configuration to be parsed.
063     *
064     * @param config the configuration object
065     */
066    public HierarchicalConfigurationXMLReader(
067            final HierarchicalConfiguration<T> config)
068    {
069        this();
070        setConfiguration(config);
071    }
072
073    /**
074     * Returns the configuration object to be parsed.
075     *
076     * @return the configuration object to be parsed
077     */
078    public HierarchicalConfiguration<T> getConfiguration()
079    {
080        return configuration;
081    }
082
083    /**
084     * Sets the configuration object to be parsed.
085     *
086     * @param config the configuration object to be parsed
087     */
088    public void setConfiguration(final HierarchicalConfiguration<T> config)
089    {
090        configuration = config;
091    }
092
093    /**
094     * Returns the configuration object to be processed.
095     *
096     * @return the actual configuration object
097     */
098    @Override
099    public Configuration getParsedConfiguration()
100    {
101        return getConfiguration();
102    }
103
104    /**
105     * Processes the actual configuration object to generate SAX parsing events.
106     */
107    @Override
108    protected void processKeys()
109    {
110        final NodeHandler<T> nodeHandler =
111                getConfiguration().getNodeModel().getNodeHandler();
112        NodeTreeWalker.INSTANCE.walkDFS(nodeHandler.getRootNode(),
113                new SAXVisitor(), nodeHandler);
114    }
115
116    /**
117     * A specialized visitor class for generating SAX events for a hierarchical
118     * node structure.
119     */
120    private class SAXVisitor extends ConfigurationNodeVisitorAdapter<T>
121    {
122        /** Constant for the attribute type. */
123        private static final String ATTR_TYPE = "CDATA";
124
125        /**
126         * Visits the specified node after its children have been processed.
127         *
128         * @param node the actual node
129         * @param handler the node handler
130         */
131        @Override
132        public void visitAfterChildren(final T node, final NodeHandler<T> handler)
133        {
134            fireElementEnd(nodeName(node, handler));
135        }
136
137        /**
138         * Visits the specified node.
139         *
140         * @param node the actual node
141         * @param handler the node handler
142         */
143        @Override
144        public void visitBeforeChildren(final T node, final NodeHandler<T> handler)
145        {
146            fireElementStart(nodeName(node, handler),
147                    fetchAttributes(node, handler));
148
149            final Object value = handler.getValue(node);
150            if (value != null)
151            {
152                fireCharacters(value.toString());
153            }
154        }
155
156        /**
157         * Checks if iteration should be terminated. This implementation stops
158         * iteration after an exception has occurred.
159         *
160         * @return a flag if iteration should be stopped
161         */
162        @Override
163        public boolean terminate()
164        {
165            return getException() != null;
166        }
167
168        /**
169         * Returns an object with all attributes for the specified node.
170         *
171         * @param node the current node
172         * @param handler the node handler
173         * @return an object with all attributes of this node
174         */
175        protected Attributes fetchAttributes(final T node, final NodeHandler<T> handler)
176        {
177            final AttributesImpl attrs = new AttributesImpl();
178
179            for (final String attr : handler.getAttributes(node))
180            {
181                final Object value = handler.getAttributeValue(node, attr);
182                if (value != null)
183                {
184                    attrs.addAttribute(NS_URI, attr, attr, ATTR_TYPE,
185                            value.toString());
186                }
187            }
188
189            return attrs;
190        }
191
192        /**
193         * Helper method for determining the name of a node. If a node has no
194         * name (which is true for the root node), the specified default name
195         * will be used.
196         *
197         * @param node the node to be checked
198         * @param handler the node handler
199         * @return the name for this node
200         */
201        private String nodeName(final T node, final NodeHandler<T> handler)
202        {
203            final String nodeName = handler.nodeName(node);
204            return nodeName == null ? getRootName() : nodeName;
205        }
206    }
207}