AnnotatedBytes.java revision dc802b06607cde3eadaaffeae888bfd6146000f1
1373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver/* 2373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * Copyright 2013, Google Inc. 3373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * All rights reserved. 4373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * 5373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * Redistribution and use in source and binary forms, with or without 6373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * modification, are permitted provided that the following conditions are 7373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * met: 8373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * 9373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * * Redistributions of source code must retain the above copyright 10373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * notice, this list of conditions and the following disclaimer. 11373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * * Redistributions in binary form must reproduce the above 12373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * copyright notice, this list of conditions and the following disclaimer 13373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * in the documentation and/or other materials provided with the 14373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * distribution. 15373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * * Neither the name of Google Inc. nor the names of its 16373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * contributors may be used to endorse or promote products derived from 17373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * this software without specific prior written permission. 18373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * 19373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver */ 31373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver 32373ff22ec69bb6e93646994347b6d80502be1588Ben Gruverpackage org.jf.dexlib2.util; 33373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver 3431d87776c459972f311a3527694e0d630d92a84bBen Gruverimport com.google.common.base.Strings; 35373ff22ec69bb6e93646994347b6d80502be1588Ben Gruverimport com.google.common.collect.Lists; 3631d87776c459972f311a3527694e0d630d92a84bBen Gruverimport com.google.common.collect.Maps; 37dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver 38dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruverimport org.jf.util.ExceptionWithContext; 39373ff22ec69bb6e93646994347b6d80502be1588Ben Gruverimport org.jf.util.Hex; 40373ff22ec69bb6e93646994347b6d80502be1588Ben Gruverimport org.jf.util.TwoColumnOutput; 41373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver 42373ff22ec69bb6e93646994347b6d80502be1588Ben Gruverimport javax.annotation.Nonnull; 4331d87776c459972f311a3527694e0d630d92a84bBen Gruverimport javax.annotation.Nullable; 44373ff22ec69bb6e93646994347b6d80502be1588Ben Gruverimport java.io.IOException; 45373ff22ec69bb6e93646994347b6d80502be1588Ben Gruverimport java.io.Writer; 46373ff22ec69bb6e93646994347b6d80502be1588Ben Gruverimport java.util.List; 4731d87776c459972f311a3527694e0d630d92a84bBen Gruverimport java.util.Map; 4831d87776c459972f311a3527694e0d630d92a84bBen Gruverimport java.util.TreeMap; 49373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver 50373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver/** 5131d87776c459972f311a3527694e0d630d92a84bBen Gruver * Collects/presents a set of textual annotations, each associated with a range of bytes or a specific point 5231d87776c459972f311a3527694e0d630d92a84bBen Gruver * between bytes. 5331d87776c459972f311a3527694e0d630d92a84bBen Gruver * 5431d87776c459972f311a3527694e0d630d92a84bBen Gruver * Point annotations cannot occur within the middle of a range annotation, only at the endpoints, or some other area 5531d87776c459972f311a3527694e0d630d92a84bBen Gruver * with no range annotation. 5631d87776c459972f311a3527694e0d630d92a84bBen Gruver * 5731d87776c459972f311a3527694e0d630d92a84bBen Gruver * Multiple point annotations can be defined for a given point. They will be printed in insertion order. 5831d87776c459972f311a3527694e0d630d92a84bBen Gruver * 5931d87776c459972f311a3527694e0d630d92a84bBen Gruver * Only a single range annotation may exist for any given range of bytes. Range annotations may not overlap. 60373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver */ 61373ff22ec69bb6e93646994347b6d80502be1588Ben Gruverpublic class AnnotatedBytes { 6231d87776c459972f311a3527694e0d630d92a84bBen Gruver /** 6331d87776c459972f311a3527694e0d630d92a84bBen Gruver * This defines the bytes ranges and their associated range and point annotations. 6431d87776c459972f311a3527694e0d630d92a84bBen Gruver * 6531d87776c459972f311a3527694e0d630d92a84bBen Gruver * A range is defined by 2 consecutive keys in the map. The first key is the inclusive start point, the second key 6631d87776c459972f311a3527694e0d630d92a84bBen Gruver * is the exclusive end point. The range annotation for a range is associated with the first key for that range. 6731d87776c459972f311a3527694e0d630d92a84bBen Gruver * The point annotations for a point are associated with the key at that point. 6831d87776c459972f311a3527694e0d630d92a84bBen Gruver */ 6931d87776c459972f311a3527694e0d630d92a84bBen Gruver @Nonnull private TreeMap<Integer, AnnotationEndpoint> annotatations = Maps.newTreeMap(); 7031d87776c459972f311a3527694e0d630d92a84bBen Gruver 71373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver private int cursor; 72373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver private int indentLevel; 73373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver 74373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver /** >= 40 (if used); the desired maximum output width */ 75373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver private int outputWidth; 76373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver 77373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver /** 78373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * >= 8 (if used); the number of bytes of hex output to use 79373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * in annotations 80373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver */ 81373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver private int hexCols = 8; 82373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver 83dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver private int startLimit = -1; 84dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver private int endLimit = -1; 85dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver 86373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver public AnnotatedBytes(int width) { 87373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver this.outputWidth = width; 88373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver } 89373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver 90373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver /** 9131d87776c459972f311a3527694e0d630d92a84bBen Gruver * Moves the cursor to a new location 92658dfb08805b2344d214d8beebf8385027ee7ffaBen Gruver * 9331d87776c459972f311a3527694e0d630d92a84bBen Gruver * @param offset The offset to move to 94658dfb08805b2344d214d8beebf8385027ee7ffaBen Gruver */ 9531d87776c459972f311a3527694e0d630d92a84bBen Gruver public void moveTo(int offset) { 9631d87776c459972f311a3527694e0d630d92a84bBen Gruver cursor = offset; 9731d87776c459972f311a3527694e0d630d92a84bBen Gruver } 9831d87776c459972f311a3527694e0d630d92a84bBen Gruver 9931d87776c459972f311a3527694e0d630d92a84bBen Gruver /** 10031d87776c459972f311a3527694e0d630d92a84bBen Gruver * Moves the cursor forward or backward by some amount 10131d87776c459972f311a3527694e0d630d92a84bBen Gruver * 10231d87776c459972f311a3527694e0d630d92a84bBen Gruver * @param offset The amount to move the cursor 10331d87776c459972f311a3527694e0d630d92a84bBen Gruver */ 10431d87776c459972f311a3527694e0d630d92a84bBen Gruver public void moveBy(int offset) { 10531d87776c459972f311a3527694e0d630d92a84bBen Gruver cursor += offset; 10631d87776c459972f311a3527694e0d630d92a84bBen Gruver } 10731d87776c459972f311a3527694e0d630d92a84bBen Gruver 10831d87776c459972f311a3527694e0d630d92a84bBen Gruver public void annotateTo(int offset, @Nonnull String msg, Object... formatArgs) { 10931d87776c459972f311a3527694e0d630d92a84bBen Gruver annotate(offset - cursor, msg, formatArgs); 110658dfb08805b2344d214d8beebf8385027ee7ffaBen Gruver } 111658dfb08805b2344d214d8beebf8385027ee7ffaBen Gruver 112658dfb08805b2344d214d8beebf8385027ee7ffaBen Gruver /** 113373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * Add an annotation of the given length at the current location. 114373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * 11531d87776c459972f311a3527694e0d630d92a84bBen Gruver * The location 11631d87776c459972f311a3527694e0d630d92a84bBen Gruver * 11731d87776c459972f311a3527694e0d630d92a84bBen Gruver * 118373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * @param length the length of data being annotated 119373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * @param msg the annotation message 120373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * @param formatArgs format arguments to pass to String.format 121373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver */ 122373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver public void annotate(int length, @Nonnull String msg, Object... formatArgs) { 123dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver if (startLimit != -1 && endLimit != -1 && (cursor < startLimit || cursor >= endLimit)) { 124dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver throw new ExceptionWithContext("Annotating outside the parent bounds"); 125dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver } 126dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver 12731d87776c459972f311a3527694e0d630d92a84bBen Gruver String formattedMsg = String.format(msg, formatArgs); 12831d87776c459972f311a3527694e0d630d92a84bBen Gruver int exclusiveEndOffset = cursor + length; 12931d87776c459972f311a3527694e0d630d92a84bBen Gruver 13031d87776c459972f311a3527694e0d630d92a84bBen Gruver AnnotationEndpoint endPoint = null; 13131d87776c459972f311a3527694e0d630d92a84bBen Gruver 13231d87776c459972f311a3527694e0d630d92a84bBen Gruver // Do we have an endpoint at the beginning of this annotation already? 13331d87776c459972f311a3527694e0d630d92a84bBen Gruver AnnotationEndpoint startPoint = annotatations.get(cursor); 13431d87776c459972f311a3527694e0d630d92a84bBen Gruver if (startPoint == null) { 13531d87776c459972f311a3527694e0d630d92a84bBen Gruver // Nope. We need to check that we're not in the middle of an existing range annotation. 13631d87776c459972f311a3527694e0d630d92a84bBen Gruver Map.Entry<Integer, AnnotationEndpoint> previousEntry = annotatations.lowerEntry(cursor); 13731d87776c459972f311a3527694e0d630d92a84bBen Gruver if (previousEntry != null) { 13831d87776c459972f311a3527694e0d630d92a84bBen Gruver AnnotationEndpoint previousAnnotations = previousEntry.getValue(); 13931d87776c459972f311a3527694e0d630d92a84bBen Gruver AnnotationItem previousRangeAnnotation = previousAnnotations.rangeAnnotation; 14031d87776c459972f311a3527694e0d630d92a84bBen Gruver if (previousRangeAnnotation != null) { 141dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver throw new ExceptionWithContext( 142dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver "Cannot add annotation %s, due to existing annotation %s", 14331d87776c459972f311a3527694e0d630d92a84bBen Gruver formatAnnotation(cursor, cursor + length, formattedMsg), 144dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver formatAnnotation(previousEntry.getKey(), 145dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver previousRangeAnnotation.annotation)); 14631d87776c459972f311a3527694e0d630d92a84bBen Gruver } 14731d87776c459972f311a3527694e0d630d92a84bBen Gruver } 14831d87776c459972f311a3527694e0d630d92a84bBen Gruver } else if (length > 0) { 14931d87776c459972f311a3527694e0d630d92a84bBen Gruver AnnotationItem existingRangeAnnotation = startPoint.rangeAnnotation; 15031d87776c459972f311a3527694e0d630d92a84bBen Gruver if (existingRangeAnnotation != null) { 151dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver throw new ExceptionWithContext( 152dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver "Cannot add annotation %s, due to existing annotation %s", 15331d87776c459972f311a3527694e0d630d92a84bBen Gruver formatAnnotation(cursor, cursor + length, formattedMsg), 154dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver formatAnnotation(cursor, existingRangeAnnotation.annotation)); 15531d87776c459972f311a3527694e0d630d92a84bBen Gruver } 15631d87776c459972f311a3527694e0d630d92a84bBen Gruver } 15731d87776c459972f311a3527694e0d630d92a84bBen Gruver 15831d87776c459972f311a3527694e0d630d92a84bBen Gruver if (length > 0) { 15931d87776c459972f311a3527694e0d630d92a84bBen Gruver // Ensure that there is no later annotation that would intersect with this one 16031d87776c459972f311a3527694e0d630d92a84bBen Gruver Map.Entry<Integer, AnnotationEndpoint> nextEntry = annotatations.higherEntry(cursor); 16131d87776c459972f311a3527694e0d630d92a84bBen Gruver if (nextEntry != null) { 16231d87776c459972f311a3527694e0d630d92a84bBen Gruver int nextKey = nextEntry.getKey(); 16331d87776c459972f311a3527694e0d630d92a84bBen Gruver if (nextKey < exclusiveEndOffset) { 16431d87776c459972f311a3527694e0d630d92a84bBen Gruver // there is an endpoint that would intersect with this annotation. Find one of the annotations 16531d87776c459972f311a3527694e0d630d92a84bBen Gruver // associated with the endpoint, to print in the error message 16631d87776c459972f311a3527694e0d630d92a84bBen Gruver AnnotationEndpoint nextEndpoint = nextEntry.getValue(); 16731d87776c459972f311a3527694e0d630d92a84bBen Gruver AnnotationItem nextRangeAnnotation = nextEndpoint.rangeAnnotation; 16831d87776c459972f311a3527694e0d630d92a84bBen Gruver if (nextRangeAnnotation != null) { 169dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver throw new ExceptionWithContext( 170dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver "Cannot add annotation %s, due to existing annotation %s", 17131d87776c459972f311a3527694e0d630d92a84bBen Gruver formatAnnotation(cursor, cursor + length, formattedMsg), 172dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver formatAnnotation(nextKey, nextRangeAnnotation.annotation)); 17331d87776c459972f311a3527694e0d630d92a84bBen Gruver } 17431d87776c459972f311a3527694e0d630d92a84bBen Gruver if (nextEndpoint.pointAnnotations.size() > 0) { 175dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver throw new ExceptionWithContext( 176dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver "Cannot add annotation %s, due to existing annotation %s", 17731d87776c459972f311a3527694e0d630d92a84bBen Gruver formatAnnotation(cursor, cursor + length, formattedMsg), 17831d87776c459972f311a3527694e0d630d92a84bBen Gruver formatAnnotation(nextKey, nextKey, 179dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver nextEndpoint.pointAnnotations.get(0).annotation)); 18031d87776c459972f311a3527694e0d630d92a84bBen Gruver } 18131d87776c459972f311a3527694e0d630d92a84bBen Gruver // There are no annotations on this endpoint. This "shouldn't" happen. We can still throw an exception. 182dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver throw new ExceptionWithContext( 183dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver "Cannot add annotation %s, due to existing annotation endpoint at %d", 18431d87776c459972f311a3527694e0d630d92a84bBen Gruver formatAnnotation(cursor, cursor + length, formattedMsg), 185dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver nextKey); 18631d87776c459972f311a3527694e0d630d92a84bBen Gruver } 18731d87776c459972f311a3527694e0d630d92a84bBen Gruver 18831d87776c459972f311a3527694e0d630d92a84bBen Gruver if (nextKey == exclusiveEndOffset) { 18931d87776c459972f311a3527694e0d630d92a84bBen Gruver // the next endpoint matches the end of the annotation we are adding 19031d87776c459972f311a3527694e0d630d92a84bBen Gruver endPoint = nextEntry.getValue(); 19131d87776c459972f311a3527694e0d630d92a84bBen Gruver } 19231d87776c459972f311a3527694e0d630d92a84bBen Gruver } 19331d87776c459972f311a3527694e0d630d92a84bBen Gruver } 19431d87776c459972f311a3527694e0d630d92a84bBen Gruver 19531d87776c459972f311a3527694e0d630d92a84bBen Gruver // Now, actually add the annotation 19631d87776c459972f311a3527694e0d630d92a84bBen Gruver // If startPoint is null, we need to create a new one and add it to annotations. Otherwise, we just need to add 19731d87776c459972f311a3527694e0d630d92a84bBen Gruver // the annotation to the existing AnnotationEndpoint 19831d87776c459972f311a3527694e0d630d92a84bBen Gruver // the range annotation 19931d87776c459972f311a3527694e0d630d92a84bBen Gruver if (startPoint == null) { 20031d87776c459972f311a3527694e0d630d92a84bBen Gruver startPoint = new AnnotationEndpoint(); 20131d87776c459972f311a3527694e0d630d92a84bBen Gruver annotatations.put(cursor, startPoint); 20231d87776c459972f311a3527694e0d630d92a84bBen Gruver } 20331d87776c459972f311a3527694e0d630d92a84bBen Gruver if (length == 0) { 20431d87776c459972f311a3527694e0d630d92a84bBen Gruver startPoint.pointAnnotations.add(new AnnotationItem(indentLevel, formattedMsg)); 20531d87776c459972f311a3527694e0d630d92a84bBen Gruver } else { 20631d87776c459972f311a3527694e0d630d92a84bBen Gruver startPoint.rangeAnnotation = new AnnotationItem(indentLevel, formattedMsg); 20731d87776c459972f311a3527694e0d630d92a84bBen Gruver 20831d87776c459972f311a3527694e0d630d92a84bBen Gruver // If endPoint is null, we need to create a new, empty one and add it to annotations 20931d87776c459972f311a3527694e0d630d92a84bBen Gruver if (endPoint == null) { 21031d87776c459972f311a3527694e0d630d92a84bBen Gruver endPoint = new AnnotationEndpoint(); 21131d87776c459972f311a3527694e0d630d92a84bBen Gruver annotatations.put(exclusiveEndOffset, endPoint); 21231d87776c459972f311a3527694e0d630d92a84bBen Gruver } 21331d87776c459972f311a3527694e0d630d92a84bBen Gruver } 21431d87776c459972f311a3527694e0d630d92a84bBen Gruver 215373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver cursor += length; 216373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver } 217373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver 21831d87776c459972f311a3527694e0d630d92a84bBen Gruver private String formatAnnotation(int offset, String annotationMsg) { 21931d87776c459972f311a3527694e0d630d92a84bBen Gruver Integer endOffset = annotatations.higherKey(offset); 22031d87776c459972f311a3527694e0d630d92a84bBen Gruver return formatAnnotation(offset, endOffset, annotationMsg); 22131d87776c459972f311a3527694e0d630d92a84bBen Gruver } 22231d87776c459972f311a3527694e0d630d92a84bBen Gruver 22331d87776c459972f311a3527694e0d630d92a84bBen Gruver private String formatAnnotation(int offset, Integer endOffset, String annotationMsg) { 22431d87776c459972f311a3527694e0d630d92a84bBen Gruver if (endOffset != null) { 22531d87776c459972f311a3527694e0d630d92a84bBen Gruver return String.format("[0x%x, 0x%x) \"%s\"", offset, endOffset, annotationMsg); 22631d87776c459972f311a3527694e0d630d92a84bBen Gruver } else { 22731d87776c459972f311a3527694e0d630d92a84bBen Gruver return String.format("[0x%x, ) \"%s\"", offset, annotationMsg); 22831d87776c459972f311a3527694e0d630d92a84bBen Gruver } 22931d87776c459972f311a3527694e0d630d92a84bBen Gruver } 23031d87776c459972f311a3527694e0d630d92a84bBen Gruver 231373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver public void indent() { 232373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver indentLevel++; 233373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver } 234373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver 235373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver public void deindent() { 236373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver indentLevel--; 237373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver if (indentLevel < 0) { 238373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver indentLevel = 0; 239373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver } 240373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver } 241373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver 242373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver public int getCursor() { 243373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver return cursor; 244373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver } 245373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver 24631d87776c459972f311a3527694e0d630d92a84bBen Gruver private static class AnnotationEndpoint { 24731d87776c459972f311a3527694e0d630d92a84bBen Gruver /** Annotations that are associated with a specific point between bytes */ 24831d87776c459972f311a3527694e0d630d92a84bBen Gruver @Nonnull 24931d87776c459972f311a3527694e0d630d92a84bBen Gruver public final List<AnnotationItem> pointAnnotations = Lists.newArrayList(); 25031d87776c459972f311a3527694e0d630d92a84bBen Gruver /** Annotations that are associated with a range of bytes */ 25131d87776c459972f311a3527694e0d630d92a84bBen Gruver @Nullable 25231d87776c459972f311a3527694e0d630d92a84bBen Gruver public AnnotationItem rangeAnnotation = null; 25331d87776c459972f311a3527694e0d630d92a84bBen Gruver } 25431d87776c459972f311a3527694e0d630d92a84bBen Gruver 255373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver private static class AnnotationItem { 256373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver public final int indentLevel; 257373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver public final String annotation; 258373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver 25931d87776c459972f311a3527694e0d630d92a84bBen Gruver public AnnotationItem(int indentLevel, String annotation) { 260373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver this.indentLevel = indentLevel; 261373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver this.annotation = annotation; 262373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver } 263373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver } 264373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver 265373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver /** 266373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * Gets the width of the right side containing the annotations 267373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * @return 268373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver */ 269373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver public int getAnnotationWidth() { 270373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver int leftWidth = 8 + (hexCols * 2) + (hexCols / 2); 271373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver 272373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver return outputWidth - leftWidth; 273373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver } 274373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver 275373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver /** 276373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * Writes the annotated content of this instance to the given writer. 277373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * 278373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * @param out non-null; where to write to 279373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver */ 280373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver public void writeAnnotations(Writer out, byte[] data) throws IOException { 281373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver int rightWidth = getAnnotationWidth(); 282373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver int leftWidth = outputWidth - rightWidth - 1; 283373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver 28431d87776c459972f311a3527694e0d630d92a84bBen Gruver String padding = Strings.repeat(" ", 1000); 285373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver 286373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver TwoColumnOutput twoc = new TwoColumnOutput(out, leftWidth, rightWidth, "|"); 28731d87776c459972f311a3527694e0d630d92a84bBen Gruver 28831d87776c459972f311a3527694e0d630d92a84bBen Gruver Integer[] keys = new Integer[annotatations.size()]; 28931d87776c459972f311a3527694e0d630d92a84bBen Gruver keys = annotatations.keySet().toArray(keys); 29031d87776c459972f311a3527694e0d630d92a84bBen Gruver 29131d87776c459972f311a3527694e0d630d92a84bBen Gruver AnnotationEndpoint[] values = new AnnotationEndpoint[annotatations.size()]; 29231d87776c459972f311a3527694e0d630d92a84bBen Gruver values = annotatations.values().toArray(values); 29331d87776c459972f311a3527694e0d630d92a84bBen Gruver 29431d87776c459972f311a3527694e0d630d92a84bBen Gruver for (int i=0; i<keys.length-1; i++) { 29531d87776c459972f311a3527694e0d630d92a84bBen Gruver int rangeStart = keys[i]; 29631d87776c459972f311a3527694e0d630d92a84bBen Gruver int rangeEnd = keys[i+1]; 29731d87776c459972f311a3527694e0d630d92a84bBen Gruver 29831d87776c459972f311a3527694e0d630d92a84bBen Gruver AnnotationEndpoint annotations = values[i]; 29931d87776c459972f311a3527694e0d630d92a84bBen Gruver 30031d87776c459972f311a3527694e0d630d92a84bBen Gruver for (AnnotationItem pointAnnotation: annotations.pointAnnotations) { 30131d87776c459972f311a3527694e0d630d92a84bBen Gruver String paddingSub = padding.substring(0, pointAnnotation.indentLevel*2); 30231d87776c459972f311a3527694e0d630d92a84bBen Gruver twoc.write("", paddingSub + pointAnnotation.annotation); 303373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver } 304373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver 30531d87776c459972f311a3527694e0d630d92a84bBen Gruver String right; 30631d87776c459972f311a3527694e0d630d92a84bBen Gruver AnnotationItem rangeAnnotation = annotations.rangeAnnotation; 30731d87776c459972f311a3527694e0d630d92a84bBen Gruver if (rangeAnnotation != null) { 30831d87776c459972f311a3527694e0d630d92a84bBen Gruver right = padding.substring(0, rangeAnnotation.indentLevel*2); 30931d87776c459972f311a3527694e0d630d92a84bBen Gruver right += rangeAnnotation.annotation; 31031d87776c459972f311a3527694e0d630d92a84bBen Gruver } else { 31131d87776c459972f311a3527694e0d630d92a84bBen Gruver right = ""; 31231d87776c459972f311a3527694e0d630d92a84bBen Gruver } 313373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver 31431d87776c459972f311a3527694e0d630d92a84bBen Gruver String left = Hex.dump(data, rangeStart, rangeEnd - rangeStart, rangeStart, hexCols, 6); 315373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver 31631d87776c459972f311a3527694e0d630d92a84bBen Gruver twoc.write(left, right); 317373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver } 318373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver 31931d87776c459972f311a3527694e0d630d92a84bBen Gruver int lastKey = keys[keys.length-1]; 32031d87776c459972f311a3527694e0d630d92a84bBen Gruver if (lastKey < data.length) { 32131d87776c459972f311a3527694e0d630d92a84bBen Gruver String left = Hex.dump(data, lastKey, data.length - lastKey, lastKey, hexCols, 6); 32231d87776c459972f311a3527694e0d630d92a84bBen Gruver twoc.write(left, ""); 323373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver } 324373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver } 325dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver 326dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver public void setLimit(int start, int end) { 327dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver this.startLimit = start; 328dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver this.endLimit = end; 329dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver } 330dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver 331dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver public void clearLimit() { 332dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver this.startLimit = -1; 333dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver this.endLimit = -1; 334dc802b06607cde3eadaaffeae888bfd6146000f1Ben Gruver } 335373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver}