View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.myfaces.tobago.util;
21  
22  import org.w3c.dom.Document;
23  import org.w3c.dom.Element;
24  import org.w3c.dom.Node;
25  import org.w3c.dom.NodeList;
26  import org.xml.sax.EntityResolver;
27  import org.xml.sax.InputSource;
28  import org.xml.sax.SAXException;
29  
30  import javax.xml.parsers.DocumentBuilder;
31  import javax.xml.parsers.DocumentBuilderFactory;
32  import javax.xml.parsers.ParserConfigurationException;
33  import java.io.IOException;
34  import java.io.InputStream;
35  import java.io.StringReader;
36  import java.util.Properties;
37  
38  public final class XmlUtils {
39  
40    public static String escape(final String s) {
41      return escape(s, true);
42    }
43  
44    public static String escape(final String s, final boolean isAttributeValue) {
45      if (null == s) {
46        return "";
47      }
48      int len = s.length();
49      StringBuilder buffer = new StringBuilder(len);
50      for (int i = 0; i < len; i++) {
51        appendEntityRef(buffer, s.charAt(i), isAttributeValue);
52      }
53      return buffer.toString();
54    }
55  
56    public static String escape(final char[] chars, final int offset, final int length, final boolean isAttributeValue) {
57      if (null == chars) {
58        return "";
59      }
60      StringBuilder buffer = new StringBuilder(length);
61      for (int i = offset; i < length; i++) {
62        appendEntityRef(buffer, chars[i], isAttributeValue);
63      }
64      return buffer.toString();
65    }
66  
67    private static void appendEntityRef(final StringBuilder buffer, final char ch,
68        final boolean isAttributeValue) {
69      // Encode special XML characters into the equivalent character references.
70      // These five are defined by default for all XML documents.
71      switch (ch) {
72        case '<':
73          buffer.append("&lt;");
74          break;
75        case '&':
76          buffer.append("&amp;");
77          break;
78        case '"': // need inside attributes values
79          if (isAttributeValue) {
80            buffer.append("&quot;");
81          } else {
82            buffer.append(ch);
83          }
84          break;
85        case '\'': // need inside attributes values
86          if (isAttributeValue) {
87            buffer.append("&apos;");
88          } else {
89            buffer.append(ch);
90          }
91          break;
92        case '>': // optional
93          buffer.append("&gt;");
94          break;
95        default:
96          buffer.append(ch);
97      }
98    }
99  
100   /** @deprecated */
101   @Deprecated
102   public static void load(final Properties properties, final InputStream stream)
103       throws IOException {
104     Document document;
105     try {
106       document = createDocument(stream);
107     } catch (SAXException e) {
108       throw new RuntimeException("Invalid properties format", e);
109     }
110     Element propertiesElement = (Element) document.getChildNodes().item(
111         document.getChildNodes().getLength() - 1);
112     importProperties(properties, propertiesElement);
113   }
114 
115   private static Document createDocument(final InputStream stream)
116       throws SAXException, IOException {
117     DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
118     factory.setIgnoringElementContentWhitespace(true);
119     factory.setValidating(false);
120     factory.setCoalescing(true);
121     factory.setIgnoringComments(true);
122     try {
123       DocumentBuilder builder = factory.newDocumentBuilder();
124       builder.setEntityResolver(new Resolver());
125       InputSource source = new InputSource(stream);
126       return builder.parse(source);
127     } catch (ParserConfigurationException e) {
128       throw new Error(e);
129     }
130   }
131 
132   static void importProperties(final Properties properties, final Element propertiesElement) {
133     NodeList entries = propertiesElement.getChildNodes();
134     int numEntries = entries.getLength();
135     int start = numEntries > 0
136         && entries.item(0).getNodeName().equals("comment") ? 1 : 0;
137     for (int i = start; i < numEntries; i++) {
138       Node child = entries.item(i);
139       if (child instanceof Element) {
140         Element entry = (Element) child;
141         if (entry.hasAttribute("key")) {
142           Node node = entry.getFirstChild();
143           String value = (node == null) ? "" : node.getNodeValue();
144           properties.setProperty(entry.getAttribute("key"), value);
145         }
146       }
147     }
148   }
149 
150   private static class Resolver implements EntityResolver {
151 
152     public InputSource resolveEntity(final String publicId, final String systemId)
153         throws SAXException {
154       String dtd = "<!ELEMENT properties (comment?, entry*)>"
155           + "<!ATTLIST properties version CDATA #FIXED '1.0'>"
156           + "<!ELEMENT comment (#PCDATA)>"
157           + "<!ELEMENT entry (#PCDATA)>"
158           + "<!ATTLIST entry key CDATA #REQUIRED>";
159       InputSource inputSource = new InputSource(new StringReader(dtd));
160       inputSource.setSystemId("http://java.sun.com/dtd/properties.dtd");
161       return inputSource;
162     }
163   }
164 
165 }