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 <code>CRLF</code> 
027 * has been encountered.
028 * 
029 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
030 */
031public abstract class ConsumeToCrLfDecodingState implements DecodingState {
032
033    /**
034     * Carriage return character
035     */
036    private static final byte CR = 13;
037
038    /**
039     * Line feed character
040     */
041    private static final byte LF = 10;
042
043    private boolean lastIsCR;
044
045    private IoBuffer buffer;
046
047    /**
048     * Creates a new instance.
049     */
050    public ConsumeToCrLfDecodingState() {
051        // Do nothing
052    }
053
054    public DecodingState decode(IoBuffer in, ProtocolDecoderOutput out) throws Exception {
055        int beginPos = in.position();
056        int limit = in.limit();
057        int terminatorPos = -1;
058
059        for (int i = beginPos; i < limit; i++) {
060            byte b = in.get(i);
061            if (b == CR) {
062                lastIsCR = true;
063            } else {
064                if (b == LF && lastIsCR) {
065                    terminatorPos = i;
066                    break;
067                }
068                lastIsCR = false;
069            }
070        }
071
072        if (terminatorPos >= 0) {
073            IoBuffer product;
074
075            int endPos = terminatorPos - 1;
076
077            if (beginPos < endPos) {
078                in.limit(endPos);
079
080                if (buffer == null) {
081                    product = in.slice();
082                } else {
083                    buffer.put(in);
084                    product = buffer.flip();
085                    buffer = null;
086                }
087
088                in.limit(limit);
089            } else {
090                // When input contained only CR or LF rather than actual data...
091                if (buffer == null) {
092                    product = IoBuffer.allocate(0);
093                } else {
094                    product = buffer.flip();
095                    buffer = null;
096                }
097            }
098            in.position(terminatorPos + 1);
099            return finishDecode(product, out);
100        }
101
102        in.position(beginPos);
103
104        if (buffer == null) {
105            buffer = IoBuffer.allocate(in.remaining());
106            buffer.setAutoExpand(true);
107        }
108
109        buffer.put(in);
110
111        if (lastIsCR) {
112            buffer.position(buffer.position() - 1);
113        }
114
115        return this;
116    }
117
118    /**
119     * {@inheritDoc}
120     */
121    public DecodingState finishDecode(ProtocolDecoderOutput out) throws Exception {
122        IoBuffer product;
123        // When input contained only CR or LF rather than actual data...
124        if (buffer == null) {
125            product = IoBuffer.allocate(0);
126        } else {
127            product = buffer.flip();
128            buffer = null;
129        }
130        return finishDecode(product, out);
131    }
132
133    /**
134     * Invoked when this state has reached a <code>CRLF</code>.
135     * 
136     * @param product the read bytes including the <code>CRLF</code>.
137     * @param out the current {@link ProtocolDecoderOutput} used to write 
138     *        decoded messages.
139     * @return the next state if a state transition was triggered (use 
140     *         <code>this</code> for loop transitions) or <code>null</code> if 
141     *         the state machine has reached its end.
142     * @throws Exception if the read data violated protocol specification.
143     */
144    protected abstract DecodingState finishDecode(IoBuffer product, ProtocolDecoderOutput out) throws Exception;
145}