001    /*
002     * Copyright 2004-2005 Stephen McConnell
003     *
004     * Licensed  under the  Apache License,  Version 2.0  (the "License");
005     * you may not use  this file  except in  compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *   http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed  under the  License is distributed on an "AS IS" BASIS,
012     * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
013     * implied.
014     *
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    
019    package net.dpml.util;
020    
021    import java.io.File;
022    import java.io.FileNotFoundException;
023    import java.io.InputStream;
024    import java.util.ArrayList;
025    import java.util.Properties;
026    
027    import javax.xml.parsers.DocumentBuilder;
028    import javax.xml.parsers.DocumentBuilderFactory;
029    
030    import org.w3c.dom.Document;
031    import org.w3c.dom.Element;
032    import org.w3c.dom.Node;
033    import org.w3c.dom.NodeList;
034    
035    /**
036     * Utility class supporting the translation of DOM content into local child, children,
037     * attribute and value values.
038     *
039     * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
040     * @version 1.1.0
041     */
042    public final class ElementHelper
043    {
044        private static final Resolver SIMPLE_RESOLVER = new SimpleResolver();
045        
046        private ElementHelper()
047        {
048            // utility class
049        }
050    
051       /**
052        * Return the root element of the supplied file.
053        * @param definition the file to load
054        * @return the root element
055        * @exception Exception if the error occurs during root element establishment
056        */
057        public static Element getRootElement( final File definition )
058          throws Exception
059        {
060            if( !definition.exists() )
061            {
062                throw new FileNotFoundException( definition.toString() );
063            }
064    
065            if( !definition.isFile() )
066            {
067                final String error =
068                  "Source is not a file: " + definition;
069                throw new IllegalArgumentException( error );
070            }
071    
072            final DocumentBuilderFactory factory =
073            DocumentBuilderFactory.newInstance();
074            factory.setValidating( false );
075            factory.setNamespaceAware( false );
076            final Document document =
077              factory.newDocumentBuilder().parse( definition );
078            return document.getDocumentElement();
079        }
080    
081       /**
082        * Return the root element of the supplied input stream.
083        * @param input the input stream containing a XML definition
084        * @return the root element
085        * @exception Exception if an error occurs
086        */
087        public static Element getRootElement( final InputStream input )
088          throws Exception
089        {
090            final DocumentBuilderFactory factory =
091              DocumentBuilderFactory.newInstance();
092            factory.setValidating( false );
093            factory.setNamespaceAware( false );
094            factory.setExpandEntityReferences( false );
095            DocumentBuilder builder = factory.newDocumentBuilder();
096            return getRootElement( builder, input );
097        }
098    
099       /**
100        * Return the root element of the supplied input stream.
101        * @param builder the document builder
102        * @param input the input stream containing a XML definition
103        * @return the root element
104        * @exception Exception if an error occurs
105        */
106        public static Element getRootElement( final DocumentBuilder builder, final InputStream input )
107          throws Exception
108        {
109            final Document document = builder.parse( input );
110            return document.getDocumentElement();
111        }
112    
113       /**
114        * Return a named child relative to a supplied element.
115        * @param root the parent DOM element
116        * @param name the name of a child element
117        * @return the child element of null if the child does not exist
118        */
119        public static Element getChild( final Element root, final String name )
120        {
121            if( null == root )
122            {
123                return null;
124            }
125            Element[] children = getChildren( root );
126            for( int i=0; i<children.length; i++ )
127            {
128                Element child = children[i];
129                if( name.equals( child.getTagName() ) )
130                {
131                    return child;
132                }
133            }
134            return null;
135        }
136    
137       /**
138        * Return all children matching the supplied element name.
139        * @param root the parent DOM element
140        * @param name the name against which child element will be matched
141        * @return the array of child elements with a matching name
142        */
143        public static Element[] getChildren( final Element root, final String name )
144        {
145            if( null == root )
146            {
147                return new Element[0];
148            }
149            Element[] children = getChildren( root );
150            final ArrayList result = new ArrayList();
151            for( int i=0; i<children.length; i++ )
152            {
153                final Element child = children[i];
154                if( name.equals( child.getTagName() ) )
155                {
156                    result.add( child );
157                }
158            }
159            return (Element[]) result.toArray( new Element[0] );
160        }
161    
162       /**
163        * Return all children of the supplied parent.
164        * @param root the parent DOM element
165        * @return the array of all children
166        */
167        public static Element[] getChildren( final Element root )
168        {
169            if( null == root )
170            {
171                return new Element[0];
172            }
173            final NodeList list = root.getChildNodes();
174            final int n = list.getLength();
175            if( n < 1 )
176            {
177                return new Element[0];
178            }
179            final ArrayList result = new ArrayList();
180            for( int i=0; i < n; i++ )
181            {
182                final Node item = list.item( i );
183                if( item instanceof Element )
184                {
185                    result.add( item );
186                }
187            }
188            return (Element[]) result.toArray( new Element[0] );
189        }
190    
191       /**
192        * Return the value of an element.
193        * @param node the DOM node
194        * @return the node value
195        */
196        public static String getValue( final Element node )
197        {
198            return getValue( node, null );
199        }
200        
201       /**
202        * Return the value of an element.
203        * @param node the DOM node
204        * @return the node value
205        */
206        public static String getValue( final Element node, Resolver resolver )
207        {
208            if( null == node )
209            {
210                return null;
211            }
212            String value;
213            if( node.getChildNodes().getLength() > 0 )
214            {
215                value = node.getFirstChild().getNodeValue();
216            }
217            else
218            {
219                value = node.getNodeValue();
220            }
221            return normalize( resolver, value );
222        }
223    
224       /**
225        * Return the value of an element attribute.
226        * @param node the DOM node
227        * @param key the attribute key
228        * @return the attribute value or null if the attribute is undefined
229        */
230        public static String getAttribute( final Element node, final String key )
231        {
232            return getAttribute( node, key, null );
233        }
234    
235       /**
236        * Return the value of an element attribute.
237        * @param node the DOM node
238        * @param key the attribute key
239        * @param def the default value if the attribute is undefined
240        * @return the attribute value or the default value if undefined
241        */
242        public static String getAttribute( final Element node, final String key, final String def )
243        {
244            return getAttribute( node, key, def, null );
245        }
246        
247       /**
248        * Return the value of an element attribute.
249        * @param node the DOM node
250        * @param key the attribute key
251        * @param def the default value if the attribute is undefined
252        * @return the attribute value or the default value if undefined
253        */
254        public static String getAttribute( final Element node, final String key, final String def, Resolver resolver )
255        {
256            if( null == node )
257            {
258                return def;
259            }
260            if( !node.hasAttribute( key ) )
261            {
262                return def;
263            }
264            String v = node.getAttribute( key );
265            return normalize( resolver, v );
266        }
267    
268       /**
269        * Return the value of an element attribute as a boolean
270        * @param node the DOM node
271        * @param key the attribute key
272        * @return the attribute value as a boolean or false if undefined
273        */
274        public static boolean getBooleanAttribute( final Element node, final String key )
275        {
276            return getBooleanAttribute( node, key, false );
277        }
278    
279       /**
280        * Return the value of an element attribute as a boolean.
281        * @param node the DOM node
282        * @param key the attribute key
283        * @param def the default value if the attribute is undefined
284        * @return the attribute value or the default value if undefined
285        */
286        public static boolean getBooleanAttribute( final Element node, final String key, final boolean def )
287        {
288            return getBooleanAttribute( node, key, def, null );
289        }
290        
291       /**
292        * Return the value of an element attribute as a boolean.
293        * @param node the DOM node
294        * @param key the attribute key
295        * @param def the default value if the attribute is undefined
296        * @return the attribute value or the default value if undefined
297        */
298        public static boolean getBooleanAttribute( 
299          final Element node, final String key, final boolean def, Resolver resolver )
300        {
301            if( null == node )
302            {
303                return def;
304            }
305    
306            if( !node.hasAttribute( key ) )
307            {
308                return def;
309            } 
310    
311            String value = node.getAttribute( key );
312            value = normalize( resolver, value );
313            if( value.equals( "" ) )
314            {
315                return def;
316            }
317            if( value.equalsIgnoreCase( "true" ) )
318            {
319                return true;
320            }
321            if( value.equalsIgnoreCase( "false" ) )
322            {
323                return false;
324            }
325            final String error =
326              "Boolean argument [" + value + "] not recognized.";
327            throw new IllegalArgumentException( error );
328        }
329    
330       /**
331        * Parse the value for any property tokens relative to system properties.
332        * @param value the value to parse
333        * @return the normalized string
334        */
335        static String normalize( String value )
336        {
337            return SIMPLE_RESOLVER.resolve( value );
338        }
339        
340       /**
341        * Parse the value for any property tokens relative to system properties.
342        * @param value the value to parse
343        * @return the normalized string
344        */
345        static String normalize( Resolver resolver, String value )
346        {
347            if( null != resolver )
348            {
349                return resolver.resolve( value );
350            }
351            else
352            {
353                return value;
354            }
355        }
356        
357       /**
358        * Parse the value for any property tokens relative to the supplied properties.
359        * @param value the value to parse
360        * @param props the reference properties
361        * @return the normalized string
362        */
363        static String normalize( String value, Properties props )
364        {
365            return PropertyResolver.resolve( props, value );
366        }
367    }