001/* 002 * Copyright (C) 2009-2011 Mathias Doenitz 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.parboiled.buffers; 018 019import org.parboiled.common.IntArrayStack; 020import org.parboiled.errors.ParserRuntimeException; 021import org.parboiled.support.Chars; 022import org.parboiled.support.IndexRange; 023import org.parboiled.support.Position; 024import org.parboiled.common.IntArrayStack; 025import org.parboiled.common.Preconditions; 026import org.parboiled.errors.ParserRuntimeException; 027import org.parboiled.support.Chars; 028import org.parboiled.support.IndexRange; 029import org.parboiled.support.Position; 030 031import java.util.Arrays; 032 033import static org.parboiled.common.Preconditions.checkArgNotNull; 034import static org.parboiled.common.Preconditions.checkArgument; 035 036/** 037 * Immutable default implementation of an InputBuffer. 038 */ 039public class DefaultInputBuffer implements InputBuffer { 040 private final int length; 041 private final char[] buffer; 042 043 // the indices of the newline characters in the buffer 044 // built lazily, since the newline information is normally only needed in the case of parse errors when 045 // error messages need to be generated 046 private int[] newlines; 047 048 /** 049 * Constructs a new DefaultInputBuffer wrapping the given char array. 050 * CAUTION: For performance reasons the given char array is not defensively copied. 051 * 052 * @param buffer the chars 053 */ 054 public DefaultInputBuffer(char[] buffer) { 055 Preconditions.checkArgNotNull(buffer, "buffer"); 056 this.buffer = buffer; 057 this.length = buffer.length; 058 } 059 060 public char charAt(int index) { 061 return 0 <= index && index < length ? buffer[index] : 062 index - length > 100000 ? throwParsingException() : Chars.EOI; 063 } 064 065 private char throwParsingException() { 066 throw new ParserRuntimeException("Parser read more than 100K chars beyond EOI, " + 067 "verify that your grammar does not consume EOI indefinitely!"); 068 } 069 070 public boolean test(int index, char[] characters) { 071 int len = characters.length; 072 if (index < 0 || index > length - len) { 073 return false; 074 } 075 for (int i = 0; i < len; i++) { 076 if (buffer[index + i] != characters[i]) return false; 077 } 078 return true; 079 } 080 081 public String extract(int start, int end) { 082 if (start < 0) start = 0; 083 if (end >= length) end = length; 084 if (end <= start) return ""; 085 return new String(buffer, start, end - start); 086 } 087 088 public String extract(IndexRange range) { 089 return new String(buffer, range.start, Math.min(range.end, length) - range.start); 090 } 091 092 public Position getPosition(int index) { 093 buildNewlines(); 094 int line = getLine0(newlines, index); 095 int column = index - (line > 0 ? newlines[line - 1] : -1); 096 return new Position(line + 1, column); 097 } 098 099 public int getOriginalIndex(int index) { 100 return index; 101 } 102 103 // returns the zero based input line number the character with the given index is found in 104 private static int getLine0(int[] newlines, int index) { 105 int j = Arrays.binarySearch(newlines, index); 106 return j >= 0 ? j : -(j + 1); 107 } 108 109 public String extractLine(int lineNumber) { 110 buildNewlines(); 111 Preconditions.checkArgument(0 < lineNumber && lineNumber <= newlines.length + 1); 112 int start = lineNumber > 1 ? newlines[lineNumber - 2] + 1 : 0; 113 int end = lineNumber <= newlines.length ? newlines[lineNumber - 1] : length; 114 if (charAt(end - 1) == '\r') end--; 115 return extract(start, end); 116 } 117 118 public int getLineCount() { 119 buildNewlines(); 120 return newlines.length + 1; 121 } 122 123 private void buildNewlines() { 124 if (newlines == null) { 125 IntArrayStack newlines = new IntArrayStack(); 126 for (int i = 0; i < length; i++) { 127 if (buffer[i] == '\n') { 128 newlines.push(i); 129 } 130 } 131 this.newlines = new int[newlines.size()]; 132 newlines.getElements(this.newlines, 0); 133 } 134 } 135} 136