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 }