source: SRUCQIBridge/src/main/java/eu/clarin/sru/cqibridge/CqiClient.java @ 2105

Last change on this file since 2105 was 2105, checked in by akislev, 12 years ago

This project provides a complete implementation of an SRU endpoint using a CQI server as a backend.

File size: 32.4 KB
Line 
1/**
2 * This software is copyright (c) 2012 by
3 * - TXM (http://txm.sourceforge.net/), Seminar fuer Sprachwissenschaft  (http://www.sfs.uni-tuebingen.de/)
4 * This is free software. You can redistribute it
5 * and/or modify it under the terms described in
6 * the GNU General Public License v3 of which you
7 * should have received a copy. Otherwise you can download
8 * it from
9 *
10 *   http://www.gnu.org/licenses/gpl-3.0.txt
11 *
12 * @copyright Seminar fuer Sprachwissenschaft (http://www.sfs.uni-tuebingen.de/)
13 *
14 * @license http://www.gnu.org/licenses/gpl-3.0.txt
15 *  GNU General Public License v3
16 */
17package eu.clarin.sru.cqibridge;
18
19import java.io.*;
20import java.net.InetSocketAddress;
21import java.net.Socket;
22import java.net.SocketAddress;
23import java.nio.charset.Charset;
24
25/**
26 * This class implements a Java CQi client.
27 *
28 * @author Jean-Philippe Magué
29 */
30public class CqiClient {
31
32    private static final byte[] CQI_PADDING = {(byte) 0x00};
33    private static final byte[] CQI_STATUS_OK = {(byte) 0x01, (byte) 0x01};
34    private static final byte[] CQI_STATUS_CONNECT_OK = {(byte) 0x01, (byte) 0x02};
35    private static final byte[] CQI_STATUS_BYE_OK = {(byte) 0x01, (byte) 0x03};
36    private static final byte[] CQI_STATUS_PING_OK = {(byte) 0x01, (byte) 0x04};
37    private static final byte[] CQI_DATA_BYTE = {(byte) 0x03, (byte) 0x01};
38    private static final byte[] CQI_DATA_BOOL = {(byte) 0x03, (byte) 0x02};
39    private static final byte[] CQI_DATA_INT = {(byte) 0x03, (byte) 0x03};
40    private static final byte[] CQI_DATA_STRING = {(byte) 0x03, (byte) 0x04};
41    private static final byte[] CQI_DATA_BYTE_LIST = {(byte) 0x03, (byte) 0x05};
42    private static final byte[] CQI_DATA_BOOL_LIST = {(byte) 0x03, (byte) 0x06};
43    private static final byte[] CQI_DATA_INT_LIST = {(byte) 0x03, (byte) 0x07};
44    private static final byte[] CQI_DATA_STRING_LIST = {(byte) 0x03, (byte) 0x08};
45    private static final byte[] CQI_DATA_INT_INT = {(byte) 0x03, (byte) 0x09};
46    private static final byte[] CQI_DATA_INT_INT_INT_INT = {(byte) 0x03, (byte) 0x0A};
47    private static final byte[] CQI_DATA_INT_TABLE = {(byte) 0x03, (byte) 0x0B};
48    private static final byte[] CQI_CTRL_CONNECT = {(byte) 0x11, (byte) 0x01};
49    private static final byte[] CQI_CTRL_BYE = {(byte) 0x11, (byte) 0x02};
50    private static final byte[] CQI_CTRL_LAST_GENERAL_ERROR = {(byte) 0x11, (byte) 0x05};
51    private static final byte[] CQI_CTRL_LAST_CQP_ERROR = {(byte) 0x11, (byte) 0x06};
52    private static final byte[] CQI_CORPUS_LIST_CORPORA = {(byte) 0x13, (byte) 0x01};
53    private static final byte[] CQI_CORPUS_CHARSET = {(byte) 0x13, (byte) 0x03};
54    private static final byte[] CQI_CORPUS_POSITIONAL_ATTRIBUTES = {(byte) 0x13, (byte) 0x05};
55    private static final byte[] CQI_CORPUS_STRUCTURAL_ATTRIBUTES = {(byte) 0x13, (byte) 0x06};
56    private static final byte[] CQI_CORPUS_STRUCTURAL_ATTRIBUTE_HAS_VALUES = {(byte) 0x13, (byte) 0x07};
57    private static final byte[] CQI_CORPUS_FULL_NAME = {(byte) 0x13, (byte) 0x09};
58    private static final byte[] CQI_CL_ATTRIBUTE_SIZE = {(byte) 0x14, (byte) 0x01};
59    private static final byte[] CQI_CL_LEXICON_SIZE = {(byte) 0x14, (byte) 0x02};
60    private static final byte[] CQI_CL_CPOS2STR = {(byte) 0x14, (byte) 0x08};
61    private static final byte[] CQI_CL_CPOS2LBOUND = {(byte) 0x14, (byte) 0x20};
62    private static final byte[] CQI_CL_CPOS2RBOUND = {(byte) 0x14, (byte) 0x21};
63    private static final byte[] CQI_CQP_QUERY = {(byte) 0x15, (byte) 0x01};
64    private static final byte[] CQI_CQP_LIST_SUBCORPORA = {(byte) 0x15, (byte) 0x02};
65    private static final byte[] CQI_CQP_SUBCORPUS_SIZE = {(byte) 0x15, (byte) 0x03};
66    private static final byte[] CQI_CQP_SUBCORPUS_HAS_FIELD = {(byte) 0x15, (byte) 0x04};
67    private static final byte[] CQI_CQP_DUMP_SUBCORPUS = {(byte) 0x15, (byte) 0x05};
68    private static final byte[] CQI_CQP_DROP_SUBCORPUS = {(byte) 0x15, (byte) 0x09};
69    private static final Charset DEFAULT_CHARSET = Charset.forName("ASCII");
70    public static final byte CQI_CONST_FIELD_MATCH = (byte) 0x10;
71    public static final byte CQI_CONST_FIELD_MATCHEND = (byte) 0x11;
72    public static final byte CQI_CONST_FIELD_TARGET = (byte) 0x00;
73    /**
74     * Error messages
75     */
76    private static final String UNEXPECTED_ANSWER = "Unexpected answer";
77    private static final String SERVER_NOT_FOUND = "Server not found";
78    private static final String INTERNAL_ERROR = "Internal error";
79    private static final String INTERNAL_CQI_ERROR = "Internal CQI error";
80    private static final String INTERNAL_CQP_ERROR = "Internal CQP error";
81    private static final String INTERNAL_CL_ERROR = "Internal CL error";
82    private static final String OUT_OF_MEMORY_ERROR = "Out of memory";
83    private static final String CORPUS_ACCESS_ERROR = "Corpus access error";
84    private static final String WRONG_ATTRIBUTE_TYPE_ERROR = "Wrong attribute type";
85    private static final String OUT_OF_RANGE_ERROR = "Out of range";
86    private static final String REGEX_ERROR = "Regex error";
87    private static final String NO_SUCH_ATTRIBUTE_ERROR = "No such attribute";
88    private static final String NO_SUCH_CORPUS_CQP_ERROR = "No such corpus";
89    private static final String INVALID_FIELD_CQP_ERROR = "Invalid field";
90    private static final String OUT_OF_RANGE_CQP_ERROR = "Out of range";
91    private static final String SYNTAX_CQP_ERROR = "CQP Syntax error";
92    private static final String GENERAL_SQP_ERROR = "General CQP error";
93    private static final String CONNECTION_REFUSED_ERROR = "Connection refused";
94    private static final String USER_ABORT_ERROR = "User abort";
95    private static final String SYNTAX_ERROR = "Syntax error";
96    private static final String GENERAL_ERROR = "General error";
97    private static final String INSUFFICIENT_BUFFER_SIZE = "Insufficient buffer size";
98    private static final String SERVER_IO_ERROR = "IO Error while communicating to the server";
99    private static final int BUFFER_SIZE = 40;
100    private Socket socket;
101    private SocketAddress serverAddress;
102    private DataOutput streamToServer;
103    private DataInput streamFromServer;
104    private final byte[] buffer = new byte[BUFFER_SIZE];
105
106    /**
107     * Instantiates a new cqi client.
108     *
109     * @param host the host of the CQI server
110     * @param port the port of the CQI server
111     *
112     * @throws CqiClientException the server not found exception
113     */
114    public CqiClient(String host, int port) throws CqiClientException {
115        try {
116            this.socket = new Socket();
117            this.serverAddress = new InetSocketAddress(host, port);
118            this.socket.connect(serverAddress);
119            this.streamToServer = new DataOutputStream(this.socket.getOutputStream());
120            this.streamFromServer = new DataInputStream(this.socket.getInputStream());
121        } catch (IOException e) {
122            throw new CqiClientException(SERVER_NOT_FOUND, e);
123        }
124    }
125
126    /**
127     * Connect the client to a server
128     *
129     * @param username the username
130     * @param password the password
131     * @return true, if successful
132     * @throws CqiClientException
133     */
134    public synchronized boolean connect(String username, String password)
135            throws CqiClientException {
136        try {
137            this.streamToServer.write(CQI_CTRL_CONNECT);
138            this.writeString(username);
139            this.writeString(password);
140            return (readHeaderFromServer() == CQI_STATUS_CONNECT_OK);
141        } catch (IOException ex) {
142            throw new CqiClientException(SERVER_IO_ERROR, ex);
143        }
144    }
145
146    /**
147     * Disconnect
148     *
149     * @return true, if successful
150     *
151     * @throws CqiClientException
152     */
153    public synchronized boolean disconnect() throws CqiClientException {
154        try {
155            this.streamToServer.write(CQI_CTRL_BYE);
156            return (readHeaderFromServer() == CQI_STATUS_BYE_OK);
157        } catch (IOException ex) {
158            throw new CqiClientException(SERVER_IO_ERROR, ex);
159        }
160    }
161
162    /**
163     * Lists the corpora available on the server
164     *
165     * @return the name of the corpora
166     * @throws CqiClientException
167     */
168    public synchronized String[] listCorpora() throws CqiClientException {
169        try {
170            this.streamToServer.write(CQI_CORPUS_LIST_CORPORA);
171            return readStringArray(DEFAULT_CHARSET);
172        } catch (IOException e) {
173            throw new CqiClientException(SERVER_IO_ERROR, e);
174        }
175    }
176
177    /**
178     * Gives the corpus positional attributes.
179     *
180     * @param corpusID the corpus id
181     * @return the name of the attributes
182     * @throws CqiClientException
183     */
184    public synchronized String[] corpusPositionalAttributes(String corpus)
185            throws CqiClientException {
186        return genericStringToStringArray(corpus,
187                CQI_CORPUS_POSITIONAL_ATTRIBUTES);
188    }
189
190    /**
191     * Gives the corpus structural attributes.
192     *
193     * @param corpus the corpus
194     * @return the name of the attributes
195     * @throws CqiClientException
196     */
197    public synchronized String[] corpusStructuralAttributes(String corpus)
198            throws CqiClientException {
199        return genericStringToStringArray(corpus,
200                CQI_CORPUS_STRUCTURAL_ATTRIBUTES);
201    }
202
203    /**
204     * Check whether a structural attribute has values
205     *
206     * @param attribute the attribute
207     * @return true, if it has values
208     * @throws CqiClientException
209     */
210    public synchronized boolean corpusStructuralAttributeHasValues(
211            String attribute) throws CqiClientException {
212        try {
213            this.streamToServer.write(CQI_CORPUS_STRUCTURAL_ATTRIBUTE_HAS_VALUES);
214            this.writeString(attribute);
215            return readBoolean();
216        } catch (IOException e) {
217            throw new CqiClientException(SERVER_IO_ERROR, e);
218        }
219    }
220
221    /**
222     * Write a string on the socket.
223     *
224     * @param string the string
225     *
226     * @throws IOException Signals that an I/O exception has occurred.
227     */
228    private synchronized void writeString(String string, Charset charset) throws IOException {
229        byte[] bytes = string.getBytes(charset);
230        this.streamToServer.writeShort(bytes.length);
231        this.streamToServer.write(bytes);
232    }
233
234    private synchronized void writeString(String string) throws IOException {
235        writeString(string, DEFAULT_CHARSET);
236    }
237
238    /**
239     * Write int array on the socket.
240     *
241     * @param ints the int array
242     *
243     * @throws IOException Signals that an I/O exception has occurred.
244     */
245    private synchronized void writeIntArray(int[] ints) throws IOException {
246        int length = ints.length;
247        this.streamToServer.writeInt(length);
248        for (int i = 0; i < length; i++) {
249            // System.out.println(i+"/"+length);
250            this.streamToServer.writeInt(ints[i]);
251        }
252    }
253
254    /**
255     * Write int array on the socket.
256     *
257     * @param ints the int array
258     *
259     * @throws IOException Signals that an I/O exception has occurred.
260     */
261    private synchronized void writeIntArray(int[] ints, int length) throws IOException {
262        this.streamToServer.writeInt(length);
263        for (int i = 0; i < length; i++) {
264            this.streamToServer.writeInt(ints[i]);
265        }
266    }
267
268    /**
269     * Read a string from the socket.
270     *
271     * @return the string
272     *
273     * @throws CqiClientException Signals that the data read on the socket is
274     * unexpected
275     * @throws IOException Signals that an I/O exception has occurred.
276     */
277    private synchronized String readString(Charset charset) throws
278            CqiClientException, IOException {
279        if (readHeaderFromServer() != CQI_DATA_STRING) {
280            throw new CqiClientException(UNEXPECTED_ANSWER);
281        }
282        short length = this.streamFromServer.readShort();
283        byte[] bytes = new byte[length];
284        this.streamFromServer.readFully(bytes);
285        String res = new String(bytes, charset);
286        return res;
287    }
288
289    private synchronized String readString() throws
290            CqiClientException, IOException {
291        return readString(DEFAULT_CHARSET);
292    }
293
294    /**
295     * Read a boolean from the socket.
296     *
297     * @return the boolean
298     *
299     * @throws CqiClientException
300     */
301    private synchronized boolean readBoolean() throws CqiClientException,
302            IOException {
303        if (readHeaderFromServer() != CQI_DATA_BOOL) {
304            throw new CqiClientException(UNEXPECTED_ANSWER);
305        }
306        return this.streamFromServer.readByte() == 1;
307    }
308
309    /**
310     * Read an int from the socket.
311     *
312     * @return the int
313     *
314     * @throws CqiClientException Signals that the data read on the socket is
315     * unexpected
316     * @throws IOException Signals that an I/O exception has occurred.
317     */
318    private synchronized int readInt() throws CqiClientException, IOException {
319        if (readHeaderFromServer() != CQI_DATA_INT) {
320            throw new CqiClientException(UNEXPECTED_ANSWER);
321        }
322        int res = this.streamFromServer.readInt();
323        return res;
324    }
325
326    /**
327     * Read a string array from the socket.
328     *
329     * @return the string array
330     *
331     * @throws CqiClientException
332     */
333    private synchronized String[] readStringArray(Charset charset)
334            throws CqiClientException {
335        try {
336            byte[] header = readHeaderFromServer();
337            if (header != CQI_DATA_STRING_LIST) {
338                throw new CqiClientException(UNEXPECTED_ANSWER);
339            }
340            int arrayLength = this.streamFromServer.readInt();
341            String[] strings = new String[arrayLength];
342            for (int i = 0; i < arrayLength; i++) {
343                short stringLength = this.streamFromServer.readShort();
344                byte[] bytes = new byte[stringLength];
345                this.streamFromServer.readFully(bytes);
346                strings[i] = new String(bytes, charset);
347            }
348            return strings;
349        } catch (IOException e) {
350            throw new CqiClientException("Error reading a string array", e);
351        }
352    }
353
354    /**
355     * Read an int array from the socket.
356     *
357     * @return the int array
358     *
359     * @throws CqiClientException
360     * @throws IOException
361     */
362    private synchronized void readIntList(int[] output) throws CqiClientException,
363            IOException {
364        if ((readHeaderFromServer()) != CQI_DATA_INT_LIST) {
365            throw new CqiClientException(UNEXPECTED_ANSWER);
366        }
367        int arrayLength = this.streamFromServer.readInt();
368        if (output.length < arrayLength) {
369            throw new CqiClientException(INSUFFICIENT_BUFFER_SIZE);
370        }
371        int bsize = arrayLength * 4;
372        if (buffer.length < bsize) {
373            throw new CqiClientException(INSUFFICIENT_BUFFER_SIZE);
374        }
375        streamFromServer.readFully(buffer, 0, bsize);
376        for (int i = 0; i + 3 < bsize; i += 4) {
377            output[i >> 2] = bytesToInt(buffer[i], buffer[i + 1], buffer[i + 2], buffer[i + 3]);
378        }
379    }
380
381    /**
382     * Bytes to int.
383     *
384     * @param a the a
385     * @param b the b
386     * @param c the c
387     * @param d the d
388     * @return the int
389     */
390    private synchronized static int bytesToInt(byte a, byte b, byte c, byte d) {
391        return (((a & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8) | (d & 0xff));
392    }
393
394    /**
395     * Read the header of the data send by the server.
396     *
397     * @return the header
398     *
399     * @throws CqiClientException\
400     * @throws IOException
401     */
402    private synchronized byte[] readHeaderFromServer()
403            throws CqiClientException, IOException {
404        byte b = this.streamFromServer.readByte();
405        switch (b) {
406            case 0x00:// cf cqi.h:29
407                return CQI_PADDING;
408            case 0x01:// cf cqi.h:37
409                b = this.streamFromServer.readByte();
410                switch (b) {
411                    case 0x01:// cf cqi.h:39
412                        return CQI_STATUS_OK;
413                    case 0x02:// cf cqi.h:40
414                        return CQI_STATUS_CONNECT_OK;
415                    case 0x03:// cf cqi.h:41
416                        return CQI_STATUS_BYE_OK;
417                    case 0x04:// cf cqi.h:42
418                        return CQI_STATUS_PING_OK;
419                }
420                break;
421            case 0x02:// cf cqi.h:45
422                b = this.streamFromServer.readByte();
423                switch (b) {
424                    case 0x01:// cf cqi.h:39
425                        throw new CqiClientException(GENERAL_ERROR);
426                    case 0x02:// cf cqi.h:40
427                        throw new CqiClientException(CONNECTION_REFUSED_ERROR);
428                    case 0x03:// cf cqi.h:41
429                        throw new CqiClientException(USER_ABORT_ERROR);
430                    case 0x04:// cf cqi.h:42
431                        throw new CqiClientException(SYNTAX_ERROR);
432                    default:
433                        throw new CqiClientException(INTERNAL_CQI_ERROR);
434                }
435            case 0x03:// cf cqi.h:53
436                b = this.streamFromServer.readByte();
437
438                switch (b) {
439                    case 0x01:// cf cqi.h:39
440                        return CQI_DATA_BYTE;
441                    case 0x02:// cf cqi.h:40
442                        return CQI_DATA_BOOL;
443                    case 0x03:// cf cqi.h:41
444                        return CQI_DATA_INT;
445                    case 0x04:// cf cqi.h:42
446                        return CQI_DATA_STRING;
447                    case 0x05:// cf cqi.h:42
448                        return CQI_DATA_BYTE_LIST;
449                    case 0x06:// cf cqi.h:42
450                        return CQI_DATA_BOOL_LIST;
451                    case 0x07:// cf cqi.h:42
452                        return CQI_DATA_INT_LIST;
453                    case 0x08:// cf cqi.h:42
454                        return CQI_DATA_STRING_LIST;
455                    case 0x09:// cf cqi.h:42
456                        return CQI_DATA_INT_INT;
457                    case 0x0A:// cf cqi.h:42
458                        return CQI_DATA_INT_INT_INT_INT;
459                    case 0x0B:// cf cqi.h:42
460                        return CQI_DATA_INT_TABLE;
461                }
462                break;
463            case 0x04:// cf cqi.h:67
464
465                b = this.streamFromServer.readByte();
466                switch (b) {
467                    case 0x01:// cf cqi.h:39
468                        throw new CqiClientException(NO_SUCH_ATTRIBUTE_ERROR);
469                    case 0x02:// cf cqi.h:40
470                        throw new CqiClientException(WRONG_ATTRIBUTE_TYPE_ERROR);
471                    case 0x03:// cf cqi.h:41
472                        throw new CqiClientException(OUT_OF_RANGE_ERROR);
473                    case 0x04:// cf cqi.h:42
474                        throw new CqiClientException(REGEX_ERROR + ": " + getLastCqiError());
475                    case 0x05:// cf cqi.h:42
476                        throw new CqiClientException(CORPUS_ACCESS_ERROR);
477                    case 0x06:// cf cqi.h:42
478                        throw new CqiClientException(OUT_OF_MEMORY_ERROR);
479                    case 0x07:// cf cqi.h:42
480                        throw new CqiClientException(INTERNAL_ERROR);
481                    default:
482                        throw new CqiClientException(INTERNAL_CL_ERROR);
483                }
484            case 0x05:// cf cqi.h:94
485
486                b = this.streamFromServer.readByte();
487                switch (b) {
488                    case 0x01:// cf cqi.h:39
489                        throw new CqiClientException(GENERAL_SQP_ERROR);
490                    case 0x02:// cf cqi.h:40
491                        throw new CqiClientException(NO_SUCH_CORPUS_CQP_ERROR);
492                    case 0x03:// cf cqi.h:41
493                        throw new CqiClientException(INVALID_FIELD_CQP_ERROR);
494                    case 0x04:// cf cqi.h:42
495                        throw new CqiClientException(OUT_OF_RANGE_CQP_ERROR);
496                    case 0x05:// cf cqi.h:44
497                        throw new CqiClientException(SYNTAX_CQP_ERROR + ": " + getLastCQPError());
498                    default:
499                        throw new CqiClientException(INTERNAL_CQP_ERROR);
500                }
501        }
502        return null;
503    }
504
505    /**
506     * Ask the server to execute a function with a String->String signature.
507     *
508     * @param string the argument
509     * @param function the function to be executed
510     * @return the result
511     * @throws CqiClientException Signals that the data read on the socket is
512     * unexpected
513     * @throws IOException Signals that an I/O exception has occurred.
514     */
515    private synchronized String genericStringToString(String string,
516            byte[] function) throws CqiClientException, IOException {
517        this.streamToServer.write(function);
518        this.writeString(string, DEFAULT_CHARSET);
519        return readString(DEFAULT_CHARSET);
520    }
521
522    /**
523     * Ask the server to execute a function with a String->StringArray
524     * signature.
525     *
526     * @param string the argument
527     * @param function the function to be executed
528     * @return the result
529     * @throws CqiClientException
530     */
531    private synchronized String[] genericStringToStringArray(String string,
532            byte[] function) throws CqiClientException {
533        try {
534            this.streamToServer.write(function);
535            this.writeString(string, DEFAULT_CHARSET);
536        } catch (IOException e) {
537            throw new CqiClientException(SERVER_IO_ERROR, e);
538        }
539        return readStringArray(DEFAULT_CHARSET);
540    }
541
542    /**
543     * Ask the server to execute a function with a String x int[]->String[]
544     * signature.
545     *
546     * @param string the string argument
547     * @param ints the int[] argument
548     * @param function the function to be executed
549     * @return the result
550     * @throws CqiClientException
551     */
552    private synchronized String[] genericStringXIntArraytoStringArray(
553            String string, int[] ints, byte[] function, Charset charset) throws
554            CqiClientException {
555        try {
556            this.streamToServer.write(function);
557            this.writeString(string, charset);
558            this.writeIntArray(ints);
559            String[] res = readStringArray(charset);
560            return res;
561        } catch (IOException e) {
562            throw new CqiClientException(SERVER_IO_ERROR, e);
563        }
564
565    }
566
567    /**
568     * Ask the server to execute a function with a String x int[]->int[]
569     * signature.
570     *
571     * @param string the string argument
572     * @param ints the int[] argument
573     * @param function the function to be executed
574     * @return the result
575     * @throws CqiClientException
576     */
577    private synchronized void genericStringXIntArraytoIntArray(String string,
578            int[] ints, byte[] function, int[] output, int size) throws CqiClientException {
579        try {
580            this.streamToServer.write(function);
581            this.writeString(string);
582            this.writeIntArray(ints, size);
583            readIntList(output);
584        } catch (IOException e) {
585            throw new CqiClientException(SERVER_IO_ERROR, e);
586        }
587    }
588
589    /**
590     * return the last CQP error.
591     *
592     * @return the last error
593     * @throws CqiClientException Signals that the data read on the socket is
594     * unexpected
595     * @throws IOException Signals that an I/O exception has occurred.
596     * @throws CqiServerError Signals that the cqi server raised an error
597     */
598    private synchronized String getLastCqiError() throws CqiClientException, IOException {
599        this.streamToServer.write(CQI_CTRL_LAST_GENERAL_ERROR);
600
601        try {
602            String ret = readString();
603            return ret;
604        } catch (CqiClientException e) {
605            return "getLastCQiError: " + e;
606        }
607    }
608
609    /**
610     * return the last CQP error.
611     *
612     * @return the last error
613     * @throws CqiClientException Signals that the data read on the socket is
614     * unexpected
615     * @throws IOException Signals that an I/O exception has occurred.
616     * @throws CqiServerError Signals that the cqi server raised an error
617     */
618    public synchronized String getLastCQPError() throws CqiClientException, IOException {
619
620        this.streamToServer.write(CQI_CTRL_LAST_CQP_ERROR);
621
622        try {
623            String ret = readString();
624            return ret;
625        } catch (CqiClientException e) {
626            return "getLastCQPError: " + e;
627        }
628    }
629
630    /**
631     * Gives the corpus charset.
632     *
633     * @param corpus the corpus
634     * @return the name of the charset
635     * @throws CqiClientException Signals that the data read on the socket is
636     * unexpected
637     * @throws IOException Signals that an I/O exception has occurred.
638     * @throws CqiServerError Signals that the cqi server raised an error
639     */
640    public synchronized String corpusCharset(String corpus)
641            throws CqiClientException, IOException {
642        return genericStringToString(corpus, CQI_CORPUS_CHARSET);
643    }
644
645    /**
646     * Gives the corpus full name.
647     *
648     * @param corpus the corpus
649     * @return the full name
650     * @throws CqiClientException Signals that the data read on the socket is
651     * unexpected
652     * @throws IOException Signals that an I/O exception has occurred.
653     * @throws CqiServerError Signals that the cqi server raised an error
654     */
655    public synchronized String corpusFullName(String corpus)
656            throws CqiClientException, IOException {
657        return genericStringToString(corpus, CQI_CORPUS_FULL_NAME);
658    }
659
660    /**
661     * Drop a corpus.
662     *
663     * @param corpus the corpus
664     */
665    public synchronized void dropCorpus(String corpus) {
666        throw new UnsupportedOperationException();
667    }
668
669    /**
670     * Gives an attribute size (the number of token).
671     *
672     * @param attribute the attribute
673     * @return the size
674     * @throws CqiClientException
675     */
676    public synchronized int attributeSize(String attribute) throws
677            CqiClientException {
678        try {
679            this.streamToServer.write(CQI_CL_ATTRIBUTE_SIZE);
680            this.writeString(attribute);
681            return readInt();
682        } catch (IOException e) {
683            throw new CqiClientException(SERVER_IO_ERROR, e);
684        }
685    }
686
687    /**
688     * Gives the lexicon size of an attribute.
689     *
690     * @param attribute the attribute
691     * @return the int
692     * @throws CqiClientException @returns the number of entries in the lexicon
693     * of a positional attribute
694     */
695    public synchronized int lexiconSize(String attribute) throws CqiClientException {
696        try {
697            this.streamToServer.write(CQI_CL_LEXICON_SIZE);
698            this.writeString(attribute);
699            return readInt();
700        } catch (IOException e) {
701            throw new CqiClientException(SERVER_IO_ERROR, e);
702        }
703    }
704
705    /**
706     * Converts an array of position to their value given an attribute.
707     *
708     * @param attribute the attribute
709     * @param cpos the cpos
710     * @return the values
711     * @throws CqiClientException
712     */
713    public synchronized String[] cpos2Str(String attribute, int[] cpos, Charset charset)
714            throws CqiClientException {
715        return genericStringXIntArraytoStringArray(attribute, cpos,
716                CQI_CL_CPOS2STR, charset);
717    }
718
719    /**
720     * Computes for each position of an array the position of the left boundary
721     * of the enclosing structural attribute.
722     *
723     * @param attribute the attribute
724     * @param cpos the cpos
725     * @return the positions of the left boundaries
726     * @throws CqiClientException
727     */
728    public synchronized void cpos2LBound(String attribute, int[] cpos, int[] output, int size)
729            throws CqiClientException {
730        genericStringXIntArraytoIntArray(attribute, cpos, CQI_CL_CPOS2LBOUND, output, size);
731    }
732
733    /**
734     * Computes for each position of an array the position of the right boundary
735     * of the enclosing structural attribute.
736     *
737     * @param attribute the attribute
738     * @param cpos the cpos
739     * @return the positions of the right boundaries
740     * @throws CqiClientException
741     */
742    public synchronized void cpos2RBound(String attribute, int[] cpos, int[] output, int size)
743            throws CqiClientException {
744        genericStringXIntArraytoIntArray(attribute, cpos, CQI_CL_CPOS2RBOUND, output, size);
745    }
746
747    /**
748     * Runs a CQP query.
749     *
750     * @param corpus the corpus
751     * @param subcorpus the subcorpus
752     * @param query the query
753     * @param charset the charset
754     * @throws CqiClientException
755     */
756    private synchronized void cqpQuery(String corpus, String subcorpus,
757            String query, Charset charset) throws CqiClientException {
758        try {
759            this.streamToServer.write(CQI_CQP_QUERY);
760            this.writeString(corpus);
761            this.writeString(subcorpus);
762            this.writeString(query, charset);
763            this.readHeaderFromServer();
764        } catch (IOException e) {
765            throw new CqiClientException(SERVER_IO_ERROR, e);
766        }
767
768    }
769
770    /**
771     * Runs a CQP query.
772     *
773     * @param corpus the corpus
774     * @param subcorpus the subcorpus
775     * @param query the query
776     * @param charset the charset
777     * @throws CqiClientException
778     */
779    public synchronized CqiResult cqpQuery(String corpus, String query) throws CqiClientException {
780        String queryName = String.format("Q%d", System.currentTimeMillis());
781        String subcorpus = String.format("%s:%s", corpus, queryName);
782        Charset charset;
783        try {
784            charset = Charset.forName(corpusCharset(corpus));
785        } catch (IOException e) {
786            throw new CqiClientException(SERVER_IO_ERROR, e);
787        }
788        cqpQuery(corpus, queryName, query, charset);
789        return new CqiResult(this, corpus, subcorpus, charset, subCorpusSize(subcorpus));
790    }
791
792    /**
793     * Lists all the subcorpora of a corpus.
794     *
795     * @param corpus the corpus
796     * @return the name of the subcorpora
797     * @throws CqiClientException
798     */
799    public synchronized String[] listSubcorpora(String corpus)
800            throws CqiClientException {
801        return genericStringToStringArray(corpus, CQI_CQP_LIST_SUBCORPORA);
802    }
803
804    /**
805     * Gives the size of a subcorpus .
806     *
807     * @param subcorpus the subcorpus
808     *
809     * @return the size
810     *
811     * @throws CqiClientException
812     */
813    public synchronized int subCorpusSize(String subcorpus) throws
814            CqiClientException {
815        try {
816            this.streamToServer.write(CQI_CQP_SUBCORPUS_SIZE);
817            this.writeString(subcorpus);
818            return this.readInt();
819        } catch (IOException e) {
820            throw new CqiClientException(SERVER_IO_ERROR, e);
821        }
822    }
823
824    /**
825     * Checks wether a subcorpus has a field.
826     *
827     * @param subcorpus the subcorpus
828     * @param field the field
829     *
830     * @return true, if the subcorpus has the field
831     *
832     * @throws IOException Signals that an I/O exception has occurred.
833     * @throws CqiClientException Signals that the data read on the socket is
834     * unexpected
835     */
836    public synchronized boolean subCorpusHasField(String subcorpus, byte field)
837            throws IOException, CqiClientException {
838        this.streamToServer.write(CQI_CQP_SUBCORPUS_HAS_FIELD);
839        this.writeString(subcorpus);
840        this.streamToServer.writeByte(field);
841        return this.readBoolean();
842    }
843
844    /**
845     * Dumps the values of <field> for match ranges <first> .. <last> in
846     * <subcorpus>. <field> is one of the CQI_CONST_FIELD_* constants.
847     *
848     * @param subcorpus the subcorpus
849     * @param field the field
850     * @param first the first
851     * @param last the last
852     *
853     * @return the values
854     *
855     * @throws CqiClientException
856     */
857    public synchronized void dumpSubCorpus(String subcorpus, byte field,
858            int first, int last, int[] output) throws CqiClientException {
859        try {
860            this.streamToServer.write(CQI_CQP_DUMP_SUBCORPUS);
861            this.writeString(subcorpus);
862            this.streamToServer.writeByte(field);
863            this.streamToServer.writeInt(first);
864            this.streamToServer.writeInt(last);
865            this.readIntList(output);
866        } catch (IOException e) {
867            throw new CqiClientException(SERVER_IO_ERROR, e);
868        }
869    }
870
871    /**
872     * Drops a subcorpus.
873     *
874     * @param subcorpus the subcorpus
875     *
876     * @throws CqiClientException
877     */
878    public synchronized void dropSubCorpus(String subcorpus) throws
879            CqiClientException {
880        try {
881            this.streamToServer.write(CQI_CQP_DROP_SUBCORPUS);
882            this.writeString(subcorpus);
883            this.readHeaderFromServer();
884        } catch (IOException e) {
885            throw new CqiClientException(SERVER_IO_ERROR, e);
886        }
887    }
888}
Note: See TracBrowser for help on using the repository browser.