//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // PLEASE DO NOT EDIT WITHOUT THE EXPLICIT CONSENT OF THE AUTHOR! \\ //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ using System; using System.IO; using System.Text; using Ilog.Language.Util; using Ilog.Language.Tools; namespace Ilog.Language.IO { /** * This class implements a Reader that may include another Reader * in midstream reading, and eventually resume reading at that point * upon reaching EOF in the included Reader. * *

* * By default, included readers are read seamlessly. That is, when starting or * ending an inclusion, the next character is that which starts the included * reader or the next one in the enclosing reader. Inclusion seams may or not be * made visible with setSeamless(bool). Called with false on * an IncludeReader object, it will make its read() return * CC.SOI ('start of inclusion') or CC.EOI ('end of inclusion'), * both int constants, when starting or ending, respectively, reading an * inclusion. Calling it with true re/sets seamless reading. * *

* * NB: It is possible to prevent circular inclusions, although * not in all situations. Indeed there is no way, in general, for an * arbitary Reader to be identified as reading from a source being * actively included. However, this can be done for IncludeReaders * constructed from file readers by using the IncludeReader(string) * constructor and the include(string) method, which provide * identifiable file names. For file readers included using this * constructor and method, any circular inclusion will be detected * and cause a CircularInclusionException to be thrown. * * @see CircularInclusionException * * @version Last modified on Sun May 29 13:27:04 2005 by hak * @author Hassan Aït-Kaci * @copyright © 2002 ILOG, S.A. */ public class IncludeReader : TextReader { /** * The current underlying reader. */ private TextReader _reader; /** * The name of the file associated to the current reader - or null * if the current reader is not reading from a file. */ private string _file; /** * The current line number of the current reader. */ private int _line = 1; /** * The current column number on the current line of the current reader. */ private int _col = 0; /** * The stack recording the suspended readers. */ private Stack _readerStack = new Stack(); /** * The set of files currently being actively included. */ private HashSet _dejaVu = new HashSet(); /** * A flag indicating whether inclusions are seamless. */ private bool _isSeamless = true; /** * A flag indicating that nothing has been read yet from the * current reader. */ private bool _isInclusionStart = false; /** * The latest character read in the current reader. */ private int _chr = CC.SOI; // HAK 2 HAK: the constructors should throw an exception if used with a // reader of type Ilog.Language.Io.LAReader!!! /** * Constructs an IncludeReader with the specified Reader. */ public IncludeReader (TextReader reader) { _reader = reader; } /** * Constructs an IncludeReader with the specified file name. * * @throws FileNotFoundException (f the specified file does not exist) */ public IncludeReader (string file) // throws FileNotFoundException { _reader = new StreamReader(file); _file = file; _dejaVu[file] = true; } /** * Sets or unsets seamless inclusion mode. */ public void SetSeamless (bool flag) { _isSeamless = flag; } /** * Returns true iff this IncludeReader is seamless. */ public bool IsSeamless () { return _isSeamless; } /** * This is the name of the file associated to the current reader - * or null if the current reader is not reading from a file. */ public string FileName { get { if (_file == null && _reader is IncludeReader) return ((IncludeReader)_reader).FileName; return _file; } set { _file = value; } } /** * This is latest character read in the current reader. */ public int LatestChar { get { return _chr; } } /** * This is the current line number of the current reader. */ public int LineNumber { get { return _line; } } /** * This is the current column number of the current reader. */ public int ColumnNumber { get { return _col; } } /** * Closes all readers associated with this IncludeReader. * * @throws IOException (if an I/O error occurs) */ public override void Close () // Throws IOException { while (!(_readerStack.Count == 0)) ((ReaderStackElement)_readerStack.Pop()).Reader().Close(); _reader.Close(); } /** * Returns the depth of inclusion of the current reader. */ public int Depth () { return _readerStack.Count; } /** * Returns true iff this is reading from the outermost reader. */ public bool IsOutermost () { return _readerStack.Count == 0; } /** * Reads and returns the next character in this IncludeReader. When * not in seamless mode, returns CC.SOI (resp., CC.EOI) right * before starting (resp., after ending) reading an inclusion. Returns * CC.EOF only at the end of the outermost enclosing reader. * * @return The character read (0 to 65535), CC.EOF, CC.EOI, or CC.SOI. * @throws IOException (if an I/O error occurs) */ public override int Read () // throws IOException { if (_isInclusionStart) { _isInclusionStart = false; _chr = CC.SOI; } else { _chr = _reader.Read(); while (_chr == CC.EOF) { if (_readerStack.Count == 0) break; _reader.Close(); if (_file != null) _dejaVu[_file] = false; ReaderStackElement elt = (ReaderStackElement)_readerStack.Pop(); _reader = elt.Reader(); _file = elt.File(); _line = elt.Line(); _col = elt.Col(); _chr = CC.EOI; } } switch (_chr) { case CC.SOI: _line = 1; _col = 0; break; case CC.EOI: case CC.EOF: break; case CC.EOL: _line++; _col = 0; break; case CC.BSP: if (_col > 0) _col--; break; default: _col++; break; } if (_isSeamless && (_chr == CC.SOI || _chr == CC.EOI)) return Read(); eof = (_chr == CC.EOF); return _chr; } /** * Returns the next character in this IncludeReader without * removing it as next to be read. When not in seamless mode, this will * return CC.SOI (resp., CC.EOI) if they are next * to be read. Returns CC.EOF only if the character that would * be returned by an actual read is the end of the outermost enclosing * reader. * * @return The character read (0 to 65535), CC.EOF, CC.EOI, or CC.SOI. * @throws IOException (if an I/O error occurs) */ public override int Peek () // throws IOException { if (_isInclusionStart && !_isSeamless) return CC.SOI; int next = _reader.Peek(); if (next == CC.EOF) { if (_readerStack.Count == 0) return CC.EOF; foreach (Object elt in _readerStack) { next = ((ReaderStackElement)elt).Reader().Peek(); if (next != CC.EOF) break; } if (next != CC.EOF && !_isSeamless) return CC.EOI; } return next; } /** * Suspends reading from the current reader and makes further reads * proceed with the specified reader. Reading from the suspended reader * at the exact point it was left resumes automatically upon reaching * the end of the included reader. * */ public void Include (TextReader reader) { _readerStack.Push(new ReaderStackElement(_reader,_line,_col)); _reader = reader; _isInclusionStart = true; } /** * Suspends reading from the current reader and makes further reads * proceed with a reader from the specified file. Reading from the * suspended file reader at the exact point it was left resumes * automatically upon reaching the end of the included file. * * @throws FileNotFoundException (if the specified file does not exist) * @throws CircularInclusionException (if trying to open an enclosing Reader) */ public void Include (string file) // throws FileNotFoundException, CircularInclusionException { if (_dejaVu[file]) throw new CircularInclusionException(file); _readerStack.Push(new ReaderStackElement(_reader,_file,_line,_col)); _reader = new StreamReader(file); _file = file; _dejaVu[file] = true; _isInclusionStart = true; } /** * Read characters into a portion of an array. Note that if not in seamless mode, * CC.EOI and CC.SOI will be read into the array like any other character. * * @param cbuf Destination buffer * @param off Offset at which to start storing characters * @param len Maximum number of characters to read * @return number of characters read or CC.EOF * @throws IOException (if an I/O error occurs) */ public override int ReadBlock (char[] cbuf, int off, int len) // throws IOException { return Read(cbuf,off,len); } /** * Read characters into a portion of an array. Note that if not in seamless mode, * CC.EOI and CC.SOI will be read into the array like any other character. * * @param cbuf Destination buffer * @param off Offset at which to start storing characters * @param len Maximum number of characters to read * @return number of characters read or CC.EOF * @throws IOException (if an I/O error occurs) */ public override int Read (char[] cbuf, int off, int len) // throws IOException { int c; int count = 0; while (off < cbuf.Length && count < len) switch (c = Read()) { case CC.EOF: return c; default: cbuf[off] = (char)c; off++; count++; break; } return count; } /** * Read characters into an array. * * @param cbuf Destination buffer * @return Number of characters read, or one of CC.EOF, CC.SOI, or CC.EOI. * @throws IOException (if an I/O error occurs) */ public int Read (char[] cbuf) // throws IOException { return Read(cbuf,0,cbuf.Length); } /** * Skip n characters or until CC.EOF is found. Note that * CC.SOI and CC.EOI are skipped but not counted, regardless of * whether the inclusion mode is seamless or not. * * @param n The number of characters to skip * @return The number of characters actually skipped * @throws IllegalArgumentException (if n is negative) * @throws IOException (if an I/O error occurs) */ public long Skip (long n) // throws IOException { if (n < 0) throw new ArgumentException ("Cannot skip negative number of chars: "+n); int count = 0; while (count < n) switch (Read()) { case CC.EOF: return count; case CC.EOI: case CC.SOI: continue; default: count++; break; } return count; } /** * This is true iff the end of the outermost reader has been reached. */ private bool eof = false; /** * Reads up to the end of line or the end of current reader, * whichever comes first, and returns the line as a string. * Returns null if end of file has been reached. */ public override string ReadLine () { if (eof) return null; StringBuilder buf = new StringBuilder(); for (int chr = Read(); (chr != CC.EOL && chr != CC.EOF); chr = Read()) buf.Append((char)chr); return buf.ToString(); } /** * If in seamless mode, reads from the current buffer's position up * to the end of the outermost reader and returns the result as a * string. Otherwise, reads up to the end of the current reader and * returns the result as a string. All exhausted readers are closed * after this is called. */ public override string ReadToEnd () { StringBuilder buf = new StringBuilder(_reader.ReadToEnd()); _reader.Close(); if (_isSeamless) while (_readerStack.Count != 0) { _reader = ((ReaderStackElement)_readerStack.Pop()).Reader(); buf.Append(_reader.ReadToEnd()); _reader.Close(); } return buf.ToString(); } /** * Returns a string form describing the current state of the reader. */ public override string ToString () { return ""; } // Ancillary classes definitions... /** * This defines the type of elements pushed on the reader stack. It * consists simply of a quadruple <reader,file,line,column>. */ private struct ReaderStackElement { private TextReader _reader; private string _file; private int _line; private int _col; internal ReaderStackElement (TextReader reader, int line, int col) { _reader = reader; _file = "(no file)"; _line = line; _col = col; } internal ReaderStackElement (TextReader reader, string file, int line, int col) { _reader = reader; _file = file; _line = line; _col = col; } internal TextReader Reader () { return _reader; } internal string File () { return _file; } internal int Line () { return _line; } internal int Col () { return _col; } } } /** * This extends IOException to signals an attempt to include a file * already being included by an IncludeReader. */ public class CircularInclusionException : IOException { /** * Constructs a new CircularInclusionException with a file name. */ internal CircularInclusionException (string file) : base("File: "+file) { } } }