001package jmri.jmrix;
002
003import org.slf4j.Logger;
004import org.slf4j.LoggerFactory;
005
006/**
007 * Abstract base class for replies in a message/reply protocol.
008 * <p>
009 * Handles the character manipulation.
010 * <p>
011 * This is a variable length reply, which can grow as needed. The length is
012 * given by the largest index written so far.
013 *
014 * @author Bob Jacobsen Copyright (C) 2003
015 */
016abstract public class AbstractMRReply extends AbstractMessage {
017    // is this logically an abstract class?
018
019    /**
020     * Create a new AbstractMRReply instance.
021     */
022    public AbstractMRReply() {
023        setBinary(false);
024        unsolicited = false;
025        _dataChars = new int[maxSize()];
026    }
027
028    /**
029     * Copy a Reply to a new AbstractMRReply instance.
030     *
031     * @param m the reply to copy
032     */
033    public AbstractMRReply(AbstractMRReply m) {
034        this();
035        if (m == null) {
036            log.error("copy ctor of null message");
037        } else {
038            _nDataChars = m._nDataChars;
039            if (_nDataChars >= 0)
040                System.arraycopy(m._dataChars, 0, _dataChars, 0, _nDataChars);
041        }
042    }
043
044    /**
045     * Create a new AbstractMRReply instance from a string.
046     *
047     * @param s String to use as reply content
048     */
049    public AbstractMRReply(String s) {
050        this();
051        _nDataChars = s.length();
052        for (int i = 0; i < _nDataChars; i++) {
053            _dataChars[i] = s.charAt(i);
054        }
055    }
056
057    // keep track of length
058    @Override
059    public void setElement(int n, int v) {
060        _dataChars[n] = (char) v;
061        _nDataChars = Math.max(_nDataChars, n + 1);
062    }
063
064    /**
065     * Set the OpCode.
066     * Sets Element 0 to character value of integer.
067     * @param i Opcode value.
068     */
069    public void setOpCode(int i) {
070        _dataChars[0] = (char) i;
071    }
072
073    /**
074     * Get the OpCode.
075     * @return value of Element 0.
076     */
077    public int getOpCode() {
078        return _dataChars[0];
079    }
080
081    /**
082     * Flush the message.
083     * Sets number of data characters to 0.
084     * Does not reset array length or data.
085     */
086    public void flush() {
087        _nDataChars = 0;
088    }
089
090    // mode accessors
091    private boolean _isBinary;
092
093    /**
094     * Get if the Reply has Binary form flag set.
095     * @return true if binary, else false.
096     */
097    public boolean isBinary() {
098        return _isBinary;
099    }
100
101    /**
102     * Set flag for if the Reply is Binary form.
103     * @param b true if binary, else false.
104     */
105    public void setBinary(boolean b) {
106        _isBinary = b;
107    }
108
109    /**
110     * Set flag for Unsolicited to true.
111     */
112    public final void setUnsolicited() {
113        unsolicited = true;
114    }
115
116    /**
117     * Get flag for Unsolicited.
118     * @return true if Unsolicited, else false.
119     */
120    public boolean isUnsolicited() {
121        return unsolicited;
122    }
123
124    /*
125     * Override in system specific classes if required.
126     *
127     * @return 'true' if the message is an error and we can automatically
128     * recover by retransmitting the message.
129     */
130    public boolean isRetransmittableErrorMsg() {
131        return false;
132    }
133
134    // display format
135    @Override
136    public String toString() {
137        String s = "";
138        for (int i = 0; i < _nDataChars; i++) {
139            if (_isBinary) {
140                if (i != 0) {
141                    s += " ";
142                }
143                s = jmri.util.StringUtil.appendTwoHexFromInt(_dataChars[i] & 0xFF, s);
144            } else {
145                s += (char) _dataChars[i];
146            }
147        }
148        return s;
149    }
150
151    abstract protected int skipPrefix(int index);
152
153    public int value() { // integer value of 1st three digits
154        int index = 0;
155        index = skipWhiteSpace(index);
156        index = skipPrefix(index);
157        index = skipWhiteSpace(index);
158        String s = "" + (char) getElement(index) + (char) getElement(index + 1) + (char) getElement(index + 2);
159        int val = -1;
160        try {
161            val = Integer.parseInt(s);
162        } catch (RuntimeException e) {
163            log.error("Unable to get number from reply: \"{}\" index: {} message: \"{}\"", s, index, toString());
164        }
165        return val;
166    }
167
168    public int pollValue() { // integer value of HHHH
169        int index = 0;
170        index = skipWhiteSpace(index);
171        index = skipPrefix(index);
172        index = skipWhiteSpace(index);
173        String s = "" + (char) getElement(index) + (char) getElement(index + 1)
174                + (char) getElement(index + 2) + (char) getElement(index + 3);
175        int val = -1;
176        try {
177            val = Integer.parseInt(s, 16);
178        } catch (RuntimeException e) {
179            log.error("Unable to get number from reply: \"{}\" index: {} message: \"{}\"", s, index, toString());
180        }
181        return val;
182    }
183
184    public int match(String s) {
185        // loop over starting positions
186        outer:
187        for (int i = 0; i < _nDataChars - s.length() + 1; i++) {
188            // loop to check each start position
189            for (int j = 0; j < s.length(); j++) {
190                if (_dataChars[i + j] != s.charAt(j)) {
191                    continue outer;
192                }
193            }
194            // here we succeed
195            return i;
196        }
197
198        return -1;
199    }
200
201    public int skipWhiteSpace(int index) {
202        // start at index, passing any whitespace & control characters at the start of the buffer
203        while (index < getNumDataElements() - 1
204                && ((char) getElement(index) <= ' ')) {
205            index++;
206        }
207        return index;
208    }
209
210    public int maxSize() {
211        return DEFAULTMAXSIZE;
212    }
213    static public final int DEFAULTMAXSIZE = 120;
214
215    // contents
216    private boolean unsolicited;
217
218    private final static Logger log = LoggerFactory.getLogger(AbstractMRReply.class);
219
220}