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 }