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;
022import org.parboiled.support.Var;
023import org.parboiled.support.Var;
024
025import java.util.List;
026
027import static org.parboiled.common.Preconditions.checkArgNotNull;
028
029/**
030 * Special wrapping matcher that manages the creation and destruction of execution frames for a number of action vars.
031 */
032public class VarFramingMatcher implements Matcher {
033    private final Matcher inner;
034    private final Var[] variables;
035
036    public VarFramingMatcher(Rule inner, Var[] variables) {
037        this.inner = checkArgNotNull((Matcher)inner, "inner");
038        this.variables = checkArgNotNull(variables, "variables");
039    }
040
041    public <V> boolean match(MatcherContext<V> context) {
042        for (Var var : variables) {
043            var.enterFrame();
044        }
045
046        boolean matched = inner.match(context);
047
048        for (Var var : variables) {
049            var.exitFrame();
050        }
051
052        return matched;
053    }
054
055    // GraphNode
056
057    public List<Matcher> getChildren() {
058        return inner.getChildren();
059    }
060
061    // Rule
062
063    public Rule label(String label) {
064        return new VarFramingMatcher(inner.label(label), variables);
065    }
066
067    public Rule suppressNode() {
068        return new VarFramingMatcher(inner.suppressNode(), variables);
069    }
070
071    public Rule suppressSubnodes() {
072        return new VarFramingMatcher(inner.suppressSubnodes(), variables);
073    }
074
075    public Rule skipNode() {
076        return new VarFramingMatcher(inner.skipNode(), variables);
077    }
078
079    public Rule memoMismatches() {
080        return new VarFramingMatcher(inner.memoMismatches(), variables);
081    }
082
083    // Matcher
084
085    public String getLabel() {return inner.getLabel();}
086
087    public boolean hasCustomLabel() {return inner.hasCustomLabel();}
088
089    public boolean isNodeSuppressed() {return inner.isNodeSuppressed();}
090
091    public boolean areSubnodesSuppressed() {return inner.areSubnodesSuppressed();}
092
093    public boolean isNodeSkipped() {return inner.isNodeSkipped();}
094
095    public boolean areMismatchesMemoed() { return inner.areMismatchesMemoed(); }
096
097    public void setTag(Object tagObject) { inner.setTag(tagObject); }
098
099    public Object getTag() { return inner.getTag(); }
100    
101    public MatcherContext getSubContext(MatcherContext context) {
102        MatcherContext subContext = inner.getSubContext(context);
103        subContext.setMatcher(this); // we need to inject ourselves here otherwise we get cut out
104        return subContext;
105    }
106
107    public <R> R accept(MatcherVisitor<R> visitor) {
108        checkArgNotNull(visitor, "visitor");
109        return inner.accept(visitor);
110    }
111
112    @Override
113    public String toString() { return inner.toString(); }
114
115    /**
116     * Retrieves the innermost Matcher that is not a VarFramingMatcher.
117     *
118     * @param matcher the matcher to unwrap
119     * @return the given instance if it is not a VarFramingMatcher, otherwise the innermost Matcher
120     */
121    public static  Matcher unwrap(Matcher matcher) {
122        if (matcher instanceof VarFramingMatcher) {
123            VarFramingMatcher varFramingMatcher = (VarFramingMatcher) matcher;
124            return unwrap(varFramingMatcher.inner);
125        }
126        return matcher;
127    }
128
129}