aboutsummaryrefslogtreecommitdiff
path: root/core/src/main/java/org/owasp/encoder/ChainedEncoder.java
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/main/java/org/owasp/encoder/ChainedEncoder.java')
-rw-r--r--core/src/main/java/org/owasp/encoder/ChainedEncoder.java164
1 files changed, 164 insertions, 0 deletions
diff --git a/core/src/main/java/org/owasp/encoder/ChainedEncoder.java b/core/src/main/java/org/owasp/encoder/ChainedEncoder.java
new file mode 100644
index 0000000..c28ae40
--- /dev/null
+++ b/core/src/main/java/org/owasp/encoder/ChainedEncoder.java
@@ -0,0 +1,164 @@
+// Copyright (c) 2012 Jeff Ichnowski
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// * Redistributions of source code must retain the above
+// copyright notice, this list of conditions and the following
+// disclaimer.
+//
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials
+// provided with the distribution.
+//
+// * Neither the name of the OWASP nor the names of its
+// contributors may be used to endorse or promote products
+// derived from this software without specific prior written
+// permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+// OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package org.owasp.encoder;
+
+import java.nio.CharBuffer;
+import java.nio.charset.CoderResult;
+
+/**
+ * ChainedEncoder -- An encoder that chains together two encoders in
+ * order. This is included as an example, but not actually exposed or
+ * used as it requires an internal buffer making it not thread-safe.
+ * Sequences of 3 or more encodings require chaining together chained
+ * encoders.
+ *
+ * @author Jeff Ichnowski
+ */
+class ChainedEncoder extends Encoder {
+ /** The first encoder to apply in sequence. */
+ final Encoder _first;
+ /** The second encoder to apply in sequence. */
+ final Encoder _last;
+
+ /**
+ * The buffer used to store the output of the first encoder before
+ * sending as input to the second encoder.
+ */
+ // TODO: because of this buffer, the ChainedEncoder is the only stateful
+ // encoder. This needs to be removed somehow, or state control APIs need
+ // to be added (E.g. reset/flush)
+ final CharBuffer _buffer = CharBuffer.allocate(1024);
+
+ /**
+ * Creates an ChainedEncoder that applies the encoding sequence
+ * {@code input --> first --> last --> output}.
+ *
+ * @param first the first encoder to apply
+ * @param last the second/last encoder to apply.
+ */
+ ChainedEncoder(Encoder first, Encoder last) {
+ _first = first;
+ _last = last;
+ }
+
+ /**
+ * Encodes an input string to an output string.
+ *
+ * @param str the string to encode
+ * @return the encoded string.
+ */
+ public String encode(String str) {
+ if (str == null) {
+ str = "null";
+ }
+
+ int n = str.length();
+ int j = _first.firstEncodedOffset(str, 0, n);
+ if (j == n) {
+ // string is unchanged after encoding with the first encoder
+ return Encode.encode(_last, str);
+ }
+
+ final int remaining = n - j;
+ final int m = j + _first.maxEncodedLength(n);
+ CharBuffer input = CharBuffer.allocate(m);
+ str.getChars(0, j, input.array(), 0);
+ str.getChars(j, n, input.array(), m - remaining);
+
+ input.limit(m).position(m - remaining);
+ CharBuffer tmp = input.duplicate();
+ tmp.position(j);
+
+ CoderResult cr = _first.encode(input, tmp, true);
+ assert cr.isUnderflow() : "maxEncodedLength was incorrect";
+
+ CharBuffer output = CharBuffer.allocate(_last.maxEncodedLength(tmp.position()));
+ tmp.flip();
+
+ cr = _last.encode(tmp, output, true);
+ assert cr.isUnderflow() : "maxEncodedLength was incorrect";
+
+ return new String(output.array(), 0, output.position());
+ }
+
+ @Override
+ protected int firstEncodedOffset(String input, int off, int len) {
+ int i = _first.firstEncodedOffset(input, off, len);
+ return _last.firstEncodedOffset(input, off, i - off);
+ }
+
+ @Override
+ protected int maxEncodedLength(int n) {
+ return _last.maxEncodedLength(
+ _first.maxEncodedLength(n));
+ }
+
+// @Override
+// protected int encode(char[] input, int off, int len, char[] buffer, int j, boolean endOfInput) {
+// char[] tmp = new char[_first.maxEncodedLength(len)];
+// int i = _first.encode(input, off, len, tmp, 0, endOfInput);
+// return _last.encode(tmp, 0, i, buffer, j, endOfInput);
+// }
+
+ @Override
+ public CoderResult encode(CharBuffer input, CharBuffer output, boolean endOfInput) {
+ for (;;) {
+ CoderResult cr1 = _first.encode(input, _buffer, endOfInput);
+ _buffer.flip();
+ CoderResult cr2 = _last.encode(_buffer, output, endOfInput && cr1.isUnderflow());
+ _buffer.compact();
+
+ if (cr2.isOverflow()) {
+ // no more room in output, even if we still have buffered input.
+ return cr2;
+ }
+
+ if (cr1.isUnderflow()) {
+ // we've consumed everything from input
+ return cr2;
+ }
+ }
+ }
+
+ @Override
+ protected CoderResult encodeArrays(CharBuffer input, CharBuffer output, boolean endOfInput) {
+ return encode(input, output, endOfInput); // TODO: invert.
+ }
+
+ @Override
+ public String toString() {
+ return "["+_first+","+_last+"]";
+ }
+}