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,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019
020 package org.apache.myfaces.tobago.util;
021
022 import org.w3c.dom.Document;
023 import org.w3c.dom.Element;
024 import org.w3c.dom.Node;
025 import org.w3c.dom.NodeList;
026 import org.xml.sax.EntityResolver;
027 import org.xml.sax.InputSource;
028 import org.xml.sax.SAXException;
029
030 import javax.xml.parsers.DocumentBuilder;
031 import javax.xml.parsers.DocumentBuilderFactory;
032 import javax.xml.parsers.ParserConfigurationException;
033 import java.io.IOException;
034 import java.io.InputStream;
035 import java.io.StringReader;
036 import java.util.Properties;
037
038 public final class XmlUtils {
039
040 public static String escape(final String s) {
041 return escape(s, true);
042 }
043
044 public static String escape(final String s, final boolean isAttributeValue) {
045 if (null == s) {
046 return "";
047 }
048 int len = s.length();
049 StringBuilder buffer = new StringBuilder(len);
050 for (int i = 0; i < len; i++) {
051 appendEntityRef(buffer, s.charAt(i), isAttributeValue);
052 }
053 return buffer.toString();
054 }
055
056 public static String escape(final char[] chars, final int offset, final int length, final boolean isAttributeValue) {
057 if (null == chars) {
058 return "";
059 }
060 StringBuilder buffer = new StringBuilder(length);
061 for (int i = offset; i < length; i++) {
062 appendEntityRef(buffer, chars[i], isAttributeValue);
063 }
064 return buffer.toString();
065 }
066
067 private static void appendEntityRef(final StringBuilder buffer, final char ch,
068 final boolean isAttributeValue) {
069 // Encode special XML characters into the equivalent character references.
070 // These five are defined by default for all XML documents.
071 switch (ch) {
072 case '<':
073 buffer.append("<");
074 break;
075 case '&':
076 buffer.append("&");
077 break;
078 case '"': // need inside attributes values
079 if (isAttributeValue) {
080 buffer.append(""");
081 } else {
082 buffer.append(ch);
083 }
084 break;
085 case '\'': // need inside attributes values
086 if (isAttributeValue) {
087 buffer.append("'");
088 } else {
089 buffer.append(ch);
090 }
091 break;
092 case '>': // optional
093 buffer.append(">");
094 break;
095 default:
096 buffer.append(ch);
097 }
098 }
099
100 public static void load(final Properties properties, final InputStream stream)
101 throws IOException {
102 Document document;
103 try {
104 document = createDocument(stream);
105 } catch (SAXException e) {
106 throw new RuntimeException("Invalid properties format", e);
107 }
108 Element propertiesElement = (Element) document.getChildNodes().item(
109 document.getChildNodes().getLength() - 1);
110 importProperties(properties, propertiesElement);
111 }
112
113 private static Document createDocument(final InputStream stream)
114 throws SAXException, IOException {
115 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
116 factory.setIgnoringElementContentWhitespace(true);
117 factory.setValidating(false);
118 factory.setCoalescing(true);
119 factory.setIgnoringComments(true);
120 try {
121 DocumentBuilder builder = factory.newDocumentBuilder();
122 builder.setEntityResolver(new Resolver());
123 InputSource source = new InputSource(stream);
124 return builder.parse(source);
125 } catch (ParserConfigurationException e) {
126 throw new Error(e);
127 }
128 }
129
130 static void importProperties(final Properties properties, final Element propertiesElement) {
131 NodeList entries = propertiesElement.getChildNodes();
132 int numEntries = entries.getLength();
133 int start = numEntries > 0
134 && entries.item(0).getNodeName().equals("comment") ? 1 : 0;
135 for (int i = start; i < numEntries; i++) {
136 Node child = entries.item(i);
137 if (child instanceof Element) {
138 Element entry = (Element) child;
139 if (entry.hasAttribute("key")) {
140 Node node = entry.getFirstChild();
141 String value = (node == null) ? "" : node.getNodeValue();
142 properties.setProperty(entry.getAttribute("key"), value);
143 }
144 }
145 }
146 }
147
148 private static class Resolver implements EntityResolver {
149
150 public InputSource resolveEntity(final String publicId, final String systemId)
151 throws SAXException {
152 String dtd = "<!ELEMENT properties (comment?, entry*)>"
153 + "<!ATTLIST properties version CDATA #FIXED '1.0'>"
154 + "<!ELEMENT comment (#PCDATA)>"
155 + "<!ELEMENT entry (#PCDATA)>"
156 + "<!ATTLIST entry key CDATA #REQUIRED>";
157 InputSource inputSource = new InputSource(new StringReader(dtd));
158 inputSource.setSystemId("http://java.sun.com/dtd/properties.dtd");
159 return inputSource;
160 }
161 }
162
163 }