/******************************************************************************* * * Copyright (c) 2008 Fujitsu Services Ltd. * * Author: Nick Battle * * This file is part of VDMJ. * * VDMJ is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * VDMJ is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with VDMJ. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ package org.overture.parser.lex; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.nio.charset.Charset; import java.util.Arrays; import java.util.Stack; import org.overture.ast.messages.InternalException; /** * A class to allow arbitrary checkpoints and backtracking while parsing a file. */ public class BacktrackInputReader extends Reader { /** * The different types of reader that may be obtained from a {@link BacktrackInputReader} * * @author pvj */ public enum ReaderType { Doc, Docx, Odf, Latex }; /** * Cached empty array for reader data */ private static final char[] EMPTY_DATA = new char[] {}; /** A stack of position markers for popping. */ private Stack<Integer> stack = new Stack<Integer>(); /** The characters from the file. */ private final char[] data; /** The current read position. */ private int pos = 0; /** The total number of characters in the file. */ private int max = 0; /** * Create an object to read the file name passed with the given charset. * * @param file * The filename to open * @param charset */ public BacktrackInputReader(File file, String charset) { try { InputStreamReader isr = readerFactory(file, charset); char[] buffer = new char[readerLength(file, isr)]; max = isr.read(buffer); data = Arrays.copyOf(buffer, max); pos = 0; isr.close(); } catch (IOException e) { throw new InternalException(0, e.getMessage()); } } /** * Create an object to read the file name passed with the default charset. * * @param file * The filename to open */ public BacktrackInputReader(File file) { this(file, Charset.defaultCharset().name()); } /** * Create an object to read the string passed. This is used in the interpreter to parse expressions typed in. * * @param expression * @param charset */ public BacktrackInputReader(String expression, String charset) { char[] buf = EMPTY_DATA; try { ByteArrayInputStream is = new ByteArrayInputStream(expression.getBytes(charset)); InputStreamReader isr = new LatexStreamReader(is, charset); buf = new char[expression.length() + 1]; max = isr.read(buf); pos = 0; isr.close(); is.close(); } catch (IOException e) { // This can never really happen... } finally { data = buf; } } /** * Create an object to read the string passed. This is used in the interpreter to parse expressions typed in. * * @param expression * @param charset * @param file * @param streamReaderType */ public BacktrackInputReader(String expression, String charset, File file, ReaderType streamReaderType) { char[] buf = EMPTY_DATA; try { if (expression.contains("\r\n")) { expression = expression.replace("\r\n", " \n"); } ByteArrayInputStream is = new ByteArrayInputStream(expression.getBytes(charset)); InputStreamReader isr = null; switch (streamReaderType) { case Doc: isr = new DocStreamReader(new FileInputStream(file), charset); break; case Docx: isr = new DocxStreamReader(new FileInputStream(file)); break; case Odf: isr = new ODFStreamReader(new FileInputStream(file)); break; case Latex: default: isr = new LatexStreamReader(is, charset); break; } if (!expression.contains("\r\n")) { expression = expression.replace("\n", " \n"); } buf = new char[expression.length() + 1]; max = isr.read(buf); pos = 0; isr.close(); is.close(); } catch (IOException e) { e.printStackTrace(); // This can never really happen... } finally { data = buf; } } /** * Create an InputStreamReader from a File, depending on the filename. * * @param file * @param charset * @return * @throws IOException */ public static InputStreamReader readerFactory(File file, String charset) throws IOException { String name = file.getName(); if (name.toLowerCase().endsWith(".doc")) { return new DocStreamReader(new FileInputStream(file), charset); } else if (name.toLowerCase().endsWith(".docx")) { return new DocxStreamReader(new FileInputStream(file)); } else if (name.toLowerCase().endsWith(".odt")) { return new ODFStreamReader(new FileInputStream(file)); } else { return new LatexStreamReader(new FileInputStream(file), charset); } } /** * Calculate the length to allocate for a given file/stream. */ private int readerLength(File file, InputStreamReader isr) { String name = file.getName(); if (name.endsWith(".docx")) { return ((DocxStreamReader) isr).length(); } else if (name.endsWith(".odt")) { return ((ODFStreamReader) isr).length(); } else { return (int) (file.length() + 1); } } /** * Create an object to read the string passed with the default charset. * * @param expression */ public BacktrackInputReader(String expression) { this(expression, Charset.defaultCharset().name()); } /** * Push the current location to permit backtracking. */ public void push() { stack.push(pos); } /** * Pop the last location, but do not backtrack to it. This is used when the parser reached a point where a potential * ambiguity has been resolved, and it knows that it will never need to backtrack. */ public void unpush() { stack.pop(); // don't set pos though } /** * Pop the last location and reposition the stream at that position. The state of the stream is such that the next * read operation will return the same character that would have been read immediately after the push() operation * that saved the position. */ public void pop() { pos = stack.pop(); } /** * Read one character. * * @return */ public char readCh() { return data.length <= pos || pos == max ? (char) -1 : data[pos++]; } /** * Read characters into the array passed. */ @Override public int read(char[] cbuf, int off, int len) { int n = 0; while (pos != max && n < len) { cbuf[off + n++] = data[pos++]; } return n == 0 ? -1 : n; } /** * Close the input stream. */ @Override public void close() { return; // Stream was closed at the start anyway. } public int getCurrentRawReadOffset() { return pos; } }