/** * Copyright (C) 2011 rwitzel75@googlemail.com * * 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.rwitzel.streamflyer.experimental.stateful.util; import java.util.regex.MatchResult; import com.github.rwitzel.streamflyer.core.AfterModification; import com.github.rwitzel.streamflyer.experimental.stateful.State; import com.github.rwitzel.streamflyer.experimental.stateful.StatefulAfterModification; import com.github.rwitzel.streamflyer.internal.thirdparty.ZzzValidate; import com.github.rwitzel.streamflyer.regex.MatchProcessor; import com.github.rwitzel.streamflyer.regex.MatchProcessorResult; import com.github.rwitzel.streamflyer.regex.RegexModifier; /** * This state refers to a token (described by a regular expression). If that token is found, then the state might be * changed. * <p> * The default behavior of this class neither changes the state nor the character buffer. Override * <ul> * <li>{@link #findState(StringBuilder, int, MatchResult)}, * <li> {@link #modifyBuffer(StringBuilder, int, MatchResult)}, * <li> * {@link #processWithoutMatch(StringBuilder, int, boolean, AfterModification)} * </ul> * in order to modify the default behavior. * * @author rwoo * @since 14.09.2011 */ public class RegexTransitionState implements State, MatchProcessor { // // injected // private RegexModifier nextTokensRegexModifier; // // state // /** * The next state determined by evaluating the result of a match. Null if there was no match. */ private State nextState; /** * @param nextTokensRegexModifier */ public RegexTransitionState(RegexModifier nextTokensRegexModifier) { super(); ZzzValidate.notNull(nextTokensRegexModifier, "nextTokensRegexModifier must not be null"); this.nextTokensRegexModifier = nextTokensRegexModifier; } /** * @see com.github.rwitzel.streamflyer.experimental.stateful.State#modify(java.lang.StringBuilder, int, boolean) */ @Override public StatefulAfterModification modify(StringBuilder characterBuffer, int firstModifiableCharacterInBuffer, boolean endOfStreamHit) { AfterModification afterModification = nextTokensRegexModifier.modify(characterBuffer, firstModifiableCharacterInBuffer, endOfStreamHit); StatefulAfterModification result; if (nextState != null) { // there was a match -> use the next state result = new StatefulAfterModification(afterModification, nextState); nextState = null; } else { // there was no match -> use the existing state afterModification = processWithoutMatch(characterBuffer, firstModifiableCharacterInBuffer, endOfStreamHit, afterModification); result = new StatefulAfterModification(afterModification, this); } return result; } /** * @see com.github.rwitzel.streamflyer.regex.MatchProcessor#process(java.lang.StringBuilder, int, * java.util.regex.MatchResult) */ @Override public MatchProcessorResult process(StringBuilder characterBuffer, int firstModifiableCharacterInBuffer, MatchResult matchResult) { // *** find the next state (this depends on the match result) // TODO use the content of the top-level groups nextState = findState(characterBuffer, firstModifiableCharacterInBuffer, matchResult); // *** modify the buffer firstModifiableCharacterInBuffer = modifyBuffer(characterBuffer, firstModifiableCharacterInBuffer, matchResult); // do we have to change the state? if (!nextState.equals(this)) { // yes, state must be changed -> we must not continue to match with // this object return new MatchProcessorResult(firstModifiableCharacterInBuffer, false); } else { // no, state must not be changed -> we continue to match with this // object return new MatchProcessorResult(firstModifiableCharacterInBuffer, true); } } /** * Finds the next state (evaluating the given match result). * <p> * The default implementation returns 'this' state. */ protected State findState(StringBuilder characterBuffer, int firstModifiableCharacterInBuffer, MatchResult matchResult) { return this; } /** * Modifies the character buffer if the token was found. Returns the position of the first character that is * modifiable. * <p> * The default implementation does not modify the buffer. Returns the end of the match as new first modifiable * character in the buffer. */ protected int modifyBuffer(StringBuilder characterBuffer, int firstModifiableCharacterInBuffer, MatchResult matchResult) { return matchResult.end(); } /** * Modifies the character buffer if no token is found. * <p> * The default implementation returns the given {@link AfterModification}. */ protected AfterModification processWithoutMatch(StringBuilder characterBuffer, int firstModifiableCharacterInBuffer, boolean endOfStreamHit, AfterModification afterModification) { return afterModification; } // // override Object.* // /** * @see java.lang.Object#toString() */ @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("RegexTransitionState [nextTokensRegexModifier="); builder.append(nextTokensRegexModifier); builder.append(", \nnextState="); // builder.append(nextState); builder.append("<nextState>"); // no endless loop, please builder.append("]"); return builder.toString(); } }