1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1997,2008 Oracle.  All rights reserved.
5 *
6 * $Id: BtRecExample.java,v 12.8 2008/01/08 20:58:32 bostic Exp $
7 */
8
9
10package db;
11
12import com.sleepycat.db.*;
13import java.io.BufferedReader;
14import java.io.File;
15import java.io.FileNotFoundException;
16import java.io.FileReader;
17import java.io.InputStreamReader;
18import java.io.IOException;
19import java.io.PrintStream;
20
21public class BtRecExample {
22    static final String progname =  "BtRecExample"; // Program name.
23    static final String database =  "access.db";
24    static final String wordlist =  "../test/wordlist";
25
26    BtRecExample(BufferedReader reader)
27        throws DatabaseException, IOException, FileNotFoundException {
28
29        OperationStatus status;
30
31        // Remove the previous database.
32        File f = new File(database);
33        f.delete();
34
35        DatabaseConfig config = new DatabaseConfig();
36
37        config.setErrorStream(System.err);
38        config.setErrorPrefix(progname);
39        config.setPageSize(1024);           // 1K page sizes.
40
41        config.setBtreeRecordNumbers(true);
42        config.setType(DatabaseType.BTREE);
43        config.setAllowCreate(true);
44        db = new Database(database, null, config);
45
46        //
47        // Insert records into the database, where the key is the word
48        // preceded by its record number, and the data is the same, but
49        // in reverse order.
50        //
51
52        for (int cnt = 1; cnt <= 1000; ++cnt) {
53            String numstr = String.valueOf(cnt);
54            while (numstr.length() < 4)
55                numstr = "0" + numstr;
56            String buf = numstr + '_' + reader.readLine();
57            StringBuffer rbuf = new StringBuffer(buf).reverse();
58
59            StringEntry key = new StringEntry(buf);
60            StringEntry data = new StringEntry(rbuf.toString());
61
62            status = db.putNoOverwrite(null, key, data);
63            if (status != OperationStatus.SUCCESS &&
64                status!= OperationStatus.KEYEXIST)
65                throw new DatabaseException("Database.put failed " + status);
66        }
67    }
68
69    void run() throws DatabaseException {
70        int recno;
71        OperationStatus status;
72
73        // Acquire a cursor for the database.
74        cursor = db.openCursor(null, null);
75
76        //
77        // Prompt the user for a record number, then retrieve and display
78        // that record.
79        //
80        InputStreamReader reader = new InputStreamReader(System.in);
81
82        for (;;) {
83            // Get a record number.
84            String line = askForLine(reader, System.out, "recno #> ");
85            if (line == null)
86                break;
87
88            try {
89                recno = Integer.parseInt(line);
90            } catch (NumberFormatException nfe) {
91                System.err.println("Bad record number: " + nfe);
92                continue;
93            }
94
95            //
96            // Start with a fresh key each time, the db.get() routine returns
97            // the key and data pair, not just the key!
98            //
99            RecnoStringEntry key = new RecnoStringEntry(recno, 100);
100            RecnoStringEntry data = new RecnoStringEntry(100);
101
102            status = cursor.getSearchRecordNumber(key, data, null);
103            if (status != OperationStatus.SUCCESS)
104                throw new DatabaseException("Cursor.setRecno failed: " + status);
105
106            // Display the key and data.
107            show("k/d\t", key, data);
108
109            // Move the cursor a record forward.
110            status = cursor.getNext(key, data, null);
111            if (status != OperationStatus.SUCCESS)
112                throw new DatabaseException("Cursor.getNext failed: " + status);
113
114            // Display the key and data.
115            show("next\t", key, data);
116
117            RecnoStringEntry datano = new RecnoStringEntry(100);
118
119            //
120            // Retrieve the record number for the following record into
121            // local memory.
122            //
123            status = cursor.getRecordNumber(datano, null);
124            if (status != OperationStatus.SUCCESS &&
125                status != OperationStatus.NOTFOUND &&
126                status != OperationStatus.KEYEMPTY)
127                throw new DatabaseException("Cursor.get failed: " + status);
128            else {
129                recno = datano.getRecordNumber();
130                System.out.println("retrieved recno: " + recno);
131            }
132        }
133
134        cursor.close();
135        cursor = null;
136    }
137
138    //
139    // Print out the number of records in the database.
140    //
141    void stats() throws DatabaseException {
142        BtreeStats stats;
143
144        stats = (BtreeStats)db.getStats(null, null);
145        System.out.println(progname + ": database contains " +
146               stats.getNumData() + " records");
147    }
148
149    void show(String msg, RecnoStringEntry key, RecnoStringEntry data)
150        throws DatabaseException {
151
152        System.out.println(msg + key.getString() + ": " + data.getString());
153    }
154
155    public void shutdown() throws DatabaseException {
156        if (cursor != null) {
157            cursor.close();
158            cursor = null;
159        }
160        if (db != null) {
161            db.close();
162            db = null;
163        }
164    }
165
166    public static void main(String[] argv) {
167        try {
168            // Open the word database.
169            FileReader freader = new FileReader(wordlist);
170
171            BtRecExample app = new BtRecExample(new BufferedReader(freader));
172
173            // Close the word database.
174            freader.close();
175            freader = null;
176
177            app.stats();
178            app.run();
179        } catch (FileNotFoundException fnfe) {
180            System.err.println(progname + ": unexpected open error " + fnfe);
181            System.exit (1);
182        } catch (IOException ioe) {
183            System.err.println(progname + ": open " + wordlist + ": " + ioe);
184            System.exit (1);
185        } catch (DatabaseException dbe) {
186            System.err.println("Exception: " + dbe);
187            System.exit(dbe.getErrno());
188        }
189
190        System.exit(0);
191    }
192
193    // Prompts for a line, and keeps prompting until a non blank
194    // line is returned.  Returns null on error.
195    //
196    public static String askForLine(InputStreamReader reader,
197                                    PrintStream out, String prompt) {
198        String result = "";
199        while (result != null && result.length() == 0) {
200            out.print(prompt);
201            out.flush();
202            result = getLine(reader);
203        }
204        return result;
205    }
206
207    // Not terribly efficient, but does the job.
208    // Works for reading a line from stdin or a file.
209    // Returns null on EOF.  If EOF appears in the middle
210    // of a line, returns that line, then null on next call.
211    //
212    public static String getLine(InputStreamReader reader) {
213        StringBuffer b = new StringBuffer();
214        int c;
215        try {
216            while ((c = reader.read()) != -1 && c != '\n') {
217                if (c != '\r')
218                    b.append((char)c);
219            }
220        } catch (IOException ioe) {
221            c = -1;
222        }
223
224        if (c == -1 && b.length() == 0)
225            return null;
226        else
227            return b.toString();
228    }
229
230    private Cursor cursor;
231    private Database db;
232
233    // Here's an example of how you can extend DatabaseEntry in a
234    // straightforward way to allow easy storage/retrieval of strings.
235    // We've declared it as a static inner class, but it need not be.
236    //
237    static class StringEntry extends DatabaseEntry {
238        StringEntry() {}
239
240        StringEntry(String value) {
241            setString(value);
242        }
243
244        void setString(String value) {
245            byte[] data = value.getBytes();
246            setData(data);
247            setSize(data.length);
248        }
249
250        String getString() {
251            return new String(getData(), 0, getSize());
252        }
253    }
254
255    // Here's an example of how you can extend DatabaseEntry to store
256    // (potentially) both recno's and strings in the same structure.
257    //
258    static class RecnoStringEntry extends DatabaseEntry {
259        RecnoStringEntry(int maxsize) {
260            this(0, maxsize);     // let other constructor do most of the work
261        }
262
263        RecnoStringEntry(int value, int maxsize) {
264            arr = new byte[maxsize];
265            setData(arr);                // use our local array for data
266            setUserBuffer(maxsize, true);
267            setRecordNumber(value);
268        }
269
270        RecnoStringEntry(String value) {
271            byte[] data = value.getBytes();
272            setData(data);                // use our local array for data
273            setUserBuffer(data.length, true);
274        }
275
276        void setString(String value) {
277            byte[] data = value.getBytes();
278            setData(data);
279            setSize(data.length);
280        }
281
282        String getString() {
283            return new String(getData(), getOffset(), getSize());
284        }
285
286        byte[] arr;
287    }
288}
289