source: CQIClient/src/main/java/eu/clarin/cqi/client/CqiClient.java @ 2761

Last change on this file since 2761 was 2761, checked in by akislev, 11 years ago

one-off bug is fixed

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