2 * Copyright 2006 ProductiveMe Inc.
3 * Copyright 2013 JetBrains s.r.o.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
20 import com.pme.util.BitsUtil;
21 import com.pme.util.OffsetTrackingInputStream;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.lang.reflect.Array;
32 public abstract class Bin {
33 private String myName;
34 private String myDescription;
35 private long myOffset = 0;
36 ArrayList<Bin.Value> myOffsetHolders = new ArrayList<Bin.Value>(0);
37 ArrayList<Bin.Value> mySizeHolders = new ArrayList<Bin.Value>(0);
39 public Bin(String name) {
43 public String getName() {
47 public void setName(String name) {
51 public String getDescription() {
52 if (myDescription != null) {
58 public long getOffset() {
62 public void resetOffsets(long offset) {
64 for (Value holder : myOffsetHolders) {
65 holder.setValue(offset);
67 for (Value holder : mySizeHolders) {
68 holder.setValue(sizeInBytes());
72 public void copyFrom(Bin value) {
75 public void addOffsetHolder(Value offsetHolder) {
76 myOffsetHolders.add( offsetHolder );
79 public void addSizeHolder(Value sizeHolder) {
80 mySizeHolders.add( sizeHolder );
83 public abstract long sizeInBytes();
85 public void setDescription(String description) {
86 myDescription = description;
89 public abstract void read(DataInput stream) throws IOException;
91 public abstract void write(DataOutput stream) throws IOException;
93 public abstract void report(OutputStreamWriter writer) throws IOException;
95 public static class Comment extends Bin {
96 public Comment(String comment) {
100 public long sizeInBytes() {
104 public void read(DataInput stream) throws IOException {
107 public void write(DataOutput stream) throws IOException {
110 public void report(OutputStreamWriter writer) throws IOException {
111 _report(writer, getName());
115 public static class Structure extends Bin {
116 private ArrayList<Bin> myMembers = new ArrayList<Bin>(1);
117 private HashMap<String, Bin> myMembersMap = new HashMap<String, Bin>(1);
119 public Structure(String name) {
123 public void resetOffsets(long newOffset) {
124 super.resetOffsets(newOffset);
125 long offset = getOffset();
126 for (Bin bin : myMembers) {
127 bin.resetOffsets(offset);
128 offset = offset + bin.sizeInBytes();
132 public void copyFrom(Bin binStructure) {
133 Bin.Structure structure = (Bin.Structure)binStructure;
134 ArrayList<Bin> members = structure.getMembers();
135 for (Bin bin : members) {
136 Bin valueMember = getMember(bin.getName());
137 if (valueMember != null) {
138 valueMember.copyFrom(bin);
143 public long sizeInBytes() {
145 for (Bin bin : myMembers) {
146 size += bin.sizeInBytes();
151 public ArrayList<Bin> getMembers() {
154 public void addMember(Bin bin, String description) {
155 bin.setDescription(description);
159 public void addComment(String message) {
160 myMembers.add(new Comment(message));
163 public Bin insertMember(int index, Bin bin) {
164 ArrayList<Bin> list = new ArrayList<Bin>( myMembers.size() + 1 );
165 for ( int i = 0; i < index; ++i ){
166 list.add( myMembers.get( i ) );
169 for ( int i = index; i < myMembers.size(); ++i ){
170 list.add( myMembers.get( i ) );
173 addMemberToMapOnly(bin);
177 public Bin addMember(Bin bin) {
179 addMemberToMapOnly(bin);
183 //such members are not read by framework
184 //it is read by parent in 'read' overrided method
185 public void addMemberToMapOnly(Bin bin) {
186 myMembersMap.put(bin.getName(), bin);
189 public Bin getMember(String name) {
190 return (Bin) myMembersMap.get(name);
193 public Bin.Value getValueMember(String name) {
194 return (Bin.Value) myMembersMap.get(name);
197 public Bin.Structure getStructureMember(String name) {
198 return (Bin.Structure) myMembersMap.get(name);
201 public long getValue(String name) {
202 return ((Bin.Value) myMembersMap.get(name)).getValue();
205 public void read(DataInput stream) throws IOException {
206 for (Bin bin : myMembers) {
211 public void write(DataOutput stream) throws IOException {
212 for (Bin bin : myMembers) {
217 public void report(OutputStreamWriter writer) throws IOException {
218 _report(writer, "--- '" + getName() + "' Structure --- size = " + sizeInBytes());
219 _report(writer, "{Offset = " + Long.toHexString(getOffset()) + "}");
220 for (Bin bin : myMembers) {
223 _report(writer, "--- End Of '" + getName() + "' Structure ---");
227 public abstract static class Value extends Bin {
228 private long myValue;
230 public Value(String name) {
234 public abstract long getValue();
235 public abstract Value setValue(long value);
237 public void copyFrom(Bin value) {
238 setValue(((Value)value).getValue());
241 public long getRawValue() {
245 public Value setRawValue(long value) {
250 public Value setValue(short value) {
251 myValue = BitsUtil.revertBytesOfShort(value);
256 public static class Byte extends Value {
257 public Byte(String name) {
261 public long sizeInBytes() {
265 public long getValue() {
266 return getRawValue();
268 public Value setValue(long value) {
273 public void read(DataInput stream) throws IOException {
274 setRawValue(stream.readByte());
277 public void write(DataOutput stream) throws IOException {
278 stream.writeByte((byte) getRawValue());
281 public void report(OutputStreamWriter writer) throws IOException {
282 _report(writer, getDescription(), (byte) getValue());
287 public static class Word extends Value {
292 public Word(String name) {
296 public long sizeInBytes() {
300 public long getValue() {
301 return BitsUtil.revertBytesOfShort((short) super.getRawValue());
304 public Value setValue(long value) {
305 setRawValue(BitsUtil.revertBytesOfShort((short) value));
309 public void read(DataInput stream) throws IOException {
310 setRawValue(stream.readShort());
313 public void write(DataOutput stream) throws IOException {
314 stream.writeShort((short) getRawValue());
317 public void report(OutputStreamWriter writer) throws IOException {
318 short sh = (short) getValue();
319 _report(writer, getDescription(), sh);
321 public String toString() {
322 return BitsUtil.shortToHexString( (int)getValue() );
326 public static class DWord extends Value {
327 public DWord(String name) {
331 public long sizeInBytes() {
335 public Value setValue(long value) {
336 setRawValue(BitsUtil.revertBytesOfInt((int) value));
340 public long getValue() {
341 return BitsUtil.revertBytesOfInt((int) super.getRawValue());
344 public void read(DataInput stream) throws IOException {
345 setRawValue(stream.readInt());
348 public void write(DataOutput stream) throws IOException {
349 stream.writeInt((int) getRawValue());
352 public void report(OutputStreamWriter writer) throws IOException {
353 _report(writer, getDescription(), (int) getValue());
355 public String toString() {
356 return BitsUtil.intToHexString( (int)getValue() );
360 public static class Padding extends Bin {
363 public Padding(int bytes) {
369 public long sizeInBytes() {
370 throw new UnsupportedOperationException();
374 public void read(DataInput stream) throws IOException {
375 if (stream instanceof OffsetTrackingInputStream) {
376 long offset = ((OffsetTrackingInputStream) stream).getOffset();
377 int offsetMask = myBytes-1;
378 long offsetBits = offset & offsetMask;
379 if (offsetBits > 0) {
380 stream.skipBytes((int) (myBytes - offsetBits));
386 public void write(DataOutput stream) throws IOException {
390 public void report(OutputStreamWriter writer) throws IOException {
394 public static class Txt extends Bin {
395 private StringBuffer myBuffer = new StringBuffer();
396 private Bin.Value mySize;
397 private byte[] myBytes;
399 public Txt(String name, byte[] bytes) {
402 mySize = new DWord("").setValue(bytes.length);
406 public Txt(String name, String string) {
408 myBytes = new byte[string.length() * 2];
409 byte[] bytes = string.getBytes();
410 for (int i = 0; i < bytes.length; ++i) {
411 myBytes[i * 2] = bytes[i];
412 myBytes[i * 2 + 1] = 0;
414 mySize = new DWord("").setValue(myBytes.length);
418 public Txt(String name, Bin.Value size) {
423 public Txt(String name, int size) {
424 this(name, new DWord("size").setValue(size));
427 public String getText() {
428 return myBuffer.toString();
431 public long sizeInBytes() {
432 return mySize.getValue();
435 private void setValue(){
436 for (int i = 0; i < mySize.getValue(); ++i) {
437 int b = BitsUtil.unsignedByte(myBytes[i]);
439 myBuffer.append((char) b);
444 public void read(DataInput stream) throws IOException {
445 myBuffer.setLength(0);
446 myBytes = new byte[(int) mySize.getValue()];
447 for (int i = 0; i < mySize.getValue(); ++i) {
448 myBytes[i] = stream.readByte();
453 public void write(DataOutput stream) throws IOException {
454 stream.write(myBytes);
457 public void report(OutputStreamWriter writer) throws IOException {
458 _report(writer, myBuffer.toString());
462 public static class WChar extends Bin {
463 private String myValue;
465 public WChar(String name) {
470 public long sizeInBytes() {
471 throw new UnsupportedOperationException();
475 public void read(DataInput stream) throws IOException {
476 StringBuilder valueBuilder = new StringBuilder();
478 char c = BitsUtil.readChar(stream);
480 valueBuilder.append(c);
482 myValue = valueBuilder.toString();
486 public void write(DataOutput stream) throws IOException {
487 throw new UnsupportedOperationException();
491 public void report(OutputStreamWriter writer) throws IOException {
492 _report(writer, myValue);
495 public String getValue() {
500 public static class Bytes extends Bin {
501 private byte[] myBytes;
502 private Value myStartOffset;
503 private Value mySize;
504 private int myBytesInRow = 16;
506 public Bytes(String name, Bin.Value size) {
511 public Bytes(String name, long size) {
513 mySize = new DWord("size").setValue(size);
516 public Bytes(String name, Bin.Value startOffset, Bin.Value size) {
518 reset(startOffset, size);
521 public void reset(int startOffset, int size) {
522 reset(new DWord("startOffset").setValue(startOffset), new DWord("size").setValue(size));
525 public void reset(Bin.Value startOffset, Bin.Value size) {
526 myStartOffset = startOffset;
530 public byte[] getBytes() {
534 public void setBytes(byte[] bytes) {
535 setBytes(bytes, bytes.length);
538 public void setBytes(byte[] bytes, int size) {
539 if (bytes.length < size) {
540 throw new RuntimeException("bytes.length < size");
543 mySize.setValue(size);
546 public long sizeInBytes() {
547 return mySize.getValue();
550 public void read(DataInput stream) throws IOException {
551 if (myStartOffset != null) {
552 RandomAccessFile file = (RandomAccessFile) stream;
553 file.seek(myStartOffset.getValue());
555 myBytes = new byte[(int) mySize.getValue()];
556 stream.readFully(myBytes);
559 public void write(DataOutput stream) throws IOException {
560 stream.write(myBytes, 0, (int) sizeInBytes());
563 private StringBuffer myBuffer = new StringBuffer();
565 public void report(OutputStreamWriter writer) throws IOException {
566 _report(writer, getName());
567 _report(writer, "Number of bytes: " + mySize.getValue());
568 int rowCount = (myBytes.length / myBytesInRow);
569 if (myBytes.length % myBytesInRow != 0) {
573 for (int i = 0; i < rowCount; i++) {
574 myBuffer.setLength(0);
575 myBuffer.append("\n");
576 for (int j = 0; j < myBytesInRow && byteCount < myBytes.length; j++) {
577 byte aByte = myBytes[byteCount++];
578 myBuffer.append(" ").append(BitsUtil.byteToHexString(aByte));
580 writer.write(myBuffer.toString());
585 public static class ArrayOfBins extends Bin {
586 private Bin[] myValues;
587 private Bin.Value mySize;
588 private Class myClass;
589 private Bin.Value myCountHolder = null;
591 public ArrayOfBins(String name, Class cl, Bin.Value size) {
598 public ArrayOfBins(String name, Class cl, int size) {
599 this(name, cl, new DWord("size").setValue(size));
602 public void addBin( Bin bin ){
603 Bin[] newArray = new Bin[myValues.length+1];
604 System.arraycopy( myValues, 0, newArray, 0, myValues.length );
605 newArray[myValues.length] = bin;
607 mySize.setValue( mySize.getValue() + 1 );
610 public void copyFrom(Bin bin) {
611 ArrayOfBins value = (ArrayOfBins)bin;
612 for (int i = 0; i < myValues.length; i++) {
613 myValues[i].copyFrom( value.get(i) );
617 public void setCountHolder(Value countHolder) {
618 myCountHolder = countHolder;
621 private void init() {
622 myValues = (Bin[]) Array.newInstance(myClass, (int) mySize.getValue());
624 for (int i = 0; i < myValues.length; i++) {
626 Bin bin = (Bin) myClass.newInstance();
627 bin.setName("[" + i + "]");
629 } catch (InstantiationException e) {
630 throw new RuntimeException(e.getMessage());
631 } catch (IllegalAccessException e) {
632 throw new RuntimeException(e.getMessage());
637 public void resetOffsets(long newOffset) {
638 super.resetOffsets(newOffset);
639 long offset = getOffset();
640 if (myCountHolder != null) {
641 myCountHolder.setValue(myValues.length);
643 for (Bin bin : myValues) {
644 bin.resetOffsets(offset);
645 offset = offset + bin.sizeInBytes();
650 return myValues.length;
653 public Bin[] getArray() {
657 public Bin get(int index) {
658 return myValues[index];
661 public long sizeInBytes() {
663 for (Bin value : myValues) {
664 size += value.sizeInBytes();
669 public void read(DataInput stream) throws IOException {
671 for (Bin value : myValues) {
676 public void write(DataOutput stream) throws IOException {
677 for (Bin value : myValues) {
682 public void report(OutputStreamWriter writer) throws IOException {
683 writer.write("\n" + "Array size: " + myValues.length);
684 for (Bin value : myValues) {
685 value.report(writer);
690 protected void _report(OutputStreamWriter buffer, String name, int value) throws IOException {
691 buffer.write("\n" + name + " : " + BitsUtil.intToHexString(value));
694 protected void _report(OutputStreamWriter buffer, String name, short value) throws IOException {
695 buffer.write("\n" + name + " : " + BitsUtil.shortToHexString(value));
698 protected void _report(OutputStreamWriter buffer, String name, byte value) throws IOException {
699 buffer.write("\n" + name + " : " + BitsUtil.byteToHexString(value));
702 protected void _report(OutputStreamWriter buffer, String message) throws IOException {
703 buffer.write("\n" + message);