diff options
author | Ben Gruver <bgruv@google.com> | 2013-03-03 18:00:53 -0800 |
---|---|---|
committer | Ben Gruver <bgruv@google.com> | 2013-03-03 18:00:53 -0800 |
commit | 31d87776c459972f311a3527694e0d630d92a84b (patch) | |
tree | ed538360c0fc94b9b77a1383d9a5593dcc63dc6f /util | |
parent | 46cefa3d5be5a99484075cf60d1c55ca36ccffd3 (diff) | |
download | smali-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.java | 106 | ||||
-rw-r--r-- | util/src/main/java/org/jf/util/TwoColumnOutput.java | 208 | ||||
-rw-r--r-- | util/src/test/java/org/jf/util/StringWrapperTest.java | 118 |
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 { /** > 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 > 0; width of the first column - * @param spacer non-null; spacer string - * @param s2 non-null; second string - * @param width2 > 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 > 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]); + } + } +} |