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.parserunners; 018 019import org.parboiled.MatchHandler; 020import org.parboiled.MatcherContext; 021import org.parboiled.Rule; 022import org.parboiled.buffers.InputBuffer; 023import org.parboiled.errors.InvalidInputError; 024import org.parboiled.matchervisitors.IsSingleCharMatcherVisitor; 025import org.parboiled.support.MatcherPath; 026import org.parboiled.support.ParsingResult; 027import org.parboiled.support.MatcherPath; 028import org.parboiled.support.ParsingResult; 029 030import java.util.ArrayList; 031import java.util.List; 032 033import static org.parboiled.common.Preconditions.checkArgNotNull; 034 035/** 036 * A {@link ParseRunner} implementation that creates an 037 * {@link InvalidInputError} for the error at a known error location. 038 * It never causes the parser to perform more than one parsing run and is rarely used directly. 039 * Instead its functionality is relied upon by the {@link ReportingParseRunner} and {@link RecoveringParseRunner} classes. 040 */ 041public class ErrorReportingParseRunner<V> extends AbstractParseRunner<V> implements MatchHandler { 042 private final IsSingleCharMatcherVisitor isSingleCharMatcherVisitor = new IsSingleCharMatcherVisitor(); 043 private final int errorIndex; 044 private final MatchHandler inner; 045 private final List<MatcherPath> failedMatchers = new ArrayList<MatcherPath>(); 046 private boolean seeking; 047 048 /** 049 * Creates a new ErrorReportingParseRunner instance for the given rule and the given errorIndex. 050 * 051 * @param rule the parser rule 052 * @param errorIndex the index of the error to report 053 */ 054 public ErrorReportingParseRunner(Rule rule, int errorIndex) { 055 this(rule, errorIndex, null); 056 } 057 058 /** 059 * Creates a new ErrorReportingParseRunner instance for the given rule and the given errorIndex. 060 * The given MatchHandler is used as a delegate for the actual match handling. 061 * 062 * @param rule the parser rule 063 * @param errorIndex the index of the error to report 064 * @param inner another MatchHandler to delegate the actual match handling to, can be null 065 */ 066 public ErrorReportingParseRunner(Rule rule, int errorIndex, MatchHandler inner) { 067 super(rule); 068 this.errorIndex = errorIndex; 069 this.inner = inner; 070 } 071 072 public ParsingResult<V> run(InputBuffer inputBuffer) { 073 checkArgNotNull(inputBuffer, "inputBuffer"); 074 resetValueStack(); 075 failedMatchers.clear(); 076 seeking = errorIndex > 0; 077 078 // run without fast string matching to properly get to the error location 079 MatcherContext<V> rootContext = createRootContext(inputBuffer, this, false); 080 boolean matched = match(rootContext); 081 if (!matched) { 082 getParseErrors().add(new InvalidInputError(inputBuffer, errorIndex, failedMatchers, null)); 083 } 084 return createParsingResult(matched, rootContext); 085 } 086 087 public boolean match(MatcherContext<?> context) { 088 boolean matched = inner == null && context.getMatcher().match(context) || inner != null && inner.match(context); 089 if (context.getCurrentIndex() == errorIndex) { 090 if (matched && seeking) { 091 seeking = false; 092 } 093 if (!matched && !seeking && context.getMatcher().accept(isSingleCharMatcherVisitor)) { 094 failedMatchers.add(context.getPath()); 095 } 096 } 097 return matched; 098 } 099} 100