//---------------------------------------------------------------------

        /// <summary>
        /// Reads a percentage for partial thinning of a cohort age or age
        /// range.
        /// </summary>
        /// <remarks>
        /// The percentage is bracketed by parentheses.
        /// </remarks>
        public static InputValue<Percentage> ReadPercentage(StringReader reader,
                                                            out int      index)
        {
            TextReader.SkipWhitespace(reader);
            index = reader.Index;

            //  Read left parenthesis
            int nextChar = reader.Peek();
            if (nextChar == -1)
                throw new InputValueException();  // Missing value
            if (nextChar != '(')
                throw MakeInputValueException(TextReader.ReadWord(reader),
                                              "Value does not start with \"(\"");
            StringBuilder valueAsStr = new StringBuilder();
            valueAsStr.Append((char) (reader.Read()));

            //  Read whitespace between '(' and percentage
            valueAsStr.Append(ReadWhitespace(reader));

            //  Read percentage
            string word = ReadWord(reader, ')');
            if (word == "")
                throw MakeInputValueException(valueAsStr.ToString(),
                                              "No percentage after \"(\"");
            valueAsStr.Append(word);
            Percentage percentage;
            try {
                percentage = Percentage.Parse(word);
            }
            catch (System.FormatException exc) {
                throw MakeInputValueException(valueAsStr.ToString(),
                                              exc.Message);
            }
            if (percentage < 0.0 || percentage > 1.0)
                throw MakeInputValueException(valueAsStr.ToString(),
                                              string.Format("{0} is not between 0% and 100%", word));

            //  Read whitespace and ')'
            valueAsStr.Append(ReadWhitespace(reader));
            char? ch = TextReader.ReadChar(reader);
            if (! ch.HasValue)
                throw MakeInputValueException(valueAsStr.ToString(),
                                              "Missing \")\"");
            valueAsStr.Append(ch.Value);
            if (ch != ')')
                throw MakeInputValueException(valueAsStr.ToString(),
                                              string.Format("Value ends with \"{0}\" instead of \")\"", ch));

            return new InputValue<Percentage>(percentage, valueAsStr.ToString());
        }
 public void NonEmptyString()
 {
     string str = "Hello World!";
     StringReader reader = new StringReader(str);
     int expectedIndex = 0;
     foreach (char expectedCh in str) {
         Assert.AreEqual(expectedIndex, reader.Index);
         int i = reader.Read();
         Assert.IsTrue(i != -1);
         Assert.AreEqual(expectedCh, (char) i);
         expectedIndex++;
     }
     Assert.AreEqual(expectedIndex, reader.Index);
     Assert.AreEqual(-1, reader.Peek());
 }
        public void ReadBlock()
        {
            string str = "Four score and seven years ago ...";
            StringReader reader = new StringReader(str);
            char[] buffer = new char[str.Length];
            int blockSize = 5;

            for (int bufferIndex = 0; bufferIndex < buffer.Length; bufferIndex += blockSize) {
                Assert.AreEqual(bufferIndex, reader.Index);
                int countToRead;
                if (bufferIndex + blockSize > buffer.Length)
                    countToRead = buffer.Length - bufferIndex;
                else
                    countToRead = blockSize;
                Assert.AreEqual(countToRead, reader.Read(buffer, bufferIndex,
                                                         countToRead));
            }
            Assert.AreEqual(str.Length, reader.Index);
            Assert.AreEqual(-1, reader.Peek());
            Assert.AreEqual(str, new string(buffer));
        }
 //---------------------------------------------------------------------
 /// <summary>
 /// Reads a word from a string reader.
 /// </summary>
 /// <remarks>
 /// The word is terminated by whitespace, the end of input, or a
 /// particular delimiter character.
 /// </remarks>
 public static string ReadWord(StringReader reader,
     char         delimiter)
 {
     StringBuilder word = new StringBuilder();
     int i = reader.Peek();
     while (i != -1 && ! char.IsWhiteSpace((char) i) && i != delimiter) {
         word.Append((char) reader.Read());
         i = reader.Peek();
     }
     return word.ToString();
 }
 //---------------------------------------------------------------------
 /// <summary>
 /// Reads whitespace from a string reader.
 /// </summary>
 public static string ReadWhitespace(StringReader reader)
 {
     StringBuilder whitespace = new StringBuilder();
     int i = reader.Peek();
     while (i != -1 && char.IsWhiteSpace((char) i)) {
         whitespace.Append((char) reader.Read());
         i = reader.Peek();
     }
     return whitespace.ToString();
 }
        //---------------------------------------------------------------------
        /// <summary>
        /// Read a string from a StringReader.
        /// </summary>
        /// <remarks>
        /// This method reads a string from a StringReader.  The string is
        /// either an unquoted word or a quoted string.  A word is one or more
        /// adjacent non-whitespace characters.  A quoted string is zero or
        /// more characters surrounded by a pair of quotes.  The quotes may be
        /// a pair of double quotes (") or a pair of single quotes (').  A
        /// quote character can be included inside a quoted string by escaping
        /// it with a backslash (\).
        /// <example>
        /// Here are some valid strings:
        /// <code>
        ///   foo
        ///   a-brief-phrase
        ///   C:\some\path\to\a\file.ext
        ///   ""
        ///   "Four score and seven years ago ..."
        ///   "That's incredulous!"
        ///   'That\'s incredulous!'
        ///   ''
        ///   'He said "Boo."'
        ///   "He said \"Boo.\""
        /// </code>
        /// </example>
        /// Whitespace preceeding the word or the quoted string is skipped.
        /// The delimiting quotes of a quoted string are removed.
        /// </remarks>
        /// <exception cref="">System.IO.EndOfStreamException</exception>
        public static InputValue<string> Read(StringReader reader,
            out int      index)
        {
            TextReader.SkipWhitespace(reader);
            if (reader.Peek() == -1)
                throw new InputValueException();

            index = reader.Index;
            char nextChar = (char) reader.Peek();
            if (nextChar == '\'' || nextChar == '"') {
                //  Quoted string
                char startQuote = (char) reader.Read();
                StringBuilder quotedStr = new StringBuilder();
                quotedStr.Append(startQuote);
                StringBuilder actualStr = new StringBuilder();
                bool endQuoteFound = false;
                while (! endQuoteFound) {
                    char? ch = TextReader.ReadChar(reader);
                    if (! ch.HasValue)
                        throw new InputValueException(quotedStr.ToString(),
                                                      "String has no end quote: {0}",
                                                      quotedStr.ToString());
                    if (ch.Value == startQuote) {
                        endQuoteFound = true;
                        quotedStr.Append(ch.Value);
                    }
                    else {
                        if (ch.Value == '\\') {
                            //  Get the next character if it's a quote.
                            nextChar = (char) reader.Peek();
                            if (nextChar == '\'' || nextChar == '"') {
                                quotedStr.Append(ch.Value);
                                ch = TextReader.ReadChar(reader);
                            }
                        }
                        actualStr.Append(ch.Value);
                        quotedStr.Append(ch.Value);
                    }
                }
                return new InputValue<string>(actualStr.ToString(),
                                              quotedStr.ToString());
            }
            else {
                string word = TextReader.ReadWord(reader);
                return new InputValue<string>(word, word);
            }
        }