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