1 // Simple test that we can use xapian from java
3 // Copyright (C) 2005,2006,2007,2008,2011,2016,2017,2019,2023 Olly Betts
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation; either version 2 of the
8 // License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
22 // FIXME: need to sort out throwing wrapped Xapian::Error subclasses
23 //import org.xapian.errors.*;
25 // FIXME: "implements" not "extends" in JNI Java API
26 class MyMatchDecider
extends MatchDecider
{
27 public boolean accept(Document d
) {
28 // NB It's not normally appropriate to call getData() in a MatchDecider
29 // but we do it here to make sure we don't get an empty document.
31 return d
.getData().length() == 0;
33 } catch (XapianError e) {
40 // FIXME: "implements" not "extends" in JNI Java API
41 class MyExpandDecider
extends ExpandDecider
{
42 public boolean accept(String s
) { return s
.charAt(0) != 'a'; }
45 class MyFieldProcessor
extends FieldProcessor
{
46 public Query
apply(String str
) {
47 if (str
.equals("spam"))
48 return new Query("eggs");
49 return new Query("spam");
53 public class SmokeTest
{
54 public static void main(String
[] args
) throws Exception
{
55 TermGenerator termGenerator
= new TermGenerator();
56 termGenerator
.setFlags(TermGenerator
.FLAG_SPELLING
);
58 // Test the version number reporting functions give plausible
65 v
+= Version
.revision();
66 String v2
= Version
.string();
68 System
.err
.println("Unexpected version output (" + v
+ " != " + v2
+ ")");
72 Stem stem
= new Stem("english");
73 if (!stem
.toString().equals("Xapian::Stem(english)")) {
74 System
.err
.println("Unexpected stem.toString()");
77 Document doc
= new Document();
78 doc
.setData("a\000b");
79 String s
= doc
.getData();
81 System
.err
.println("getData+setData truncates at a zero byte");
84 if (!s
.equals("a\000b")) {
85 System
.err
.println("getData+setData doesn't transparently handle a zero byte");
88 if (!doc
.toString().equals("Document(docid=0, data=a\\xc0\\x80b)")) {
89 System
.err
.println("XPASS: UTF-8 encoding of zero byte fixed!");
93 // Surrogate pair case:
94 String falafel
= new String(Character
.toChars(0x1f9c6));
97 if (!s
.equals(falafel
)) {
98 System
.err
.println("getData+setData doesn't transparently handle a surrogate pair");
101 if (!doc
.toString().equals("Document(docid=0, data=\\xed\\xa0\\xbe\\xed\\xb7\\x86)")) {
102 System
.err
.println("XPASS: UTF-8 encoding of character >= U+10000 fixed!");
106 doc
.setData("is there anybody out there?");
107 doc
.addTerm("XYzzy");
108 // apply was stemWord() in the JNI bindings
109 doc
.addPosting(stem
.apply("is"), 1);
110 doc
.addPosting(stem
.apply("there"), 2);
111 doc
.addPosting(stem
.apply("anybody"), 3);
112 doc
.addPosting(stem
.apply("out"), 4);
113 doc
.addPosting(stem
.apply("there"), 5);
114 WritableDatabase db
= new WritableDatabase("", Xapian
.DB_BACKEND_INMEMORY
);
116 if (db
.getDocCount() != 1) {
117 System
.err
.println("Unexpected db.getDocCount()");
121 QueryParser qp
= new QueryParser();
123 // Test wrapping of null-able grouping parameter.
124 qp
.addBooleanPrefix("colour", "XC");
125 qp
.addBooleanPrefix("color", "XC");
126 qp
.addBooleanPrefix("foo", "XFOO", null);
127 qp
.addBooleanPrefix("bar", "XBAR", "XBA*");
128 qp
.addBooleanPrefix("baa", "XBAA", "XBA*");
129 DateRangeProcessor rpdate
= new DateRangeProcessor(1, Xapian
.RP_DATE_PREFER_MDY
, 1960);
130 qp
.addRangeprocessor(rpdate
);
131 qp
.addRangeprocessor(rpdate
, null);
132 qp
.addRangeprocessor(rpdate
, "foo");
134 if (!Query
.MatchAll
.toString().equals("Query(<alldocuments>)")) {
135 System
.err
.println("Unexpected Query.MatchAll.toString()");
139 if (!Query
.MatchNothing
.toString().equals("Query()")) {
140 System
.err
.println("Unexpected Query.MatchNothing.toString()");
144 String
[] terms
= { "smoke", "test", "terms" };
145 Query query
= new Query(Query
.OP_OR
, terms
);
146 if (!query
.toString().equals("Query((smoke OR test OR terms))")) {
147 System
.err
.println("Unexpected query.toString()");
150 Query
[] queries
= { new Query("smoke"), query
, new Query("string") };
151 Query query2
= new Query(Query
.OP_XOR
, queries
);
152 if (!query2
.toString().equals("Query((smoke XOR (smoke OR test OR terms) XOR string))")) {
153 System
.err
.println("Unexpected query2.toString()");
156 String
[] subqs
= { "a", "b" };
157 Query query3
= new Query(Query
.OP_OR
, subqs
);
158 if (!query3
.toString().equals("Query((a OR b))")) {
159 System
.err
.println("Unexpected query3.toString()");
162 Enquire enq
= new Enquire(db
);
164 // Check Xapian::BAD_VALUENO is wrapped suitably.
165 enq
.setCollapseKey(Xapian
.BAD_VALUENO
);
167 // Test that the non-constant wrapping prior to 1.4.10 still works.
168 enq
.setCollapseKey(Xapian
.getBAD_VALUENO());
170 enq
.setQuery(new Query(Query
.OP_OR
, "there", "is"));
171 MSet mset
= enq
.getMSet(0, 10);
172 if (mset
.size() != 1) {
173 System
.err
.println("Unexpected mset.size()");
176 MSetIterator m_itor
= mset
.begin();
177 Document m_doc
= null;
179 while(m_itor
.hasNext()) {
180 m_id
= m_itor
.next();
181 if(m_itor
.hasNext()) {
182 m_doc
= mset
.getDocument(m_id
);
186 // Only one doc exists in this mset
187 if(m_doc
!= null && m_doc
.getDocId() != 0) {
188 System
.err
.println("Unexpected docid");
192 String term_str
= "";
193 TermIterator itor
= enq
.getMatchingTermsBegin(mset
.getElement(0));
194 while (itor
.hasNext()) {
195 term_str
+= itor
.next();
199 if (!term_str
.equals("is there")) {
200 System
.err
.println("Unexpected term_str");
203 /* FIXME:dc: Fails since Xapian::Error is still unmapped
206 Database db_fail = new Database("NOsuChdaTabASe");
207 // Ignore the return value.
208 db_fail.getDocCount();
209 } catch (DatabaseOpeningError e) {
213 System.err.println("Managed to open non-existent database");
218 if (Query.OP_ELITE_SET != 10) {
219 System.err.println("OP_ELITE_SET is " + Query.OP_ELITE_SET + " not 10");
223 RSet rset
= new RSet();
225 ESet eset
= enq
.getESet(10, rset
, new MyExpandDecider());
226 // FIXME: temporary simple check
227 if (0 == eset
.size()) {
228 System
.err
.println("ESet.size() was 0");
233 for(ESetIterator eit
= eset
.begin(); eit
.hasNext(); ) {
234 // for (int i = 0; i < eset.size(); i++) {
235 if (eit
.getTerm().charAt(0) == 'a') {
236 System
.err
.println("MyExpandDecider wasn't used");
242 if (count
!= eset
.size()) {
243 System
.err
.println("ESet.size() mismatched number of terms returned by ESetIterator");
244 System
.err
.println(count
+ " " + eset
.size());
249 MSet mset2 = enq.getMSet(0, 10, null, new MyMatchDecider());
250 if (mset2.size() > 0) {
251 System.err.println("MyMatchDecider wasn't used");
256 if (!enq
.getQuery().toString().equals("Query((there OR is))")) {
257 System
.err
.println("Enquire::getQuery() returned the wrong query: " + enq
.getQuery().toString());
262 qp
.addPrefix("food", new MyFieldProcessor());
263 if (!qp
.parseQuery("food:spam").toString().equals("Query(eggs)")) {
264 System
.err
.println("FieldProcessor subclass doesn't work as expected");
270 // Wrapped functions which take/return byte[] for std::string.
272 // Check that serialisation returns byte[], that
273 // unserialisation takes byte[], and round-tripping works.
274 byte[] res
= Xapian
.sortableSerialise(1.675);
275 if (Xapian
.sortableUnserialise(res
) != 1.675) {
276 System
.err
.println("sortableSerialise() and/or sortableUnserialise() don't work as expected");
280 // Check that serialisation returns byte[], that
281 // unserialisation takes byte[], and round-tripping works.
282 Query q
= new Query("foo");
284 Query q_out
= Query
.unserialise(res
);
285 if (!q
.toString().equals(q_out
.toString())) {
286 System
.err
.println("Query serialisation doesn't work as expected");
290 // Check Document.addValue() takes byte[], that getValue()
291 // returns byte[], that serialisation returns byte[], that
292 // unserialisation takes byte[], and round-tripping works.
293 Document d
= new Document();
296 byte[] res2
= d
.getValue(7);
297 if (!java
.util
.Arrays
.equals(res
, res2
)) {
298 System
.err
.println("Document.getValue() returns a different byte[] to the one set with addValue()");
302 Document d_out
= Document
.unserialise(res
);
303 // Make sure the "terms_here" flag is set so the descriptions match.
304 d_out
.termListCount();
305 if (!d
.toString().equals(d_out
.toString())) {
306 System
.err
.println("Document serialisation doesn't work as expected");
307 System
.err
.println(d
.toString());
308 System
.err
.println(d_out
.toString());
312 // Check that serialisation returns byte[], that
313 // unserialisation takes byte[], and round-tripping works.
314 LatLongCoord llc
= new LatLongCoord(10.5, 45.25);
315 res
= llc
.serialise();
316 LatLongCoord llc_out
= new LatLongCoord();
317 llc_out
.unserialise(res
);
318 if (!llc
.toString().equals(llc_out
.toString())) {
319 System
.err
.println("LatLongCoord serialisation doesn't work as expected");
323 // Check that serialisation returns byte[], that
324 // unserialisation takes byte[], and round-tripping works.
325 LatLongCoords llcs
= new LatLongCoords();
327 res
= llcs
.serialise();
328 LatLongCoords llcs_out
= new LatLongCoords();
329 llcs_out
.unserialise(res
);
330 if (!llcs
.toString().equals(llcs_out
.toString())) {
331 System
.err
.println("LatLongCoords serialisation doesn't work as expected");
335 // Check `range_limit` mapped to byte[].
336 q
= new Query(Query
.op
.OP_VALUE_GE
, 0, res
);
337 if (q
.toString().length() == 0) {
338 // Mostly just a way to actually use the constructed object.
339 System
.err
.println("Query description shouldn't be empty");
343 // Check `range_limit` mapped to byte[].
344 q
= new Query(Query
.op
.OP_VALUE_LE
, 1, res
);
345 if (q
.toString().length() == 0) {
346 // Mostly just a way to actually use the constructed object.
347 System
.err
.println("Query description shouldn't be empty");
351 // Check `range_lower` and `range_upper` mapped to byte[].
352 q
= new Query(Query
.op
.OP_VALUE_RANGE
, 2, res
, res
);
353 if (q
.toString().length() == 0) {
354 // Mostly just a way to actually use the constructed object.
355 System
.err
.println("Query description shouldn't be empty");
359 // Check ValueSetMatchDecider.addValue() and removeValue() take
361 ValueSetMatchDecider vsmd
= new ValueSetMatchDecider(1, false);
363 vsmd
.removeValue(res
);
365 // Check Database.getValueLowerBound() and getValueUpperBound()
367 byte[] lo
= "abba".getBytes();
368 byte[] hi
= "xyzzy".getBytes();
370 WritableDatabase wdb
= new WritableDatabase("", Xapian
.DB_BACKEND_INMEMORY
);
371 Document document
= new Document();
372 document
.addValue(42, hi
);
373 wdb
.addDocument(document
);
374 document
.addValue(42, lo
);
375 wdb
.addDocument(document
);
379 if (!java
.util
.Arrays
.equals(db
.getValueLowerBound(42), lo
)) {
380 System
.err
.println("Database.getValueLowerBound() doesn't work as expected");
383 if (!java
.util
.Arrays
.equals(db
.getValueUpperBound(42), hi
)) {
384 System
.err
.println("Database.getValueUpperBound() doesn't work as expected");
388 ValueIterator it
= db
.valuestreamBegin(42);
389 if (!java
.util
.Arrays
.equals(it
.getValue(), hi
)) {
390 System
.err
.println("ValueIterator.getValue() doesn't work as expected");
394 } catch (Exception e
) {
395 System
.err
.println("Caught unexpected exception " + e
.toString());