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 java.util.Queue;
023import java.util.concurrent.ConcurrentLinkedQueue;
024
025import org.apache.mina.core.buffer.IoBuffer;
026import org.apache.mina.core.session.IoSession;
027import org.apache.mina.filter.codec.ProtocolDecoder;
028import org.apache.mina.filter.codec.ProtocolDecoderOutput;
029
030/**
031 * {@link ProtocolDecoder} which uses a {@link DecodingState} to decode data.
032 * Use a {@link DecodingStateMachine} as {@link DecodingState} to create
033 * a state machine which can decode your protocol.
034 * <p>
035 * NOTE: This is a stateful decoder. You should create one instance per session.
036 * </p>
037 * 
038 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
039 */
040public class DecodingStateProtocolDecoder implements ProtocolDecoder {
041    private final DecodingState state;
042
043    private final Queue<IoBuffer> undecodedBuffers = new ConcurrentLinkedQueue<IoBuffer>();
044
045    private IoSession session;
046
047    /**
048     * Creates a new instance using the specified {@link DecodingState} 
049     * instance.
050     * 
051     * @param state the {@link DecodingState}.
052     * @throws IllegalArgumentException if the specified state is <code>null</code>.
053     */
054    public DecodingStateProtocolDecoder(DecodingState state) {
055        if (state == null) {
056            throw new IllegalArgumentException("state");
057        }
058        this.state = state;
059    }
060
061    /**
062     * {@inheritDoc}
063     */
064    public void decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
065        if (this.session == null) {
066            this.session = session;
067        } else if (this.session != session) {
068            throw new IllegalStateException(getClass().getSimpleName() + " is a stateful decoder.  "
069                    + "You have to create one per session.");
070        }
071
072        undecodedBuffers.offer(in);
073        for (;;) {
074            IoBuffer b = undecodedBuffers.peek();
075            if (b == null) {
076                break;
077            }
078
079            int oldRemaining = b.remaining();
080            state.decode(b, out);
081            int newRemaining = b.remaining();
082            if (newRemaining != 0) {
083                if (oldRemaining == newRemaining) {
084                    throw new IllegalStateException(DecodingState.class.getSimpleName() + " must "
085                            + "consume at least one byte per decode().");
086                }
087            } else {
088                undecodedBuffers.poll();
089            }
090        }
091    }
092
093    /**
094     * {@inheritDoc}
095     */
096    public void finishDecode(IoSession session, ProtocolDecoderOutput out) throws Exception {
097        state.finishDecode(out);
098    }
099
100    /**
101     * {@inheritDoc}
102     */
103    public void dispose(IoSession session) throws Exception {
104        // Do nothing
105    }
106}