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

org.apache.drill.exec.compile.FsmDescriptor Maven / Gradle / Ivy

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.drill.exec.compile;

import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.google.common.base.Preconditions;

/**
 * Describes a finite state machine in terms of a mapping of tokens to
 * characters, a regular expression describing the valid transitions
 * using the characters, and an end state. Used to validate state
 * transitions. One simple example is the implementation of the
 * Check*VisitorFsm classes used to validate call sequences in ASM
 * visitors (classes like CheckClassAdapter only validate per-call
 * arguments, not the entire sequence of calls, according to
 * http://asm.ow2.org/asm50/javadoc/user/org/objectweb/asm/util/CheckClassAdapter.html
 *
 * 

The current implementation is very simple, and requires some user setup. * Basically, we use Java's {@link java.util.regex.Pattern} and * {@link java.util.regex.Matcher} to verify a regular expression on demand. * The user must map state transitions' string names onto characters, and * specify a regex that describes the state machine. Using this technique, we * can only validate when we transition to an end state; we just check to see * if accumulated characters comprise an allowed regular expression. * *

In this simple implementation, the tokens and characters may represent * states or transitions, depending on what is most convenient. In either case, * we just check that a sequence of them matches the regular expression governing * this state machine. */ public class FsmDescriptor { private final Map tokenMap; // state/transition -> character private final Pattern fsmPattern; // compiled regex representing the FSM private final char lastTransition; // the character that represents the last state/transition /** * Create a finite state machine descriptor. The descriptor is immutable, and may * be shared across many cursors that are executing the FSM. * * @param tokenMap mapping of transitions/states to characters * @param fsmRegex the regular expression, defined using the characters from the tokenMap * @param lastTransition the name of the final transition/state */ public FsmDescriptor(final Map tokenMap, final String fsmRegex, final String lastTransition) { Preconditions.checkNotNull(tokenMap); Preconditions.checkNotNull(fsmRegex); Preconditions.checkNotNull(lastTransition); Preconditions.checkArgument(tokenMap.containsKey(lastTransition)); // make sure the characters in the tokenMap are unique final HashSet charSet = new HashSet<>(); for(Map.Entry me : tokenMap.entrySet()) { final Character character = me.getValue(); if (charSet.contains(character)) { throw new IllegalArgumentException("Duplicate tokenMap char: '" + character + "'"); } charSet.add(character); } this.tokenMap = Collections.unmodifiableMap(tokenMap); this.fsmPattern = Pattern.compile(fsmRegex); this.lastTransition = this.tokenMap.get(lastTransition).charValue(); } /** * Create a cursor for performing and validating transitions according to this * state machine. * * @return the new cursor */ public FsmCursor createCursor() { return new FsmCursor(this); } /** * Validate the given transitions against this state machine. * * @param transitions a character sequence indicating a set of transitions or * states as defined by the tokenMap used at construction time. * @throws IllegalStateException if the set of transitions is not allowed */ void validateTransitions(final CharSequence transitions) { final long length = transitions.length(); if (length == 0) { return; // assume we haven't started yet } final Matcher matcher = fsmPattern.matcher(transitions); if (!matcher.find() || (matcher.start() != 0) || (matcher.end() != length)) { throw new IllegalStateException("Illegal state transition(s): " + transitions); } } /** * Look up the character used to represent the transition or state denoted by * the given token. * * @param token the transition or state to look up * @return the character used to represent that transition or state */ char getChar(final String token) { Preconditions.checkNotNull(token); final Character character = tokenMap.get(token); Preconditions.checkNotNull(character); return character.charValue(); } /** * Determine whether the indicated character represents the final transition or state. * * @param transitionChar the character to look up * @return true if the character represents the final transition or state */ boolean isLastTransition(final char transitionChar) { return transitionChar == lastTransition; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy