aboutsummaryrefslogtreecommitdiff
path: root/WordPress/src/main/java/org/xmlrpc/android/LoggedInputStream.java
blob: ad16c40a09f267f1f5a7a68e74d4a9fe52680db3 (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
package org.xmlrpc.android;

import java.io.IOException;
import java.io.InputStream;


/**
 * A LoggedInputStream adds logging functionality to another input stream.
 * <p>Note that calls on a LoggedInputStream are passed "as-is" to the underlying stream.</p>
 *
 *
 * <p>We're using a LoggedInputStream in {@code XMLRPClient.java} to log the XML-RPC response document in case of parser errors.<br />
 *
 * There are plenty of other ways to log the response, but a {@code XmlPullParser} wants an InputStream as input parameter, and
 * a LoggedInputStream seems the most reliable solution, with the smallest memory footprint.<br />
 * Below are other examples of logging we tried:</p>
 * <ul>
 * <li>Read the first 1000 characters from the original input stream, then create a new SequenceInputStream with both the characters just read (a new ByteArrayInputStream),
 * and the original input stream.</li>
 * <li>Read the whole content in a String and log it, then create an StringInputStream over the string, and pass the new stream to the parser.</li>
 * </ul>
 */

public final class LoggedInputStream extends InputStream {
    private final InputStream inputStream;

    private final static int MAX_LOG_SIZE = 1000;
    private final byte[] loggedString = new byte[MAX_LOG_SIZE];
    private int loggedStringSize = 0;

    public LoggedInputStream(InputStream input) {
        this.inputStream = input;
    }

    @Override
    public int available() throws IOException {
        return inputStream.available();
    }

    @Override
    public void close() throws IOException {
        inputStream.close();
    }

    @Override
    public void mark(int readlimit) {
        inputStream.mark(readlimit);
    }

    @Override
    public boolean markSupported() {
        return inputStream.markSupported();
    }

    @Override
    public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
        int bytesRead = inputStream.read(buffer, byteOffset, byteCount);
        if (bytesRead != -1) {
            log(buffer, byteOffset, bytesRead);
        }
        return bytesRead;
    }

    @Override
    public int read(byte[] buffer) throws IOException {
        return this.read(buffer, 0, buffer.length);
    }

    @Override
    public int read() throws IOException {
        int characterRead = inputStream.read();
        if (characterRead != -1) {
            log(characterRead);
        }
        return characterRead;
    }

    @Override
    public synchronized void reset() throws IOException {
        inputStream.reset();
    }

    @Override
    public long skip(long byteCount) throws IOException {
        return inputStream.skip(byteCount);
    }

    private void log(byte[] inputArray, int byteOffset, int byteCount) {
        int availableSpace = MAX_LOG_SIZE - loggedStringSize;
        if (availableSpace <= 0) {
            return;
        }
        int bytesLength = Math.min(availableSpace, byteCount);
        int startingPosition = MAX_LOG_SIZE - availableSpace;
        System.arraycopy(inputArray, byteOffset, loggedString, startingPosition, bytesLength);
        loggedStringSize += bytesLength;
    }

    private void log(int inputChar) {
        byte[] logThis = {(byte) inputChar};
        log(logThis, 0, 1);
    }

    public String getResponseDocument() {
        if (loggedStringSize == 0) {
            return "";
        } else {
            return new String(loggedString, 0, loggedStringSize);
        }
    }
}