44import java .util .ArrayList ;
55import java .util .Iterator ;
66import java .util .List ;
7+ import java .util .NoSuchElementException ;
78
89/**
910 *
1011 * @author thsc
1112 */
1213class AASPInMemoChunkCache implements AASPChunkCache {
14+ public static final int DEFAULT_MAX_CACHE_SIZE = 1000 ;
1315 private final CharSequence uri ;
1416 private final AASPChunkStorageFS chunkStorage ;
1517 private final int fromEra ;
@@ -21,19 +23,26 @@ class AASPInMemoChunkCache implements AASPChunkCache {
2123 private List <CharSequence > messageCache ;
2224 private int firstIndexMessageCache = -1 ;
2325 private int lastIndexMessageCache = -1 ;
24- private int maxCacheLen = 1000 ;
26+ private int maxCacheLen ;
2527
2628 private int numberOfMessages = 0 ;
2729
28- public AASPInMemoChunkCache (AASPChunkStorageFS chunkStorage ,
29- CharSequence uri , int fromEra , int toEra ) {
30-
30+ public AASPInMemoChunkCache (AASPChunkStorageFS chunkStorage ,
31+ CharSequence uri , int fromEra , int toEra , int maxCacheLen ) {
32+
3133 this .uri = uri ;
3234 this .chunkStorage = chunkStorage ;
3335 this .fromEra = fromEra ;
3436 this .toEra = toEra ;
37+ this .maxCacheLen = maxCacheLen ;
3538 }
36-
39+
40+ public AASPInMemoChunkCache (AASPChunkStorageFS chunkStorage ,
41+ CharSequence uri , int fromEra , int toEra ) {
42+
43+ this (chunkStorage , uri , fromEra , toEra , DEFAULT_MAX_CACHE_SIZE );
44+ }
45+
3746 private boolean initialized = false ;
3847
3948 private void initialize () throws IOException {
@@ -87,15 +96,10 @@ public CharSequence getURI() {
8796 }
8897
8998 @ Override
90- public Iterator <CharSequence > getMessages (boolean chronologically ) throws IOException {
99+ public Iterator <CharSequence > getMessages () throws IOException {
91100 this .initialize ();
92101
93- List <CharSequence > dummyList = new ArrayList <>();
94-
95- dummyList .add ("dummy entry 1" );
96- dummyList .add ("dummy entry 2" );
97-
98- return dummyList .iterator ();
102+ return new ChunkListMessageIterator (this .chunkList );
99103 }
100104
101105 @ Override
@@ -104,7 +108,7 @@ public CharSequence getMessage(int position, boolean chronologically)
104108
105109 this .initialize ();
106110
107- if (position > this .numberOfMessages )
111+ if (position >= this .numberOfMessages )
108112 throw new AASPException ("Position exceeds number of message" );
109113
110114 if (!chronologically ) {
@@ -125,14 +129,15 @@ public CharSequence getMessage(int position, boolean chronologically)
125129 int fittingChunkIndex = 0 ;
126130
127131 for (AASPChunk chunk : this .chunkList ) {
128- lastIndex = firstIndex + chunk .getNumberMessage ();
132+ lastIndex = firstIndex + chunk .getNumberMessage () - 1 ;
129133
130134 if (position >= firstIndex && position <= lastIndex ) {
131135 // we have got our chunk
132136 fittingChunk = chunk ;
133137 break ;
134138 }
135139
140+ firstIndex += chunk .getNumberMessage ();
136141 fittingChunkIndex ++;
137142 }
138143
@@ -145,8 +150,39 @@ public CharSequence getMessage(int position, boolean chronologically)
145150 // reset cache
146151 this .messageCache = new ArrayList <>();
147152
153+ /////////////////////////////////////////////////////////////////
148154 // simple approach in that first implementation ... we keep fitting chunk in memory
155+ /////////////////////////////////////////////////////////////////
156+
149157 Iterator <CharSequence > messages = fittingChunk .getMessages ();
158+
159+ // chunk bigger than max cache size?
160+ int chunkSize = fittingChunk .getNumberMessage ();
161+ if (chunkSize > this .maxCacheLen ) {
162+ // calculate how many messages to skip before caching
163+
164+ /*
165+ situation:
166+ chunk head |................position...........................| tail
167+ cache head |................| tail
168+
169+ solution: put position in middle of the cache
170+ chunk |................position...........................|
171+ planned cache |........position........|
172+ skipLen...|
173+ */
174+
175+ int skipLen = position - (this .maxCacheLen / 2 );
176+
177+ // first index in cache will be this one:
178+ firstIndex += skipLen ;
179+
180+ // skip
181+ for (;skipLen > 0 ; skipLen --) {
182+ this .messageCache .add (messages .next ());
183+ }
184+ }
185+
150186 this .firstIndexMessageCache = firstIndex ;
151187
152188 int counter = 0 ;
@@ -159,13 +195,83 @@ public CharSequence getMessage(int position, boolean chronologically)
159195 this .lastIndexMessageCache = this .firstIndexMessageCache + counter - 1 ;
160196
161197 // cache filled - call again
162- return this .getMessage (position , chronologically );
198+ /* not: it is always chronologically true!!
199+ a) we already came in with true -> it remains true
200+ b) we came with false -> we have already recalculated that position, we would
201+ move it around again with that call - keep position unchanged: true!
202+ */
203+
204+ return this .getMessage (position , true );
163205 }
164206
165207 @ Override
166208 public void add (CharSequence message ) throws IOException {
167209 // TODO
168210 //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
169211 }
170- }
171212
213+ private class ChunkListMessageIterator implements Iterator <CharSequence > {
214+
215+ private final List <AASPChunk > chunkList ;
216+ private AASPChunk currentChunk ;
217+ private int nextIndex ;
218+ private Iterator <CharSequence > currentIterator ;
219+ private CharSequence messageAhead ;
220+
221+ public ChunkListMessageIterator (List <AASPChunk > chunkList ) throws IOException {
222+ this .chunkList = chunkList ;
223+ this .currentChunk = null ;
224+ this .nextIndex = 0 ;
225+ this .messageAhead = null ; // mark as empty
226+ this .readAhead (); // init cache
227+ }
228+
229+ /**
230+ * read next message in field messageAhead.
231+ */
232+ private void readAhead () {
233+ if (this .currentIterator != null ) {
234+ if (this .currentIterator .hasNext ()) {
235+ // 'normal' case: we read next message
236+ this .messageAhead = this .currentIterator .next ();
237+ return ; // done
238+ }
239+ }
240+ // no more messages in that iterator / chunk
241+ if (this .chunkList == null || nextIndex >= this .chunkList .size () ) {
242+ return ; // there is no list at all or we are already through with it
243+ }
244+
245+ // open next chunk / iterator
246+ this .currentChunk = this .chunkList .get (this .nextIndex ++);
247+ try {
248+ this .currentIterator = this .currentChunk .getMessages ();
249+ this .readAhead (); // next try
250+ } catch (IOException e ) {
251+ // cannot recover from that problem
252+ return ;
253+ }
254+ }
255+
256+ @ Override
257+ public boolean hasNext () {
258+ return this .messageAhead != null ;
259+ }
260+
261+ @ Override
262+ public CharSequence next () {
263+ if (this .messageAhead == null ) {
264+ throw new NoSuchElementException ("list empty or already reached end" );
265+ }
266+
267+ // remove that single message cache
268+ CharSequence retMessage = this .messageAhead ;
269+ this .messageAhead = null ;
270+
271+ // read ahead - if possible
272+ this .readAhead ();
273+
274+ return retMessage ;
275+ }
276+ }
277+ }
0 commit comments