16
16
package com .splunk ;
17
17
18
18
import java .io .*;
19
- import java .nio .ByteBuffer ;
20
- import java .util .Arrays ;
21
- import java .util .concurrent .Callable ;
22
19
23
20
/**
24
21
* Takes an InputStream containing a UTF-8 encoded XML document containing one or more
31
28
* it is filtering.
32
29
*/
33
30
class InsertRootElementFilterInputStream extends FilterInputStream {
31
+ private static final int REREAD_BUFFER_SIZE = 512 ;
32
+ private static byte [] resultsTagBytes ;
34
33
private final ByteArrayInputStream suffix = new ByteArrayInputStream ("</doc>" .getBytes ("UTF-8" ));
34
+ private ByteArrayInputStream beforeResultsBuffer ;
35
35
private boolean wrotePrefix ;
36
36
37
37
private byte [] oneByte = new byte [1 ];
38
38
39
+ static {
40
+ try {
41
+ resultsTagBytes = "results" .getBytes ("UTF-8" );
42
+ } catch (UnsupportedEncodingException e ) {
43
+ //should not be thrown because UTF-8 is supported
44
+ throw new RuntimeException (e );
45
+ }
46
+ }
47
+
39
48
InsertRootElementFilterInputStream (InputStream in ) throws IOException {
40
49
// Wrap in with a pushback stream so we can write our modified version back
41
50
// onto the beginning of it.
42
- super (new PushbackInputStream (in , 512 ));
51
+ super (new PushbackInputStream (in , REREAD_BUFFER_SIZE ));
52
+
43
53
PushbackInputStream pin = (PushbackInputStream )this .in ;
44
54
45
55
// Read bytes until we reach '>', then push everything we read, followed by "<doc>",
46
56
// back onto the stream. If we run out of input before we reach '>', then don't
47
57
// modify the stream.
48
58
ByteArrayOutputStream beforeResultsChars = new ByteArrayOutputStream ();
49
- ByteArrayOutputStream atResultsChars = new ByteArrayOutputStream ( );
59
+ beforeResultsBuffer = new ByteArrayInputStream ( new byte [ 0 ] );
50
60
51
61
int ch ;
52
62
while (true ) {
@@ -57,32 +67,19 @@ class InsertRootElementFilterInputStream extends FilterInputStream {
57
67
pin .unread (beforeResultsChars .toByteArray ());
58
68
return ;
59
69
} else if (ch == (int )'<' ) {
60
- // Try extending
61
- atResultsChars .reset ();
62
- int ech ;
63
- boolean matched = true ;
64
- for (byte b : "results" .getBytes ("UTF-8" )) {
65
- ech = this .in .read ();
66
- atResultsChars .write (ech );
67
- if (ech != b ) {
68
- // Extension failed. Put the bytes back on and search again.
69
- pin .unread (atResultsChars .toByteArray ());
70
- matched = false ;
71
- break ;
72
- }
73
- }
70
+ boolean resultsTag = isResultsTag (pin );
74
71
75
- if (matched ) {
72
+ if (resultsTag ) {
76
73
// If we reach here, the extension succeeded, so we insert <doc>, unread everything,
77
74
// and return.
78
75
79
76
// Unread the match.
80
- pin .unread (atResultsChars . toByteArray () );
77
+ pin .unread (InsertRootElementFilterInputStream . resultsTagBytes );
81
78
// Unread the opening '<' that led to our extension
82
79
pin .unread (ch );
83
- // Add a '<doc>' element to our read charactes and unread them.
80
+ // Add a '<doc>' element to our read characters
84
81
beforeResultsChars .write ("<doc>" .getBytes ("UTF-8" ));
85
- pin . unread (beforeResultsChars .toByteArray ());
82
+ beforeResultsBuffer = new ByteArrayInputStream (beforeResultsChars .toByteArray ());
86
83
wrotePrefix = true ;
87
84
return ;
88
85
} else {
@@ -96,9 +93,38 @@ class InsertRootElementFilterInputStream extends FilterInputStream {
96
93
}
97
94
}
98
95
96
+ private boolean isResultsTag (PushbackInputStream pin ) throws IOException {
97
+ // Try extending
98
+ ByteArrayOutputStream atResultsChars = new ByteArrayOutputStream ();
99
+ int ech ;
100
+ boolean resultsTag = true ;
101
+ for (byte b : resultsTagBytes ) {
102
+ ech = this .in .read ();
103
+ atResultsChars .write (ech );
104
+ if (ech != b ) {
105
+ // Extension failed. Put the bytes back on and search again.
106
+ pin .unread (atResultsChars .toByteArray ());
107
+ resultsTag = false ;
108
+ break ;
109
+ }
110
+ }
111
+ return resultsTag ;
112
+ }
113
+
99
114
@ Override
100
115
public int read (byte [] buffer , int offset , int length ) throws IOException {
101
- int result = in .read (buffer , offset , length );
116
+ // first we read from the buffer before the first results xml tag
117
+ int result = 0 ;
118
+ int availableFromBuffer = beforeResultsBuffer .available ();
119
+ if (offset < availableFromBuffer ) {
120
+ result = beforeResultsBuffer .read (buffer , offset , length );
121
+ if (length <= result ) {
122
+ return result ;
123
+ }
124
+ }
125
+
126
+ // then we read from the original input stream
127
+ result += in .read (buffer , offset +result , length -result );
102
128
if (result == -1 && wrotePrefix ) {
103
129
// No more bytes to read from in, and we have written '<doc>' earlier in the stream
104
130
return suffix .read (buffer , offset , length );
0 commit comments