summaryrefslogtreecommitdiffstats
path: root/simple/simple-http/src/main/java/org/simpleframework/http/message/ArrayConsumer.java
blob: ef5db66b7c3301a326da67ab1a2f23b30a31e8fb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
/*
 * ArrayConsumer.java February 2007
 *
 * Copyright (C) 2007, Niall Gallagher <niallg@users.sf.net>
 *
 * 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 org.simpleframework.http.message;

import java.io.IOException;

import org.simpleframework.transport.ByteCursor;

/**
 * The <code>ArrayConsumer</code> object is a consumer that consumes 
 * bytes in to an internal array before processing. This consumes
 * all bytes read in to an internal array. Each read is met with an
 * invocation of the <code>scan</code> method, which searches for
 * the terminal token within the read chunk. Once the terminal token
 * has been read the excess bytes are reset and the data can be
 * processed by the subclass implementation. The internal array is
 * expanded if the number of consumed bytes exceeds its capacity.
 *
 * @author Niall Gallagher
 */ 
public abstract class ArrayConsumer implements ByteConsumer {

   /**
    * This is the array that is used to contain the read bytes.
    */         
   protected byte[] array;

   /**
    * This is the number of bytes that have been consumed so far.
    */ 
   protected int count;
   
   /**
    * This is the size of the chunk of bytes to read each time.
    */ 
   protected int chunk;
   
   /**
    * This determines whether the terminal token has been read.
    */
   protected boolean done;

   /**
    * Constructor for the <code>ArrayConsumer</code> object. This is
    * used to create a consumer that will consume all bytes in to an
    * internal array until a terminal token has been read. If excess
    * bytes are read by this consumer they are reset in the cursor.   
    */ 
   public ArrayConsumer() {
      this(1024);
   }        

   /**
    * Constructor for the <code>ArrayConsumer</code> object. This is
    * used to create a consumer that will consume all bytes in to an
    * internal array until a terminal token has been read. If excess
    * bytes are read by this consumer they are reset in the cursor.   
    *
    * @param size this is the initial array and chunk size to use
    */   
   public ArrayConsumer(int size) {
      this(size, 512);
   }
   
   /**
    * Constructor for the <code>ArrayConsumer</code> object. This is
    * used to create a consumer that will consume all bytes in to an
    * internal array until a terminal token has been read. If excess
    * bytes are read by this consumer they are reset in the cursor.   
    *
    * @param size this is the initial array size that is to be used
    * @param chunk this is the chunk size to read bytes as
    */   
   public ArrayConsumer(int size, int chunk) {
      this.array = new byte[size];
      this.chunk = chunk;
   }
   
   /**
    * This method is used to consume bytes from the provided cursor.
    * Each read performed is done in a specific chunk size to ensure
    * that a sufficiently large or small amount of data is read from
    * the <code>ByteCursor</code> object. After each read the byte 
    * array is scanned for the terminal token. When the terminal 
    * token is found the bytes are processed by the implementation.
    *
    * @param cursor this is the cursor to consume the bytes from
    */ 
   public void consume(ByteCursor cursor) throws IOException {
      if(!done) {
         int ready = cursor.ready();
         
         while(ready > 0) {
            int size = Math.min(ready, chunk);
         
            if(count + size > array.length) {
               resize(count + size);
            }
            size = cursor.read(array, count, size);
            count += size;
            
            if(size > 0) {
               int reset = scan();
               
               if(reset > 0) {
                  cursor.reset(reset);
               }
               if(done) {
                  process(); 
                  break;
               }
            }         
            ready = cursor.ready();         
         }
      }
   }
   
   /**
    * This method is used to add an additional chunk size to the 
    * internal array. Resizing of the internal array is required as
    * the consumed bytes may exceed the initial size of the array.
    * In such a scenario the array is expanded the chunk size.
    *
    * @param size this is the minimum size to expand the array to 
    */ 
   protected void resize(int size) throws IOException {
      if(array.length < size) {
         int expand = array.length + chunk;
         int max = Math.max(expand, size);
         byte[] temp = new byte[max];
         
         System.arraycopy(array, 0, temp, 0, count); 
         array = temp;
      }
   } 
   
   /**
    * When the terminal token is read from the cursor this will be
    * true. The <code>scan</code> method is used to determine the
    * terminal token. It is invoked after each read, when the scan
    * method returns a non-zero value then excess bytes are reset
    * and the consumer has finished.
    *
    * @return this returns true when the terminal token is read
    */ 
   public boolean isFinished() {
      return done;
   }
  
   /**
    * This method is invoked after the terminal token has been read.
    * It is used to process the consumed data and is typically used to
    * parse the input such that it can be used by the subclass for
    * some useful purpose. This is called only once by the consumer.
    */  
   protected abstract void process() throws IOException;
   
   /**
    * This method is used to scan for the terminal token. It searches
    * for the token and returns the number of bytes in the buffer 
    * after the terminal token. Returning the excess bytes allows the
    * consumer to reset the bytes within the consumer object.
    *
    * @return this returns the number of excess bytes consumed
    */ 
   protected abstract int scan() throws IOException;

}