All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.fge.grappa.matchers.ActionMatcher Maven / Gradle / Ivy

/*
 * Copyright (C) 2009-2011 Mathias Doenitz
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.github.fge.grappa.matchers;

import com.github.fge.grappa.matchers.base.AbstractMatcher;
import com.github.fge.grappa.matchers.base.Matcher;
import com.github.fge.grappa.rules.Action;
import com.github.fge.grappa.rules.SkippableAction;
import com.google.common.collect.Lists;
import com.github.fge.grappa.run.context.ContextAware;
import com.github.fge.grappa.run.context.MatcherContext;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Objects;

/**
 * A {@link Matcher} that not actually matches input but runs a given parser {@link Action}.
 */
public final class ActionMatcher
    extends AbstractMatcher
{
    private final Action action;
    private final List> contextAwares = Lists.newArrayList();
    private final boolean skipInPredicates;

    public ActionMatcher(final Action action)
    {
        super(Objects.requireNonNull(action).toString());
        this.action = action;

        skipInPredicates = action instanceof SkippableAction
            && ((SkippableAction) action).skipInPredicates();

        // check whether the action is a synthetic class generated by
        // parboiled transformation if so it will take care of context
        // management itself and we can return immediately
        /*
         * NOTE: was Class before, but see constructor
         */
        final Class actionClass = action.getClass();

        if (actionClass.isSynthetic())
            return;

        if (action instanceof ContextAware)
            contextAwares.add((ContextAware) action);

        // in order to make anonymous inner classes and other member classes
        // work seamlessly we collect the synthetic references to the outer
        // parent classes and inform them of the current parsing context if they
        // implement ContextAware
        for (final Field field: actionClass.getDeclaredFields()) {
            if (!field.isSynthetic())
                continue;
            if (!ContextAware.class.isAssignableFrom(field.getType()))
                continue;

            field.setAccessible(true);
            try {
                final ContextAware contextAware
                    = (ContextAware) field.get(action);
                if (contextAware != null)
                    contextAwares.add(contextAware);
            } catch (IllegalAccessException ignored) {
                // ignore
            } finally {
                field.setAccessible(false);
            }
        }
    }

    @Override
    public MatcherType getType()
    {
        return MatcherType.ACTION;
    }

    @Override
    public  MatcherContext getSubContext(final MatcherContext context)
    {
        final MatcherContext subContext = context.getBasicSubContext();
        subContext.setMatcher(this);
        // if we have already matched something we must be in a sequence at the
        // second or later position the subcontext contains match data that the
        // action might want to access, so we use the existing subcontext
        // without reinitializing
        return context.getCurrentIndex() > context.getStartIndex()
            ? subContext
            : context.getSubContext(this);
    }

    @Override
    @SuppressWarnings("unchecked")
    public  boolean match(final MatcherContext context)
    {
        if (skipInPredicates && context.inPredicate())
            return true;

        // actions need to run in the parent context
        final MatcherContext parentContext = context.getParent();
        for (final ContextAware contextAware: contextAwares)
            ((ContextAware) contextAware).setContext(parentContext);

        final Object valueStackSnapshot
            = context.getValueStack().takeSnapshot();
        if (!((Action) action).run(parentContext)) {
            // failing actions are not allowed to change the ValueStack
            context.getValueStack().restoreSnapshot(valueStackSnapshot);
            return false;
        }

        // since we initialize the actions own context only partially in
        // getSubContext(MatcherContext) (in order to be able to still
        // access the previous subcontexts fields in action expressions) we
        // need to make sure to not accidentally advance the current index
        // of our parent with some old index from a previous subcontext, so
        // we explicitly set the marker here
        context.setCurrentIndex(parentContext.getCurrentIndex());
        return true;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy