/* The contents of this file are subject to the license and copyright terms
* detailed in the license directory at the root of the source tree (also
* available online at http://fedora-commons.org/license/).
*/
package org.fcrepo.server.utilities;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import org.fcrepo.common.FaultException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Utility methods for working with character-based or raw sequences of data.
*
* @author Chris Wilper
*/
public abstract class StreamUtility {
private static final Logger logger =
LoggerFactory.getLogger(StreamUtility.class);
private static final char[] AMP = "&".toCharArray();
private static final char[] LT = "<".toCharArray();
private static final char[] GT = ">".toCharArray();
private static final char[] QUOT = """.toCharArray();
private static final char[] APOS = "'".toCharArray();
/**
* Returns an XML-appropriate encoding of the given String.
*
* @param in
* The String to encode.
* @return A new, encoded String.
*/
public static String enc(String in) {
if (in == null || in.isEmpty()) {
return "";
}
StringBuilder out = new StringBuilder();
enc(in, out);
return out.toString();
}
/**
* Appends an XML-appropriate encoding of the given String to the given
* Appendable.
*
* @param in
* The String to encode.
* @param out
* The StringBuilder to write to.
*/
public static void enc(String in, StringBuilder out) {
if (in == null) return;
int startAt = 0;
int inLen = in.length();
for (int i = 0; i < inLen; i++) {
char c = in.charAt(i);
if (c == '&' || c == '<' || c == '>' || c == '"' || c == '\'') {
if (i != startAt) out.append(in, startAt, i);
enc(in.charAt(i), out);
startAt = i + 1;
}
}
if (startAt == 0) {
// we never encountered a character to escape for xml
out.append(in);
} else if (startAt < inLen){
// append the remaining safe characters
out.append(in, startAt, inLen);
}
}
public static void enc(String in, PrintStream out) {
if (in == null) return;
int startAt = 0;
int inLen = in.length();
for (int i = 0; i < inLen; i++) {
char c = in.charAt(i);
if (c == '&' || c == '<' || c == '>' || c == '"' || c == '\'') {
if (i != startAt) out.append(in, startAt, i);
enc(in.charAt(i), out);
startAt = i + 1;
}
}
if (startAt == 0) {
// we never encountered a character to escape for xml
out.append(in);
} else if (startAt < inLen){
// append the remaining safe characters
out.append(in, startAt, inLen);
}
}
public static void enc(String in, Writer out) {
if (in == null) return;
int startAt = 0;
int inLen = in.length();
try {
for (int i = 0; i < inLen; i++) {
char c = in.charAt(i);
if (c == '&' || c == '<' || c == '>' || c == '"' || c == '\'') {
if (i != startAt) out.write(in, startAt, i - startAt);
enc(in.charAt(i), out);
startAt = i + 1;
}
}
if (startAt == 0) {
// we never encountered a character to escape for xml
out.append(in, 0, inLen);
} else if (startAt < inLen){
// append the remaining safe characters
out.write(in, startAt, inLen - startAt);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* Prints an XML-appropriate encoding of the given range of characters to
* the given Writer.
*
* @param in
* The char buffer to read from.
* @param start
* The starting index.
* @param length
* The number of characters in the range.
* @param out
* The Appendable to write to.
*/
public static void enc(char[] in, int start, int length, StringBuffer out) {
for (int i = start; i < length + start; i++) {
enc(in[i], out);
}
}
public static void enc(char[] in, int start, int length, StringBuilder out) {
for (int i = start; i < length + start; i++) {
enc(in[i], out);
}
}
public static void enc(char[] in, int start, int length, Writer out) {
for (int i = start; i < length + start; i++) {
enc(in[i], out);
}
}
/**
* Appends an XML-appropriate encoding of the given character to the given
* Appendable.
*
* @param in
* The character.
* @param out
* The Appendable to write to. Since we expect only PrintStream,
* PrintWriter, and the String-building classes, we wrap
* the IOException in a RuntimeException
*/
public static void enc(char in, StringBuffer out) {
if (in == '&') {
out.append("&");
} else if (in == '<') {
out.append("<");
} else if (in == '>') {
out.append(">");
} else if (in == '"') {
out.append(""");
} else if (in == '\'') {
out.append("'");
} else {
out.append(in);
}
}
public static void enc(char in, StringBuilder out) {
if (in == '&') {
out.append("&");
} else if (in == '<') {
out.append("<");
} else if (in == '>') {
out.append(">");
} else if (in == '"') {
out.append(""");
} else if (in == '\'') {
out.append("'");
} else {
out.append(in);
}
}
public static void enc(char in, PrintStream out) {
if (in == '&') {
out.print(AMP);
} else if (in == '<') {
out.print(LT);
} else if (in == '>') {
out.print(GT);
} else if (in == '"') {
out.print(QUOT);
} else if (in == '\'') {
out.print(APOS);
} else {
out.append(in);
}
}
public static void enc(char in, Writer out) {
try {
if (in == '&') {
out.write(AMP);
} else if (in == '<') {
out.write(LT);
} else if (in == '>') {
out.write(GT);
} else if (in == '"') {
out.write(QUOT);
} else if (in == '\'') {
out.write(APOS);
} else {
out.append(in);
}
} catch (IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
/**
* Copies the contents of an InputStream to an OutputStream, then closes
* both.
*
* @param in
* The source stream.
* @param out
* The target stream.
* @param bufSize
* Number of bytes to attempt to copy at a time.
* @throws IOException
* If any sort of read/write error occurs on either stream.
*/
public static void pipeStream(InputStream in, OutputStream out, int bufSize)
throws IOException {
try {
byte[] buf = new byte[bufSize];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
} finally {
try {
in.close();
out.close();
} catch (IOException e) {
logger.warn("Unable to close stream", e);
}
}
}
/**
* Gets a byte array for the given input stream.
*/
public static byte[] getBytes(InputStream in) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
pipeStream(in, out, 4096);
return out.toByteArray();
}
/**
* Gets a stream for the given string.
*/
public static InputStream getStream(String string) {
try {
return new ByteArrayInputStream(string.getBytes("UTF-8"));
} catch (UnsupportedEncodingException wontHappen) {
throw new FaultException(wontHappen);
}
}
}