001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    
019    package org.apache.hadoop.hdfs.util;
020    
021    import org.apache.hadoop.classification.InterfaceAudience;
022    import org.apache.hadoop.classification.InterfaceStability;
023    import org.xml.sax.ContentHandler;
024    import org.xml.sax.SAXException;
025    import org.xml.sax.helpers.AttributesImpl;
026    
027    import java.util.LinkedList;
028    import java.util.List;
029    import java.util.Map;
030    import java.util.TreeMap;
031    
032    /**
033     * General xml utilities.
034     *   
035     */
036    @InterfaceAudience.Private
037    @InterfaceStability.Unstable
038    public class XMLUtils {
039      /**
040       * Exception that reflects an invalid XML document.
041       */
042      static public class InvalidXmlException extends RuntimeException {
043        private static final long serialVersionUID = 1L;
044        public InvalidXmlException(String s) {
045          super(s);
046        }
047      }
048      
049      /**
050       * Add a SAX tag with a string inside.
051       *
052       * @param contentHandler     the SAX content handler
053       * @param tag                the element tag to use  
054       * @param value              the string to put inside the tag
055       */
056      public static void addSaxString(ContentHandler contentHandler,
057          String tag, String val) throws SAXException {
058        contentHandler.startElement("", "", tag, new AttributesImpl());
059        char c[] = val.toString().toCharArray();
060        contentHandler.characters(c, 0, c.length);
061        contentHandler.endElement("", "", tag);
062      }
063    
064      /**
065       * Represents a bag of key-value pairs encountered during parsing an XML
066       * file.
067       */
068      static public class Stanza {
069        private TreeMap<String, LinkedList <Stanza > > subtrees;
070        private String value;
071        
072        public Stanza() {
073          subtrees = new TreeMap<String, LinkedList <Stanza > >();
074          value = "";
075        }
076        
077        public void setValue(String value) {
078          this.value = value;
079        }
080        
081        public String getValue() {
082          return this.value;
083        }
084        
085        /** 
086         * Discover if a stanza has a given entry.
087         *
088         * @param name        entry to look for
089         * 
090         * @return            true if the entry was found
091         */
092        public boolean hasChildren(String name) {
093          return subtrees.containsKey(name);
094        }
095        
096        /** 
097         * Pull an entry from a stanza.
098         *
099         * @param name        entry to look for
100         * 
101         * @return            the entry
102         */
103        public List<Stanza> getChildren(String name) throws InvalidXmlException {
104          LinkedList <Stanza> children = subtrees.get(name);
105          if (children == null) {
106            throw new InvalidXmlException("no entry found for " + name);
107          }
108          return children;
109        }
110        
111        /** 
112         * Pull a string entry from a stanza.
113         *
114         * @param name        entry to look for
115         * 
116         * @return            the entry
117         */
118        public String getValue(String name) throws InvalidXmlException {
119          if (!subtrees.containsKey(name)) {
120            throw new InvalidXmlException("no entry found for " + name);
121          }
122          LinkedList <Stanza> l = subtrees.get(name);
123          if (l.size() != 1) {
124            throw new InvalidXmlException("More than one value found for " + name);
125          }
126          return l.get(0).getValue();
127        }
128        
129        /** 
130         * Add an entry to a stanza.
131         *
132         * @param name        name of the entry to add
133         * @param child       the entry to add
134         */
135        public void addChild(String name, Stanza child) {
136          LinkedList<Stanza> l;
137          if (subtrees.containsKey(name)) {
138            l = subtrees.get(name);
139          } else {
140            l = new LinkedList<Stanza>();
141            subtrees.put(name, l);
142          }
143          l.add(child);
144        }
145        
146        /** 
147         * Convert a stanza to a human-readable string.
148         */
149        @Override
150        public String toString() {
151          StringBuilder bld = new StringBuilder();
152          bld.append("{");
153          if (!value.equals("")) {
154            bld.append("\"").append(value).append("\"");
155          }
156          String prefix = "";
157          for (Map.Entry<String, LinkedList <Stanza > > entry :
158              subtrees.entrySet()) {
159            String key = entry.getKey();
160            LinkedList <Stanza > ll = entry.getValue();
161            for (Stanza child : ll) {
162              bld.append(prefix);
163              bld.append("<").append(key).append(">");
164              bld.append(child.toString());
165              prefix = ", ";
166            }
167          }
168          bld.append("}");
169          return bld.toString();
170        }
171      }
172    }