/** * 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.xml; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.github.rwitzel.streamflyer.core.AfterModification; import com.github.rwitzel.streamflyer.core.Modifier; import com.github.rwitzel.streamflyer.experimental.stateful.State; import com.github.rwitzel.streamflyer.experimental.stateful.StatefulModifier; import com.github.rwitzel.streamflyer.experimental.stateful.util.IdleModifierState; import com.github.rwitzel.streamflyer.experimental.stateful.util.OneTransitionState; import com.github.rwitzel.streamflyer.internal.thirdparty.ZzzValidate; import com.github.rwitzel.streamflyer.util.ModificationFactory; import com.github.rwitzel.streamflyer.xml.XmlPrologRidiculouslyLongException; /** * Same semantics as {@link com.github.rwitzel.streamflyer.xml.XmlVersionModifier} but the implementation uses a * {@link StatefulModifier}. * * @author rwoo * * @since 14.09.2011 */ public class XmlVersionModifier implements Modifier { private StatefulModifier statefulModifier; // // constants // public final int INITIAL_NUMBER_OF_CHARACTERS = 4096; // // injected properties // protected ModificationFactory factory; protected String xmlVersion; // // constructors // public XmlVersionModifier(String xmlVersion, int newNumberOfChars) { ZzzValidate.notNull(xmlVersion, "xmlVersion must not be null"); this.factory = new ModificationFactory(0, newNumberOfChars); this.xmlVersion = xmlVersion; // (1) create the initial state: No input read yet. OneTransitionState initialState = new OneTransitionState("initial") { @Override public AfterModification innerModify(StringBuilder characterBuffer, int firstModifiableCharacterInBuffer, boolean endOfStreamHit) { // you never know how many whitespace characters are in the // prolog return factory.modifyAgainImmediately(INITIAL_NUMBER_OF_CHARACTERS, firstModifiableCharacterInBuffer); } }; // (2) create version modifying state: The modifier has requested to // read the XML prolog. The version of prolog is modified or a prolog is // added if the prolog is missing. OneTransitionState versionModifyingState = new OneTransitionState("versionModifying") { @Override public AfterModification innerModify(StringBuilder characterBuffer, int firstModifiableCharacterInBuffer, boolean endOfStreamHit) { // (Should we do aware of BOMs here? No. I consider it the // responsibility of the caller to provide characters without // BOM.) Matcher matcher = Pattern.compile("<\\?xml[^>]*version\\s*=\\s*['\"]((1.0)|(1.1))['\"].*").matcher( characterBuffer); if (matcher.matches()) { // replace version in prolog characterBuffer.replace(matcher.start(1), matcher.end(1), XmlVersionModifier.this.xmlVersion); } else { // is there a prolog that is too long? Matcher matcher2 = Pattern.compile("<\\?xml.*").matcher(characterBuffer); if (matcher2.matches()) { // this is not normal at all -> throw exception throw new XmlPrologRidiculouslyLongException(characterBuffer.toString()); } // insert prolog characterBuffer.insert(0, "<?xml version='" + XmlVersionModifier.this.xmlVersion + "'>"); } return factory.skipEntireBuffer(characterBuffer, firstModifiableCharacterInBuffer, endOfStreamHit); } }; // (3) create idle state: The modifier has read the XML prolog, and has // modified it if necessary. Nothing more to do for the modifier. State idleState = new IdleModifierState(factory); // (4) link the states: The state transitions are from "initial" to // "version modifying" to "idle" initialState.setNextState(versionModifyingState); versionModifyingState.setNextState(idleState); // 5) create the stateful modifier this.statefulModifier = new StatefulModifier(initialState); } // // override Modifier.* // /** * @see com.github.rwitzel.streamflyer.core.Modifier#modify(java.lang.StringBuilder, int, boolean) */ @Override public AfterModification modify(StringBuilder characterBuffer, int firstModifiableCharacterInBuffer, boolean endOfStreamHit) { // delegate return statefulModifier.modify(characterBuffer, firstModifiableCharacterInBuffer, endOfStreamHit); } }