package org.xdef.sys;

import org.xdef.msg.SYS;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URL;
import java.net.URLConnection;
import java.text.DateFormatSymbols;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Locale;
import java.util.Stack;
import java.util.TimeZone;
import static org.xdef.sys.SParser.NOCHAR;

/** String parser used for constructing specific parsers.
 * @author  Vaclav Trojan
 */
public class StringParser extends SReporter implements SParser {
    /** XML version 1.0. */
    public static final byte XMLVER1_0 = 10;
    /** XML version 1.1. */
    public static final byte XMLVER1_1 = 11;
    /** Illegal XML character. */
    public static final byte XML_CHAR_ILLEGAL = 0;
    /** Valid XML character. */
    public static final byte XML_CHAR = 1;
    /** Whitespace XML character. */
    public static final byte XML_CHAR_WHITESPACE = 2;
    /** Colon character. */
    public static final byte XML_CHAR_COLON = 4;
    /** Start character of XML name. */
    public static final byte XML_CHAR_NAME_START = 8;
    /** XML name character (not the first one). */
    public static final byte XML_CHAR_NAME_EXT = 16;
    /** Table of XML version 1.0 characters. */
    private static final byte[] XML_CHARTAB0 = new byte[65536];
    /** Table of XML version 1.1 characters. */
    private static final byte[] XML_CHARTAB1 = new byte[65536];

    /** Actual character */
    private char _ch;
    /** Source buffer. */
    private String _source;
    /** Buffer position of end of buffer. */
    private int _endPos;
    /** Value of parsed string. */
    private String _parsedString;
    /** If true the parser will create detailed line position. By default the
     * value of this is set to false. */
    private boolean _lineInfo;
    /** Parsed date and time. */
    private MyDate _parsedDatetime;
    /** Parsed date and time. */
    private SDuration _parsedDuration;
    /** Switch to close or not close reader. */
    boolean _closeReader = true;
// <editor-fold defaultstate="collapsed" desc="XML UNICODE CHARACTER TABLES">
    static {

        ////////////////////////////////////////////////////////////////////////
        // Don't modify following part - it was authomaticaly generated!
        ////////////////////////////////////////////////////////////////////////

        // UTF16 character table of XML version 1.0
        // (default value is 0 -> illegal character)
        XML_CHARTAB0[0x0009] = XML_CHAR_WHITESPACE; // TAB
        XML_CHARTAB0[0x000a] = XML_CHAR_WHITESPACE; // LF
        XML_CHARTAB0[0x000d] = XML_CHAR_WHITESPACE; // CR
        XML_CHARTAB0[0x0020] = XML_CHAR_WHITESPACE; // SPACE
        Arrays.fill(XML_CHARTAB0, 0x0021, 0x002d, XML_CHAR); // !"#$%&'()*+,
        XML_CHARTAB0[0x002d] = XML_CHAR_NAME_EXT; // -
        XML_CHARTAB0[0x002e] = XML_CHAR_NAME_EXT; // .
        XML_CHARTAB0[0x002f] = XML_CHAR; // /
        Arrays.fill(XML_CHARTAB0, 0x0030, 0x003a, XML_CHAR_NAME_EXT); //0-9
        XML_CHARTAB0[0x003a] = XML_CHAR_COLON; // :
        Arrays.fill(XML_CHARTAB0, 0x003b, 0x0041, XML_CHAR); // ;<=>?@
        Arrays.fill(XML_CHARTAB0, 0x0041, 0x005b, XML_CHAR_NAME_START); // A-Z
        Arrays.fill(XML_CHARTAB0, 0x005b, 0x005f, XML_CHAR); // ^
        XML_CHARTAB0[0x005f] = XML_CHAR_NAME_START; // _
        XML_CHARTAB0[0x0060] = XML_CHAR; // `
        Arrays.fill(XML_CHARTAB0, 0x0061, 0x007b, XML_CHAR_NAME_START); // a-z
        Arrays.fill(XML_CHARTAB0, 0x007b, 0x00b7, XML_CHAR); // {|}~DEL ...
        XML_CHARTAB0[0x00b7] = XML_CHAR_NAME_EXT;
        Arrays.fill(XML_CHARTAB0, 0x00b8, 0x00c0, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x00c0, 0x00d7, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x00d7] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x00d8, 0x00f7, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x00f7] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x00f8, 0x0132, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0132] = XML_CHAR;
        XML_CHARTAB0[0x0133] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0134, 0x013f, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x013f] = XML_CHAR;
        XML_CHARTAB0[0x0140] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0141, 0x0149, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0149] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x014a, 0x017f, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x017f] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0180, 0x01c4, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x01c4, 0x01cd, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x01cd, 0x01f1, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x01f1, 0x01f4, XML_CHAR);
        XML_CHARTAB0[0x01f4] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x01f5] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x01f6, 0x01fa, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x01fa, 0x0218, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x0218, 0x0250, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0250, 0x02a9, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x02a9, 0x02bb, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x02bb, 0x02c2, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x02c2, 0x02d0, XML_CHAR);
        XML_CHARTAB0[0x02d0] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x02d1] = XML_CHAR_NAME_EXT;
        Arrays.fill(XML_CHARTAB0, 0x02d2, 0x0300, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0300, 0x0346, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x0346, 0x0360, XML_CHAR);
        XML_CHARTAB0[0x0360] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x0361] = XML_CHAR_NAME_EXT;
        Arrays.fill(XML_CHARTAB0, 0x0362, 0x0386, XML_CHAR);
        XML_CHARTAB0[0x0386] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0387] = XML_CHAR_NAME_EXT;
        Arrays.fill(XML_CHARTAB0, 0x0388, 0x038b, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x038b] = XML_CHAR;
        XML_CHARTAB0[0x038c] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x038d] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x038e, 0x03a2, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x03a2] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x03a3, 0x03cf, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x03cf] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x03d0, 0x03d7, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x03d7, 0x03da, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x03da, 0x03dd, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x03dd] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x03de, 0x03e1, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x03e1] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x03e2, 0x03f4, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x03f4, 0x0401, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0401, 0x040d, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x040d] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x040e, 0x0450, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0450] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0451, 0x045d, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x045d] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x045e, 0x0482, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0482] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0483, 0x0487, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x0487, 0x0490, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0490, 0x04c5, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x04c5] = XML_CHAR;
        XML_CHARTAB0[0x04c6] = XML_CHAR;
        XML_CHARTAB0[0x04c7] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x04c8] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x04c9] = XML_CHAR;
        XML_CHARTAB0[0x04ca] = XML_CHAR;
        XML_CHARTAB0[0x04cb] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x04cc] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x04cd, 0x04d0, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x04d0, 0x04ec, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x04ec] = XML_CHAR;
        XML_CHARTAB0[0x04ed] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x04ee, 0x04f6, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x04f6] = XML_CHAR;
        XML_CHARTAB0[0x04f7] = XML_CHAR;
        XML_CHARTAB0[0x04f8] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x04f9] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x04fa, 0x0531, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0531, 0x0557, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0557] = XML_CHAR;
        XML_CHARTAB0[0x0558] = XML_CHAR;
        XML_CHARTAB0[0x0559] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x055a, 0x0561, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0561, 0x0587, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x0587, 0x0591, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0591, 0x05a2, XML_CHAR_NAME_EXT);
        XML_CHARTAB0[0x05a2] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x05a3, 0x05ba, XML_CHAR_NAME_EXT);
        XML_CHARTAB0[0x05ba] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x05bb, 0x05be, XML_CHAR_NAME_EXT);
        XML_CHARTAB0[0x05be] = XML_CHAR;
        XML_CHARTAB0[0x05bf] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x05c0] = XML_CHAR;
        XML_CHARTAB0[0x05c1] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x05c2] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x05c3] = XML_CHAR;
        XML_CHARTAB0[0x05c4] = XML_CHAR_NAME_EXT;
        Arrays.fill(XML_CHARTAB0, 0x05c5, 0x05d0, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x05d0, 0x05eb, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x05eb, 0x05f0, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x05f0, 0x05f3, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x05f3, 0x0621, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0621, 0x063b, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x063b, 0x0640, XML_CHAR);
        XML_CHARTAB0[0x0640] = XML_CHAR_NAME_EXT;
        Arrays.fill(XML_CHARTAB0, 0x0641, 0x064b, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x064b, 0x0653, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x0653, 0x0660, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0660, 0x066a, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x066a, 0x0670, XML_CHAR);
        XML_CHARTAB0[0x0670] = XML_CHAR_NAME_EXT;
        Arrays.fill(XML_CHARTAB0, 0x0671, 0x06b8, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x06b8] = XML_CHAR;
        XML_CHARTAB0[0x06b9] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x06ba, 0x06bf, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x06bf] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x06c0, 0x06cf, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x06cf] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x06d0, 0x06d4, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x06d4] = XML_CHAR;
        XML_CHARTAB0[0x06d5] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x06d6, 0x06e5, XML_CHAR_NAME_EXT);
        XML_CHARTAB0[0x06e5] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x06e6] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x06e7] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x06e8] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x06e9] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x06ea, 0x06ee, XML_CHAR_NAME_EXT);
        XML_CHARTAB0[0x06ee] = XML_CHAR;
        XML_CHARTAB0[0x06ef] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x06f0, 0x06fa, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x06fa, 0x0901, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0901, 0x0904, XML_CHAR_NAME_EXT);
        XML_CHARTAB0[0x0904] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0905, 0x093a, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x093a] = XML_CHAR;
        XML_CHARTAB0[0x093b] = XML_CHAR;
        XML_CHARTAB0[0x093c] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x093d] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x093e, 0x094e, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x094e, 0x0951, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0951, 0x0955, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x0955, 0x0958, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0958, 0x0962, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0962] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x0963] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x0964] = XML_CHAR;
        XML_CHARTAB0[0x0965] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0966, 0x0970, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x0970, 0x0981, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0981, 0x0984, XML_CHAR_NAME_EXT);
        XML_CHARTAB0[0x0984] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0985, 0x098d, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x098d] = XML_CHAR;
        XML_CHARTAB0[0x098e] = XML_CHAR;
        XML_CHARTAB0[0x098f] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0990] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0991] = XML_CHAR;
        XML_CHARTAB0[0x0992] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0993, 0x09a9, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x09a9] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x09aa, 0x09b1, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x09b1] = XML_CHAR;
        XML_CHARTAB0[0x09b2] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x09b3, 0x09b6, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x09b6, 0x09ba, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x09ba] = XML_CHAR;
        XML_CHARTAB0[0x09bb] = XML_CHAR;
        XML_CHARTAB0[0x09bc] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x09bd] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x09be, 0x09c5, XML_CHAR_NAME_EXT);
        XML_CHARTAB0[0x09c5] = XML_CHAR;
        XML_CHARTAB0[0x09c6] = XML_CHAR;
        XML_CHARTAB0[0x09c7] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x09c8] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x09c9] = XML_CHAR;
        XML_CHARTAB0[0x09ca] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x09cb, 0x09ce, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x09ce, 0x09d7, XML_CHAR);
        XML_CHARTAB0[0x09d7] = XML_CHAR_NAME_EXT;
        Arrays.fill(XML_CHARTAB0, 0x09d8, 0x09dc, XML_CHAR);
        XML_CHARTAB0[0x09dc] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x09dd] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x09de] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x09df, 0x09e2, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x09e2] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x09e3] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x09e4] = XML_CHAR;
        XML_CHARTAB0[0x09e5] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x09e6, 0x09f0, XML_CHAR_NAME_EXT);
        XML_CHARTAB0[0x09f0] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x09f1] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x09f2, 0x0a02, XML_CHAR);
        XML_CHARTAB0[0x0a02] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x0a03] = XML_CHAR;
        XML_CHARTAB0[0x0a04] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0a05, 0x0a0b, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x0a0b, 0x0a0f, XML_CHAR);
        XML_CHARTAB0[0x0a0f] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0a10] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0a11] = XML_CHAR;
        XML_CHARTAB0[0x0a12] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0a13, 0x0a29, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0a29] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0a2a, 0x0a31, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0a31] = XML_CHAR;
        XML_CHARTAB0[0x0a32] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0a33] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0a34] = XML_CHAR;
        XML_CHARTAB0[0x0a35] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0a36] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0a37] = XML_CHAR;
        XML_CHARTAB0[0x0a38] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0a39] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0a3a] = XML_CHAR;
        XML_CHARTAB0[0x0a3b] = XML_CHAR;
        XML_CHARTAB0[0x0a3c] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x0a3d] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0a3e, 0x0a43, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x0a43, 0x0a47, XML_CHAR);
        XML_CHARTAB0[0x0a47] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x0a48] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x0a49] = XML_CHAR;
        XML_CHARTAB0[0x0a4a] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0a4b, 0x0a4e, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x0a4e, 0x0a59, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0a59, 0x0a5d, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0a5d] = XML_CHAR;
        XML_CHARTAB0[0x0a5e] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x0a5f, 0x0a66, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0a66, 0x0a72, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x0a72, 0x0a75, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x0a75, 0x0a81, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0a81, 0x0a84, XML_CHAR_NAME_EXT);
        XML_CHARTAB0[0x0a84] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0a85, 0x0a8c, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0a8c] = XML_CHAR;
        XML_CHARTAB0[0x0a8d] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0a8e] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0a8f, 0x0a92, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0a92] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0a93, 0x0aa9, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0aa9] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0aaa, 0x0ab1, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0ab1] = XML_CHAR;
        XML_CHARTAB0[0x0ab2] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0ab3] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0ab4] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0ab5, 0x0aba, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0aba] = XML_CHAR;
        XML_CHARTAB0[0x0abb] = XML_CHAR;
        XML_CHARTAB0[0x0abc] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x0abd] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x0abe, 0x0ac6, XML_CHAR_NAME_EXT);
        XML_CHARTAB0[0x0ac6] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0ac7, 0x0aca, XML_CHAR_NAME_EXT);
        XML_CHARTAB0[0x0aca] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0acb, 0x0ace, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x0ace, 0x0ae0, XML_CHAR);
        XML_CHARTAB0[0x0ae0] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x0ae1, 0x0ae6, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0ae6, 0x0af0, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x0af0, 0x0b01, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0b01, 0x0b04, XML_CHAR_NAME_EXT);
        XML_CHARTAB0[0x0b04] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0b05, 0x0b0d, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0b0d] = XML_CHAR;
        XML_CHARTAB0[0x0b0e] = XML_CHAR;
        XML_CHARTAB0[0x0b0f] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0b10] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0b11] = XML_CHAR;
        XML_CHARTAB0[0x0b12] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0b13, 0x0b29, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0b29] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0b2a, 0x0b31, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0b31] = XML_CHAR;
        XML_CHARTAB0[0x0b32] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0b33] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0b34] = XML_CHAR;
        XML_CHARTAB0[0x0b35] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0b36, 0x0b3a, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0b3a] = XML_CHAR;
        XML_CHARTAB0[0x0b3b] = XML_CHAR;
        XML_CHARTAB0[0x0b3c] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x0b3d] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x0b3e, 0x0b44, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x0b44, 0x0b47, XML_CHAR);
        XML_CHARTAB0[0x0b47] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x0b48] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x0b49] = XML_CHAR;
        XML_CHARTAB0[0x0b4a] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0b4b, 0x0b4e, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x0b4e, 0x0b56, XML_CHAR);
        XML_CHARTAB0[0x0b56] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x0b57] = XML_CHAR_NAME_EXT;
        Arrays.fill(XML_CHARTAB0, 0x0b58, 0x0b5c, XML_CHAR);
        XML_CHARTAB0[0x0b5c] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0b5d] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0b5e] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0b5f, 0x0b62, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x0b62, 0x0b66, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0b66, 0x0b70, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x0b70, 0x0b82, XML_CHAR);
        XML_CHARTAB0[0x0b82] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x0b83] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x0b84] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0b85, 0x0b8b, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x0b8b, 0x0b8e, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0b8e, 0x0b91, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0b91] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0b92, 0x0b96, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x0b96, 0x0b99, XML_CHAR);
        XML_CHARTAB0[0x0b99] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0b9a] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0b9b] = XML_CHAR;
        XML_CHARTAB0[0x0b9c] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0b9d] = XML_CHAR;
        XML_CHARTAB0[0x0b9e] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0b9f] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x0ba0, 0x0ba3, XML_CHAR);
        XML_CHARTAB0[0x0ba3] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0ba4] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x0ba5, 0x0ba8, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0ba8, 0x0bab, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x0bab, 0x0bae, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0bae, 0x0bb6, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0bb6] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0bb7, 0x0bba, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x0bba, 0x0bbe, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0bbe, 0x0bc3, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x0bc3, 0x0bc6, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0bc6, 0x0bc9, XML_CHAR_NAME_EXT);
        XML_CHARTAB0[0x0bc9] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0bca, 0x0bce, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x0bce, 0x0bd7, XML_CHAR);
        XML_CHARTAB0[0x0bd7] = XML_CHAR_NAME_EXT;
        Arrays.fill(XML_CHARTAB0, 0x0bd8, 0x0be7, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0be7, 0x0bf0, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x0bf0, 0x0c01, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0c01, 0x0c04, XML_CHAR_NAME_EXT);
        XML_CHARTAB0[0x0c04] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0c05, 0x0c0d, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0c0d] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0c0e, 0x0c11, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0c11] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0c12, 0x0c29, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0c29] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0c2a, 0x0c34, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0c34] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0c35, 0x0c3a, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x0c3a, 0x0c3e, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0c3e, 0x0c45, XML_CHAR_NAME_EXT);
        XML_CHARTAB0[0x0c45] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0c46, 0x0c49, XML_CHAR_NAME_EXT);
        XML_CHARTAB0[0x0c49] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0c4a, 0x0c4e, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x0c4e, 0x0c55, XML_CHAR);
        XML_CHARTAB0[0x0c55] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x0c56] = XML_CHAR_NAME_EXT;
        Arrays.fill(XML_CHARTAB0, 0x0c57, 0x0c60, XML_CHAR);
        XML_CHARTAB0[0x0c60] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0c61] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x0c62, 0x0c66, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0c66, 0x0c70, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x0c70, 0x0c82, XML_CHAR);
        XML_CHARTAB0[0x0c82] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x0c83] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x0c84] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0c85, 0x0c8d, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0c8d] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0c8e, 0x0c91, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0c91] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0c92, 0x0ca9, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0ca9] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0caa, 0x0cb4, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0cb4] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0cb5, 0x0cba, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x0cba, 0x0cbe, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0cbe, 0x0cc5, XML_CHAR_NAME_EXT);
        XML_CHARTAB0[0x0cc5] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0cc6, 0x0cc9, XML_CHAR_NAME_EXT);
        XML_CHARTAB0[0x0cc9] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0cca, 0x0cce, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x0cce, 0x0cd5, XML_CHAR);
        XML_CHARTAB0[0x0cd5] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x0cd6] = XML_CHAR_NAME_EXT;
        Arrays.fill(XML_CHARTAB0, 0x0cd7, 0x0cde, XML_CHAR);
        XML_CHARTAB0[0x0cde] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0cdf] = XML_CHAR;
        XML_CHARTAB0[0x0ce0] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0ce1] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x0ce2, 0x0ce6, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0ce6, 0x0cf0, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x0cf0, 0x0d02, XML_CHAR);
        XML_CHARTAB0[0x0d02] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x0d03] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x0d04] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0d05, 0x0d0d, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0d0d] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0d0e, 0x0d11, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0d11] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0d12, 0x0d29, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0d29] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0d2a, 0x0d3a, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x0d3a, 0x0d3e, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0d3e, 0x0d44, XML_CHAR_NAME_EXT);
        XML_CHARTAB0[0x0d44] = XML_CHAR;
        XML_CHARTAB0[0x0d45] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0d46, 0x0d49, XML_CHAR_NAME_EXT);
        XML_CHARTAB0[0x0d49] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0d4a, 0x0d4e, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x0d4e, 0x0d57, XML_CHAR);
        XML_CHARTAB0[0x0d57] = XML_CHAR_NAME_EXT;
        Arrays.fill(XML_CHARTAB0, 0x0d58, 0x0d60, XML_CHAR);
        XML_CHARTAB0[0x0d60] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0d61] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x0d62, 0x0d66, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0d66, 0x0d70, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x0d70, 0x0e01, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0e01, 0x0e2f, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0e2f] = XML_CHAR;
        XML_CHARTAB0[0x0e30] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0e31] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x0e32] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0e33] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x0e34, 0x0e3b, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x0e3b, 0x0e40, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0e40, 0x0e46, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x0e46, 0x0e4f, XML_CHAR_NAME_EXT);
        XML_CHARTAB0[0x0e4f] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0e50, 0x0e5a, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x0e5a, 0x0e81, XML_CHAR);
        XML_CHARTAB0[0x0e81] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0e82] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0e83] = XML_CHAR;
        XML_CHARTAB0[0x0e84] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0e85] = XML_CHAR;
        XML_CHARTAB0[0x0e86] = XML_CHAR;
        XML_CHARTAB0[0x0e87] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0e88] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0e89] = XML_CHAR;
        XML_CHARTAB0[0x0e8a] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0e8b] = XML_CHAR;
        XML_CHARTAB0[0x0e8c] = XML_CHAR;
        XML_CHARTAB0[0x0e8d] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x0e8e, 0x0e94, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0e94, 0x0e98, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0e98] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0e99, 0x0ea0, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0ea0] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0ea1, 0x0ea4, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0ea4] = XML_CHAR;
        XML_CHARTAB0[0x0ea5] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0ea6] = XML_CHAR;
        XML_CHARTAB0[0x0ea7] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0ea8] = XML_CHAR;
        XML_CHARTAB0[0x0ea9] = XML_CHAR;
        XML_CHARTAB0[0x0eaa] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0eab] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0eac] = XML_CHAR;
        XML_CHARTAB0[0x0ead] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0eae] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0eaf] = XML_CHAR;
        XML_CHARTAB0[0x0eb0] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0eb1] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x0eb2] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0eb3] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x0eb4, 0x0eba, XML_CHAR_NAME_EXT);
        XML_CHARTAB0[0x0eba] = XML_CHAR;
        XML_CHARTAB0[0x0ebb] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x0ebc] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x0ebd] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x0ebe] = XML_CHAR;
        XML_CHARTAB0[0x0ebf] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0ec0, 0x0ec5, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0ec5] = XML_CHAR;
        XML_CHARTAB0[0x0ec6] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x0ec7] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0ec8, 0x0ece, XML_CHAR_NAME_EXT);
        XML_CHARTAB0[0x0ece] = XML_CHAR;
        XML_CHARTAB0[0x0ecf] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0ed0, 0x0eda, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x0eda, 0x0f18, XML_CHAR);
        XML_CHARTAB0[0x0f18] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x0f19] = XML_CHAR_NAME_EXT;
        Arrays.fill(XML_CHARTAB0, 0x0f1a, 0x0f20, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0f20, 0x0f2a, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x0f2a, 0x0f35, XML_CHAR);
        XML_CHARTAB0[0x0f35] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x0f36] = XML_CHAR;
        XML_CHARTAB0[0x0f37] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x0f38] = XML_CHAR;
        XML_CHARTAB0[0x0f39] = XML_CHAR_NAME_EXT;
        Arrays.fill(XML_CHARTAB0, 0x0f3a, 0x0f3e, XML_CHAR);
        XML_CHARTAB0[0x0f3e] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x0f3f] = XML_CHAR_NAME_EXT;
        Arrays.fill(XML_CHARTAB0, 0x0f40, 0x0f48, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x0f48] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0f49, 0x0f6a, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x0f6a, 0x0f71, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0f71, 0x0f85, XML_CHAR_NAME_EXT);
        XML_CHARTAB0[0x0f85] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0f86, 0x0f8c, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x0f8c, 0x0f90, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0f90, 0x0f96, XML_CHAR_NAME_EXT);
        XML_CHARTAB0[0x0f96] = XML_CHAR;
        XML_CHARTAB0[0x0f97] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x0f98] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x0f99, 0x0fae, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x0fae, 0x0fb1, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x0fb1, 0x0fb8, XML_CHAR_NAME_EXT);
        XML_CHARTAB0[0x0fb8] = XML_CHAR;
        XML_CHARTAB0[0x0fb9] = XML_CHAR_NAME_EXT;
        Arrays.fill(XML_CHARTAB0, 0x0fba, 0x10a0, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x10a0, 0x10c6, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x10c6, 0x10d0, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x10d0, 0x10f7, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x10f7, 0x1100, XML_CHAR);
        XML_CHARTAB0[0x1100] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x1101] = XML_CHAR;
        XML_CHARTAB0[0x1102] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x1103] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x1104] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x1105, 0x1108, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x1108] = XML_CHAR;
        XML_CHARTAB0[0x1109] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x110a] = XML_CHAR;
        XML_CHARTAB0[0x110b] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x110c] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x110d] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x110e, 0x1113, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x1113, 0x113c, XML_CHAR);
        XML_CHARTAB0[0x113c] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x113d] = XML_CHAR;
        XML_CHARTAB0[0x113e] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x113f] = XML_CHAR;
        XML_CHARTAB0[0x1140] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x1141, 0x114c, XML_CHAR);
        XML_CHARTAB0[0x114c] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x114d] = XML_CHAR;
        XML_CHARTAB0[0x114e] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x114f] = XML_CHAR;
        XML_CHARTAB0[0x1150] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x1151, 0x1154, XML_CHAR);
        XML_CHARTAB0[0x1154] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x1155] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x1156, 0x1159, XML_CHAR);
        XML_CHARTAB0[0x1159] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x115a, 0x115f, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x115f, 0x1162, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x1162] = XML_CHAR;
        XML_CHARTAB0[0x1163] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x1164] = XML_CHAR;
        XML_CHARTAB0[0x1165] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x1166] = XML_CHAR;
        XML_CHARTAB0[0x1167] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x1168] = XML_CHAR;
        XML_CHARTAB0[0x1169] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x116a, 0x116d, XML_CHAR);
        XML_CHARTAB0[0x116d] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x116e] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x116f, 0x1172, XML_CHAR);
        XML_CHARTAB0[0x1172] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x1173] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x1174] = XML_CHAR;
        XML_CHARTAB0[0x1175] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x1176, 0x119e, XML_CHAR);
        XML_CHARTAB0[0x119e] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x119f, 0x11a8, XML_CHAR);
        XML_CHARTAB0[0x11a8] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x11a9] = XML_CHAR;
        XML_CHARTAB0[0x11aa] = XML_CHAR;
        XML_CHARTAB0[0x11ab] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x11ac] = XML_CHAR;
        XML_CHARTAB0[0x11ad] = XML_CHAR;
        XML_CHARTAB0[0x11ae] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x11af] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x11b0, 0x11b7, XML_CHAR);
        XML_CHARTAB0[0x11b7] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x11b8] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x11b9] = XML_CHAR;
        XML_CHARTAB0[0x11ba] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x11bb] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x11bc, 0x11c3, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x11c3, 0x11eb, XML_CHAR);
        XML_CHARTAB0[0x11eb] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x11ec, 0x11f0, XML_CHAR);
        XML_CHARTAB0[0x11f0] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x11f1, 0x11f9, XML_CHAR);
        XML_CHARTAB0[0x11f9] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x11fa, 0x1e00, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x1e00, 0x1e9c, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x1e9c, 0x1ea0, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x1ea0, 0x1efa, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x1efa, 0x1f00, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x1f00, 0x1f16, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x1f16] = XML_CHAR;
        XML_CHARTAB0[0x1f17] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x1f18, 0x1f1e, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x1f1e] = XML_CHAR;
        XML_CHARTAB0[0x1f1f] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x1f20, 0x1f46, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x1f46] = XML_CHAR;
        XML_CHARTAB0[0x1f47] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x1f48, 0x1f4e, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x1f4e] = XML_CHAR;
        XML_CHARTAB0[0x1f4f] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x1f50, 0x1f58, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x1f58] = XML_CHAR;
        XML_CHARTAB0[0x1f59] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x1f5a] = XML_CHAR;
        XML_CHARTAB0[0x1f5b] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x1f5c] = XML_CHAR;
        XML_CHARTAB0[0x1f5d] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x1f5e] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x1f5f, 0x1f7e, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x1f7e] = XML_CHAR;
        XML_CHARTAB0[0x1f7f] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x1f80, 0x1fb5, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x1fb5] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x1fb6, 0x1fbd, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x1fbd] = XML_CHAR;
        XML_CHARTAB0[0x1fbe] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x1fbf, 0x1fc2, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x1fc2, 0x1fc5, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x1fc5] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x1fc6, 0x1fcd, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x1fcd, 0x1fd0, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x1fd0, 0x1fd4, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x1fd4] = XML_CHAR;
        XML_CHARTAB0[0x1fd5] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x1fd6, 0x1fdc, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x1fdc, 0x1fe0, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x1fe0, 0x1fed, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x1fed, 0x1ff2, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x1ff2, 0x1ff5, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x1ff5] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x1ff6, 0x1ffd, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x1ffd, 0x20d0, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x20d0, 0x20dd, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x20dd, 0x20e1, XML_CHAR);
        XML_CHARTAB0[0x20e1] = XML_CHAR_NAME_EXT;
        Arrays.fill(XML_CHARTAB0, 0x20e2, 0x2126, XML_CHAR);
        XML_CHARTAB0[0x2126] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x2127, 0x212a, XML_CHAR);
        XML_CHARTAB0[0x212a] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x212b] = XML_CHAR_NAME_START;
        XML_CHARTAB0[0x212c] = XML_CHAR;
        XML_CHARTAB0[0x212d] = XML_CHAR;
        XML_CHARTAB0[0x212e] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x212f, 0x2180, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x2180, 0x2183, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x2183, 0x3005, XML_CHAR);
        XML_CHARTAB0[0x3005] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x3006] = XML_CHAR;
        XML_CHARTAB0[0x3007] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB0, 0x3008, 0x3021, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x3021, 0x302a, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x302a, 0x3030, XML_CHAR_NAME_EXT);
        XML_CHARTAB0[0x3030] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x3031, 0x3036, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x3036, 0x3041, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x3041, 0x3095, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x3095, 0x3099, XML_CHAR);
        XML_CHARTAB0[0x3099] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x309a] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x309b] = XML_CHAR;
        XML_CHARTAB0[0x309c] = XML_CHAR;
        XML_CHARTAB0[0x309d] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x309e] = XML_CHAR_NAME_EXT;
        XML_CHARTAB0[0x309f] = XML_CHAR;
        XML_CHARTAB0[0x30a0] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x30a1, 0x30fb, XML_CHAR_NAME_START);
        XML_CHARTAB0[0x30fb] = XML_CHAR;
        Arrays.fill(XML_CHARTAB0, 0x30fc, 0x30ff, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB0, 0x30ff, 0x3105, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x3105, 0x312d, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x312d, 0x4e00, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0x4e00, 0x9fa6, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0x9fa6, 0xac00, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0xac00, 0xd7a4, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB0, 0xd7a4, 0xd800, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0xe000, 0xfffe, XML_CHAR);
        Arrays.fill(XML_CHARTAB0, 0xe000, 0xfffd, XML_CHAR);

        // UTF16 character table of XML version 1.1
        // (default value is 0 -> illegal character)

        // Copy beginning part of XML_CHARTAB0 to XML_CHARTAB1
        // (0x0000 .. 0x0132  is equal to XML_CHARTAB0).
        System.arraycopy(XML_CHARTAB0, 0, XML_CHARTAB1, 0, 0x0132);
        Arrays.fill(XML_CHARTAB1, 0x0132, 0x0300, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB1, 0x0300, 0x0370, XML_CHAR_NAME_EXT);
        Arrays.fill(XML_CHARTAB1, 0x0370, 0x03d8, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB1, 0x03d8, 0x03f7, XML_CHAR);
        Arrays.fill(XML_CHARTAB1, 0x03f7, 0x2000, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB1, 0x2000, 0x200c, XML_CHAR);
        XML_CHARTAB1[0x200c] = XML_CHAR_NAME_START;
        XML_CHARTAB1[0x200d] = XML_CHAR_NAME_START;
        Arrays.fill(XML_CHARTAB1, 0x200e, 0x203f, XML_CHAR);
        XML_CHARTAB1[0x203f] = XML_CHAR_NAME_EXT;
        XML_CHARTAB1[0x2040] = XML_CHAR_NAME_EXT;
        Arrays.fill(XML_CHARTAB1, 0x2041, 0x2070, XML_CHAR);
        Arrays.fill(XML_CHARTAB1, 0x2070, 0x2190, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB1, 0x2190, 0x2c00, XML_CHAR);
        Arrays.fill(XML_CHARTAB1, 0x2c00, 0x2ff0, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB1, 0x2ff0, 0x3001, XML_CHAR);
        Arrays.fill(XML_CHARTAB1, 0x3001, 0xd800, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB1, 0xe000, 0xf900, XML_CHAR);
        Arrays.fill(XML_CHARTAB1, 0xf900, 0xfdd0, XML_CHAR_NAME_START);
        Arrays.fill(XML_CHARTAB1, 0xfdd0, 0xfdf0, XML_CHAR);
        Arrays.fill(XML_CHARTAB1, 0xfdf0, 0xfffe, XML_CHAR_NAME_START);
    }
// </editor-fold>

    /** Create new instance of StringParser */
    public StringParser() {}// all fields are set to null by Java VM

    /** Create new instance of StringParser.
     * @param source The string with source data.
     */
    public StringParser(final String source) {
        // all fields are set to null by Java VM
        setLineNumber(1L);
        _endPos = (_source = source).length();
        _ch = _endPos > 0 ? _source.charAt(0) : NOCHAR;
    }

    /** Create new instance of StringParser.
     * @param source The string with source data.
     * @param pos position in source data.
     */
    public StringParser(final String source, final int pos) {
        // all fields are set to null by Java VM
        setLineNumber(1L);
        _endPos = (_source = source).length();
        setIndex(pos);
    }

    /** Create new instance of StringParser.
     * @param source The source parsed data.
     */
    public StringParser(final SBuffer source) {
        // all fields are set to null by Java VM
        _source = source.getString();
        _endPos = _source.length();
        setFilePos(source.getFilePos() + source.getIndex());
        setLineNumber(source.getLineNumber());
        setStartLine(source.getStartLine());
        setSysId(source.getSysId());
        if (_lineInfo) {
            cloneModificationInfo(source);
        } else {
            clearModificationInfo();
        }
        _ch = _endPos > 0 ? _source.charAt(0) : NOCHAR;
    }

    /** Creates a new instance of StringParser.
     * @param reporter Report writer.
     */
    public StringParser(final ReportWriter reporter) {super(reporter);}//all fields are set to null by Java VM

    /** Creates a new instance of StringParser.
     * @param source The string with source data.
     * @param reporter Report writer.
     */
    public StringParser(final String source, final ReportWriter reporter) {
        super(reporter); // all fields are set to null by Java VM
        setLineNumber(1L);
        _endPos = (_source = source).length();
        _ch = _endPos > 0 ? _source.charAt(0) : NOCHAR;
    }
////////////////////////////////////////////////////////////////////////////////
    /** Max. size of source buffer for stream parser. */
    private static final int SOURCE_BUFFER_SIZE = 2048;
    /** Counter of nested keepBuffer methods. */
    private int _keepBufferCounter;
    /** Source reader. */
    protected Reader _reader;
    /** Buffer for reading of source*/
    private char[] _cbuf;
    /** Stack of parsers. */
    Stack<StringParser> _parserStack;

    /** Creates a new instance of StreamParser with source reader and reporter.
     * @param reader Source reader.
     * @param reporter Report writer connected to parser.
     * @throws SRuntimeException if an error occurs.
     */
    public StringParser(final Reader reader, final ReportWriter reporter) {
        super(reporter);
        setSourceReader(reader);
    }

    /** Creates a new instance of StreamParser with source reader and reporter.
     * @param url Source URL.
     * @param reporter Report writer connected to parser.
     * @param filePos RBA position of the start.
     * @throws SRuntimeException if an error occurs.
     */
    public StringParser(final URL url, final ReportWriter reporter, final long filePos) {
        super(reporter);
        try {
            URLConnection con = url.openConnection();
            setSourceReader(new InputStreamReader(con.getInputStream(), con.getContentEncoding()), filePos);
            setSysId(url.toExternalForm());
        } catch (IOException ex) {
            //URL &{0} error: &{1}{; }
            throw new SRuntimeException(SYS.SYS076, url.toExternalForm(), ex.getMessage());
        }
    }

    /** Creates a new instance of StreamParser with source reader and reporter.
     * @param reader Source reader.
     * @param reporter Report writer connected to parser.
     * @param filePos RBA position of the start.
     */
    public StringParser(final Reader reader, final ReportWriter reporter, final long filePos) {
        super(reporter);
        setSourceReader(reader, filePos);
    }

    /** Set source file.
     * @param file the file with source data.
     * @param charset Name of character table.
     * @throws SRuntimeException if an IO error occurs
     */
    final void setSourceReader(final File file, final String charset) {
        try {
            if (charset == null) {
                setSourceReader(new InputStreamReader(new FileInputStream(file)));
            } else {
                setSourceReader(new InputStreamReader(new FileInputStream(file),charset));
            }
            setSysId("file:" + file.getCanonicalPath().replace('\\','/'));
        } catch (UnsupportedEncodingException ex) {
            setSourceBuffer("");
            throw new SRuntimeException(SYS.SYS035, charset); //Unsupported character set name: &{0}
        } catch (IOException ex) {
            setSourceBuffer("");
            throw new SRuntimeException(SYS.SYS024, file); //File doesn't exist: &{0}
        }
    }

    /** Set input from URL.
     * @param url The input url.
     * @throws SRuntimeException if error occurs
     */
    final void setSourceReader(final URL url) {
        String s = url.toExternalForm();
        try {
            URLConnection con = url.openConnection();
            setSourceReader(new InputStreamReader(con.getInputStream(), con.getContentEncoding()));
            setSysId(s);
        } catch (IOException ex) {
            setSourceBuffer("");
            throw new SRuntimeException(SYS.SYS076, s,ex); //URL &{0} error: &{1}{; }
        }
    }

    /** Set source reader.
     * @param reader Input reader.
     * @throws SRuntimeException if an error occurs.
     */
    private void setSourceReader(final Reader reader) {setSourceReader(reader, 0L, null);}

    /** Set source reader.
     * @param reader Input reader.
     * @param sourceName the name of source file.
     * @throws SRuntimeException if an error occurs.
     */
    final void setSourceReader(final Reader reader, final String sourceName) {
        setSourceReader(reader, 0L, null);
        setSysId(sourceName);
    }

    /** Set source reader.
     * @param reader Input reader.
     * @param pos Starting position of the file.
     * @throws SRuntimeException if an error occurs.
     */
    private void setSourceReader(final Reader reader, final long pos) {setSourceReader(reader, pos, null);}

    /** Set source reader with reader and initial buffer.
     * @param reader The reader.
     * @param pos Starting position of the file.
     * @param source Initial source buffer.
     */
    public final void setSourceReader(final Reader reader, final long pos, final String source) {
        setLineNumber(1L);
        setFilePos(pos);
        setStartLine(getFilePos());
        clearModificationInfo();
        _keepBufferCounter = 0;
        setBuffer(source);
        if (_reader != null) {
            try {
                _reader.close();
            } catch (IOException ex) {}
        }
        _reader = reader;
        if (_cbuf == null) {
            _cbuf = new char[SOURCE_BUFFER_SIZE];
        }
        if (pos > 0) {
            try {
                _reader.skip(pos);
            } catch (IOException ex) {
                throw new SRuntimeException(SYS.SYS036, STester.printThrowable(ex));//Program exception&{0}{:}
            }
        }
        setIndex(0);
        if (_endPos == 0) {
            nextChar();
        }
    }

    /** Close source reader (if exists). */
    public final void closeReader() {
        _keepBufferCounter = 0;
        if (_reader != null) {
            if (_closeReader) {
                try {_reader.close();} catch (IOException ex) {}//ignore it
            }
            _reader = null;
        }
    }

    /** Push keepBuffer. */
    private void keepBuffer() {
        if (_reader != null) {
            _keepBufferCounter++;
            ensureBuffer(10);
        }
    }

    /** Pop freeBuffer. */
    public final void freeBuffer() {
        if (_reader != null) {
            _keepBufferCounter--;
        }
    }

    /** Fill up buffer from input stream (if available).
     * @return true if data are available in buffer.
     * @throws SRuntimeException if an error occurs
     */
    private boolean readNextBuffer() {
        if (_reader == null) {
            if (getIndex() < _endPos) {
                return true;
            }
            super.setIndex(_endPos);
            if (_parserStack == null || _parserStack.size() <= 0) {
                return false;
            }
            return popParser();
        }
        if (_keepBufferCounter > 0) {
            return ensureBuffer(10);
        }
        int len;
        try {
            if (getIndex() > _endPos) {
                super.setIndex(_endPos);
            }
            if ((len = _reader.read(_cbuf)) <= 0) {
                int count = 0;
                while (len == 0) {//some streams may return length == 0 ???
                    if (count++ > 1000) { //IO error detected on &{0}&{1}{, reason: }
                        throw new SIOException(SYS.SYS034, getSystemId(), "block length=0");
                    }
                    try {
                        sleepwile();
                    } catch (InterruptedException ex) {//IO error detected on &{0}&{1}{, reason: }
                        throw new SIOException(SYS.SYS034, getSystemId(), ex);
                    }
                    len = _reader.read(_cbuf);
                }
                if (len < 0) {
                    closeReader();
                    if (getIndex() < _endPos) {
                        return true;
                    }
                    super.setIndex(_endPos);
                    if (_parserStack == null || _parserStack.size() <= 0) {
                        return false;
                    }
                    return popParser();
                }
            }
            int remains;
            if ((remains = _endPos - getIndex()) > 0) {
                setEndBuffer(remains + len);
                if (len == _cbuf.length) {
                    setBuffer(new StringBuilder(_endPos).append(getBuffer()
                        .substring(getIndex(), getIndex() + remains)).append(_cbuf).toString());
                } else {
                    setBuffer(new StringBuilder(_endPos).append(getBuffer()
                        .substring(getIndex(), getIndex() + remains)).append(_cbuf, 0, len).toString());
                }
            } else {
                setBuffer(len == _cbuf.length ? new String(_cbuf) : new String(_cbuf, 0, len));
            }
            setFilePos(getFilePos() + getIndex());
            setIndex(0);
            return true;
        } catch (IOException ex) { //this should never happen
            closeReader();
            throw new SRuntimeException(SYS.SYS036, STester.printThrowable(ex));//Program exception&{0}{: }
        }
    }

    private void sleepwile() throws InterruptedException {Thread.sleep(5);}

    /** Increase buffer for next character.
     * @return true if next character is available in buffer.
     */
    public final boolean increaseBuffer() {
        if (getIndex() + 1 < _endPos) {
            return true;
        }
        return ensureBuffer(1);
    }

    /** Increase buffer length for given number of characters.
     * @param len length to be ensured.
     * @return true if buffer was correctly increased.
     */
    private boolean ensureBuffer(final int len) {
        int n;
        if ((n = getIndex() + len) <= _endPos) {
            return true;
        }
        if (getIndex() > _endPos) {
            super.setIndex(_endPos);
        }
        if (_reader == null) {
            return false;
        }
        StringBuilder sb = new StringBuilder(n).append(getBuffer());
        do {
            try {
                int c;
                if ((c = _reader.read()) >= 0) {
                    sb.append((char) c); //append character
                    setEndBuffer(_endPos + 1); //increase buffer end
                } else { // end of stream
                    closeReader();
                    int pos = getIndex(); //save position
                    if (popParser()) {
                        if (getIndex() < _endPos) {// append remaining buffer part
                            sb.append(getUnparsedBufferPart());
                            setEndBuffer(sb.length()); // update buffer length
                            super.setIndex(pos);
                            if (n <= _endPos) { // finished
                                break;
                            }
                            continue;
                        }
                        super.setIndex(pos); //restore bufffer position
                        continue;
                    }
                    super.setIndex(pos); //restore bufffer position
                    break; // we have no more parsers.
                }
            } catch (IOException ex) {//this should never happen
                setBuffer(sb.toString());
                closeReader();
                throw new SRuntimeException(SYS.SYS036, STester.printThrowable(ex));//Program exception&{0}{:}
            }
        } while (n > _endPos);
        setBuffer(sb.toString());
        return getIndex() < _endPos;
    }

    /** Get previous parser from stack.
     * @return true if parser has something to be parsed.
     */
    public final boolean popParser() {
        while (_parserStack != null && !_parserStack.isEmpty()) {
            StringParser parser  = _parserStack.pop();
            _keepBufferCounter = parser._keepBufferCounter;
            setReportWriter(parser.getReportWriter());
            setFilePos(parser.getFilePos());
            setStartLine(parser.getStartLine());
            setLineNumber(parser.getLineNumber());
            setBuffer(parser.getBuffer());
            setIndex(parser.getIndex());
            setSysId(parser.getSysId());
            copyModificationInfo(parser);
            setLineInfoFlag(parser.isLineInfoFlag());
            _reader = parser._reader;
            if (getIndex() < _endPos) {
                return true;
            }
        }
        return false;
    }

    /** Save actual parser to stack.
     * @param savedObject SParserSavedObject to be saved.
     */
    public final void pushParser(final SParserSavedObject savedObject) {
        if (_parserStack == null) {
            _parserStack = new Stack<>();
        } else if (_parserStack.size() > 64) {//too many nested includes
            fatal(SYS.SYS033);//Parser can't continue; too many nested includes
            return;
        }
        StringParser parser = new StringParser();
        parser._keepBufferCounter = _keepBufferCounter;
        parser.setReportWriter(getReportWriter());
        parser.setFilePos(getFilePos());
        parser.setStartLine(getStartLine());
        parser.setLineNumber(getLineNumber());
        parser.setBuffer(getBuffer());
        parser.setIndex(getIndex());
        parser.setSysId(getSysId());
        parser.copyModificationInfo(this);
        parser.setLineInfoFlag(isLineInfoFlag());
        parser._reader = _reader;
        _parserStack.push(parser);
        setLineNumber(0L);
        setStartLine(0L);
        setFilePos(0L);
        setSysId(null);
        setBuffer("");
        setIndex(0);
        clearModificationInfo();
        _reader = null;
    }

    /** Get size of internal parser stack.
     * @return size of internal parser stack.
     */
    public final int getParserStackSize() {return _parserStack == null ? 0 : _parserStack.size();}

    /** Save old parser and set new source buffer.
     * @param reader reader with source data.
     * @param pos Starting position of the file.
     * @param source Initial source buffer.
     * @param sourceName Name of source (may be null).
     * @param savedObject SParserSavedObject with saved parameters or null.
    */
    public final void pushSourceReader(final Reader reader,
        final int pos,
        final String source,
        final String sourceName,
        final SParserSavedObject savedObject) {
        pushParser(savedObject);
        setLineNumber(1L);
        setStartLine(getIndex());
        setFilePos(pos);
        setSysId(sourceName);
        setBuffer(source);
        setIndex(0);
    }

    /** Save old parser and set new source buffer.
     * @param in The reader.
     * @param srcName Name of source (may be null).
     * @param obj SParserSavedObject with saved parameters or null.
    */
    public final void pushSourceReader(final Reader in, final String srcName, final SParserSavedObject obj) {
        pushSourceReader(in, 0, null, srcName, obj);
    }

    /** Set the parser to create detailed line positions. By default the value of this is set to false.
     * @param lineInfo set the lineInfo flag.
     */
    public final void setLineInfoFlag(final boolean lineInfo) {_lineInfo = lineInfo;}

    /** Check if the detailed line positions are required.
     * @return true if and only if the detailed line positions are required.
     */
    public final boolean isLineInfoFlag() {return _lineInfo;}

    /** Get part of source buffer from given position to end.
     * @param pos starting position.
     * @return string with part of the source buffer from index to end.
     */
    public final String getBufferPartFrom(final int pos) {
        return pos < _endPos ? _source.substring(pos, _endPos) : "";
    }

    /** Increase buffer index.
     * @return new buffer index or -1 if position exceeds buffer.
     */
    public final int incBufIndex() {
        int pos = incIndex();
        if (pos < _endPos) {
            if ((_ch = _source.charAt(pos)) == '\n') {
                setNewLine();
            }
            return pos;
        } else {
            super.setIndex(_endPos);
            _ch = NOCHAR;
            return -1;
        }
    }

    @Override
    /** Get end index of source buffer.
     * @return end index of source buffer.
     */
    public int endPos() {return _endPos;}

    @Override
    /** Prepare character from the next position.
     * @return next character or NOCHAR if the position is at the end of source.
     */
    public final char nextChar() {
        int index = incIndex();
        return _ch = index <_endPos ? _source.charAt(index)
            : readNextBuffer() ? _source.charAt(getIndex()) : NOCHAR;
    }

    @Override
    /** Get character at the current position and set position to the next character in the source. If
     * the position is at the end of source return NOCHAR.
     * @return character at the current position or if the position is at the end of source return NOCHAR.
     */
    public final char peekChar() {
        char result;
        if ((result = _ch) == '\n') {
            setNewLine();
        }
        nextChar();
        return result;
    }

    @Override
    /** Get the character on actual position.
     * @return character on actual position or NOCHAR.
     */
    public final char getCurrentChar() {return _ch;}

    /** Get character at the given position in the source buffer
     * @param pos position of required character in the source buffer.
     * @return required character or <code>NOCHAR</code>.
     */
    public final char getCharAtPos(int pos) {return pos < _endPos ? _source.charAt(pos) : NOCHAR;}

    @Override
    /** Set source buffer position.
     * @param index position to be set.
     */
    public final void setIndex(final int index) {
        if (index >= _endPos) {
            _ch = NOCHAR;
            super.setIndex(_endPos);
        } else {
            super.setIndex(index);
            _ch = _source.charAt(index);
        }
    }

    /** Check if position is in buffer.
     * @return true if and only if position is in buffer.
     */
    public final boolean chkBufferIndex() {return getIndex() < _endPos;}

    /** Get actual SPosition of parsed source.
     * @return actual source position.
     */
    public final SPosition getPosition() {return new SPosition(this);}

    /** Reset buffer position to the previous position from parameter. If buffer was changed this method
     * throws an exception. The position MUST be a position previously generated by this parser and
     * MUST NOT be modified!
     * @param pos a position previously generated by this parser.
     * @throws SRuntimeException if position can't be set.
     */
    public final void resetPosition(SPosition pos) {
        int x = pos.getIndex();
        long filePos = pos.getFilePos();
        String sysId = pos.getSysId();
        String mySysId = getSysId();
        if (x > _endPos || filePos != getFilePos() ||
            mySysId != null && !mySysId.equals(sysId) ||
            mySysId == null && sysId != null) {
            throw new SRuntimeException(SYS.SYS081, x);//Can't set this buffer position for this parser&{0}{:}
        }
        setLineNumber(pos.getLineNumber());
        setStartLine(pos.getStartLine());
        copyModificationInfo(pos);
        setIndex(x);
    }

    /** Set switch if reader should be closed.
     * @param x if true, then reader is closed at the end of parsing.
     */
    public final void setCloseReader(boolean x) {_closeReader = x;}

    @Override
    /** Get value of parsed item as string.
     * @return value of parsed item as string.
     */
    public final String getParsedString() { return _parsedString; }

    /** Set parsed string. This value will be returned by method getParsedString().
     * @param s string to be set as parsed string.
     */
    public final void setParsedString(final String s){_parsedString = s;}

    /** Get SDuration object with value of parsed duration.
     * @return SDuration object with parsed values or null.
     */
    public final SDuration getParsedSDuration() {return _parsedDuration;}

    /** Get SDatetime object with value of parsed date.
     * @return SDatetime object with parsed values or null.
     */
    public final SDatetime getParsedSDatetime() {
        return _parsedDatetime == null ? null : new SDatetime(_parsedDatetime);
    }

    /** Get value of parsed date. Returns instance of Calendar with parsed
     * values. Values which were not parsed are set to zero.
     * @return Calendar with parsed values.
     * @throws SRuntimeException SYS072 Data error
     */
    public final Calendar getParsedCalendar() {
        return _parsedDatetime == null ? null : _parsedDatetime.getCalendar();
    }

    @Override
    /** Check if actual position points to a character in given interval. Set the actual position to the next
     * character if given character was in the specified interval.
     * @param minCh minimum of checked interval.
     * @param maxCh maximum of checked interval.
     * @return the actual character character from actual position, otherwise return NOCHAR.
     */
    public final char isInInterval(final char minCh, final char maxCh) {
        if (_ch < minCh || _ch > maxCh) {
            return NOCHAR;
        }
        char c;
        if ((c = _ch) == '\n') {
            setNewLine();
        }
        nextChar();
        return c;
    }

    @Override
    /** Check if actual position points to a character out of given interval. Set the actual position to the
     * next character if given character was recognized.
     * @param minCh minimum of checked interval.
     * @param maxCh maximum of checked interval.
     * @return the actual character character from actual position, otherwise return NOCHAR.
     */
    public final char notInInterval(final char minCh, final char maxCh) {
        if (_ch >= minCh && _ch <= maxCh) {
            return NOCHAR;
        }
        char c;
        if ((c = _ch) == '\n') {
            setNewLine();
        }
        nextChar();
        return c;
    }

    /** Check if actual position points to given character ignoring case. Set the actual position to the next
     * character if given character was not in specified interval.
     * @param ch Character to be checked.
     * @return true if character was present at actual position, otherwise return false.
     */
    public final boolean isCharIgnoreCase(final char ch) {
        if (Character.toLowerCase(_ch)!=Character.toLowerCase(ch)) {
            return false;
        }
        nextChar();
        return true;
    }

    @Override
    /** If character on actual position is not character specified by argument the method returns the actual
     * character and sets position to next character. Otherwise returns NOCHAR and position remains unchanged.
     * @param ch Character to be checked.
     * @return character on actual position or NOCHAR.
     */
    public final char notChar(final char ch) {
        if (_ch == ch) {
            return NOCHAR;
        }
        char c;
        if ((c = _ch) == '\n') {
            setNewLine();
        }
        nextChar();
        return c;
    }

    /** Check if actual position points to new line. If check is successful* set the actual position
     * to the next character.
     * @return true if new line is at actual position, otherwise return false.
     */
    public final boolean isNewLine() {
        if (_ch != '\n')  {
            return false;
        }
        setNewLine();
        nextChar();
        return true;
    }

    /** If character on actual position is not one of characters specified by given string argument
     * the method returns the actual character and sets position to next character. Otherwise it returns
     * NOCHAR and actual position remains unchanged.
     * @param chars String with set of characters to be checked.
     * @return character on actual position or NOCHAR.
     */
    public final char notOneOfChars(final String chars) {
        if (_ch == NOCHAR || chars.indexOf(_ch) >= 0) {
            return NOCHAR;
        }
        char c;
        if ((c = _ch) == '\n') {
            setNewLine();
        }
        nextChar();
        return c;
    }

    @Override
    /** Check if actual position points to upper case letter. Set the source position to the next character
     * if letter was recognized and return the character otherwise return NOCHAR and source position
     * remains unchanged.
     * @return character or NOCHAR.
     */
    public final char isUpperCaseLetter() {
        if (Character.isLetter(_ch) && _ch == Character.toUpperCase(_ch)) {
            char c = _ch;
            nextChar();
            return c;
        }
        return NOCHAR;
    }

    @Override
    /** Check if actual position points to lower case letter. Set the source position to the next character
     * if letter was recognized and return the character otherwise return NOCHAR and source position
     * remains unchanged.
     * @return character or NOCHAR.
     */
    public final char isLowerCaseLetter() {
        if (Character.isLetter(_ch) && _ch == Character.toLowerCase(_ch)) {
            char c = _ch;
            nextChar();
            return c;
        }
        return NOCHAR;
    }

    @Override
    /** If on source position is one of tokens specified in the argument the method return index to this item
     * and set position to the next position after a token. Otherwise it return -1.
     * @param tokens Array of tokens be checked.
     * @return Index of found token or -1.
     */
    public final int isOneOfTokens(final String... tokens) {
        int result = -1, len = -1;
        int pos = getIndex();
        for (int i = 0; i < tokens.length; i++) {
            int tlen;
            if (ensureBuffer(tlen = tokens[i].length()) && _source.startsWith(tokens[i], pos)) {
                if (tlen > len) {
                    result = i;
                    len = tokens[i].length();
                }
            }
        }
        if (result != -1) {
            ensureBuffer(len+1);
            super.setIndex(pos += len);
            _ch = pos + len < _endPos || readNextBuffer() ? _source.charAt(getIndex()) : NOCHAR;
        }
        return result;
    }

    @Override
    /** If on source position is one of tokens (ignored case) specified in the argument the method return
     * index to this item and sets position to the next position after a token. Otherwise it returns -1.
     * @param tokens Array of tokens be checked.
     * @return Index of found token or -1.
     */
    public final int isOneOfTokensIgnoreCase(final String... tokens) {
        int result = -1, len = -1;
        int pos = getIndex();
        for (int i = 0; i < tokens.length; i++) {
            String token = tokens[i];
            int tlen = token.length();
            if (ensureBuffer(tlen) && token.equalsIgnoreCase(_source.substring(pos, pos + tlen))) {
                if (tlen > len) {
                    result = i;
                    len = tlen;
                }
            }
        }
        if (result != -1) {
            ensureBuffer(len+1);
            super.setIndex(pos += len);
            _ch = pos + len < _endPos || readNextBuffer() ? _source.charAt(getIndex()) : NOCHAR;
        }
        return result;
    }

    @Override
    /** Check if actual position points to given token (case insensitive). Set the actual position
     * to the next character after the token if given token was recognized.
     * @param token The token to be checked case insensitive.
     * @return <i>true</i> if token was present at actual position.
     */
    public final boolean isTokenIgnoreCase(final String token) {
        if (Character.toLowerCase(_ch) ==
            Character.toLowerCase(token.charAt(0))) {
            int len = token.length();
            int pos = getIndex();
            if (pos + len <= _endPos || ensureBuffer(len)) {
                if (token.equalsIgnoreCase(_source.substring(pos, pos + len))) {
                    ensureBuffer(len+1);
                    super.setIndex(pos += len);
                    _ch = pos<_endPos || readNextBuffer() ? _source.charAt(getIndex()) : NOCHAR;
                    return true;
                }
            }
        }
        return false;
    }

    @Override
    /** Check if actual position points to signed integer number. Set the actual position to the
     * next character after the number if number was recognized.
     * @return true if signed integer was parsed, otherwise return false.
     */
    public final boolean isSignedInteger() {
        if (getIndex() + 1 >= _endPos && !readNextBuffer()) {
            return false;
        }
        int i = getIndex();
        keepBuffer();
        int startToken = i;
        isOneOfChars("+-");
        if (isInteger()) {
            _parsedString = _source.substring(startToken, getIndex());
            freeBuffer();
            return true;
        }
        super.setIndex(startToken);
        _ch = _source.charAt(startToken);
        freeBuffer();
        return false;
    }

    @Override
    /** Check if actual position points to signed float number. Set the actual position to the next character
     * after the number if number was recognized.
     * @return true if number was parsed, otherwise return false.
     */
    public final boolean isSignedFloat() {
        if (getIndex() + 2 >= _endPos && !readNextBuffer()) {
            return false;
        }
        int i = getIndex();
        keepBuffer();
        int startToken = i;
        isOneOfChars("+-");
        if (isFloat()) {
            _parsedString = _source.substring(startToken, getIndex());
            freeBuffer();
            return true;
        }
        super.setIndex(startToken);
        _ch = _source.charAt(startToken);
        freeBuffer();
        return false;
    }

    /** Set position to first character of next line or to the end od source.
     * @return true if and only if next line was reached.
     */
    public final boolean skipToNextLine() {
        if (_ch == '\n') {
            setNewLine();
        }
        while (incIndex() < _endPos || readNextBuffer()) {
            if (_source.charAt(getIndex()) == '\n') {
                setNewLine();
                break;
            }
        }
        if (incIndex() < _endPos || readNextBuffer()) {
            _ch = _source.charAt(getIndex());
            return true;
        }
        super.setIndex(_endPos);
        _ch = NOCHAR;
        return false;
    }

    @Override
    /** Skip to specified character. The position is set to the found character. Return true and set position
     * to the position of character. Otherwise return false and set position to the end of source.
     * @param ch Character to be searched for.
     * @return <i>true</i> if the character was found.
     */
    public final boolean findChar(final char ch) {
        if (_ch == ch) {
            return true;
        }
        while (incIndex() < _endPos || readNextBuffer()) {
            if ((_ch = _source.charAt(getIndex())) == ch) {
                return true;
            }
            if (_ch == '\n') {
                setNewLine();
            }
        }
        super.setIndex(_endPos);
        _ch = NOCHAR;
        return false;
    }

    /** Skip to next character after the argument. The position is set after the found character. Return true
     * and set position after the position of character. Otherwise return false and set position to the end.
     * @param ch Character to be searched for.
     * @return true if the character was found.
     */
    public final boolean findCharAndSkip(final char ch) {
        if (_ch == '\n') {
            setNewLine();
        }
        if (_ch == ch) {
            nextChar();
            return true;
        }
        while (incIndex() < _endPos || readNextBuffer()) {
            char c = _source.charAt(getIndex());
            if (c == '\n') {
                setNewLine();
            }
            if (c == ch) {
                nextChar();
                return true;
            }
        }
        super.setIndex(_endPos);
        _ch = NOCHAR;
        return false;
    }

    @Override
    /** Skip to first occurrence of one of specified character set. The position is set to the found
     * character. Return the character from the list of characters if the character was found, otherwise
     * return NOCHAR and set the position to the end of source.
     * @param chars String with set of characters.
     * @return found character or NOCHAR.
     */
    public final char findOneOfChars(final String chars) {
        while (getIndex() < _endPos || readNextBuffer()) {
            if (chars.indexOf(_ch = _source.charAt(getIndex())) >= 0) {
                return _ch;
            }
            incIndex();
            if (_ch == '\n') {
                setNewLine();
            }
        }
        super.setIndex(_endPos);
        return NOCHAR;
    }

    /** Skip to next character after the first occurrence of one of specified character set. Return the
     * character from the list of characters if the character was found, otherwise return NOCHAR
     * and set position to the end of source.
     * @param chars String with set of characters.
     * @return found character or NOCHAR.
     */
    public final char findOneOfCharsAndSkip(final String chars) {
        while (getIndex() < _endPos || readNextBuffer()) {
            char c;
            if ((c = _source.charAt(getIndex())) == '\n') {
                setNewLine();
            }
            int i = incIndex();
            if (chars.indexOf(c) >= 0) {
                _ch = i <_endPos || readNextBuffer() ? _source.charAt(getIndex()) : NOCHAR;
                return c;
            }
        }
        super.setIndex(_endPos);
        return NOCHAR;
    }

    @Override
    /** Find token. If the token was found returns true and sets position to the position of token.
     * Otherwise return false and set position to the end of source.
     * @param token Token to be found.
     * @return true if the token was found.
     */
    public final boolean findToken(final String token) {
        int len = token.length();
        char c = token.charAt(0);
        while (getIndex() + len <= _endPos || readNextBuffer()) {
            if (!ensureBuffer(len)) {
                break;
            }
            for (int j = getIndex(); j + len <= _endPos; j++) {
            id:
                if ((_ch = _source.charAt(j)) == c) {
                    for (int i = 1; i < len; i++) {
                        if ((_ch = _source.charAt(i+j)) != token.charAt(i)) {
                            break id;
                        }
                    }
                    ensureBuffer(len+1);
                    setIndex(j);
                    return true;
                }
                if (_ch == '\n') {
                    setNewLine();
                }
            }
            nextChar();
        }
        super.setIndex(_endPos);
        _ch = NOCHAR;
        return false;
    }

    /** Find token in source text and set position to next character after the found token.
     * Return true if the token was found, otherwise return false and sets position to the end of source.
     * @param token Token to be found.
     * @return true if the token was found.
     */
    public final boolean findTokenAndSkip(final String token) {
        if (findToken(token)) {
            int len = token.length();
            ensureBuffer(len+1);
            setIndex(getIndex() + len);
            return true;
        }
        return false;
    }

    /** Get end of buffer (first not existing position).
     * @return the end of buffer (first not existing position).
     */
    public final int getEndBufferIndex() { return _endPos; }

    /** Get parsed part of source buffer from starting position to the actual position.
     * @return string with parsed part of the source buffer from beginning.
     */
    public String getParsedBufferPart() {
        int x = getIndex();
        return x < _endPos ? _source.substring(0, x) : _source;
    }

    @Override
    /** Get parsed part of source buffer from given position to actual position.
     * @param index starting position.
     * @return string with parsed part of the source buffer starting from index.
     */
    public final String getParsedBufferPartFrom(final int index) {
        int x = getIndex();
        return index<x ? _source.substring(index, x<_endPos ? x : _endPos) : "";
    }

    @Override
    /** Get unparsed part of source buffer.
     * @return string with unparsed part of source buffer.
     */
    public final String getUnparsedBufferPart() {
        int x = getIndex();
        return x < _endPos ? _source.substring(x, _endPos) : "";
    }

    @Override
    /** Get part of source buffer from given position to end.
     * @param start starting position.
     * @param end ending position.
     * @return string with part of the source buffer from index to end.
     */
    public final String getBufferPart(final int start, final int end) {
        return start < _endPos ? _source.substring(start, end < _endPos ? end : _endPos) : "";
    }

    @Override
    /** Set position to the end of source data. */
    public final void setEos() {
        super.setIndex(_endPos);
        closeReader();
        _ch = NOCHAR;
    }

    @Override
    /** Get source buffer.
     * @return string with source buffer.
     */
    public final String getSourceBuffer() {return _source;}

    @Override
    /** Set source buffer.
     * @param source Source buffer.
     */
    public final void setSourceBuffer(final String source) {
        super.setIndex(0);
        _endPos = (_source = source).length();
        setFilePos(0L);
        setStartLine(0L);
        setLineNumber(1L);
        setSysId(null);
        clearModificationInfo();
        closeReader();
        _ch = _endPos > 0 ? _source.charAt(0) : NOCHAR;
    }

    /** Set source buffer and set position to value from argument.
     * @param source Parsed string.
     */
    public final void setSourceBuffer(final SBuffer source) {
        super.setIndex(0);
        _endPos = (_source = source.getString()).length();
        setFilePos(source.getFilePos());
        setLineNumber(source.getLineNumber());
        setStartLine(source.getStartLine());
        setSysId(source.getSysId());
        if (_lineInfo) {
            cloneModificationInfo(source);
        } else {
            clearModificationInfo();
        }
        closeReader();
        _ch = _endPos > 0 ? _source.charAt(0) : NOCHAR;
    }

    /** Change source buffer and position info.
     * @param pos position where to make change.
     * @param len number of characters to be changed.
     * @param replace string with the replacement data.
     * @param fixed if true one position will be used for whole range.
     */
    public final void changeBuffer(final int pos, final int len, final String replace, final boolean fixed) {
        int newLength = replace == null ? 0 : replace.length();
        if (newLength == 0) {
            if (len > 0) { //delete
                if (isLineInfoFlag()) {
                    updatePositions(pos, len, 0, fixed);
                }
                _endPos = (_source=new StringBuilder(_source).delete(pos, pos+len).toString()).length();
            }
        } else {
            if (isLineInfoFlag()) {
                updatePositions(pos, len, newLength, fixed);
            }
            _endPos = (_source=new StringBuilder(_source).replace(pos, pos+len, replace).toString()).length();
        }
        setIndex(pos);
    }

    /** Get value of parsed integer.
     * @return the parsed integer.
     * @throws SRuntimeException SYS072 Data error
     */
    public final int getParsedInt() {
        try {
            return Integer.parseInt(_parsedString.charAt(0)=='+' ? _parsedString.substring(1):_parsedString);
        } catch(NumberFormatException ex) {
            throw new SRuntimeException(SYS.SYS072, ex); //Data error&{0}{: }
        }
    }

    /** Get value of parsed integer as long.
     * @return Parsed long.
     * @throws SRuntimeException SYS072 Data error
     */
    public final long getParsedLong() {
        try {
            return Long.parseLong(_parsedString.charAt(0) == '+' ? _parsedString.substring(1):_parsedString);
        } catch(NumberFormatException ex) {
            throw new SRuntimeException(SYS.SYS072, ex);//Data error&{0}{: }
        }
    }

    /** Get value of parsed float.
     * @return Parsed float.
     * @throws SRuntimeException SYS072 Data error
     */
    public final float getParsedFloat() throws SRuntimeException {
        try {
            return Float.parseFloat(_parsedString.charAt(0)=='+' ? _parsedString.substring(1):_parsedString);
        } catch(NumberFormatException ex) {
            throw new SRuntimeException(SYS.SYS072, ex); //Data error&{0}{: }

        }
    }

    /** Get value of parsed float number as double.
     * @return Parsed double.
     * @throws SRuntimeException SYS072 Data error
     */
    public final double getParsedDouble() throws SRuntimeException {
        try {
            return Double.parseDouble(_parsedString.charAt(0)=='+'? _parsedString.substring(1):_parsedString);
        } catch(NumberFormatException ex) {
            throw new SRuntimeException(SYS.SYS072, ex); //Data error&{0}{: }
        }
    }

    /** Get value of parsed float number as BigInteger.
     * @return Parsed BigInteger.
     * @throws SRuntimeException SYS072 Data error
     */
    public final BigInteger getParsedBigIteger() throws SRuntimeException {
        try {
            return new BigInteger(_parsedString.charAt(0)=='+' ? _parsedString.substring(1) : _parsedString);
        } catch(Exception ex) {
            throw new SRuntimeException(SYS.SYS072, ex); //Data error&{0}{: }
        }
    }

    /** Get value of parsed float number as BigDecimal.
     * @return Parsed BigDecimal.
     * @throws SRuntimeException SYS072 Data error
     */
    public final BigDecimal getParsedDecimal() throws SRuntimeException {
        try {
            return new BigDecimal(_parsedString.charAt(0)=='+' ? _parsedString.substring(1) : _parsedString);
        } catch(Exception ex) {
            throw new SRuntimeException(SYS.SYS072, ex); //Data error&{0}{: }
        }
    }

    /** Check if parsed datetime is valid.
     * @return true if parsed date is OK.
     */
    public boolean testParsedDatetime() {return _parsedDatetime != null && _parsedDatetime.check();}

    @Override
    /** Check if parser reached the end of source.
     * @return true if parser reached the end of source, otherwise return false.
     */
    public final boolean eos() {return (getIndex()>=_endPos&&!readNextBuffer());}

    /** Get character from buffer index.
     * @param index index to source buffer.
     * @return character or <code>NOCHAR</code>.
     */
    public final char charFromBuffer(final int index) {return index<_endPos ? _source.charAt(index) : NOCHAR;}

    @Override
    /** Parse white space.
     * @return true if white space was present at actual position, otherwise return false.
     */
    public final boolean isSpace() {
        if (XML_CHARTAB0[_ch] != XML_CHAR_WHITESPACE) {
            return false;
        }
        if (_ch == '\n') {
            setNewLine();
        }
        nextChar();
        return true;
    }

    @Override
    /** Parse one or more white spaces.
     * @return true if white spaces were present at actual positio otherwise return false.
     */
    public final boolean isSpaces() {
        if (XML_CHARTAB0[_ch] != XML_CHAR_WHITESPACE) {
            return false;
        }
        if (_ch == '\n') {
            setNewLine();
        }
        if (incIndex() < _endPos || readNextBuffer()) {
            while (XML_CHARTAB0[_ch = _source.charAt(getIndex())] == XML_CHAR_WHITESPACE) {
                if (_ch == '\n') {
                    setNewLine();
                }
                if (incIndex() >= _endPos && !readNextBuffer()) {
                    _ch = NOCHAR;
                    break;
                }
            }
        } else {
            _ch = NOCHAR;
        }
        return true;
    }

    @Override
    /** Check if actual position points to given character. Set the actual position to the next character
     * if given character was recognized.
     * @param ch Character to be checked.
     * @return true if character was present at actual position, otherwise return false.
     */
    public final boolean isChar(final char ch) {
        if (_ch != ch)  {
            return false;
        }
        nextChar();
        return true;
    }

    @Override
    /** If actual character is one of characters specified in given string the method returns this character
     * and sets position to the next character. Otherwise return NOCHAR.
     * @param chars String with characters to be checked.
     * @return the actual character or NOCHAR.
     */
    public final char isOneOfChars(final String chars) {
        if (chars.indexOf(_ch) < 0) {
            return NOCHAR;
        }
        char c;
        if ((c = _ch) == '\n') {
            setNewLine();
        }
        nextChar();
        return c;
    }

    @Override
    /** Check if at the current position is a digit.
     * @return digital value of digit or -1;
     */
    public final int isDigit() {
        if (_ch >= '0' && _ch <= '9') {
            int i = _ch - '0';
            nextChar();
            return i;
        }
        return -1;
    }

    @Override
    /** Check if actual position points to letter. Set the source position to the next character if letter
     * was recognized and return the character otherwise return NOCHAR and source position remains unchanged.
     * @return character or NOCHAR.
     */
    public final char isLetter() {
        if (Character.isLetter(_ch)) {
            char c = _ch;
            nextChar();
            return c;
        }
        return NOCHAR;
    }

    @Override
    /** Check if actual position points to letter or digit. Set the source position to the next character
     * if letter was recognized and return the character otherwise return NOCHAR and source position
     * remains unchanged.
     * @return character or NOCHAR.
     */
    public final char isLetterOrDigit() {
        if (Character.isLetterOrDigit(_ch)) {
            char c = _ch;
            nextChar();
            return c;
        }
        return NOCHAR;
    }

    @Override
    /** Check if at the current position is Emoji character.
     * @return digital value of digit or -1;
     */
    public final char isEmoji() {
        if (isEmoji(String.valueOf(_ch))) {
            char c = _ch;
            nextChar();
            return c;
        }
        return NOCHAR;
    }

    @Override
    /** Check if actual position points to given token. Set the actual position to the next character after
     * the token if given token was recognized.
     * @param token The token.
     * @return true if token was present at actual position.
     */
    public final boolean isToken(final String token) {
        if (_ch == token.charAt(0)) {
            int len;
            if (getIndex() + (len =  token.length()) <= _endPos || ensureBuffer(len)){
                int x = getIndex();
                if (!_source.startsWith(token, x)) {
                    return false;
                }
                ensureBuffer(len+1);
                setIndex(getIndex() + len);
                return true;
            }
        }
        return false;
    }

    @Override
    /** Parse integer number (ASCII digits '0' .. '9'). Set the actual position to the next character after
     * the number.
     * @return true if and only if integer was parsed.
     */
    public final boolean isInteger() {
        if (_ch > '9' || _ch < '0') {
            return false;
        }
        keepBuffer();
        int startToken = getIndex();
        while(_ch >= '0' && _ch <= '9') {
            nextChar();
        }
        int x = getIndex();
        _parsedString = _source.substring(startToken, x);
        _ch = x < _endPos || readNextBuffer()?_source.charAt(getIndex()):NOCHAR;
        freeBuffer();
        return true;
    }

    @Override
    /** Parse unsigned float number. Set actual position to the next character after the number if number
     * was recognized.
     * @return true if and only if float number was parsed.
     */
    public final boolean isFloat() {
        if (getIndex() + 2 >= _endPos && !readNextBuffer()) {
            return false;
        }
        int x = getIndex();
        keepBuffer();
        int startToken = x;
        if (!isInteger()) {
            if (_ch != '.' && _ch != 'e' && _ch != 'E') {
                freeBuffer();
                return false;
            }
        }
        if (isFloatPart()) {
            _parsedString = _source.substring(startToken, getIndex());
            freeBuffer();
            return true;
        }
        super.setIndex(startToken);
        _ch = _source.charAt(startToken);
        freeBuffer();
        return false;
    }

    /** Check if actual position points to part of float number. Set the actual
     * position to the next character after the number if number part was recognized.
     * @return true if number was parsed, otherwise return false.
     */
    private boolean isFloatPart() {
        if (getIndex() >= _endPos) {
            return false;
        }
        int x = getIndex();
        boolean wasDecPoint;
        if (wasDecPoint = isChar('.')) {
            while(_ch >= '0' && _ch <= '9') {
                nextChar();
            }
            x = getIndex();
            _ch = x < _endPos || readNextBuffer() ? _source.charAt(x = getIndex()) : NOCHAR;
        }
        if (isOneOfChars("eE") > 0) {//exponent
            isOneOfChars("+-");
            if (_ch < '0' || _ch > '9') {
                setIndex(x); //missing number after exponent; reset position x
                return wasDecPoint;
            }
            while(_ch >= '0' && _ch <= '9') {
                nextChar();
            }
            return true;
        }
        return wasDecPoint;
    }

////////////////////////////////////////////////////////////////////////////////
// parsing of date and time
////////////////////////////////////////////////////////////////////////////////

    /** Parse date in ISO8601 format (see <a href = "http://www.w3.org/TR/NOTE-datetime">
     * www.w3.org/TR/NOTE-datetime</a>).
     * @return true if date on current position suits to ISO8601 date format.
     */
    public final boolean isISO8601Date() {
        return isDatetime("yyyy-MM-dd[Z]|yyyy-DDD[Z]|yyyy-'W'w-e[Z]|yyyy'W'wwe[Z]");
    }

    /** Parse time in ISO8601 format (see <a href = "http://www.w3.org/TR/NOTE-datetime">
     * www.w3.org/TR/NOTE-datetime</a>).
     * @return true if date on current position suits to ISO8601 time format.
     */
    public final boolean isISO8601Time() {return isDatetime("HH:mm[:ss[.S]][Z]");}

    /** Parse date according to RFC822 (email format). (see
     * <a href = "http://www.w3.org/Protocols/rfc822/">rfc822</a>).
     * @return true if date on current position suits to RFC822 date and time format.
     */
    public final boolean isRFC822Datetime() {
        return isDatetime(
            "[EEE, ]d MMM yyyy HH:mm[:ss][ ZZZZZ][ (z)]|[EEE, ]d MMM YY HH:mm[:ss][ ZZZZZ][ (z)]");
    }

    /** Parse date in the printable form.
     * @return true if date on current position is in printable form.
     */
    public final boolean isPrintableDatetime() {
        return isDatetime("[EEE ]MMM d HH:mm[:ss[.S]][ ZZZZZ] y|{L(*)}[EEE ]MMM d HH:mm[:ss[.S]][ ZZZZZ] y");
    }

    /** Parse date in ISO8601 date or date and time (see <a href = "http://www.w3.org/TR/NOTE-datetime">
     * www.w3.org/TR/NOTE-datetime</a>).
     * @return true if date on current position suits to ISO8601 date and time format.
     */
    public final boolean isISO8601Datetime() {
        return isDatetime("yyyy-MM-dd['T'HH:mm[:ss[.S]][Z]]" + "|yyyy-DDD['T'HH:mm[:ss[.S]][Z]]" +
            "|yyyy-'W'w-e['T'HH:mm[:ss[.S]][Z]]" + "|yyyy'W'wwe['T'HH:mm[:ss[.S]]][Z]" +
            "|--MM[-dd][Z]" + //month or month day
            "|---dd[Z]"+ //day
            "|yyyy[-MM][Z]"+ // year or year momth
            "|HH:mm[:ss[.S]][Z]]"); //time
    }

    /** Parse ISO8601 date and time format (see <a href = "http://www.w3.org/TR/NOTE-datetime">
     * www.w3.org/TR/NOTE-datetime</a>).
     * @return true if date on current position suits to ISO8601 date format.
     */
    public final boolean isISO8601DateAndTime() {
        return isDatetime("yyyy-MM-dd'T'HH:mm[:ss[.S]][Z]" + "|yyyy-DDD'T'HH:mm[:ss[.S]][Z]" +
            "|yyyy-'W'w-e'T'HH:mm[:ss[.S]][Z]" + "|yyyy'W'wwe'T'HH:mm[:ss[.S]][Z]");
    }

    /** Parse date and/or time. Argument is string with format mask where characters are as follows:
     * <br><b>a</b> AM/PM marker
     * <br><b>D</b> day in year
     * <br><b>d</b> day of month (1 through 31)
     * <br><b>E</b> day of week (text)
     * <p> E, EE, EEE - abbreviated day name (Mon, Tue, .. Sun
     * <p> EEEE (and more)- full month name (Monday, Tuesday, .. Sunday
     * <br><b>e</b> day of week (number 1=Monday, 7=Sunday)
     * <br><b>F</b> day of week in month
     * <br><b>G</b> era (0=BC, 1=AD)
     * <br><b>H</b> hour (0 through 23)
     * <br><b>h</b> hour (1..12 with am/pm)
     * <br><b>K</b> hour 0..11 with am/pm)
     * <br><b>k</b> hour 1..23
     * <br><b>M</b> month in year (1=January .. 12=December).
     * <p> M - number without leading zero
     * <p> MM - number with leading zero
     * <p> MMM - abbreviated month name (Jan, Feb, .. Dec
     * <p> MMMM (and more)- full month name (January, February, .. December
     * <br><b>m</b> minute (0 through 59)
     * <br><b>s</b> second (0 through 59, may be 60)
     * <br><b>S</b> digits representing a decimal fraction of a second
     * <br><b>y</b> year
     * <br><b>Y</b> year (ISO variant, including negative numbers and leading zeroes)
     * <br><b>YY</b> year (two digits specification, century is added from the actual year)
     * <br><b>RR</b> year - two digits specification, century is added according to ORACLE specification:
     * <p>Let c is century of actual date and y is number representing the year in actual century.
     * Let r be the specified two-digit year; then
     * <p>if 00 &lt;= y &lt;= 49 and 00 &lt;= r &lt;= 49 then result = c * 100 + r
     * <p>if 00 &lt;= y &lt;= 49 and 50 &lt;= r &lt;= 99 then result = (c + 1) * 100 + r
     * <p>if 50 &lt;= y &lt;= 99 and 00 &lt;= r &lt;= 49 then result = (c - 1) * 100 + r
     * <p>if 50 &lt;= y &lt;= 99 and 50 &lt;= r &lt;= 99 then result = c * 100 + r
     * <br><b>W</b> week in month
     * <br><b>w</b> week in year
     * <br><b>Z</b> time zone designator (Z or +hh:mm or -hh:mm)
     * <p><b>ZZ</b> time zone designator (Z or +h:m or -h:m)
     * <p><b>ZZZZZ</b> time zone designator (Z or +hhmm or -hhmm)
     * <p><b>ZZZZZZ</b> time zone designator (Z or +hh:mm or -hh:mm)
     * <br><b>z</b> zone name
     * <p><b>z, zz, zzz</b> abbreviated zone name (CET)
     * <p><b>zzzz</b> and more full zone name (Central European Time)
     * <p>One occurrence of above characters represent number without leading zeroes. Repeated characters
     * M,d,H,h,m and s represents n-digit number with leading zeroes. However, the only year specifications
     * 'y', 'yy' and 'yyyy' are permitted, otherwise  the exception SYS059 is thrown.
     * <p>Other characters are interpreted as strings which must match. If the mask should describe one of
     * above characters it must be quoted in apostrophes. The character apostrophe itself must be doubled.
     * <p>Parts of mask in square brackets are optional (e.g.:
     * <p><b>"HH:mm[:ss[.SSS]][z]"</b>
     * <p>describes time specification where fields with seconds, milliseconds or zone are optional).
     * <p>Variants of mask are separated by bar character '|' (e.g.
     * <p><b>"yyyyMMdd|EEE, d MMM y H:m:s"</b>
     * <p>allows to specify date in format of number or in "unix" format). Specification of default
     * (predefined) value is in {} brackets and must precede each variant specification (e.g.:
     * <p><b>"{H8m30}[HH:mm]ss|{H8m30}yyyyMMdd[HH:mm]"</b>
     * - if hours and minutes are not specified the value is set to 08:30).</p>
     * @param mask String with date format.
     * @return true if the date source on current position suits to given format.
     * @throws SRuntimeException if mask format is incorrect:
     * <br>SYS059 incorrect year specification
     * <br>SYS049 unclosed quoted literal
     */
    public final boolean isDatetime(final String mask) throws SRuntimeException{
        if (getIndex() + 10 >= _endPos && !readNextBuffer()) {
            return false;
        }
        MyDate myDate = new MyDate(getIndex());
        keepBuffer();
        int startPos = getIndex();
        int flen = mask.length();
        //(year,month,date,hour,minute,second)
        int fpos = 0;
        boolean failVariant = false; //true if variant fails
        int i;
        char hourKind = ' ';
        int ampm = -1;
        int era = 0;
        List<MyDate> _variants = null;
    loop:
        while (fpos < flen || failVariant) {
            char pat;
            if (failVariant) {
                if (myDate._chain != null) {
                    setIndex(myDate._startPos);
                    //(year,month,date,hour,minute,second)
                    myDate = myDate._chain;
                    while (fpos < flen) {
                        if ((pat = mask.charAt(fpos++)) == ']') {//reset
                            failVariant = false;
                            continue loop;
                        } else if (pat == '\'' || pat == '"') {// literal
                            while (fpos + 1 < flen) {
                                if (mask.charAt(fpos++) == pat) {
                                    if (fpos < flen && mask.charAt(fpos) == pat) {
                                        fpos++;
                                        continue;
                                    }
                                    break;
                                }
                            }
                        }
                    }
                } else {
                    setIndex(startPos); //reset position
                    while (fpos < flen) {
                        if ((pat = mask.charAt(fpos++)) == '|') {// reset
                            myDate.reset(startPos);
                            failVariant = false;
                            break;
                        } else if (pat == '\'') {// literal
                            fpos++;
                            while (fpos < flen) {
                                if (mask.charAt(fpos++) == '\'') {
                                    if (fpos < flen && mask.charAt(fpos) == '\'') {
                                        fpos++;
                                        continue;
                                    }
                                    break;
                                }
                            }
                        }
                    }
                    if (fpos < flen) {
                        continue;
                    } else {
                        if (_variants == null) {
                            freeBuffer();
                            return false;
                        }
                        break;
                    }
                }
            }
            int n;
            pat = mask.charAt(fpos++);
            if (fpos >= flen || pat == '\'' || mask.charAt(fpos) != pat) {
                n = 0; //one character of specified format item
            } else {
                n = fpos - 1;
                do {
                    fpos++;
                } while (fpos < flen && mask.charAt(fpos) == pat);
                n = fpos - n;
            }
            switch (pat) {
                case '|': //variant part
                    if (_variants == null) {
                        _variants = new ArrayList<>();
                    }
                    MyDate md = new MyDate(myDate);
                    md._endPos = getIndex();
                    _variants.add(md);
                    myDate.reset(startPos);
                    setIndex(myDate._startPos);
                    continue;
                case '{': //initialize part
                    myDate.reset(startPos); //???
                    StringParser p = new StringParser(mask, fpos);
                    Report r = p.checkDateFormatDefaults();
                    if (r != null) {
                        throw new SRuntimeException(r); // error in mask
                    }
                    fpos = p.getIndex();
                    myDate.setDefaults(p._parsedDatetime);
                    continue;
                case '[': //optional part
                    myDate = new MyDate(myDate, getIndex());
                    continue;
                case ']': //optional part
                    if (myDate._chain != null) {
                        myDate._chain = null;
                    }
                    continue;
                case 'D': //day in year
                    if ((i = readUnsignedIntSpecifiedLength(n)) < 0) {
                        failVariant = true;
                        continue;
                    }
                    myDate._yearDay = i;
                    continue;
                case 'F': //day of week in month
                    if ((i = readUnsignedIntSpecifiedLength(n)) < 0) {
                        failVariant = true;
                        continue;
                    }
                    myDate._dayOfWeekInMonth = i;
                    continue;
                case 'w': //week in year
                    if ((i = readUnsignedIntSpecifiedLength(n)) < 0) {
                        failVariant = true;
                        continue;
                    }
                    myDate._weekInYear = i;
                    continue;
                case 'W': //week in month
                    if ((i = readUnsignedIntSpecifiedLength(n)) < 0) {
                        failVariant = true;
                        continue;
                    }
                    myDate._weekInMonth = i;
                    continue;
                case 'G': //era designator (BC, AD)
                    i = isOneOfTokens(myDate.getLocaleFormatSymbols().getEras());
                    if (i < 0) { //error
                        failVariant = true;
                        continue;
                    }
                    era = i == 0 ? -1 : 1;
                    continue;
                case 'd': //day
                    if ((i = readUnsignedIntSpecifiedLength(n)) < 0) {
                        failVariant = true;
                        continue;
                    }
                    myDate._day = i;
                    continue;
                case 'M': //month in year
                    i = n < 3 ? readUnsignedIntSpecifiedLength(n)
                        : n == 3 ? isOneOfTokens(myDate.getLocaleFormatSymbols().getShortMonths())
                        : isOneOfTokens(myDate.getLocaleFormatSymbols().getMonths());
                    if (i < 0) {
                        failVariant = true;
                        continue;
                    }
                    if ((n >= 3)) {
                        i++;
                    }
                    myDate._month = i;
                    continue;
                case 'm': //minute
                    if ((i = readUnsignedIntSpecifiedLength(n)) < 0) {
                        failVariant = true;
                        continue;
                    }
                    myDate._minute = i;
                    continue;
                case 's': //second
                    if ((i = readUnsignedIntSpecifiedLength(n)) < 0) {
                        failVariant = true;
                        continue;
                    }
                    myDate._second = i;
                    continue;
                case 'S': {//millisecond
                    if (_ch < '0' || _ch > '9') {
                        failVariant = true;
                        continue;
                    }
                    i = _ch - '0';
                    nextChar();
                    double fraction =  i / 10.0;
                    double exp = 10.0;
                    int j = 0;
                    while (n == 0 || ++j < n) {
                        if (_ch < '0' || _ch > '9') {
                            if (n != 0 ) {
                                failVariant = true;
                                continue loop;
                            }
                            break;
                        }
                        int k = _ch - '0';
                        nextChar();
                        fraction += k / (exp *= 10.0);
                    }
                    myDate._fraction = fraction;
                    continue;
                }
                case 'a': { //AM/PM marker
                    int pm = isOneOfTokens(myDate.getLocaleFormatSymbols().getAmPmStrings());
                    if (pm < 0) { //error
                        failVariant = true;
                        continue;
                    }
                    ampm = pm;
                    continue;
                }
                case 'E': //day of week (text)
                    i = (n > 3) ? isOneOfTokens(myDate.getLocaleFormatSymbols().getWeekdays())
                        : isOneOfTokens(myDate.getLocaleFormatSymbols().getShortWeekdays());
                    if (i < 0) {//error
                        failVariant = true;
                        continue;
                    }
                    myDate._weekDay = i;
                    continue;
                case 'e': //day of week (number)
                    if ((i = readUnsignedIntSpecifiedLength(n)) < 0) {
                        failVariant = true;
                        continue;
                    }
                    switch (i) {
                        case 1: myDate._weekDay = Calendar.MONDAY; continue;
                        case 2: myDate._weekDay = Calendar.TUESDAY; continue;
                        case 3: myDate._weekDay = Calendar.WEDNESDAY; continue;
                        case 4: myDate._weekDay = Calendar.THURSDAY; continue;
                        case 5: myDate._weekDay = Calendar.FRIDAY; continue;
                        case 6: myDate._weekDay = Calendar.SATURDAY; continue;
                        case 7: myDate._weekDay = Calendar.SUNDAY; continue;
                    }
                    failVariant = true;
                    continue;
                case 'K': //hour 0..11
                case 'k': //hour in day (1..24)
                case 'H': //hour in day (0..23)
                case 'h': //hour in am/pm (1..12)
                    if ((i = readUnsignedIntSpecifiedLength(n)) < 0) {
                        failVariant = true;
                        continue;
                    }
                    if ((hourKind = pat) == 'h' && i > 12) {
                        failVariant = true;
                        continue;
                    }
                    myDate._hour = i;
                    continue;
                case 'Y': //year - two digits, century from the actual year
                    if (n != 0 && n != 2) {
                        freeBuffer();
                        // Datetime mask: incorrect year specification&{0}{, position: }
                        throw new SRuntimeException(SYS.SYS059,	fpos-3);
                    }
                    boolean minus;
                    if (minus = _ch == '-') {
                        nextChar();
                    }
                    char first = _ch;
                    int pos = getIndex();
                    if ((i = readUnsignedIntSpecifiedLength(0)) < 0) {
                        failVariant = true;
                        continue;
                    }
                    if (n == 2) { //actual century
                        int c = new GregorianCalendar().get(Calendar.YEAR) / 100;
                        myDate._year = c * 100 + i;
                    } else if (pos - getIndex() > 4 && first == '0') {
                        failVariant = true;
                        continue;
                    } else {
                        myDate._year = minus ? -i : i;
                    }
                    continue;
                case 'y': //year
                    if (n == 3) {
                        freeBuffer();
                        //Datetime mask: incorrect year specification&{0}{, position: }
                        throw new SRuntimeException(SYS.SYS059, fpos-3);
                    }
                    int sign = 1;
                    if (n == 4 || n == 0) {
                        if ((sign = _ch == '-' ? -1 : 1) == -1) {
                            nextChar();
                        }
                        int q = (fpos >= flen || "MD".indexOf(mask.charAt(fpos)) < 0) ? 0 : n;
                        int k = getIndex();
                        i = readUnsignedIntSpecifiedLength(q);
                        if (i >= 0 && n == 4 && getIndex() - k < 4) {
                            i = -1;
                        }
                    } else {
                        i = readUnsignedIntSpecifiedLength(n);
                    }
                    if (i < 0) {
                        failVariant = true;
                        continue;
                    }
                    if (n == 2 && i < 100) { // this is a nasty feature
                        int century = new GregorianCalendar().get(Calendar.YEAR);
                        int year = century % 100; // actual year in century;
                        century /= 100; // actual century
                        // decrease century if the actual year in in century is lower then the parsed value.
                        i += ((year < i) ? century - 1 : century) * 100;
                    }
                    myDate._year = i * sign;
                    continue;
                case 'R': {//year - two digits (database)
/*
  Two digits year (see Oracle format). Century is generated according to following rules:
  If RR is from the interval  00 .. 49 then
  a) if last two digits of the actual year are 00..49 then the century is taken from the actual year.
  b) if last two digits of the actual year are 50..99 then the century is taken from the actual year
     increased by 1.
  If RR is from the interval  50 .. 99 then
  c) if last two digits of the actual year are 00..49 then the century is taken
     from the actual year decreased by 1.
  d) if last two digits of the actual year are 50..99 then the century is taken from the actual year.
*/
                    if (n != 2) {
                        //SYS059=Datetime mask: incorrect year specification &{0}{, position: }
                        throw new SRuntimeException(SYS.SYS059, fpos-3);
                    }
                    if ((i = readUnsignedIntSpecifiedLength(n)) < 0) {
                        failVariant = true;
                        continue;
                    }
                    int y = new GregorianCalendar().get(Calendar.YEAR);
                    int c = y /100; //actual century
                    y = y % 100; //last two digits of the actual year actual years in the actual century < 50
                    myDate._year = i < 50 ? y < 50 ? c * 100 + i : ((c + 1) * 100 + i)
                        : y < 50 ? (c - 1) * 100 + i : (c * 100 + i);
                    continue;
                }
                case 'Z': //ISO
                case 'z': {//RFC822 zone
                    if (n != 0 && n != 2 && n != 5 && n != 6) {
                        freeBuffer();
                        //SYS050=Datetime mask: incorrect zone format &{0}{, position: }
                        throw new SRuntimeException(SYS.SYS050, fpos-n);
                    }
                    TimeZone tz = readTimeZone(n, pat);
                    if (tz != null) {
                        myDate._tz = tz;
                        continue;
                    } else {
                        failVariant = true; //not parsed
                        continue;
                    }
                }
                case '"':
                case '\'': {//quoted string
                    char delim = pat;
                    int beg = fpos - 1;
                    while (fpos < flen) {
                        if ((pat = mask.charAt(fpos++)) == delim) {
                            if (fpos >= flen || mask.charAt(fpos) != delim) {
                                break;
                            }
                            pat = mask.charAt(fpos++); //double apos
                        }
                        if (_ch == pat) {
                            nextChar();
                        } else {
                            while (fpos < flen) {
                                if (mask.charAt(fpos++) == delim) {
                                    if (fpos < flen && mask.charAt(fpos) == delim) {
                                        fpos++;
                                        continue;
                                    }
                                    failVariant = true;
                                    continue loop;
                                }
                            }
                            pat = ' ';
                            break;
                        }
                    }
                    if (pat != delim) {
                        freeBuffer();
                        //Datetime mask: unclosed quoted literal&{0}{, position: }
                        throw new SRuntimeException(SYS.SYS049, (beg+1));
                    }
                    continue;
                }
                case '?': {//one of chars
                    char delim;
                    if (fpos < flen && ((delim=mask.charAt(fpos))=='\'' || delim == '"')) {
                        //follows string specification
                        int beg = fpos++;
                        boolean foundChar = false;
                        while (fpos < flen) {
                            if ((pat = mask.charAt(fpos++)) == delim) {
                                if (fpos >= flen || mask.charAt(fpos) != delim) {
                                    break;
                                }
                                pat = mask.charAt(fpos++); //double apos
                            }
                            if (!foundChar && _ch == pat) {
                                foundChar = true;
                                do {
                                    if (_ch != pat) {
                                        foundChar = false;
                                    } else {
                                        _ch = incIndex() < _endPos ? _source.charAt(getIndex()) : NOCHAR;
                                    }
                                } while (--n > 0);
                            }
                        }
                        if (pat != delim) {
                            freeBuffer();
                            //SYS049=Datetime mask: unclosed quoted literal&{0}{, position: }
                            throw new SRuntimeException(SYS.SYS049, beg);
                        }
                        if (foundChar) {
                            continue;
                        } else {
                            failVariant = true;
                            continue;
                        }
                    }
                    do {
                        if (_ch != pat) {
                            failVariant = true;
                        } else {
                            _ch = incIndex() < _endPos ? _source.charAt(getIndex()) : NOCHAR;
                        }
                    } while (--n > 0);
                    continue;
                }
                default:
                    do {
                        if (_ch != pat) {
                            failVariant = true;
                            break;
                        } else {
                            nextChar();
                        }
                    } while (--n > 0);
            }
        }
        if (_variants != null) {
            MyDate x = myDate;
            x._endPos = getIndex();
            int endPos = 0;
            _variants.add(x);
            for (MyDate y: _variants) {
                if (y._endPos > endPos) {
                    endPos = y._endPos;
                    x = y;
                }
            }
            myDate = x;
            setIndex(myDate._endPos);
        }
        _parsedString = _source.substring(startPos, getIndex());
        if (ampm == 1 && (hourKind == 'h' || hourKind == 'K')) {
            myDate._hour += 12;
        }
        if (myDate._hour == 24 &&
            (hourKind == 'h' || hourKind == 'k')) {
            myDate._hour = 0;
        }
        if (era < 0) {
            myDate._year = - myDate._year;
        }
        if (myDate._year != Integer.MIN_VALUE &&
            myDate._month == Integer.MIN_VALUE &&
            myDate._day == Integer.MIN_VALUE) {
            if (myDate._yearDay != Integer.MIN_VALUE) {
                //compute month and date from year day
                GregorianCalendar gc = new GregorianCalendar(myDate._year, 0, 1);
                gc.add(Calendar.DAY_OF_YEAR, myDate._yearDay - 1);
                myDate._yearDay = Integer.MIN_VALUE;
                myDate._year = gc.get(Calendar.YEAR);
                myDate._month = gc.get(Calendar.MONTH) + 1;
                myDate._day = gc.get(Calendar.DAY_OF_MONTH);
            } else if (myDate._weekInYear!=Integer.MIN_VALUE && myDate._weekDay!=Integer.MIN_VALUE){
                //compute month and date from week and day
                GregorianCalendar gc = new GregorianCalendar(myDate._year, 0, 1);
                if (Calendar.SUNDAY == myDate._weekDay) {
                    myDate._weekInYear++;
                }
                //Week is between 1 to 52 (or 53 the last week in year)
                gc.add(Calendar.WEEK_OF_YEAR, myDate._weekInYear -1);
                gc.add(Calendar.DATE, myDate._weekDay - gc.get(Calendar.DAY_OF_WEEK));
                myDate._weekInYear = Integer.MIN_VALUE;
                myDate._weekDay = Integer.MIN_VALUE;
                myDate._year = gc.get(Calendar.YEAR);
                myDate._month = gc.get(Calendar.MONTH) + 1;
                myDate._day = gc.get(Calendar.DAY_OF_MONTH);
            }
        }
        freeBuffer();
        _parsedDatetime = myDate;
        return true;
    }

    /** Read from source an unsigned integer with specified number of digits. If the argument with number
     * of digits is 0 the number of digits is considered as unspecified. If on the current source position
     * is not required integer number the method returns value of Integer.MIN_VALUE.
     * @param digits Required number of digits.
     * @return Parsed integer value of unsigned integer or Integer.MIN_VALUE.
     */
    private int readUnsignedIntSpecifiedLength(final int digits) {
        int result;
        if ((result = isDigit()) >= 0) {
            int i;
            if (digits == 0) {
                while((i = isDigit()) >= 0) {
                    result = result * 10 + i;
                }
            } else {
                int ndigits = digits;
                while (--ndigits > 0) {
                    if ((i = isDigit()) < 0) {
                        return Integer.MIN_VALUE;
                    }
                    result = result * 10 + i;
                }
            }
            return result;
        }
        return Integer.MIN_VALUE;
    }

    private TimeZone readTimeZone(final int n, final char pat) {
        if (pat == 'Z' && isChar('Z') && (n == 0 || n == 6)) { //Z => UTC+00:00
            return TimeZone.getTimeZone("UTC");
        }
        char ch;
        if ((ch = isOneOfChars("+-")) == NOCHAR && (pat == 'Z' && (n > 0 && n < 6) || pat == 'z')) {
            //zone name
            ParsePosition pp = new ParsePosition(getIndex());
            boolean zoneOffsetSpec = false;
            new SimpleDateFormat(SUtils.makeStringOfChars(n==0 ? 1 : n, 'z')).parse(_source, pp);
            int newPos = pp.getIndex();
            if (newPos != getIndex()) {
                String s = _source.substring(getIndex(), pp.getIndex());
                int ndx;
                if ((ndx=s.indexOf('+')) > 0 || (ndx=s.indexOf('-')) > 0) {
                    ch = s.charAt(ndx);
                    newPos = getIndex() + ndx;
                    zoneOffsetSpec = true;
                }
            } else if (n==2 && pat == 'Z') { // zz -> try TO FIND zone id
                String id = null;
                TimeZone tz = null;
                int pos = getIndex(); // position
                for (String t : TimeZone.getAvailableIDs()) { // find zone id
                    TimeZone x = TimeZone.getTimeZone(t);
                    t = x.getID();
                    if ((id == null || t.length() > id.length()) && isToken(t)) {
                        id = t; // zone id found.
                        tz = x; // save TimeZone
                        setIndex(pos); // reset position for next test
                    }
                }
                if (id == null) {
                    return null; //zone name not found
                }
                isToken(id);
                return tz;
            } else {
                return null; //not parsed
            }
            String s = _source.substring(getIndex(), newPos);
            TimeZone tz = TimeZone.getTimeZone(("CEST".equals(s) ? "CET" : s));
            if (tz == null || "GMT".equals(tz.getID()) && !(s.equalsIgnoreCase("GMT")
                || s.equalsIgnoreCase("UTC"))) {
                return null; // error ?????
            }
            // parsed OK
            if (!zoneOffsetSpec) {
                setIndex(newPos);
                return tz;
            }
            //after UTC or GMT we still try to read offset!
            setIndex(newPos + 1);
        }
        if (eos() || ch == NOCHAR) {
            return null; //'+' or '-' expected
        }
        boolean minus = ch == '-';
        int j;
        int i;
        if (pat == 'Z' && n == 6) {
            i = readUnsignedIntSpecifiedLength(2);
            if (!isChar(':')) {
                return null; // expected explicit zone (+-)HH:mm
            }
            j = readUnsignedIntSpecifiedLength(2);
        } else if (n == 5) { // expected explicit zone (+-)HHmm
            i = readUnsignedIntSpecifiedLength(2);
            j = readUnsignedIntSpecifiedLength(2);
        } else {
            int pos = getIndex();
            int nn = pat == 'Z' ? 2 : 0;
            i = readUnsignedIntSpecifiedLength(nn);
            if (i < 0) {
                return null; //number expected
            }
            if (isChar(':')) {
                if (getIndex() - pos > 3 || eos()) {
                    return null; //number expected after ':'
                }
                pos = getIndex();
                if ((j = readUnsignedIntSpecifiedLength(nn)) < 0 || getIndex() - pos > 2) {
                    return null; //number expected
                }
            } else {
                if (n != 0 && n != 4) {
                    return null; // ':' required
                }
                if ((j = getIndex() - pos) < 3 || j > 4) {
                    return null; //if not colon then 3 or 4 digits required!!
                }
                j = i % 100; // j was 3 or 4 -> HHmm; we divide it
                i /= 100;
            }
        }
        if (i == Integer.MIN_VALUE || j == Integer.MIN_VALUE || i > 14 || j > 59) {
            return null; //something wrong
        }
        i *= 3600000;
        i += 60000 * j;
        TimeZone tz = TimeZone.getTimeZone("UTC");
        tz.setRawOffset(minus ? -i : i);
        return tz;
    }

    /** Parse duration in format format ISO 8061.
     * @return true if correct format of duration was parsed.
     */
    public final boolean isDuration() {
        if (getIndex() + 1 >= _endPos && !readNextBuffer()) {
            return false;
        }
        keepBuffer();
        int startPos = getIndex();
        try {
            if (chkDuration(false)) {
                _parsedString = _source.substring(startPos, getIndex());
                freeBuffer();
                return true;
            }
        } catch (SRuntimeException ex) {}
        setIndex(startPos);
        freeBuffer();
        return false;
    }

    /** Parse duration in format format ISO 8061.
     * @param xmlSchema if true the interval is according to XML schema (XSD) format.
     * @return true if correct format of duration was parsed.
     */
    final boolean chkDuration(final boolean xmlSchema) {
        _parsedDuration = new SDuration();
        if (xmlSchema) {
            return chkInterval(true);
        }
        if (isChar('R')) {
            if (isInteger()) {
                _parsedDuration.setRecurrence(getParsedInt());
            } else {
                throw new SRuntimeException(SYS.SYS056); //Icorrect format of time period
            }
            if (!isChar('/')) {
                throw new SRuntimeException(SYS.SYS056); //Icorrect format of time period
            }
        }
        if (isISO8601Datetime()) {
            if (!testParsedDatetime()) {
                throw new SRuntimeException(SYS.SYS056); //Icorrect format of time period
            }
            _parsedDuration.setStart(getParsedSDatetime());
            if (!isChar('/')) {
                throw new SRuntimeException(SYS.SYS056); //Icorrect format of time period
            }
            if (isISO8601Datetime()) {
                if (!testParsedDatetime()) {
                    throw new SRuntimeException(SYS.SYS056); //Icorrect format of time period
                }
                _parsedDuration.setEnd(getParsedSDatetime());
            } else if (!chkInterval(xmlSchema)) {
                    throw new SRuntimeException(SYS.SYS056); //Icorrect format of time period
                } else if (isChar('/')) {
                if (!isISO8601Datetime()) {
                    throw new SRuntimeException(SYS.SYS056); //Icorrect format of time period
                }
                _parsedDuration.setEnd(getParsedSDatetime());
            }
        } else if (chkInterval(xmlSchema)) {
            if (isChar('/')) {
                if (isISO8601Datetime()) {
                    if (!testParsedDatetime()) {
                        throw new SRuntimeException(SYS.SYS056); //Icorrect format of time period
                    }
                    _parsedDuration.setEnd(getParsedSDatetime());
                } else {
                    throw new SRuntimeException(SYS.SYS056); //Icorrect format of time period
                }
            }
        } else {
            throw new SRuntimeException(SYS.SYS056); //Icorrect format of time period
        }
        return true;
    }

    /** Check if source buffer contains at actual position correct format of ISO 8061 duration.
     * @param xmlSchema if true the interval is according to XML schema (XSD) format.
     * @return true if correct format of duration was parsed.
     * @throw SRuntimeException if an error occurs.
     */
    private boolean chkInterval(boolean xmlSchema) {
        boolean date = false;
        boolean time = false;
        _parsedDuration.setNegative(isChar('-'));
        if (isChar('P')) {
            if (!xmlSchema && isDatetime("yyyy-M-dTH:m[:s[.S]][Z]")) {
                _parsedDuration.setYears(_parsedDatetime._year);
                _parsedDuration.setMonths(_parsedDatetime._month);
                _parsedDuration.setDays(_parsedDatetime._day);
                _parsedDuration.setHours(_parsedDatetime._hour);
                _parsedDuration.setMinutes(_parsedDatetime._minute);
                _parsedDuration.setSeconds(_parsedDatetime._second);
                _parsedDuration.setFraction(_parsedDatetime.getFraction());
                date = true;
                time = true;
            } else if (!xmlSchema && isDatetime("yyyy-M-d[Z]")) {
                _parsedDuration.setYears(_parsedDatetime._year);
                _parsedDuration.setMonths(_parsedDatetime._month);
                _parsedDuration.setDays(_parsedDatetime._day);
                date = true;
            } else if (isInteger()){
                boolean weeks = false;
                int iweeks = 0;
                boolean years = false;
                boolean months = false;
                boolean days = false;
                char c;
                while ((c = isOneOfChars(xmlSchema ? "YMD" : "YMDW")) != StringParser.NOCHAR) {
                    date = true;
                    switch (c) {
                        case 'Y':
                            if (years) {
                                throw new SRuntimeException(SYS.SYS056);//Icorrect format of time period
                            }
                            if (months || weeks || days) {
                                throw new SRuntimeException(SYS.SYS056);//Icorrect format of time period
                            }
                            years = true;
                            _parsedDuration.setYears(getParsedInt());
                            break;
                        case 'M':
                            if (months) {
                                throw new SRuntimeException(SYS.SYS056);//Icorrect format of time period
                            }
                            if (weeks || days) {
                                throw new SRuntimeException(SYS.SYS056);//Icorrect format of time period
                            }
                            months = true;
                            _parsedDuration.setMonths(getParsedInt());
                            break;
                        case 'W':
                            if (weeks) {
                                throw new SRuntimeException(SYS.SYS056);//Icorrect format of time period
                            }
                            if (days) {
                                throw new SRuntimeException(SYS.SYS056);//Icorrect format of time period
                            }
                            weeks = true;
                            iweeks = getParsedInt();
                            break;
                        default:
                            //if(c == 'D') {
                            if (days) {
                                throw new SRuntimeException(SYS.SYS056);//Icorrect format of time period
                            }
                            days = true;
                            _parsedDuration.setDays(getParsedInt());
                            break;
                    }
                    if (!isInteger()) {
                        break;
                    }
                }
                if (weeks) {
                    _parsedDuration.setDays(_parsedDuration.getDays()+iweeks*7);
                }
            }
        }
        if (isChar('T')) {
            if (time) {
                throw new SRuntimeException(SYS.SYS056); //Icorrect format of time period
            }
            time = true;
            if (!xmlSchema && isDatetime("TH:m[:s[.S]]][Z]")) {
                _parsedDuration.setHours(_parsedDatetime._hour);
                _parsedDuration.setMinutes(_parsedDatetime._minute);
                _parsedDuration.setSeconds(_parsedDatetime._second);
                _parsedDuration.setFraction(_parsedDatetime.getFraction());
            } else {
                if (!isInteger()) {
                    throw new SRuntimeException(SYS.SYS056); //Icorrect format of time period
                }
                boolean hours = false;
                boolean minutes = false;
                boolean seconds = false;
                boolean millis = false;
                char c;
                while ((c = isOneOfChars("HMS.")) != StringParser.NOCHAR) {
                    switch (c) {
                        case 'H':
                            if (hours) {
                                throw new SRuntimeException(SYS.SYS056);//Icorrect format of time period
                            }
                            hours = true;
                            if (minutes || seconds || millis) {
                                throw new SRuntimeException(SYS.SYS056);//Icorrect format of time period
                            }
                            _parsedDuration.setHours(getParsedInt());
                            break;
                        case 'M':
                            if (minutes) {
                                throw new SRuntimeException(SYS.SYS056);//Icorrect format of time period
                            }
                            minutes = true;
                            if (seconds || millis) {
                                throw new SRuntimeException(SYS.SYS056);//Icorrect format of time period
                            }
                            _parsedDuration.setMinutes(getParsedInt());
                            break;
                        case 'S':
                            if (seconds) {
                                throw new SRuntimeException(SYS.SYS056);//Icorrect format of time period
                            }
                            seconds = true;
                            if (millis) {
                                throw new SRuntimeException(SYS.SYS056);//Icorrect format of time period
                            }
                            _parsedDuration.setSeconds(getParsedInt());
                            break;
                        case '.':
                            if (seconds || millis) {
                                throw new SRuntimeException(SYS.SYS056);//Icorrect format of time period
                            }
                            seconds = true;
                            millis = true;
                            _parsedDuration.setSeconds(getParsedInt());
                            double fraction =  0.0;
                            double exp = 1.0;
                            while(_ch >= '0' && _ch <= '9') {
                                fraction += (_ch - '0')/(exp *= 10.0);
                                nextChar();
                            }
                            if (fraction != 0.0) {
                                _parsedDuration.setFraction(fraction);
                            }
                            if (exp == 1.0) {//no digits after '.'
                                throw new SRuntimeException(SYS.SYS056);//Icorrect format of time period
                            }
                            if (!isChar('S')) {
                                throw new SRuntimeException(SYS.SYS056);//Icorrect format of time period
                            }
                            break;
                        default:
                            break;
                    }
                    if (!isInteger()) {
                        break;
                    }
                }
            }
        }
        return date || time;
    }

    /** Parse initial defaults section of datetime mask.
     * @return position where parsing has finished.
     */
    @SuppressWarnings("deprecation")
    final Report checkDateFormatDefaults() {
        int beg = getIndex();
        _parsedDatetime = new MyDate(beg);
        for (;;) {
            switch (_ch) {
                case NOCHAR:
                    //Datetime mask: missing closing character&{0}{: '}{'}&{1}{, position: }
                    return Report.error(SYS.SYS064, null, beg);
                case '}':
                    nextChar();
                    return null;
                case 'z':
                case 'L': {
                    char ch = _ch;
                    nextChar();
                    if (!isChar('(')) {
                        //Datetime mask: expected character&{0}{: '}{'}&{1}{, position: }
                        return Report.error(SYS.SYS079, "(", getIndex());
                    }
                    if (isChar('*')) {
                        if (!isChar(')')) {
                            //Datetime mask: missing closing character&{0}{: '}{'}&{1}{, position: }
                            return Report.error(SYS.SYS064, ")", beg);
                        }
                        if (ch == 'z') {// zone
                            _parsedDatetime._tz = TimeZone.getDefault();
                        } else {// L language
                            _parsedDatetime.setLocaleFormatSymbols(new DateFormatSymbols(
                                Locale.getDefault()));
                        }
                        continue;
                    }
                    int start = getIndex();
                    String p1 = null;
                    String p2 = "";
                    String p3 = "";
                    int i = 0;
                    for (;;) {
                        if (eos()) {
                            //Error in datetime mask format: missing closing
                            // character&{0}{: '}{'}&{1}{, position: }
                            return Report.error(SYS.SYS064, null, beg);
                        }
                        char c;
                        if ((c = isOneOfChars("\t\n\r |;!@#$%^&(=[]{}\"'?><\\")) != NOCHAR) {
                            //Datetime mask: incorrect control character&{0}{: '}{'}&{1}{, position: }
                            return Report.error(SYS.SYS063, c, getIndex());
                        }
                        if (isChar(',')) {
                            if (ch == 'z') {
                                //Datetime mask: incorrect control character&{0}{: '}{'}&{1}{, position: }
                                return Report.error(SYS.SYS063, null, start);
                            }
                            if(i == 2) {
                                continue;
                            }
                            if (i++ <= 1) {
                                String s = _source.substring(start, getIndex() - 1);
                                if (i == 1) {
                                    p1 = s;
                                } else {
                                    p2 = s;
                                }
                            } else {
                                //Datetime mask: incorrect control character&{0}{: '}{'}&{1}{, position: }
                                return Report.error(SYS.SYS063,null,getIndex());
                            }
                            start = getIndex();
                        } else if (isChar(')')) {
                            switch (i) {
                                case 2: p3 = _source.substring(start, getIndex()-1); break;
                                case 1: p2 = _source.substring(start, getIndex()-1); break;
                                case 0: p1 = _source.substring(start, getIndex()-1); break;
                                default: break;
                            }
                            if (ch == 'z') {
                                TimeZone tz;
                                if ((tz = TimeZone.getTimeZone(p1)) == null ||
                                    ("GMT".equals(tz.getID()) && !"GMT".equalsIgnoreCase(p1))) {
                                    //Datetime mask error: unsupported timezone name
                                    //&{0}{: '}{'}&{1}{, position: }
                                    return Report.error(SYS.SYS061, p1, start);
                                }
                                _parsedDatetime._tz = tz;
                            } else {
                                try { //check language code
                                    SUtils.getISO3Language(p1);
                                } catch (SRuntimeException ex) {
                                    //Datetime mask error: unsupported language code
                                    //&{0}{: '}{'}&{1}{, position: }
                                    return Report.error(SYS.SYS060,
                                        "" + p1,
                                        "" + (i == 2 ? start - p3.length() - p2.length()
                                            : i == 1 ? start - p2.length() : start));
                                }
                                if ( i > 0 && !SUtils.isCountryCode(p2)) {
                                    //Datetime mask error: unsupported country code
                                    //&{0}{: '}{'}&{1}{, position: }
                                    return Report.error(SYS.SYS067, p2, (i==2 ? start-p2.length() : start));
                                }
                                _parsedDatetime.setLocaleFormatSymbols(new DateFormatSymbols(new Locale(p1,p2,p3)));
                            }
                            break;
                        } else {
                            nextChar();
                        }
                    }
                    continue;
                }
                case 'Y': //year ISO
                case 'y': //year
                case 'M': //month in year
                case 'd': //day in month
                case 'H': //hour in day (0..23)
                case 'm': //minute
                case 's': //second
                case 'S': //millisecond
                case 'Z': { //RFC822 zone (-0800)
                    char ch = _ch;
                    String s = String.valueOf(_ch);
                    nextChar();
                    while(isChar(ch)) {
                        s += ch;
                    }
                    int start = getIndex();
                    MyDate parsedDatetime = _parsedDatetime;
                    _parsedDatetime = null;
                    try {
                        if (!isDatetime(s) || !_parsedDatetime.chkDatetime()) {
                            //Datetime mask: incorrect initial value{0}{: '}{'}&{1}{, position: }
                            return Report.error(SYS.SYS062, _source.substring(start, getIndex()), start);
                        }
                    } catch (SRuntimeException ex) {
                        //Datetime mask: incorrect initial value{0}{: '}{'}&{1}{, position: }
                        return Report.error(SYS.SYS062, _source.substring(start, getIndex()),start);
                    }
                    switch (ch) {
                        case 'Y': //year ISO
                        case 'y': parsedDatetime._year = _parsedDatetime._year; break;
                        case 'M': parsedDatetime._month = _parsedDatetime._month; break;
                        case 'd': parsedDatetime._day = _parsedDatetime._day; break;
                        case 'H': parsedDatetime._hour = _parsedDatetime._hour; break;
                        case 'm': parsedDatetime._minute = _parsedDatetime._minute; break;
                        case 's': parsedDatetime._second = _parsedDatetime._second; break;
                        case 'S': parsedDatetime._fraction = _parsedDatetime._fraction; break; //millisecond
                        case 'Z': parsedDatetime._tz = _parsedDatetime._tz; //RFC822 zone (-0800)
                    }
                    _parsedDatetime = parsedDatetime;
                    continue;
                }
                default:
                    //Datetime mask: incorrect control character&{0}{: '}{'}&{1}{, position: }
                    return Report.error(SYS.SYS063, _ch, getIndex());
            }
        }
    }

    /** Check quoted string.
     * @return null or Report if format is not valid.
     */
    private Report checkLiteral() {
        int pos = getIndex();
        nextChar();
        while (!eos()) {
            if (isChar('\'')) {
                if (!isChar('\'')) {
                    return null;
                }
            } else {
                nextChar();
            }
        }
        return Report.error(SYS.SYS049, pos + 1);//Datetime mask: unclosed quoted literal &{0}{, position: }
    }

    /** Check executable part of mask.
     * @return null or Report if format is not valid.
     */
    private Report checkDateFormatMask() {
        if (_ch == '|') {
            //Datetime mask: section can't start with '|' (variant) &{0}{, position: }
            return Report.error(SYS.SYS078, getIndex());
        }
        Report r;
        for(;;) {
            switch (_ch) {
                case NOCHAR: return null;
                case '|':
                    nextChar();
                    if (isChar('{')) {
                        if ((r = checkDateFormatDefaults()) != null) {
                            return r;
                        }
                    }
                    if ((r = checkDateFormatMask()) != null) {
                        return r;
                    }
                    continue;
                case '[':
                    nextChar();
                    if ((r = checkDateFormatOptional()) != null) {
                        return r;
                    }
                    continue;
                case '{':
                case '}':
                    //Datetime mask:  format: incorrect control character&{0}{: '}{'}&{1}{, position: }
                    return Report.error(SYS.SYS063, _ch, getIndex());
                case ']': return null;
                case 'D': //day in year
                case 'F': //day of week in month
                case 'w': //week in year
                case 'W': //week in month
                case 'G': //era designator (BC, AD)
                case 'd': //day
                case 'M': //month in year
                case 'm': //minute
                case 's': //second
                case 'S': //millisecond
                case 'a': //AM/PM marker
                case 'E': //day of week (text)
                case 'e': //day of week (number)
                case 'K': //hour 0..11
                case 'k': //hour in day (1..24)
                case 'H': //hour in day (0..23)
                case 'h': //hour (1..12) AM/PM
                case 'Y': //year ISO
                case 'y': //year
                case 'R': //year (two digits database)
                case 'Z': //RFC822 zone
                case 'z': { //general zone
                    char ch = _ch;
                    int n = 0;
                    while (isChar(ch)) {
                        n++;
                    }
                    if (ch == 'y' && n == 3 ||
                        ch == 'Y' && n != 1 && n != 2 ||
                        ch == 'R'&& n != 2) {
                        //Datetime mask: incorrect year specification&{0}{, position: }
                        return Report.error(SYS.SYS059, getIndex() - 3);
                    }
                    continue;
                }
                case '?': //one of chars
                    nextChar();
                    if (_ch != '\'') {
                        //Datetime mask: expected character&{0}{: '}{'}&{1}{, position: }
                        return Report.error(SYS.SYS079, "'", getIndex());
                    }
                    if ((r = checkLiteral()) != null) {
                        return r;
                    }
                    continue;
                case '\'': {
                    if ((r = checkLiteral()) != null) {
                        return r;
                    }
                    continue;
                }
                default:
                    if (eos()) {
                        return null;
                    }
                    nextChar();
            }
        }
    }

    /** Check optional part of mask.
     * @return null or Report if format is not valid.
     */
    private Report checkDateFormatOptional() {
        Report r;
        int beg = getIndex();
        if ((r = checkDateFormatMask()) != null) {
            return r;
        }
        if (!isChar(']')) {
            //Datetime mask: missing closing character&{0}{: '}{'}&{1}{, position: }
            return Report.error(SYS.SYS064, "]", beg);
        }
        return null;
    }

    /** Check if date format is valid.
     * @return null or Report if format is not valid.
     */
    public final Report checkDateFormat() {
        Report r;
        if (isChar('{')) {
            if ((r = checkDateFormatDefaults()) != null) {
                return r;
            }
        }
        return checkDateFormatMask();
    }

    /** Check if date format is valid.
     * @param format String with format data.
     * @return null or Report if format is not valid.
     */
    public static final Report checkDateFormat(final String format) {
        StringParser p = new StringParser(format);
        Report r;
        if ((r = p.checkDateFormat()) == null) {
            if (!p.eos()) {
                //Datetime mask: incorrect control character&{0}{: '}{'}&{1}{, position: }
                r = Report.error(SYS.SYS063, p._ch, p.getIndex());
            }
        }
        return r;
    }

    /** Parse Java identifier and save result to _parsedString.
     * @return true if actual input is Java identifier.
     */
    public final boolean isJavaName() {
        if (_ch > ' ' && Character.isJavaIdentifierStart(_ch)) {
            _parsedString = String.valueOf(_ch);
            nextChar();
            while(_ch  > ' ' && Character.isJavaIdentifierPart(_ch)) {
                _parsedString += _ch;
                nextChar();
            }
            return true;
        }
        return false;
    }

    /** Parse Java fully qualified identifier and save result to _parsedString.
     * @return true if actual input is Java fully qualified identifier.
     */
    public final boolean isJavaQName() {
        if (isJavaName()) {
            String s = _parsedString;
            while(ensureBuffer(2) && isChar('.')) {
                if (!isJavaName()) {
                    setIndex(getIndex() - 1);
                    break;
                }
                s += "." + _parsedString;
            }
            _parsedString = s;
            return true;
        }
        return false;
    }

////////////////////////////////////////////////////////////////////////////////
// XML parsing methods
////////////////////////////////////////////////////////////////////////////////

    /** Get type of character. This method returns one of:
     * <p>XML_CHAR_ILLEGAL .. unknown type (0)
     * <p>XML_CHAR .. valid XML character (1)
     * <p>XML_CHAR_WHITESPACE .. space, TAB, CR, LF (2)
     * <p>XML_CHAR_COLON .. ':' (4)
     * <p>XML_CHAR_NAME_START .. first character of XML name (8)
     * <p>XML_CHAR_NAME_EXT .. character of XML name (16)
     * @param xmlVersion 10 .. "1.0", 11 .. "1.1" (see XMLVER1_0 and XMLVER1_1).
     * @return type of character.
     */
    public final byte getXmlCharType(final byte xmlVersion) {
        return xmlVersion == XMLVER1_1 ? XML_CHARTAB1[_ch] : XML_CHARTAB0[_ch];
    }

    /** Parse NMToken.
     * <p>[7] NMToken::= (NameChar)+
     * <p>[4] NameChar::= Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender
     * @param xmlVersion 10 .. "1.0", 11 .. "1.1" (see XMLVER1_0 and XMLVER1_1).
     * @return true if rule passed.
     */
    public final boolean isNMToken(final byte xmlVersion) {
        if (getXmlCharType(xmlVersion) < XML_CHAR_COLON) {
            return false;
        }
        if (getIndex() >= _endPos && !readNextBuffer()) {
            _parsedString = String.valueOf(_ch);
            super.setIndex(_endPos);
            _ch = NOCHAR;
            return true;
        }
        StringBuilder result = new StringBuilder(String.valueOf(_ch));
        while (incIndex() < _endPos || readNextBuffer()) {
            _ch = _source.charAt(getIndex());
            if (getXmlCharType(xmlVersion) < XML_CHAR_COLON) {
                break;
            }
            result.append(_ch);
        }
        _parsedString = result.toString();
        if (getIndex() >= _endPos) {
            super.setIndex(_endPos);
            _ch = NOCHAR;
        }
        return true;
    }

    /** Parse NCName according to Namespaces in W3C.
     * <p>[4] NCName::= (Letter | '_') (NCNameChar)* //An XML Name, minus the ":"
     * <p>[5] NCNameChar::= Letter | Digit | '.' | '-' | '_' | CombiningChar | Extender
     * @param xmlVersion 10 .. "1.0", 11 .. "1.1" (see XMLVER1_0 and XMLVER1_1).
     * @return true if NCNname was recognized.
     */
    public final boolean isNCName(final byte xmlVersion) {
        if (getXmlCharType(xmlVersion) != XML_CHAR_NAME_START) {
            return false;
        }
        if (getIndex() >= _endPos && !readNextBuffer()) {
            _parsedString = String.valueOf(_ch);
            super.setIndex(_endPos);
            _ch = NOCHAR;
            return true;
        }
        StringBuilder result = new StringBuilder(String.valueOf(_ch));
        while ((incIndex() < _endPos || readNextBuffer())) {
            _ch = _source.charAt(getIndex());
            if (getXmlCharType(xmlVersion) < XML_CHAR_NAME_START) {
                break;
            }
            result.append(_ch);
        }
        _parsedString = result.toString();
        if (getIndex() >= _endPos) {
            super.setIndex(_endPos);
            _ch = NOCHAR;
        }
        return true;
    }

    /** Parse XML name and save result to _parsedString.
     * @param xmlVersion 10 .. "1.0", 11 .. "1.1" (see XMLVER1_0 and XMLVER1_1).
     * @return true if XMLName was parsed.
     */
    public final boolean isXMLName(final byte xmlVersion) {
        if (getXmlCharType(xmlVersion) != XML_CHAR_NAME_START && _ch != ':') {
            return false;
        }
        int pos = getIndex();
        if (pos >= _endPos && !increaseBuffer()) {
            _parsedString = String.valueOf(_ch);
            super.setIndex(_endPos);
            _ch = NOCHAR;
            return true;
        }
        while (incIndex() < _endPos || increaseBuffer()) {
            _ch = _source.charAt(getIndex());
            if (getXmlCharType(xmlVersion) < XML_CHAR_COLON) {
                break;
            }
        }
        int pos1 = getIndex();
        SPosition spos = getPosition();
        isSpaces();
        if (isChar('(')) {
            int lastDot = _source.substring(pos, pos1).lastIndexOf('.');
            if (lastDot > 0) {
                pos1 = pos + lastDot;
            }
        } else if (_source.charAt(pos1 - 1) == '.') {
            isSpaces();
            if (isJavaName()) {
                isSpaces();
                if (isChar('(')) {
                    pos1--;
                }
            }
        }
        setPosition(spos);
        setIndex(pos1);
        _parsedString = _source.substring(pos, pos1);
        if (getIndex() >= _endPos) {
            super.setIndex(_endPos);
            _ch = NOCHAR;
        }
        return true;
    }

    private boolean isQNamePart(final byte xmlVersion) {
        if (getXmlCharType(xmlVersion) != XML_CHAR_NAME_START || _ch == ':') {
            return false;
        }
        while (!eos() || increaseBuffer()) {
            _ch = _source.charAt(getIndex());
            int x = getXmlCharType(xmlVersion); //8
            if (x < XML_CHAR_NAME_START) {
                break;
            }
            incIndex();
        }
        return true;
    }

    /** Parse XML name and save result to _parsedString.
     * @param xmlVersion 10 .. "1.0", 11 .. "1.1" (see XMLVER1_0 and XMLVER1_1).
     * @return true if XMLName was parsed.
     */
    public final boolean isXMLQName(final byte xmlVersion) {
        int pos = getIndex();
        if (isQNamePart(xmlVersion)) {
            if (!isChar(':')) {
                _parsedString = _source.substring(pos, getIndex());
                return true;
            }
            String s = _source.substring(pos, getIndex());
            pos = getIndex();
            if (!isQNamePart(xmlVersion)) {
                return false;
            }
            _parsedString = s + _source.substring(pos, getIndex());;
            return true;
        }
        return false;
    }

    /** Parse valid XML character.
     * @param xmlVersion 10 .. "1.0", 11 .. "1.1" (see XMLVER1_0 and XMLVER1_1).
     * @return parsed character or NOCHAR.
     */
    public final char isXMLChar(final byte xmlVersion) {
        if (getXmlCharType(xmlVersion) == 0) {
            return NOCHAR;
        }
        char c = _ch;
        nextChar();
        return c;
    }

    /** Parse XML whitespace character.
     * @return parsed character or NOCHAR.
     */
    public final char isXMLWhitespaceChar() {
        if ( XML_CHARTAB0[_ch] != XML_CHAR_WHITESPACE) {
            return NOCHAR;
        }
        char c;
        if ((c = _ch) == '\n') {
            setNewLine();
        }
        nextChar();
        return c;
    }

    /** Parse XML name start character.
     * @param xmlVersion 10 .. "1.0", 11 .. "1.1" (see XMLVER1_0 and XMLVER1_1).
     * @return parsed character or NOCHAR.
     */
    public final char isXMLNamestartChar(final byte xmlVersion) {
        if (getXmlCharType(xmlVersion) != XML_CHAR_NAME_START) {
            return NOCHAR;
        }
        char c = _ch;
        nextChar();
        return c;
    }

    /** Parse XML name extension character.
     * @param xmlVersion 10 .. "1.0", 11 .. "1.1" (see XMLVER1_0 and XMLVER1_1).
     * @return parsed character or NOCHAR.
     */
    public final char isXMLNameExtensionChar(final byte xmlVersion) {
        if (getXmlCharType(xmlVersion) != XML_CHAR_NAME_EXT) {
            return NOCHAR;
        }
        char c = _ch;
        nextChar();
        return c;
    }

    /** Get XML character table.
     * @param xmlVersion1 false: XML version 1.0, true: XML version 1.1.
     * @return character table (byte[65536]).
     */
    protected final byte[] getXMLCharTab(final boolean xmlVersion1) {
        return xmlVersion1 ? XML_CHARTAB1 : XML_CHARTAB0;
    }

    /** Parse duration in format format ISO 8061.
     * @return true if correct format of duration was parsed.
     */
    public final boolean isXMLDuration() {
        if (getIndex() + 1 >= _endPos && !readNextBuffer()) {
            return false;
        }
        keepBuffer();
        int startPos = getIndex();
        try {
            if (chkDuration(true)) {
                _parsedString = _source.substring(startPos, getIndex());
                freeBuffer();
                return true;
            }
        } catch (SRuntimeException ex) {}
        freeBuffer();
        setIndex(startPos);
        return false;
    }

    /** Parse date in format ISO 8061 (y:MM:ddTHH:mm:ss[.S][Z]).
     * @return true if correct format of duration was parsed.
     */
    public final boolean isXMLDatetime() {
        if (getIndex() + 17 >= _endPos && !readNextBuffer()) {
            return false;
        }
        int start = getIndex();
        keepBuffer();
        _parsedDatetime = new MyDate(getIndex());
        if (!readXMLDate()) {
            freeBuffer();
            return false;
        }
        if (_parsedDatetime._tz != null) { //zone can't be here
            setIndex(start);
            freeBuffer();
            return false;
        }
        if (!isChar('T')) {
            setIndex(start);
            freeBuffer();
            return false;
        }
        if (!readXMLTime()) {
            setIndex(start);
            freeBuffer();
            return false;
        }
        return true;
    }

    /** Read XML form of time zone.
     * @return true if zone was parsed.
     */
    private boolean readXMLZone() {
        _parsedDatetime._tz = null;
        int start = getIndex();
        char sign;
        if ((sign = isOneOfChars("+-Z")) == 0) {
            return false;
        }
        _parsedDatetime._tz = TimeZone.getTimeZone("GMT"); //UTC
        if (sign == 'Z') {
            return true;
        }
        int pos1 = getIndex();
        int i;
        if (!isInteger() || getIndex() - pos1 != 2) {
            setIndex(start);
            _parsedDatetime._tz = null;
            return false;
        }
        i = getParsedInt();
        if (i > 14) {
            //Time zone must be in interval -14 .. 14 hours
            getReportWriter().putReport(Report.error(SYS.SYS086));
            i = 0;
        }
        int offset = i * 3600000;
        pos1 = getIndex();
        if (isChar(':') && isInteger()) {
            if (getIndex() - pos1 != 3) {
                setIndex(start);
                return false;
            }
            int j = getParsedInt();
            if (j > 59) {
                getReportWriter().putReport(Report.error(SYS.SYS087));//Minutes must be in interval 0 .. 59
                j = 59;
            } else if (i == 14 && j > 0) {
                //Time zone must be in interval -14 .. 14 hours
                getReportWriter().putReport(Report.error(SYS.SYS086));
                j = 0;
            }
            offset += j * 60000;
        } else {
            setIndex(start);
            return false;
        }
        if (sign == '-') {
            offset = -offset;
        }
        _parsedDatetime._tz.setRawOffset(offset);
        return true;
    }

    /** Read XML form od date.
     * @return true if date was parsed.
     */
    private boolean readXMLDate() {
        int start = getIndex();
        boolean wasQuote = isChar('"');//year without zone may be quoted in JSON
        int sign = isChar('-') ? -1 : 1;
        int pos = getIndex(); //migt be start + 1
        char firstdigit = getCurrentChar();
        if (!isInteger()) {
            setIndex(start);
            return false;
        }
        if (sign == 1 && getIndex() - pos < 4) {
            setIndex(start);
            return false;
        }
        if (sign == 1 && firstdigit == '0' && getIndex() - pos > 4) {
            setIndex(start);
            return false;
        }
        _parsedDatetime._year = getParsedInt() * sign;
        if (wasQuote) { //quoted year
            if (!isChar('"')) {
                setIndex(start);
                return false;
            }
            return true;
        }
        freeBuffer();
        pos = getIndex();
        if (!isChar('-') || !isInteger() || getIndex() - pos != 3){
            setIndex(start);
            return false;
        }
        _parsedDatetime._month = getParsedInt();
        pos = getIndex();
        if (!isChar('-') || !isInteger() || getIndex() - pos != 3){
            setIndex(start);
            return false;
        }
        _parsedDatetime._day = getParsedInt();
        return true;
    }

    /** Parse date in format y:MM:dd[Z]
     * @return true if correct format of date was parsed.
     */
    public final boolean isXMLDate() {
        if (getIndex() + 10 >= _endPos && !readNextBuffer()) {
            return false;
        }
        _parsedDatetime = new MyDate(getIndex());
        keepBuffer();
        if (!readXMLDate()) {
            freeBuffer();
            return false;
        }
        readXMLZone();
        freeBuffer();
        return true;
    }

    /** Parse date in format HH:mm:ss[.S][Z].
     * @return true if correct format of time was parsed.
     */
    public final boolean readXMLTime() {
        int start = getIndex();
        if (!isInteger() || getIndex() - start != 2) {
            setIndex(start);
            return false;
        }
        _parsedDatetime._hour = getParsedInt();
        int pos = getIndex();
        if (!isChar(':') || !isInteger() || getIndex() - pos != 3) {
            setIndex(start);
            return false;
        }
        _parsedDatetime._minute = getParsedInt();
         pos = getIndex();
        if (!isChar(':') || !isInteger() || getIndex() - pos != 3) {
            setIndex(start);
            return false;
        } else {
            _parsedDatetime._second = getParsedInt();
            if (isChar('.')) {
                if (!isInteger()) {
                    setIndex(start);
                    return false;
                }
                _parsedDatetime._fraction = Double.parseDouble("0."+_parsedString);
            }
        }
        readXMLZone();
        return true;
    }

    /** Parse date in format HH:mm:ss[Z].
     * @return true if correct format of time was parsed.
     */
    public final boolean isXMLTime() {
        if (getIndex() + 5 >= _endPos && !readNextBuffer()) {
            return false;
        }
        _parsedDatetime = new MyDate(getIndex());
        keepBuffer();
        boolean result = readXMLTime();
        freeBuffer();
        return result;
    }

    /** Read XML format of year.
     * @return true if year was parsed.
     */
    private boolean readXMLYear() {
        int start = getIndex();
        boolean wasQuote = isChar('"'); //year without zone may be quoted in JSON
        boolean negative = isChar('-');
        if (!isInteger()) {
            setIndex(start);
            return false;
        }
        _parsedDatetime._year = getParsedInt() * (negative? -1 : 1);
        if (wasQuote) { //quoted year
            if (!isChar('"')) {
                setIndex(start);
                return false;
            }
        }
        freeBuffer();
        return true;
    }

    /** Parse date in format y[Z].
     * @return true if correct format of date was parsed.
     */
    public final boolean isXMLYear() {
        if (getIndex() + 1 >= _endPos && !readNextBuffer()) {
            return false;
        }
        _parsedDatetime = new MyDate(getIndex());
        keepBuffer();
        if (!readXMLYear()) {
            freeBuffer();
            return false;
        }
        readXMLZone();
        freeBuffer();
        return true;
    }

    /** Parse date in format y-MM[Z].
     * @return true if correct format of date was parsed.
     */
    public final boolean isXMLYearMonth() {
        if (getIndex() + 4 >= _endPos && !readNextBuffer()) {
            return false;
        }
        _parsedDatetime = new MyDate(getIndex());
        keepBuffer();
        int start = getIndex();
        boolean negative = isChar('-');
        int pos = getIndex();
        char firstdigit = getCurrentChar();
        if (!isInteger()) {
            setIndex(start);
            freeBuffer();
            return false;
        }
        if (!negative && getIndex() - pos < 4) {
            setIndex(start);
            freeBuffer();
            return false;
        }
        if (!negative && firstdigit == '0' && getIndex() - pos > 4) {
            setIndex(start);
            freeBuffer();
            return false;
        }
        _parsedDatetime._year = getParsedInt() * (negative? -1 : 1);
        pos = getIndex();
        if (!isChar('-') || !isInteger() || getIndex() - pos != 3) {
            setIndex(start);
            freeBuffer();
            return false;
        }
        _parsedDatetime._month = getParsedInt();
        readXMLZone();
        freeBuffer();
        return true;
    }

    private boolean readXMLMonth() {
        int start = getIndex();
        if (!isToken("--")) {
            return false;
        }
        int pos = getIndex();
        if (!isInteger()) {
            setIndex(start);
            return false;
        }
        if (getIndex() - pos != 2) {
            setIndex(start);
            return false;
        }
        _parsedDatetime._month = getParsedInt();
        return true;
    }

    /** Parse date in format --MM[Z].
     * @return true if correct format of date was parsed.
     */
    public final boolean isXMLMonth() {
        if (getIndex() + 4 >= _endPos && !readNextBuffer()) {
            return false;
        }
        _parsedDatetime = new MyDate(getIndex());
        keepBuffer();
        boolean result = readXMLMonth();
        if (result) {
            readXMLZone();
        }
        freeBuffer();
        return result;
    }

    /** Parse date in format --MM-dd-[Z].
     * @return true if correct format of date was parsed.
     */
    public final boolean isXMLMonthDay() {
        if (getIndex() + 7 >= _endPos && !readNextBuffer()) {
            return false;
        }
        _parsedDatetime = new MyDate(getIndex());
        keepBuffer();
        int start = getIndex();
        if (!isToken("--")) {
            return false;
        }
        int pos = getIndex();
        if (!isInteger()) {
            setIndex(start);
            return false;
        }
        if (getIndex() - pos != 2) {
            setIndex(start);
            return false;
        }
        _parsedDatetime._month = getParsedInt();
        if (!isChar('-')) {
            setIndex(start);
            freeBuffer();
            return false;
        }
        pos = getIndex();
        if (!isInteger()) {
            setIndex(start);
            return false;
        }
        if (getIndex() - pos != 2) {
            setIndex(start);
            return false;
        }
        _parsedDatetime._day = getParsedInt();
        readXMLZone();
        freeBuffer();
        return true;
    }

    /** Parse date in format ---dd[Z].
     * @return true if correct format of date was parsed.
     */
    public final boolean isXMLDay() {
        if (getIndex() + 7 >= _endPos && !readNextBuffer()) {
            return false;
        }
        if (!isToken("---")) {
            return false;
        }
        _parsedDatetime = new MyDate(getIndex());
        keepBuffer();
        int start = getIndex();
        int pos = start;
        if (!isInteger()) {
            setIndex(start);
            return false;
        }
        if (getIndex() - pos != 2) {
            setIndex(start);
            return false;
        }
        _parsedDatetime._day = getParsedInt();
        readXMLZone();
        freeBuffer();
        return true;
    }

    /** Read string enclosed in delimiters (' or ").
     * <p>string::=  S ('"' ^['"']* '"' | "'" ^["'"]* "'")</p>
     * @return string or null if on the source position is not a valid string declaration.
     */
    public final String readString() {
        isSpaces();
        char delimiter;
        if ((delimiter = isOneOfChars("'\"")) == NOCHAR) {
            return null;
        }
        int pos = getIndex();
        char c;
        while ((c = peekChar()) != NOCHAR) {
            if (c == delimiter) {
                return getBufferPart(pos, getIndex() -1);
            }
        }
        return null;
    }

////////////////////////////////////////////////////////////////////////////////
// Auxiliary methods for stream paresr.
////////////////////////////////////////////////////////////////////////////////

    /** Set end of buffer (first not existing position).
     * @param endPos the end of buffer (first not existing position).
     */
    final void setEndBuffer(final int endPos) {_endPos = endPos;}

    /** Get the source buffer.
     * @return string with source buffer.
     */
    final String getBuffer() {return _source;}

    /** Set source buffer (position is not changed).
     * @param source the source buffer.
     */
    final void setBuffer(final String source) {
        if (source == null || source.isEmpty()) {
            _source = "";
            _endPos = 0;
        } else {
            _source = source;
            _endPos = _source.length();
        }
    }

    /** This class is used in the datetime parser. */
    private static class MyDate extends SDatetime {
        private MyDate _chain;
        private int _weekDay;
        private int _yearDay;
        private int _dayOfWeekInMonth;
        private int _weekInYear;
        private int _weekInMonth;
        private int _startPos;
        private int _endPos;
        private SDatetime _defaults;

        private MyDate(final MyDate date) {
            super(date);
            _weekDay = date._weekDay;
            _yearDay = date._yearDay;
            _dayOfWeekInMonth = date._dayOfWeekInMonth;
            _weekInYear = date._weekInYear;
            _weekInMonth = date._weekInMonth;
            _startPos = date._startPos;
            _defaults = date._defaults;
        }

        private MyDate(final int sourcePos) {
            super();
            _weekDay = Integer.MIN_VALUE;
            _yearDay = Integer.MIN_VALUE;
            _dayOfWeekInMonth = Integer.MIN_VALUE;
            _weekInYear = Integer.MIN_VALUE;
            _weekInMonth = Integer.MIN_VALUE;
            _startPos = sourcePos;
        }

        private MyDate(final MyDate chain, final int sourcePos) {
            _defaults = chain._defaults;
            _day = chain._day;
            _month = chain._month;
            _year = chain._year;
            _hour = chain._hour;
            _minute = chain._minute;
            _second = chain._second;
            _fraction = chain._fraction;
            if (chain._tz != null) {
                _tz = (TimeZone) chain._tz.clone();
            }
            _calendar = null;
            _weekDay = chain._weekDay;
            _yearDay = chain._yearDay;
            _dayOfWeekInMonth = chain._dayOfWeekInMonth;
            _weekInYear = chain._weekInYear;
            _weekInMonth = chain._weekInMonth;
            _chain = chain;
            _startPos = sourcePos;
            _endPos = 0;
        }

        private void setDefaults(SDatetime defaults) {
            _defaults = new SDatetime(defaults);
            resetDefaults();
        }

        private void resetDefaults() {
            if (_defaults != null) {
                _dfs = _defaults._dfs;
                _day = _defaults._day;
                _eon = _defaults._eon;
                _fraction = _defaults._fraction;
                _hour = _defaults._hour;
                _minute = _defaults._minute;
                _month = _defaults._month;
                _second = _defaults._second;
                _tz = _defaults._tz;
                _year = _defaults._year;
            }
        }

        private void reset(final int sourcePos) {
            clear();
            _chain = null;
            resetDefaults();
            _weekDay = Integer.MIN_VALUE;
            _yearDay = Integer.MIN_VALUE;
            _dayOfWeekInMonth = Integer.MIN_VALUE;
            _weekInYear = Integer.MIN_VALUE;
            _weekInMonth = Integer.MIN_VALUE;
            _startPos = sourcePos;
            _endPos = 0;
        }

        private boolean check() {
            if (!chkDatetime()) {
                return false;
            }
            if (_weekDay >= 0 && getCalendar().get(Calendar.DAY_OF_WEEK) != _weekDay) {
                return false;
            }
            if (_yearDay >= 0 && getCalendar().get(Calendar.DAY_OF_YEAR) != _yearDay) {
                return false;
            }
            if (_dayOfWeekInMonth>=0 && getCalendar().get(Calendar.DAY_OF_WEEK_IN_MONTH)!=_dayOfWeekInMonth) {
                return false;
            }
            if (_weekInYear >= 0 && getCalendar().get(Calendar.WEEK_OF_YEAR) != _weekInYear) {
                return false;
            }
            return _weekInMonth < 0 || getCalendar().get(Calendar.WEEK_OF_MONTH) == _weekInYear;
        }
    }

////////////////////////////////////////////////////////////////////////////////
// static methods
////////////////////////////////////////////////////////////////////////////////

    /** Check if the string in argument is a number (can be negative).
     * @param s the string to be checked.
     * @return true if argument is a number.
     */
    public static final boolean chkNumber(final String s) {
        StringParser p = new StringParser(s);
        p.isChar('-');
        return (p.isFloat() || p.isInteger()) && p.eos();
    }

    /** Check if the string in argument is Java identifier.
     * @param s the string to be checked.
     * @return true if argument is Java identifier.
     */
    public static final boolean chkJavaName(final String s) {
        StringParser p = new StringParser(s);
        return p.isJavaName() && p.eos();
    }

    /** Check if the string in argument is Java qualified identifier.
     * @param s the string to be checked.
     * @return true if argument is Java qualified identifier.
     */
    public static final boolean chkJavaQName(final String s) {
        StringParser p = new StringParser(s);
        return p.isJavaQName() && p.eos();
    }

    /** Check if argument is a whitespace - see {@link StringParser#isSpace()}.
     * @param ch character to be checked.
     * @return true is only if argument is a whitespace according to XML specification..
     */
    public static final boolean chkXmlWhiteSpaceChar(final char ch) {
        return XML_CHARTAB0[ch] == XML_CHAR_WHITESPACE;
    }

    /** Get type of character - static version of {@link StringParser#getXmlCharType(byte)}.
     * @param ch character to be checked.
     * @param xmlVersion 10 .. "1.0", 11 .. "1.1" (see XMLVER1_0 and XMLVER1_1).
     * @return type of character.
     */
    public static final byte getXmlCharType(final char ch, final byte xmlVersion) {
        return xmlVersion == XMLVER1_1 ? XML_CHARTAB1[ch] : XML_CHARTAB0[ch];
    }

    /** Parse NCName - see {@link StringParser#isNCName(byte)}.
     * @param name string to be checked.
     * @param xmlVersion 10 .. "1.0", 11 .. "1.1" (see XMLVER1_0 and XMLVER1_1).
     * @return true if the argument is NCName according to XML specification.
     */
    public static final boolean chkNCName(final String name, final byte xmlVersion) {
        int max;
        if (name == null || (max = name.length()) == 0 ||
            getXmlCharType(name.charAt(0), xmlVersion) != XML_CHAR_NAME_START) {
            return false;
        }
        int i = 0;
        while ((++i < max) && getXmlCharType(name.charAt(i), xmlVersion) >= XML_CHAR_NAME_START){}
        return i == max;
    }

    /** Parse XML name - see {@link StringParser#isXMLName(byte)}.
     * @param name string to be checked.
     * @param xmlVersion 10 .. "1.0", 11 .. "1.1" (see XMLVER1_0 and XMLVER1_1).
     * @return true if the argument is XML name according to XML specification.
     * {@link StringParser#isXMLName(byte)}.
     */
    public static final boolean chkXMLName(final String name, final byte xmlVersion) {
        int max;
        byte type;
        if (name == null || (max = name.length()) == 0
            || (type = getXmlCharType(name.charAt(0), xmlVersion)) != XML_CHAR_NAME_START
            && type != XML_CHAR_COLON) {
            return false;
        }
        int i = 0;
        while ((++i < max) && getXmlCharType(name.charAt(i), xmlVersion) >= XML_CHAR_COLON) {}
        return i == max;
    }

    /** Parse NMToken - see {@link StringParser#isNMToken(byte)}.
     * @param name string to be checked.
     * @param xmlVersion 10 .. "1.0", 11 .. "1.1" (see XMLVER1_0 and XMLVER1_1).
     * @return true if the argument is NMToken according to XML specification.
     * {@link StringParser#isNMToken(byte)}.
     */
    public static final boolean chkNMToken(final String name, final byte xmlVersion) {
        int max;
        if (name == null || (max = name.length()) == 0 ||
            getXmlCharType(name.charAt(0),  xmlVersion) < XML_CHAR_COLON) {
            return false;
        }
        int i = 0;
        while ((++i < max) && getXmlCharType(name.charAt(i),  xmlVersion) >= XML_CHAR_COLON) {}
        return i == max;
    }

    public static final boolean isEmoji(final String s) {
        return s.matches("(?:[\uD83C\uDF00-\uD83D\uDDFF]|[\uD83E\uDD00-\uD83E\uDDFF]|" +
            "[\uD83D\uDE00-\uD83D\uDE4F]|[\uD83D\uDE80-\uD83D\uDEFF]|" +
            "[\u2600-\u26FF]\uFE0F?|[\u2700-\u27BF]\uFE0F?|\u24C2\uFE0F?|[\uD83C\uDDE6-\uD83C\uDDFF]{1,2}|" +
            "[\uD83C\uDD70\uD83C\uDD71\uD83C\uDD7E\uD83C\uDD7F\uD83C\uDD8E\uD83C\uDD91-\uD83C\uDD9A]\uFE0F?|"+
            "[\u0023\u002A\u0030-\u0039]\uFE0F?\u20E3|[\u2194-\u2199\u21A9-\u21AA]\uFE0F?|" +
            "[\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55]\uFE0F?|" +
            "[\u2934\u2935]\uFE0F?|[\u3030\u303D]\uFE0F?|[\u3297\u3299]\uFE0F?|" +
            "[\uD83C\uDE01\uD83C\uDE02\uD83C\uDE1A\uD83C\uDE2F\uD83C\uDE32-" +
            "\uD83C\uDE3A\uD83C\uDE50\uD83C\uDE51]\uFE0F?|" +
            "[\u203C\u2049]\uFE0F?|[\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE]\uFE0F?|" +
            "[\u00A9\u00AE]\uFE0F?|[\u2122\u2139]\uFE0F?|\uD83C\uDC04\uFE0F?|\uD83C\uDCCF\uFE0F?|" +
            "[\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA]\uFE0F?)+");
    }

////////////////////////////////////////////////////////////////////////////////
// deprecated methods
////////////////////////////////////////////////////////////////////////////////

    /** Check if the string in argument is Java identifier.
     * @deprecated Please use StringParser.5Name instead.
     * @param s the string to be checked.
     * @return true if argument is Java identifier.
     */
    @Deprecated
    public static final boolean isJavaName(final String s) {return chkJavaName(s);}

    /** Check if the string in argument is Java qualified identifier.
     * @deprecated Please use StringParser.chkJavaQName instead.
     * @param s the string to be checked.
     * @return true if argument is Java qualified identifier.
     */
    @Deprecated
    public static final boolean isJavaQName(final String s) {
        StringParser p = new StringParser(s);
        return p.isJavaQName() && p.eos();
    }
}
