001 package org.otfeed.support;
002
003 import java.nio.ByteBuffer;
004
005
006
007 /**
008 * Converts ByteBuffer type to/from a string.
009 * <p/>
010 * Formats buffer into multi-line string, each line
011 * showing up to 16 bytes of the data in a standard "hexdump"
012 * format. For example,
013 * <pre>
014 * 00000001 40360000 00000000 21000001 %....@6......!...
015 * 148a6fe0 69 %..o.i
016 * </pre>
017 * <p/>
018 * When parsing, ignores empty lines, and treat separator
019 * symbol as start of comment. Therefore, following is a valid
020 * input to the parse method:
021 * <pre>
022 * %
023 * % buffer snippet
024 * %
025 * 00000001 40360000 00000000 21000001 %....@6......!...
026 * 148a6fe0 69 %..o.i
027 * </pre>
028 * <p/>
029 */
030 public class BufferFormat implements IFormat<ByteBuffer> {
031
032 private final char DEFAULT_SEPARATOR = '%';
033
034 private final IBufferAllocator allocator;
035
036 /**
037 * Creates format using the provided allocator service
038 * (used for {@link #parse(String) parsing} only.
039 *
040 * @param allocator allocator.
041 */
042 public BufferFormat(IBufferAllocator allocator) {
043 this.allocator = allocator;
044 }
045
046 /**
047 * Creates format using the default
048 * {@link ByteReverseBufferAllocator byte-reversed allocation service}.
049 */
050 public BufferFormat() {
051 this(new ByteReverseBufferAllocator());
052 }
053
054 private char separator = DEFAULT_SEPARATOR;
055
056 /**
057 * Symbol that marks the beginnig of comment.
058 *
059 * @return separator character.
060 */
061 public char getSeparator() {
062 return separator;
063 }
064
065 /**
066 * Sets separator character.
067 *
068 * @param val separator character.
069 */
070 public void setSeparator(char val) {
071 separator = val;
072 }
073
074 private static final String PRINTABLES
075 = "~`!@#$%^&*()_+-=[]{}\\|,.<>/?;:'\"";
076
077 private static char translateToPrintable(int val) {
078 if((val >= 'a' && val <= 'z')
079 || (val >= 'A' && val <= 'Z')
080 || Character.isDigit(val)
081 || PRINTABLES.indexOf((char) val) >= 0) {
082 return (char) val;
083 } else {
084 return '.';
085 }
086 }
087
088 private static final String HEX = "0123456789abcdef";
089
090 public String format(ByteBuffer buffer) {
091 StringBuilder builder = new StringBuilder();
092 StringBuilder verbose = new StringBuilder();
093
094 int length = buffer.limit() - buffer.position();
095 for(int i = 0; i < length; i++) {
096 int val = buffer.get(buffer.position() + i);
097 if(val < 0) val += 256; // unsigned!
098
099 verbose.append(translateToPrintable(val));
100
101 builder.append(HEX.charAt(val >> 4));
102 builder.append(HEX.charAt(val & 0xf));
103 if((i % 16) == 15) {
104 builder.append("\t");
105 builder.append(separator);
106 builder.append(verbose);
107 builder.append("\n");
108 verbose.setLength(0);
109 } else if((i % 4) == 3) {
110 builder.append(" ");
111 }
112 }
113
114 int padding = length % 16;
115 if(padding > 0) {
116 while(padding < 16) {
117
118 builder.append(" ");
119
120 if((padding % 16) == 15) {
121 builder.append("\t");
122 builder.append(separator);
123 builder.append(verbose);
124 builder.append("\n");
125 } else if((padding % 4) == 3) {
126 builder.append(" ");
127 }
128
129 padding++;
130 }
131 }
132
133 return builder.toString();
134 }
135
136 public ByteBuffer parse(String val) {
137
138 ByteBuffer buffer = allocator.allocate(val.length() / 2);
139 String [] lines = val.split("\n");
140 for(int i = 0; i < lines.length; i++) {
141 String line = lines[i];
142 int index = line.indexOf(separator);
143 if(index >= 0) line = line.substring(0, index).trim();
144
145 line = line.replaceAll("\\s", "").toLowerCase();
146 if(line.length() == 0) {
147 // ignore comments and empty lines
148 continue;
149 }
150
151 int size = line.length() / 2;
152 if(line.length() != size * 2) {
153 throw new IllegalArgumentException("can't parse line " + (i + 1)
154 + " of the buffer: odd number of nibbles");
155 }
156
157 for(int j = 0; j < size; j++) {
158 int high = line.charAt(j * 2);
159 int low = line.charAt(j * 2 + 1);
160
161 high = HEX.indexOf((char) high);
162 low = HEX.indexOf((char) low);
163
164 if(high < 0 || low < 0) {
165 throw new IllegalArgumentException("not a hex number: "
166 + line + ", position=" + (j * 2));
167 }
168
169 buffer.put((byte)((high << 4) + low));
170 }
171 }
172
173 buffer.flip();
174
175 return buffer;
176 }
177
178 }