1 package org.apache.maven.index.reader;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.Closeable;
23 import java.io.DataInput;
24 import java.io.DataInputStream;
25 import java.io.EOFException;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.UTFDataFormatException;
29 import java.util.Date;
30 import java.util.HashMap;
31 import java.util.Iterator;
32 import java.util.Map;
33 import java.util.NoSuchElementException;
34 import java.util.zip.GZIPInputStream;
35
36
37
38
39
40
41 public class ChunkReader
42 implements Closeable, Iterable<Map<String, String>>
43 {
44 private final String chunkName;
45
46 private final DataInputStream dataInputStream;
47
48 private final int version;
49
50 private final Date timestamp;
51
52 public ChunkReader( final String chunkName, final InputStream inputStream )
53 throws IOException
54 {
55 this.chunkName = chunkName.trim();
56 this.dataInputStream = new DataInputStream( new GZIPInputStream( inputStream, 2 * 1024 ) );
57 this.version = ( (int) dataInputStream.readByte() ) & 0xff;
58 this.timestamp = new Date( dataInputStream.readLong() );
59 }
60
61
62
63
64 public String getName()
65 {
66 return chunkName;
67 }
68
69
70
71
72 public int getVersion()
73 {
74 return version;
75 }
76
77
78
79
80 public Date getTimestamp()
81 {
82 return timestamp;
83 }
84
85
86
87
88 public Iterator<Map<String, String>> iterator()
89 {
90 try
91 {
92 return new IndexIterator( dataInputStream );
93 }
94 catch ( IOException e )
95 {
96 throw new RuntimeException( "error", e );
97 }
98 }
99
100
101
102
103 public void close()
104 throws IOException
105 {
106 dataInputStream.close();
107 }
108
109
110
111
112 private static class IndexIterator
113 implements Iterator<Map<String, String>>
114 {
115 private final DataInputStream dataInputStream;
116
117 private Map<String, String> nextRecord;
118
119 private IndexIterator( final DataInputStream dataInputStream )
120 throws IOException
121 {
122 this.dataInputStream = dataInputStream;
123 this.nextRecord = nextRecord();
124 }
125
126 public boolean hasNext()
127 {
128 return nextRecord != null;
129 }
130
131 public Map<String, String> next()
132 {
133 if ( nextRecord == null )
134 {
135 throw new NoSuchElementException( "chunk depleted" );
136 }
137 Map<String, String> result = nextRecord;
138 nextRecord = nextRecord();
139 return result;
140 }
141
142 public void remove()
143 {
144 throw new UnsupportedOperationException( "remove" );
145 }
146
147 private Map<String, String> nextRecord()
148 {
149 try
150 {
151 return readRecord( dataInputStream );
152 }
153 catch ( IOException e )
154 {
155 throw new RuntimeException( "read error", e );
156 }
157 }
158 }
159
160
161
162
163 private static Map<String, String> readRecord( final DataInput dataInput )
164 throws IOException
165 {
166 int fieldCount;
167 try
168 {
169 fieldCount = dataInput.readInt();
170 }
171 catch ( EOFException ex )
172 {
173 return null;
174 }
175
176 Map<String, String> recordMap = new HashMap<>();
177 for ( int i = 0; i < fieldCount; i++ )
178 {
179 readField( recordMap, dataInput );
180 }
181 return recordMap;
182 }
183
184 private static void readField( final Map<String, String> record, final DataInput dataInput )
185 throws IOException
186 {
187 dataInput.readByte();
188 String name = dataInput.readUTF();
189 String value = readUTF( dataInput );
190 record.put( name, value );
191 }
192
193 private static String readUTF( final DataInput dataInput )
194 throws IOException
195 {
196 int utflen = dataInput.readInt();
197
198 byte[] bytearr;
199 char[] chararr;
200
201 try
202 {
203 bytearr = new byte[utflen];
204 chararr = new char[utflen];
205 }
206 catch ( OutOfMemoryError e )
207 {
208 throw new IOException( "Index data content is corrupt", e );
209 }
210
211 int c, char2, char3;
212 int count = 0;
213 int chararrCount = 0;
214
215 dataInput.readFully( bytearr, 0, utflen );
216
217 while ( count < utflen )
218 {
219 c = bytearr[count] & 0xff;
220 if ( c > 127 )
221 {
222 break;
223 }
224 count++;
225 chararr[chararrCount++] = (char) c;
226 }
227
228 while ( count < utflen )
229 {
230 c = bytearr[count] & 0xff;
231 switch ( c >> 4 )
232 {
233 case 0:
234 case 1:
235 case 2:
236 case 3:
237 case 4:
238 case 5:
239 case 6:
240 case 7:
241
242 count++;
243 chararr[chararrCount++] = (char) c;
244 break;
245
246 case 12:
247 case 13:
248
249 count += 2;
250 if ( count > utflen )
251 {
252 throw new UTFDataFormatException( "malformed input: partial character at end" );
253 }
254 char2 = bytearr[count - 1];
255 if ( ( char2 & 0xC0 ) != 0x80 )
256 {
257 throw new UTFDataFormatException( "malformed input around byte " + count );
258 }
259 chararr[chararrCount++] = (char) ( ( ( c & 0x1F ) << 6 ) | ( char2 & 0x3F ) );
260 break;
261
262 case 14:
263
264 count += 3;
265 if ( count > utflen )
266 {
267 throw new UTFDataFormatException( "malformed input: partial character at end" );
268 }
269 char2 = bytearr[count - 2];
270 char3 = bytearr[count - 1];
271 if ( ( ( char2 & 0xC0 ) != 0x80 ) || ( ( char3 & 0xC0 ) != 0x80 ) )
272 {
273 throw new UTFDataFormatException( "malformed input around byte " + ( count - 1 ) );
274 }
275 chararr[chararrCount++] =
276 (char) ( ( ( c & 0x0F ) << 12 ) | ( ( char2 & 0x3F ) << 6 ) | ( char3 & 0x3F ) );
277 break;
278
279 default:
280
281 throw new UTFDataFormatException( "malformed input around byte " + count );
282 }
283 }
284
285
286 return new String( chararr, 0, chararrCount );
287 }
288 }