aboutsummaryrefslogtreecommitdiff
path: root/util
diff options
context:
space:
mode:
authorBen Gruver <bgruv@google.com>2013-03-03 18:00:53 -0800
committerBen Gruver <bgruv@google.com>2013-03-03 18:00:53 -0800
commit31d87776c459972f311a3527694e0d630d92a84b (patch)
treeed538360c0fc94b9b77a1383d9a5593dcc63dc6f /util
parent46cefa3d5be5a99484075cf60d1c55ca36ccffd3 (diff)
downloadsmali-31d87776c459972f311a3527694e0d630d92a84b.tar.gz
Refactor/rewrite the AnnotatedBytes class and helpers
This allows annotations to be added out-of-order, rather than the previous requirement of forward-only annotating
Diffstat (limited to 'util')
-rw-r--r--util/src/main/java/org/jf/util/StringWrapper.java106
-rw-r--r--util/src/main/java/org/jf/util/TwoColumnOutput.java208
-rw-r--r--util/src/test/java/org/jf/util/StringWrapperTest.java118
3 files changed, 273 insertions, 159 deletions
diff --git a/util/src/main/java/org/jf/util/StringWrapper.java b/util/src/main/java/org/jf/util/StringWrapper.java
new file mode 100644
index 00000000..91808300
--- /dev/null
+++ b/util/src/main/java/org/jf/util/StringWrapper.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2013, Google Inc.
+ * 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 Google Inc. 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
+ * OWNER 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.jf.util;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+public class StringWrapper {
+ /**
+ * Splits the given string into lines using on any embedded newlines, and wrapping the text as needed to conform to
+ * the given maximum line width.
+ *
+ * This uses and assumes unix-style newlines
+ *
+ * @param str The string to split
+ * @param maxWidth The maximum length of any line
+ * @param output If given, try to use this array as the return value. If there are more values than will fit
+ * into the array, a new array will be allocated and returned, while the given array will be filled
+ * with as many lines as would fit.
+ * @return The split lines from the original, as an array of Strings. The returned array may be larger than the
+ * number of lines. If this is the case, the end of the split lines will be denoted by a null entry in the
+ * array. If there is no null entry, then the size of the array exactly matches the number of lines.
+ * The returned lines will not contain an ending newline
+ */
+ public static String[] wrapString(@Nonnull String str, int maxWidth, @Nullable String[] output) {
+ if (output == null) {
+ output = new String[(int)((str.length() / maxWidth) * 1.5d + 1)];
+ }
+
+ int lineStart = 0;
+ int arrayIndex = 0;
+ int i;
+ for (i=0; i<str.length(); i++) {
+ char c = str.charAt(i);
+
+ if (c == '\n') {
+ output = addString(output, str.substring(lineStart, i), arrayIndex++);
+ lineStart = i+1;
+ } else if (i - lineStart == maxWidth) {
+ output = addString(output, str.substring(lineStart, i), arrayIndex++);
+ lineStart = i;
+ }
+ }
+ if (lineStart != i || i == 0) {
+ output = addString(output, str.substring(lineStart), arrayIndex++, output.length+1);
+ }
+
+ if (arrayIndex < output.length) {
+ output[arrayIndex] = null;
+ }
+ return output;
+ }
+
+ private static String[] addString(@Nonnull String[] arr, String str, int index) {
+ if (index >= arr.length) {
+ arr = enlargeArray(arr, (int)(Math.ceil((arr.length + 1) * 1.5)));
+ }
+
+ arr[index] = str;
+ return arr;
+ }
+
+ private static String[] addString(@Nonnull String[] arr, String str, int index, int newLength) {
+ if (index >= arr.length) {
+ arr = enlargeArray(arr, newLength);
+ }
+
+ arr[index] = str;
+ return arr;
+ }
+
+ private static String[] enlargeArray(String[] arr, int newLength) {
+ String[] newArr = new String[newLength];
+ System.arraycopy(arr, 0, newArr, 0, arr.length);
+ return newArr;
+ }
+}
diff --git a/util/src/main/java/org/jf/util/TwoColumnOutput.java b/util/src/main/java/org/jf/util/TwoColumnOutput.java
index 6f923d46..a04d9a5d 100644
--- a/util/src/main/java/org/jf/util/TwoColumnOutput.java
+++ b/util/src/main/java/org/jf/util/TwoColumnOutput.java
@@ -31,7 +31,11 @@
package org.jf.util;
-import java.io.*;
+import javax.annotation.Nonnull;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
/**
* Class that takes a combined output destination and provides two
@@ -45,48 +49,9 @@ public final class TwoColumnOutput {
/** &gt; 0; the left column width */
private final int leftWidth;
- /** non-null; pending left column output */
- private final StringBuffer leftBuf;
+ private final int rightWidth;
- /** non-null; pending right column output */
- private final StringBuffer rightBuf;
-
- /** non-null; left column writer */
- private final WrappedIndentingWriter leftColumn;
-
- /** non-null; right column writer */
- private final WrappedIndentingWriter rightColumn;
-
- /**
- * Turns the given two strings (with widths) and spacer into a formatted
- * two-column string.
- *
- * @param s1 non-null; first string
- * @param width1 &gt; 0; width of the first column
- * @param spacer non-null; spacer string
- * @param s2 non-null; second string
- * @param width2 &gt; 0; width of the second column
- * @return non-null; an appropriately-formatted string
- */
- public static String toString(String s1, int width1, String spacer,
- String s2, int width2) {
- int len1 = s1.length();
- int len2 = s2.length();
-
- StringWriter sw = new StringWriter((len1 + len2) * 3);
- TwoColumnOutput twoOut =
- new TwoColumnOutput(sw, width1, width2, spacer);
-
- try {
- twoOut.getLeft().write(s1);
- twoOut.getRight().write(s2);
- } catch (IOException ex) {
- throw new RuntimeException("shouldn't happen", ex);
- }
-
- twoOut.flush();
- return sw.toString();
- }
+ private final String spacer;
/**
* Constructs an instance.
@@ -96,11 +61,8 @@ public final class TwoColumnOutput {
* @param rightWidth &gt; 0; width of the right column, in characters
* @param spacer non-null; spacer string to sit between the two columns
*/
- public TwoColumnOutput(Writer out, int leftWidth, int rightWidth,
- String spacer) {
- if (out == null) {
- throw new NullPointerException("out == null");
- }
+ public TwoColumnOutput(@Nonnull Writer out, int leftWidth, int rightWidth,
+ @Nonnull String spacer) {
if (leftWidth < 1) {
throw new IllegalArgumentException("leftWidth < 1");
@@ -110,20 +72,10 @@ public final class TwoColumnOutput {
throw new IllegalArgumentException("rightWidth < 1");
}
- if (spacer == null) {
- throw new NullPointerException("spacer == null");
- }
-
- StringWriter leftWriter = new StringWriter(1000);
- StringWriter rightWriter = new StringWriter(1000);
-
this.out = out;
this.leftWidth = leftWidth;
- this.leftBuf = leftWriter.getBuffer();
- this.rightBuf = rightWriter.getBuffer();
- this.leftColumn = new WrappedIndentingWriter(leftWriter, leftWidth);
- this.rightColumn =
- new WrappedIndentingWriter(rightWriter, rightWidth, spacer);
+ this.rightWidth = rightWidth;
+ this.spacer = spacer;
}
/**
@@ -139,114 +91,52 @@ public final class TwoColumnOutput {
this(new OutputStreamWriter(out), leftWidth, rightWidth, spacer);
}
- /**
- * Gets the writer to use to write to the left column.
- *
- * @return non-null; the left column writer
- */
- public Writer getLeft() {
- return leftColumn;
- }
-
- /**
- * Gets the writer to use to write to the right column.
- *
- * @return non-null; the right column writer
- */
- public Writer getRight() {
- return rightColumn;
- }
-
- /**
- * Flushes the output. If there are more lines of pending output in one
- * column, then the other column will get filled with blank lines.
- */
- public void flush() {
- try {
- appendNewlineIfNecessary(leftBuf, leftColumn);
- appendNewlineIfNecessary(rightBuf, rightColumn);
- outputFullLines();
- flushLeft();
- flushRight();
- } catch (IOException ex) {
- throw new RuntimeException(ex);
- }
- }
-
- /**
- * Outputs to the final destination as many full line pairs as
- * there are in the pending output, removing those lines from
- * their respective buffers. This method terminates when at
- * least one of the two column buffers is empty.
- */
- private void outputFullLines() throws IOException {
- for (;;) {
- int leftLen = leftBuf.indexOf("\n");
- if (leftLen < 0) {
- return;
- }
-
- int rightLen = rightBuf.indexOf("\n");
- if (rightLen < 0) {
- return;
+ private String[] leftLines = null;
+ private String[] rightLines = null;
+ public void write(String left, String right) throws IOException {
+ leftLines = StringWrapper.wrapString(left, leftWidth, leftLines);
+ rightLines = StringWrapper.wrapString(right, rightWidth, rightLines);
+ int leftCount = leftLines.length;
+ int rightCount = rightLines.length;
+
+ for (int i=0; i<leftCount || i <rightCount; i++) {
+ String leftLine = null;
+ String rightLine = null;
+
+ if (i < leftCount) {
+ leftLine = leftLines[i];
+ if (leftLine == null) {
+ leftCount = i;
+ }
}
- if (leftLen != 0) {
- out.write(leftBuf.substring(0, leftLen));
+ if (i < rightCount) {
+ rightLine = rightLines[i];
+ if (rightLine == null) {
+ rightCount = i;
+ }
}
- if (rightLen != 0) {
- writeSpaces(out, leftWidth - leftLen);
- out.write(rightBuf.substring(0, rightLen));
- }
+ if (leftLine != null || rightLine != null) {
+ int written = 0;
+ if (leftLine != null) {
+ out.write(leftLine);
+ written = leftLine.length();
+ }
- out.write('\n');
+ int remaining = leftWidth - written;
+ if (remaining > 0) {
+ writeSpaces(out, remaining);
+ }
- leftBuf.delete(0, leftLen + 1);
- rightBuf.delete(0, rightLen + 1);
- }
- }
+ out.write(spacer);
- /**
- * Flushes the left column buffer, printing it and clearing the buffer.
- * If the buffer is already empty, this does nothing.
- */
- private void flushLeft() throws IOException {
- appendNewlineIfNecessary(leftBuf, leftColumn);
+ if (rightLine != null) {
+ out.write(rightLine);
+ }
- while (leftBuf.length() != 0) {
- rightColumn.write('\n');
- outputFullLines();
- }
- }
-
- /**
- * Flushes the right column buffer, printing it and clearing the buffer.
- * If the buffer is already empty, this does nothing.
- */
- private void flushRight() throws IOException {
- appendNewlineIfNecessary(rightBuf, rightColumn);
-
- while (rightBuf.length() != 0) {
- leftColumn.write('\n');
- outputFullLines();
- }
- }
-
- /**
- * Appends a newline to the given buffer via the given writer, but
- * only if it isn't empty and doesn't already end with one.
- *
- * @param buf non-null; the buffer in question
- * @param out non-null; the writer to use
- */
- private static void appendNewlineIfNecessary(StringBuffer buf,
- Writer out)
- throws IOException {
- int len = buf.length();
-
- if ((len != 0) && (buf.charAt(len - 1) != '\n')) {
- out.write('\n');
+ out.write('\n');
+ }
}
}
diff --git a/util/src/test/java/org/jf/util/StringWrapperTest.java b/util/src/test/java/org/jf/util/StringWrapperTest.java
new file mode 100644
index 00000000..64dca33e
--- /dev/null
+++ b/util/src/test/java/org/jf/util/StringWrapperTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2013, Google Inc.
+ * 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 Google Inc. 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
+ * OWNER 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.jf.util;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class StringWrapperTest {
+ @Test
+ public void testWrapString() {
+ validateResult(
+ new String[]{"abc", "abcdef", "abcdef"},
+ StringWrapper.wrapString("abc\nabcdefabcdef", 6, null));
+
+ validateResult(
+ new String[]{"abc"},
+ StringWrapper.wrapString("abc", 6, new String[3]));
+
+ validateResult(
+ new String[]{"abc"},
+ StringWrapper.wrapString("abc", 6, new String[0]));
+
+ validateResult(
+ new String[]{"abc"},
+ StringWrapper.wrapString("abc", 6, new String[1]));
+
+ validateResult(
+ new String[]{""},
+ StringWrapper.wrapString("", 6, new String[3]));
+
+ validateResult(
+ new String[]{"abcdef"},
+ StringWrapper.wrapString("abcdef", 6, new String[3]));
+
+ validateResult(
+ new String[]{"abcdef", "abcdef"},
+ StringWrapper.wrapString("abcdef\nabcdef", 6, new String[3]));
+
+ validateResult(
+ new String[]{"abc", "", "def"},
+ StringWrapper.wrapString("abc\n\ndef", 6, new String[3]));
+
+ validateResult(
+ new String[]{"", "abcdef"},
+ StringWrapper.wrapString("\nabcdef", 6, new String[3]));
+
+ validateResult(
+ new String[]{"", "", "abcdef"},
+ StringWrapper.wrapString("\n\nabcdef", 6, new String[3]));
+
+ validateResult(
+ new String[]{"", "", "abcdef"},
+ StringWrapper.wrapString("\n\nabcdef", 6, new String[4]));
+
+ validateResult(
+ new String[]{"", "", "abcdef", ""},
+ StringWrapper.wrapString("\n\nabcdef\n\n", 6, new String[4]));
+
+ validateResult(
+ new String[]{"", "", "abcdef", "a", ""},
+ StringWrapper.wrapString("\n\nabcdefa\n\n", 6, new String[4]));
+
+ validateResult(
+ new String[]{"", "", "abcdef", "a", ""},
+ StringWrapper.wrapString("\n\nabcdefa\n\n", 6, new String[0]));
+
+ validateResult(
+ new String[]{"", "", "abcdef", "a", ""},
+ StringWrapper.wrapString("\n\nabcdefa\n\n", 6, new String[5]));
+
+ validateResult(
+ new String[]{"", "", "a", "b", "c", "d", "e", "f", "a", ""},
+ StringWrapper.wrapString("\n\nabcdefa\n\n", 1, new String[5]));
+ }
+
+ public static void validateResult(String[] expected, String[] actual) {
+ Assert.assertTrue(actual.length >= expected.length);
+
+ int i;
+ for (i=0; i<actual.length; i++) {
+ if (actual[i] == null) {
+ Assert.assertTrue(i == expected.length);
+ return;
+ }
+ Assert.assertTrue(i < expected.length);
+ Assert.assertEquals(expected[i], actual[i]);
+ }
+ }
+}