001package jmri.jmrix.loconet.sdf;
002
003import java.io.File;
004import java.io.IOException;
005import java.io.InputStream;
006import java.util.ArrayList;
007import java.util.Arrays;
008import java.util.List;
009import org.slf4j.Logger;
010import org.slf4j.LoggerFactory;
011
012/**
013 * Provide tools for reading, writing and accessing Digitrax SPJ files.
014 * <p>
015 * Maintains several representations:
016 * <ul>
017 *   <li>A byte array of the SDF contents after assembly. This is not complete, as
018 *   it can't contain information like contents, labels, etc. Nor can it
019 *   distinguish certain options with identical values (e.g. several constants
020 *   that boil down to a zero value)
021 *   <li>An array of nested SdfMacro objects. These contain more detailed
022 *   representations of the macro source, in the limit containing the entire
023 *   thing.
024 * </ul>
025 *
026 * @author Bob Jacobsen Copyright (C) 2007, 2008, 2010
027 */
028public class SdfBuffer {
029
030    public SdfBuffer(byte[] buffer) {
031        this.buffer = Arrays.copyOf(buffer, buffer.length);
032        loadMacroList();
033    }
034
035    public SdfBuffer(String name) throws IOException {
036        File file = new File(name);
037        int length = (int) file.length();
038
039        InputStream s = new java.io.BufferedInputStream(new java.io.FileInputStream(file));
040
041        try {
042            // Assume we can get all this in memory
043            buffer = new byte[length];
044
045            for (int i = 0; i < length; i++) {
046                buffer[i] = (byte) (s.read() & 0xFF);
047            }
048            loadMacroList();
049        } catch (java.io.IOException e1) {
050            log.error("error reading file", e1);
051            throw e1;
052        } finally {
053            try {
054                s.close();
055            } catch (java.io.IOException e2) {
056                log.error("Exception closing file", e2);
057            }
058        }
059    }
060
061    protected int index;
062
063    public void resetIndex() {
064        index = 0;
065    }
066
067    public int getAtIndex() {
068        return buffer[index] & 0xFF;
069    }
070
071    public int getAtIndexAndInc() {
072        return buffer[index++] & 0xFF;
073    }
074
075    public boolean moreData() {
076        return index < buffer.length;
077    }
078
079    public void setAtIndex(int data) {
080        buffer[index] = (byte) (data & 0xFF);
081    }
082
083    public void setAtIndexAndInc(int data) {
084        buffer[index++] = (byte) (data & 0xFF);
085    }
086
087    /**
088     * Reload the byte buffer from the contained instruction objects
089     */
090    public void loadByteArray() {
091        // first get length of new array
092        int length = 0;
093        for (int i = 0; i < ops.size(); i++) {
094            length += ops.get(i).totalLength();
095        }
096        buffer = new byte[length];
097        log.debug("create buffer of length {}", length);
098        resetIndex();
099        // recurse to store bytes
100        for (int i = 0; i < ops.size(); i++) {
101            ops.get(i).loadByteArray(this);
102        }
103        if (index != length) {
104            log.error("Lengths did not match: {} {}", index, length);
105        }
106    }
107
108    @Override
109    public String toString() {
110        StringBuilder out = new StringBuilder("");
111        for (int i = 0; i < ops.size(); i++) {
112            SdfMacro m = ops.get(i);
113
114            out.append(m.allInstructionString("    "));
115        }
116        return out.toString();
117    }
118
119    public byte[] getByteArray() {
120        return Arrays.copyOf(buffer, buffer.length);
121    }
122
123    public List<SdfMacro> getMacroList() {
124        return ops;
125    }
126
127    void loadMacroList() {
128        resetIndex();
129        ops = new ArrayList<>();
130        while (moreData()) {
131            SdfMacro m = SdfMacro.decodeInstruction(this);
132            ops.add(m);
133        }
134    }
135
136    // List of contained instructions
137    ArrayList<SdfMacro> ops;
138
139    // byte[] representation
140    byte[] buffer;
141
142    private final static Logger log = LoggerFactory.getLogger(SdfBuffer.class);
143
144}