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.matchers; 018 019import org.parboiled.MatcherContext; 020import org.parboiled.Rule; 021import org.parboiled.matchervisitors.MatcherVisitor; 022 023import java.util.List; 024 025import static org.parboiled.common.Preconditions.checkArgNotNull; 026 027/** 028 * Special wrapping matcher that performs memoization of the last mismatch of the wrapped sub rule. 029 */ 030public class MemoMismatchesMatcher implements Matcher { 031 private final Matcher inner; 032 033 public MemoMismatchesMatcher(Rule inner) { 034 this.inner = checkArgNotNull((Matcher) inner, "inner"); 035 } 036 037 @SuppressWarnings({"unchecked"}) 038 public <V> boolean match(MatcherContext<V> context) { 039 if (context.hasMismatched()) { 040 return false; 041 } 042 if (inner.match(context)) { 043 return true; 044 } 045 context.memoizeMismatch(); 046 return false; 047 } 048 049 // GraphNode 050 051 public List<Matcher> getChildren() { 052 return inner.getChildren(); 053 } 054 055 // Rule 056 057 public Rule label(String label) { 058 return new MemoMismatchesMatcher(inner.label(label)); 059 } 060 061 public Rule suppressNode() { 062 return new MemoMismatchesMatcher(inner.suppressNode()); 063 } 064 065 public Rule suppressSubnodes() { 066 return new MemoMismatchesMatcher(inner.suppressSubnodes()); 067 } 068 069 public Rule skipNode() { 070 return new MemoMismatchesMatcher(inner.skipNode()); 071 } 072 073 public Rule memoMismatches() { 074 return this; // already done 075 } 076 077 // Matcher 078 079 public String getLabel() {return inner.getLabel();} 080 081 public boolean hasCustomLabel() {return inner.hasCustomLabel();} 082 083 public boolean isNodeSuppressed() {return inner.isNodeSuppressed();} 084 085 public boolean areSubnodesSuppressed() {return inner.areSubnodesSuppressed();} 086 087 public boolean isNodeSkipped() {return inner.isNodeSkipped();} 088 089 public boolean areMismatchesMemoed() { return true; } 090 091 public void setTag(Object tagObject) { inner.setTag(tagObject); } 092 093 public Object getTag() { return inner.getTag(); } 094 095 public MatcherContext getSubContext(MatcherContext context) { 096 MatcherContext subContext = inner.getSubContext(context); 097 subContext.setMatcher(this); // we need to inject ourselves here otherwise we get cut out 098 return subContext; 099 } 100 101 public <R> R accept(MatcherVisitor<R> visitor) { 102 checkArgNotNull(visitor, "visitor"); 103 return inner.accept(visitor); 104 } 105 106 @Override 107 public String toString() { return inner.toString(); } 108 109 /** 110 * Retrieves the innermost Matcher that is not a MemoMismatchesMatcher. 111 * 112 * @param matcher the matcher to unwrap 113 * @return the given instance if it is not a MemoMismatchesMatcher, otherwise the innermost Matcher 114 */ 115 public static Matcher unwrap(Matcher matcher) { 116 if (matcher instanceof MemoMismatchesMatcher) { 117 MemoMismatchesMatcher memoMismatchesMatcher = (MemoMismatchesMatcher) matcher; 118 return unwrap(memoMismatchesMatcher.inner); 119 } 120 return matcher; 121 } 122}