001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.mina.filter.codec.statemachine; 021 022import org.apache.mina.core.buffer.IoBuffer; 023import org.apache.mina.filter.codec.ProtocolDecoderOutput; 024 025/** 026 * {@link DecodingState} which consumes all bytes until a fixed (ASCII) 027 * character is reached. The terminator is skipped. 028 * 029 * @author <a href="http://mina.apache.org">Apache MINA Project</a> 030 */ 031public abstract class ConsumeToTerminatorDecodingState implements DecodingState { 032 033 private final byte terminator; 034 035 private IoBuffer buffer; 036 037 /** 038 * Creates a new instance using the specified terminator character. 039 * 040 * @param terminator the terminator character. 041 */ 042 public ConsumeToTerminatorDecodingState(byte terminator) { 043 this.terminator = terminator; 044 } 045 046 /** 047 * {@inheritDoc} 048 */ 049 public DecodingState decode(IoBuffer in, ProtocolDecoderOutput out) throws Exception { 050 int terminatorPos = in.indexOf(terminator); 051 052 if (terminatorPos >= 0) { 053 int limit = in.limit(); 054 IoBuffer product; 055 056 if (in.position() < terminatorPos) { 057 in.limit(terminatorPos); 058 059 if (buffer == null) { 060 product = in.slice(); 061 } else { 062 buffer.put(in); 063 product = buffer.flip(); 064 buffer = null; 065 } 066 067 in.limit(limit); 068 } else { 069 // When input contained only terminator rather than actual data... 070 if (buffer == null) { 071 product = IoBuffer.allocate(0); 072 } else { 073 product = buffer.flip(); 074 buffer = null; 075 } 076 } 077 in.position(terminatorPos + 1); 078 return finishDecode(product, out); 079 } 080 081 if (buffer == null) { 082 buffer = IoBuffer.allocate(in.remaining()); 083 buffer.setAutoExpand(true); 084 } 085 086 buffer.put(in); 087 return this; 088 } 089 090 /** 091 * {@inheritDoc} 092 */ 093 public DecodingState finishDecode(ProtocolDecoderOutput out) throws Exception { 094 IoBuffer product; 095 // When input contained only terminator rather than actual data... 096 if (buffer == null) { 097 product = IoBuffer.allocate(0); 098 } else { 099 product = buffer.flip(); 100 buffer = null; 101 } 102 return finishDecode(product, out); 103 } 104 105 /** 106 * Invoked when this state has reached the terminator byte. 107 * 108 * @param product the read bytes not including the terminator. 109 * @param out the current {@link ProtocolDecoderOutput} used to write 110 * decoded messages. 111 * @return the next state if a state transition was triggered (use 112 * <code>this</code> for loop transitions) or <code>null</code> if 113 * the state machine has reached its end. 114 * @throws Exception if the read data violated protocol specification. 115 */ 116 protected abstract DecodingState finishDecode(IoBuffer product, ProtocolDecoderOutput out) throws Exception; 117}