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.util.byteaccess;
021
022import org.apache.mina.core.buffer.IoBuffer;
023
024/**
025 * Provides restricted, relative, write-only access to the bytes in a
026 * <code>CompositeByteArray</code>.
027 *
028 * Using this interface has the advantage that it can be automatically
029 * determined when a component <code>ByteArray</code> can no longer be written
030 * to, and thus components can be automatically flushed. This makes it easier to
031 * use pooling for underlying <code>ByteArray</code>s.
032 *
033 * By providing an appropriate <code>Expander</code> it is also possible to
034 * automatically add more backing storage as more data is written.
035 *<br>
036 *<br>
037 * TODO: Get flushing working.
038 * 
039 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
040 */
041public class CompositeByteArrayRelativeWriter extends CompositeByteArrayRelativeBase implements IoRelativeWriter {
042
043    /**
044     * An object that knows how to expand a <code>CompositeByteArray</code>.
045     */
046    public interface Expander {
047        void expand(CompositeByteArray cba, int minSize);
048    }
049
050    /**
051     * No-op expander.  The overridden method does nothing.
052     * 
053     */
054    public static class NopExpander implements Expander {
055        public void expand(CompositeByteArray cba, int minSize) {
056            // Do nothing.
057        }
058    }
059
060    /**
061     * Expands the supplied {@link CompositeByteArray} by the number of
062     * bytes provided in the constructor
063     * 
064     */
065    public static class ChunkedExpander implements Expander {
066
067        private final ByteArrayFactory baf;
068
069        private final int newComponentSize;
070
071        public ChunkedExpander(ByteArrayFactory baf, int newComponentSize) {
072            this.baf = baf;
073            this.newComponentSize = newComponentSize;
074        }
075
076        public void expand(CompositeByteArray cba, int minSize) {
077            int remaining = minSize;
078            while (remaining > 0) {
079                ByteArray component = baf.create(newComponentSize);
080                cba.addLast(component);
081                remaining -= newComponentSize;
082            }
083        }
084
085    }
086
087    /**
088     * An object that knows how to flush a <code>ByteArray</code>.
089     */
090    public interface Flusher {
091        // document free() behaviour
092        void flush(ByteArray ba);
093    }
094
095    /**
096     * The expander to use when the array underflows.
097     */
098    private final Expander expander;
099
100    /**
101     * The flusher to use when flushing component <code>ByteArray</code>s.
102     */
103    private final Flusher flusher;
104
105    /**
106     * Whether or not to automatically flush a component once the cursor moves
107     * past it.
108     */
109    private final boolean autoFlush;
110
111    /**
112     * 
113     * Creates a new instance of CompositeByteArrayRelativeWriter.
114     *
115     * @param cba
116     *  The CompositeByteArray to use to back this class
117     * @param expander
118     *  The expander.  Will increase the size of the internal ByteArray
119     * @param flusher
120     *  Flushed the ByteArray when necessary
121     * @param autoFlush
122     *  Should this class automatically flush?
123     */
124    public CompositeByteArrayRelativeWriter(CompositeByteArray cba, Expander expander, Flusher flusher,
125            boolean autoFlush) {
126        super(cba);
127        this.expander = expander;
128        this.flusher = flusher;
129        this.autoFlush = autoFlush;
130    }
131
132    private void prepareForAccess(int size) {
133        int underflow = cursor.getIndex() + size - last();
134        if (underflow > 0) {
135            expander.expand(cba, underflow);
136        }
137    }
138
139    /**
140     * Flush to the current index.
141     */
142    public void flush() {
143        flushTo(cursor.getIndex());
144    }
145
146    /**
147     * Flush to the given index.
148     * 
149     * @param index The end position
150     */
151    public void flushTo(int index) {
152        ByteArray removed = cba.removeTo(index);
153        flusher.flush(removed);
154    }
155
156    /**
157     * {@inheritDoc}
158     */
159    public void skip(int length) {
160        cursor.skip(length);
161    }
162
163    @Override
164    protected void cursorPassedFirstComponent() {
165        if (autoFlush) {
166            flushTo(cba.first() + cba.getFirst().length());
167        }
168    }
169
170    /**
171     * {@inheritDoc}
172     */
173    public void put(byte b) {
174        prepareForAccess(1);
175        cursor.put(b);
176    }
177
178    /**
179     * {@inheritDoc}
180     */
181    public void put(IoBuffer bb) {
182        prepareForAccess(bb.remaining());
183        cursor.put(bb);
184    }
185
186    /**
187     * {@inheritDoc}
188     */
189    public void putShort(short s) {
190        prepareForAccess(2);
191        cursor.putShort(s);
192    }
193
194    /**
195     * {@inheritDoc}
196     */
197    public void putInt(int i) {
198        prepareForAccess(4);
199        cursor.putInt(i);
200    }
201
202    /**
203     * {@inheritDoc}
204     */
205    public void putLong(long l) {
206        prepareForAccess(8);
207        cursor.putLong(l);
208    }
209
210    /**
211     * {@inheritDoc}
212     */
213    public void putFloat(float f) {
214        prepareForAccess(4);
215        cursor.putFloat(f);
216    }
217
218    /**
219     * {@inheritDoc}
220     */
221    public void putDouble(double d) {
222        prepareForAccess(8);
223        cursor.putDouble(d);
224    }
225
226    /**
227     * {@inheritDoc}
228     */
229    public void putChar(char c) {
230        prepareForAccess(2);
231        cursor.putChar(c);
232    }
233}