001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.wicket.util.io;
018:
019: import java.io.BufferedReader;
020: import java.io.IOException;
021: import java.io.InputStream;
022: import java.io.InputStreamReader;
023: import java.io.OutputStream;
024: import java.io.Reader;
025: import java.io.StringReader;
026: import java.util.Properties;
027:
028: import javax.xml.parsers.DocumentBuilder;
029: import javax.xml.parsers.DocumentBuilderFactory;
030: import javax.xml.parsers.ParserConfigurationException;
031:
032: import org.apache.wicket.WicketRuntimeException;
033: import org.w3c.dom.Document;
034: import org.w3c.dom.Element;
035: import org.w3c.dom.Node;
036: import org.w3c.dom.NodeList;
037: import org.xml.sax.EntityResolver;
038: import org.xml.sax.ErrorHandler;
039: import org.xml.sax.InputSource;
040: import org.xml.sax.SAXException;
041: import org.xml.sax.SAXParseException;
042:
043: /**
044: * Utilities methods for working with input and output streams.
045: *
046: * @author Jonathan Locke
047: */
048: public final class Streams {
049: private static final String XML_PROPERTIES_DTD = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
050: + "<!-- DTD for properties -->"
051: + "<!ELEMENT properties ( comment?, entry* ) >"
052: + "<!ATTLIST properties"
053: + " version CDATA #FIXED \"1.0\">"
054: + "<!ELEMENT comment (#PCDATA) >"
055: + "<!ELEMENT entry (#PCDATA) >"
056: + "<!ATTLIST entry "
057: + " key CDATA #REQUIRED>";
058:
059: /**
060: * Writes the input stream to the output stream. Input is done without a
061: * Reader object, meaning that the input is copied in its raw form.
062: *
063: * @param in
064: * The input stream
065: * @param out
066: * The output stream
067: * @return Number of bytes copied from one stream to the other
068: * @throws IOException
069: */
070: public static int copy(final InputStream in, final OutputStream out)
071: throws IOException {
072: final byte[] buffer = new byte[4096];
073: int bytesCopied = 0;
074: while (true) {
075: int byteCount = in.read(buffer, 0, buffer.length);
076: if (byteCount <= 0) {
077: break;
078: }
079: out.write(buffer, 0, byteCount);
080: bytesCopied += byteCount;
081: }
082: return bytesCopied;
083: }
084:
085: /**
086: * Loads properties from an XML input stream into the provided properties
087: * object.
088: *
089: * @param properties
090: * The object to load the properties into
091: * @param inputStream
092: * @throws IOException
093: * When the input stream could not be read from
094: */
095: public static void loadFromXml(Properties properties,
096: InputStream inputStream) throws IOException {
097: if (properties == null) {
098: throw new IllegalArgumentException(
099: "properties must not be null");
100: }
101: if (inputStream == null) {
102: throw new IllegalArgumentException(
103: "inputStream must not be null");
104: }
105:
106: // TODO in a Wicket version that supports Java 5 (Wicket 2.0?), we can
107: // just use the loadFromXml method on java.util.Properties directly
108: // rather than manual as we do here
109:
110: DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory
111: .newInstance();
112: documentBuilderFactory
113: .setIgnoringElementContentWhitespace(true);
114: documentBuilderFactory.setValidating(true);
115: documentBuilderFactory.setCoalescing(true);
116: documentBuilderFactory.setIgnoringComments(true);
117: try {
118: DocumentBuilder db = documentBuilderFactory
119: .newDocumentBuilder();
120: db.setEntityResolver(new EntityResolver() {
121: public InputSource resolveEntity(String publicId,
122: String systemId) throws SAXException {
123: if (systemId
124: .equals("http://java.sun.com/dtd/properties.dtd")) {
125: InputSource inputSource;
126: inputSource = new InputSource(new StringReader(
127: XML_PROPERTIES_DTD));
128: inputSource
129: .setSystemId("http://java.sun.com/dtd/properties.dtd");
130: return inputSource;
131: } else {
132: throw new SAXException(
133: "Invalid system identifier: "
134: + systemId);
135: }
136: }
137: });
138: db.setErrorHandler(new ErrorHandler() {
139: public void error(SAXParseException e)
140: throws SAXException {
141: throw e;
142: }
143:
144: public void fatalError(SAXParseException e)
145: throws SAXException {
146: throw e;
147: }
148:
149: public void warning(SAXParseException e)
150: throws SAXException {
151: throw e;
152: }
153: });
154: InputSource is = new InputSource(inputStream);
155: Document doc = db.parse(is);
156: NodeList entries = ((Element) doc.getChildNodes().item(1))
157: .getChildNodes();
158: int len = entries.getLength();
159: for (int i = (len > 0 && entries.item(0).getNodeName()
160: .equals("comment")) ? 1 : 0; i < len; i++) {
161: Element entry = (Element) entries.item(i);
162: if (entry.hasAttribute("key")) {
163: Node node = entry.getFirstChild();
164: String val = (node == null) ? "" : node
165: .getNodeValue();
166: properties.setProperty(entry.getAttribute("key"),
167: val);
168: }
169: }
170: } catch (ParserConfigurationException e) {
171: throw new WicketRuntimeException(e);
172: } catch (SAXException e) {
173: throw new WicketRuntimeException(
174: "invalid XML properties format", e);
175: }
176: }
177:
178: /**
179: * Reads a stream as a string.
180: *
181: * @param in
182: * The input stream
183: * @return The string
184: * @throws IOException
185: */
186: public static String readString(final InputStream in)
187: throws IOException {
188: return readString(new BufferedReader(new InputStreamReader(in)));
189: }
190:
191: /**
192: * Reads a string using a character encoding.
193: *
194: * @param in
195: * The input
196: * @param encoding
197: * The character encoding of the input data
198: * @return The string
199: * @throws IOException
200: */
201: public static String readString(final InputStream in,
202: final CharSequence encoding) throws IOException {
203: return readString(new BufferedReader(new InputStreamReader(in,
204: encoding.toString())));
205: }
206:
207: /**
208: * Reads all input from a reader into a string.
209: *
210: * @param in
211: * The input
212: * @return The string
213: * @throws IOException
214: */
215: public static String readString(final Reader in) throws IOException {
216: final StringBuffer buffer = new StringBuffer(2048);
217: int value;
218:
219: while ((value = in.read()) != -1) {
220: buffer.append((char) value);
221: }
222:
223: return buffer.toString();
224: }
225:
226: /**
227: * Private to prevent instantiation.
228: */
229: private Streams() {
230: }
231: }
|