fix "limit" and "skip" logic in io (still not very good, though)
[org.ibex.io.git] / src / org / ibex / io / Fountain.java
1 // Copyright 2000-2005 the Contributors, as shown in the revision logs.
2 // Licensed under the Apache Public Source License 2.0 ("the License").
3 // You may not use this file except in compliance with the License.
4
5 package org.ibex.io;
6
7 import java.io.*;
8 import java.net.*;
9 import java.util.*;
10 import java.util.zip.*;
11 import org.ibex.util.*;
12 import java.sql.*;
13
14 /** a source of streams */
15 public interface Fountain {
16
17     public Stream getStream();
18     public long   getLength();
19     public int    getNumLines();
20
21     public static class File implements Fountain {
22         private final java.io.File file;
23         public File(java.io.File file) { this.file = file; }
24         public Stream getStream()      { return new Stream(file); }
25         public long getLength()        { return (int)file.length(); }
26         public int getNumLines()       { return Stream.countLines(getStream()); } 
27     }
28
29     // sketchy since the bytes can be modified
30     public static class ByteArray implements Fountain {
31         private final byte[] bytes;
32         private final int off;
33         private final int len;
34         protected ByteArray(String s) {
35             try {
36                 byte[] bytes = s.getBytes("UTF-8");
37                 this.bytes = bytes;
38                 this.off = 0;
39                 this.len = bytes.length;
40             } catch (UnsupportedEncodingException e) {
41                 throw new RuntimeException(e);
42             }
43         }
44         public ByteArray(byte[] bytes)                   { this(bytes, 0, bytes.length); }
45         public ByteArray(byte[] bytes, int off, int len) { this.bytes = bytes; this.off=off; this.len=len; }
46         public Stream getStream()                        { return new Stream(bytes, off, len); }
47         public long getLength()                          { return len; }
48         public int getNumLines()                         { return Stream.countLines(getStream()); } 
49     }
50
51
52     public static class StringFountain extends ByteArray {
53         public StringFountain(String s)                  { super(s); }
54     }
55
56     public static class SubFountain implements Fountain {
57         private final Fountain f;
58         private final int start;
59         private final int len;
60         public SubFountain(Fountain f, int start) { this(f, start, -1); }
61         public SubFountain(Fountain f, int start, int len) {
62             this.f = f;
63             this.start = start;
64             this.len = len;
65         }
66         public Stream getStream() {
67             Stream s = f.getStream();
68             // FIXME: this is really fragile and IMAP needs it
69             int remain = start;
70             while(remain > 0) {
71                 long result = s.skip(start);
72                 Log.error("skip", result + " / " + start);
73                 if (result == -1) return s;
74                 remain -= result;
75             }
76             if (len != -1) s.setLimit(len);
77             return s;
78         }
79         public long   getLength() {
80             if (len == -1) return f.getLength()-start;
81             return Math.min(f.getLength()-start, len);
82         }
83         public int    getNumLines() { return Stream.countLines(getStream()); }
84     }
85
86     public static class Concatenate implements Fountain {
87         private Fountain[] founts;
88         public Concatenate(Fountain f1, Fountain f2)     { this(new Fountain[] { f1, f2 }); }
89         public Concatenate(Fountain[] f)                 { this.founts = f; }
90         public Stream getStream() {
91             Stream ret = null;
92             for(int i=founts.length-1; i>=0; i--)
93                 ret = ret==null ? founts[i].getStream() : founts[i].getStream().appendStream(ret);
94             return ret;
95         }
96         public long getLength() {
97             long ret = 0;
98             for(int i=0; i<founts.length; i++)
99                 ret += founts[i].getLength();
100             return ret;
101         }
102         public int getNumLines() {
103             int ret = 0;
104             for(int i=0; i<founts.length; i++)
105                 ret += founts[i].getNumLines();
106             return ret;
107         }
108     }
109
110     // FIXME: untested
111     // note: only one ResultSet is allowed per Statement
112     public static class DatabaseColumn implements Fountain {
113         private ResultSet rs;
114         private int row;
115         private int column;
116         public DatabaseColumn(ResultSet rs, int column) throws SQLException {
117             this(rs, column, rs.getRow());
118         }
119         public DatabaseColumn(ResultSet rs, int column, int row) throws SQLException {
120             this.rs = rs;
121             this.row = row;
122             this.column = column;
123             synchronized(rs) {
124                 int type = rs.getType();
125                 if ((type | ResultSet.TYPE_SCROLL_INSENSITIVE) == 0)
126                     throw new RuntimeException("Fountain.DatabaseColumn(ResultSet,int) requires a SCROLL_INSENSITIVE ResultSet");
127                 if ((type | ResultSet.TYPE_FORWARD_ONLY) != 0)
128                     throw new RuntimeException("Fountain.DatabaseColumn(ResultSet,int) cannot use TYPE_FORWARD_ONLY ResultSets");
129             }
130             // FIXME: do we want to check HOLD_CURSORS_OVER_COMMIT ?
131         }
132         private Blob getBlob() throws SQLException {
133             synchronized(rs) {
134                 if (!rs.absolute(row))
135                     throw new RuntimeException("resultset was unable to move to desired row");
136                 return rs.getBlob(column);
137             }
138         }
139         public Stream getStream() {
140             try {
141                 synchronized(rs) {
142                     return new Stream(rs.getBinaryStream(column));
143                 }
144             } catch (SQLException e) { throw new RuntimeException(e); }
145         }
146         public long getLength() {
147             try {
148                 synchronized(rs) {
149                     return getBlob().length();
150                 }
151             } catch (SQLException e) { throw new RuntimeException(e); }
152         }
153         public int getNumLines()       { 
154             throw new RuntimeException("not implemented");
155         } 
156     }
157
158
159     //public static class LazyCachingStreamFountain implements Fountain {
160     //}
161
162     /*
163     public static interface Transformer {
164         public Fountain transform(Fountain in);
165
166         public static class Lift implements Fountain.Transformer {
167             private final Stream.Transformer func;
168             public Lift(Stream.Transformer func) { this.func = func; }
169             public Fountain transform(final Fountain in) {
170                 return new Fountain() {
171                         public Stream getStream() {
172                             return func.transform(in.getStream()); } };
173             }
174         }
175     }
176     */
177
178     public static class Util {
179         public static Fountain subFountain(Fountain f, int start) { return new SubFountain(f, start); }
180         public static Fountain subFountain(Fountain f, int start, int len) { return new SubFountain(f, start, len); }
181         public static Fountain create(String s) { return new StringFountain(s); }
182         public static Fountain concat(Fountain[] f) { return new Concatenate(f); }
183         public static Fountain concat(Fountain f1, Fountain f2) {
184             return new Concatenate(f1, f2);
185         }
186         public static Fountain concat(Fountain f1, Fountain f2, Fountain f3) {
187             return new Concatenate(f1, new Concatenate(f2, f3));
188         }
189     }
190 }