예제 #1
0
  public static void main(String args[]) throws Exception {
    String inputFile = "samplein.txt";
    String outputFile = "sampleout.txt";

    RandomAccessFile inf = new RandomAccessFile(inputFile, "r");
    RandomAccessFile outf = new RandomAccessFile(outputFile, "rw");
    long inputLength = new File(inputFile).length();

    FileChannel inc = inf.getChannel();
    FileChannel outc = outf.getChannel();

    MappedByteBuffer inputData = inc.map(FileChannel.MapMode.READ_ONLY, 0, inputLength);

    Charset latin1 = Charset.forName("ISO-8859-1");
    CharsetDecoder decoder = latin1.newDecoder();
    CharsetEncoder encoder = latin1.newEncoder();

    CharBuffer cb = decoder.decode(inputData);

    // Process char data here

    ByteBuffer outputData = encoder.encode(cb);

    outc.write(outputData);

    inf.close();
    outf.close();
  }
예제 #2
0
  // Check if the datagramsocket adaptor can send with a packet
  // that has not been initialized with an address; the legacy
  // datagram socket will send in this case
  private static void test2() throws Exception {
    DatagramChannel sndChannel = DatagramChannel.open();
    sndChannel.socket().bind(null);
    InetSocketAddress sender =
        new InetSocketAddress(InetAddress.getLocalHost(), sndChannel.socket().getLocalPort());

    DatagramChannel rcvChannel = DatagramChannel.open();
    rcvChannel.socket().bind(null);
    InetSocketAddress receiver =
        new InetSocketAddress(InetAddress.getLocalHost(), rcvChannel.socket().getLocalPort());

    rcvChannel.connect(sender);
    sndChannel.connect(receiver);

    byte b[] = "hello".getBytes("UTF-8");
    DatagramPacket pkt = new DatagramPacket(b, b.length);
    sndChannel.socket().send(pkt);

    ByteBuffer bb = ByteBuffer.allocate(256);
    rcvChannel.receive(bb);
    bb.flip();
    CharBuffer cb = Charset.forName("US-ASCII").newDecoder().decode(bb);
    if (!cb.toString().startsWith("h")) throw new RuntimeException("Test failed");

    // Check that the pkt got set with the target address;
    // This is legacy behavior
    if (!pkt.getSocketAddress().equals(receiver)) throw new RuntimeException("Test failed");

    rcvChannel.close();
    sndChannel.close();
  }
예제 #3
0
  public static void main(String[] args) throws MessagingException, IOException {
    Properties props = new Properties();
    try (InputStream in = Files.newInputStream(Paths.get("mail", "mail.properties"))) {
      props.load(in);
    }
    List<String> lines = Files.readAllLines(Paths.get(args[0]), Charset.forName("UTF-8"));

    String from = lines.get(0);
    String to = lines.get(1);
    String subject = lines.get(2);

    StringBuilder builder = new StringBuilder();
    for (int i = 3; i < lines.size(); i++) {
      builder.append(lines.get(i));
      builder.append("\n");
    }

    Console console = System.console();
    String password = new String(console.readPassword("Password: "));

    Session mailSession = Session.getDefaultInstance(props);
    // mailSession.setDebug(true);
    MimeMessage message = new MimeMessage(mailSession);
    message.setFrom(new InternetAddress(from));
    message.addRecipient(RecipientType.TO, new InternetAddress(to));
    message.setSubject(subject);
    message.setText(builder.toString());
    Transport tr = mailSession.getTransport();
    try {
      tr.connect(null, password);
      tr.sendMessage(message, message.getAllRecipients());
    } finally {
      tr.close();
    }
  }
예제 #4
0
 /**
  * Constructor.
  *
  * @param enc encoding
  * @throws IOException I/O exception
  */
 private Generic(final String enc) throws IOException {
   try {
     csd = Charset.forName(enc).newDecoder();
   } catch (final Exception ex) {
     throw new EncodingException(ex);
   }
 }
예제 #5
0
  // Check if DatagramChannel.send while connected can include
  // address without throwing
  private static void test1() throws Exception {

    DatagramChannel sndChannel = DatagramChannel.open();
    sndChannel.socket().bind(null);
    InetSocketAddress sender =
        new InetSocketAddress(InetAddress.getLocalHost(), sndChannel.socket().getLocalPort());

    DatagramChannel rcvChannel = DatagramChannel.open();
    rcvChannel.socket().bind(null);
    InetSocketAddress receiver =
        new InetSocketAddress(InetAddress.getLocalHost(), rcvChannel.socket().getLocalPort());

    rcvChannel.connect(sender);
    sndChannel.connect(receiver);

    ByteBuffer bb = ByteBuffer.allocate(256);
    bb.put("hello".getBytes());
    bb.flip();
    int sent = sndChannel.send(bb, receiver);
    bb.clear();
    rcvChannel.receive(bb);
    bb.flip();
    CharBuffer cb = Charset.forName("US-ASCII").newDecoder().decode(bb);
    if (!cb.toString().startsWith("h")) throw new RuntimeException("Test failed");

    rcvChannel.close();
    sndChannel.close();
  }
예제 #6
0
 public static void main(String[] arguments) {
   try {
     // read byte data into a byte buffer
     String data = "friends.dat";
     FileInputStream inData = new FileInputStream(data);
     FileChannel inChannel = inData.getChannel();
     long inSize = inChannel.size();
     ByteBuffer source = ByteBuffer.allocate((int) inSize);
     inChannel.read(source, 0);
     source.position(0);
     System.out.println("Original byte data:");
     for (int i = 0; source.remaining() > 0; i++) {
       System.out.print(source.get() + " ");
     }
     // convert byte data into character data
     source.position(0);
     Charset ascii = Charset.forName("US-ASCII");
     CharsetDecoder toAscii = ascii.newDecoder();
     CharBuffer destination = toAscii.decode(source);
     destination.position(0);
     System.out.println("\n\nNew character data:");
     for (int i = 0; destination.remaining() > 0; i++) {
       System.out.print(destination.get());
     }
     System.out.println();
   } catch (FileNotFoundException fne) {
     System.out.println(fne.getMessage());
   } catch (IOException ioe) {
     System.out.println(ioe.getMessage());
   }
 }
예제 #7
0
  /**
   * Constructor.
   *
   * @param os output stream
   * @param sopts serializer options
   * @throws QueryIOException query I/O exception
   */
  protected OutputSerializer(final OutputStream os, final SerializerOptions sopts)
      throws QueryIOException {

    this.sopts = sopts;
    indent = sopts.yes(INDENT);

    // project-specific options
    indents = sopts.get(INDENTS);
    tab = sopts.yes(TABULATOR) ? '\t' : ' ';

    encoding = Strings.normEncoding(sopts.get(ENCODING), true);
    PrintOutput po;
    if (encoding == Strings.UTF8) {
      po = PrintOutput.get(os);
    } else {
      try {
        po = new EncoderOutput(os, Charset.forName(encoding));
      } catch (final Exception ex) {
        throw SERENCODING_X.getIO(encoding);
      }
    }
    final int limit = sopts.get(LIMIT);
    if (limit != -1) po.setLimit(limit);

    final byte[] nl = token(sopts.get(NEWLINE).newline());
    if (nl.length != 1 || nl[0] != '\n') po = new NewlineOutput(po, nl);
    out = po;
  }
  @Override
  protected void doPost(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {
    resp.setCharacterEncoding("UTF-8");
    HttpSession user = req.getSession();
    PrintWriter out = resp.getWriter();
    SpejdResultAnalyser result = null;
    JsonObject response;

    JsonReader jsonReader =
        Json.createReader(new InputStreamReader(req.getInputStream(), StandardCharsets.ISO_8859_1));
    JsonObject jsonObject = jsonReader.readObject();
    jsonReader.close();
    System.out.println("REQUEST");
    System.out.println(jsonObject);
    String originalText = jsonObject.getString("text");
    int space = jsonObject.getInt("space");

    String xml = getXml(originalText, user.getId());
    try {
      result = new SpejdResultAnalyser(xml.replace("SYSTEM \"xcesAnaIPI.dtd\"", ""));
    } catch (ParserConfigurationException | SAXException e) {
      out.println(e.toString());
    }

    assert result != null;
    /** do analysis of xml spejd result */
    SubstantivePairs pairs = result.doAnalysis().makePair(space);

    /**
     * create json response { text: ... xml: ... pairs: [ { subst1: ... subst2: ... quantity: ... },
     * ...] }
     */
    JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
    for (SubstantivePair pair : pairs) {
      arrayBuilder.add(
          Json.createObjectBuilder()
              .add("subst1", pair.getSubst1())
              .add("subst2", pair.getSubst2())
              .add("quantity", pair.getQuantity()));
    }

    Charset.forName("UTF-8").encode(originalText);
    JsonArray array = arrayBuilder.build();
    response =
        Json.createObjectBuilder()
            .add("text", originalText)
            .add("xml", xml)
            .add("pairs", array)
            .build();
    System.out.println("RESPONSE");
    System.out.println(response);
    out.print(response);
  }
예제 #9
0
  /**
   * Get text content of a source file.
   *
   * @param path The canonical path of source file.
   * @param charset Source file encoding.
   * @return Source file content.
   */
  public String read(String path, String charset) {
    String str = null;
    byte[] bin = read(path);

    try {
      str = Charset.forName(charset).newDecoder().decode(ByteBuffer.wrap(bin)).toString();
    } catch (CharacterCodingException e) {
      App.exit("Cannot read " + path + " as " + charset + " encoded file");
    }

    return str;
  }
예제 #10
0
 /**
  * Tests response handling with specified charset in the header 'Content-Type'.
  *
  * @throws IOException I/O Exception
  * @throws QueryException query exception
  */
 @Test
 public void responseWithCharset() throws IOException, QueryException {
   // Create fake HTTP connection
   final FakeHttpConnection conn = new FakeHttpConnection(new URL("http://www.test.com"));
   // Set content type
   conn.contentType = "text/plain; charset=CP1251";
   // set content encoded in CP1251
   final String test = "\u0442\u0435\u0441\u0442";
   conn.content = Charset.forName("CP1251").encode(test).array();
   final ItemList res = new HttpResponse(null, ctx.options).getResponse(conn, true, null);
   // compare results
   assertEquals(test, string(res.get(1).string(null)));
 }
예제 #11
0
  /**
   * Check if the first X characters of a byte stream match a String.
   *
   * @param data The byte array to process
   * @param pattern The String to match
   * @return True if the pattern was found, false otherwise
   */
  private static boolean bytesEqualsString(byte[] data, String pattern) {
    byte[] bytes = new byte[pattern.length()];
    Charset csets = Charset.forName("US-ASCII");
    boolean fin = false;
    int currChar = 0;

    // remove any CR and/or LF characters at the beginning of the article
    // data
    while (!fin) {
      if (currChar >= data.length) break;

      byte in = data[currChar];
      ByteBuffer bb = ByteBuffer.wrap(new byte[] {(byte) in});
      CharBuffer cb = csets.decode(bb);
      char c = cb.charAt(0);

      if (data.length > 0 && (c == '\n' || c == '\r')) currChar++;
      else fin = true;

      if (data.length == 0) fin = true;
    }

    // extract bytes (chars) to check from article data
    for (int i = 0; i < bytes.length && i < data.length; i++, currChar++) {
      byte in = data[currChar];
      bytes[i] = (byte) in;
    }

    // decode byte data to characters
    ByteBuffer bb = ByteBuffer.wrap(bytes);
    CharBuffer cb = csets.decode(bb);

    // compare these characters to the pattern String
    for (int i = 0; i < pattern.length(); i++) if (cb.charAt(i) != pattern.charAt(i)) return false;

    return true;
  }
예제 #12
0
public class AdParser {
  int level = 0;
  char saved = 0;
  private Reader r;
  boolean body_only = false;
  private static RuntimeException eof = new RuntimeException("end of stream reached");
  public static Charset defaultCharset = Charset.forName("UTF-8");

  public static final String SEP = ",;& \n\r";
  public static final String EQ = ":=";
  public static final String OB = "{[(<"; // It's a fish!
  public static final String CB = "}])>";
  public static final String WS = " \t\n\r\b";

  // This includes decorator hints for printing. Or will, maybe. For now
  // it just picks a printer based on the opening bracket. The hint should
  // be set after at least one object is inserted so we know whether this
  // ad is a map or list.
  public static class ParsedAd extends Ad {
    public AdPrinter printer = AdPrinter.CLASSAD;

    public AdPrinter setHint(char c) {
      if (isList())
        switch (c) {
          case '[':
            return printer = AdPrinter.JSON;
          case '{':
            return printer = AdPrinter.CLASSAD;
        }
      else
        switch (c) {
          case '{':
            return printer = AdPrinter.JSON;
          case '[':
            return printer = AdPrinter.CLASSAD;
        }
      return printer;
    }

    public String toString() {
      return printer.toString(this);
    }
  }

  AdParser(CharSequence s, boolean body_only) {
    this(new StringReader(s.toString()), body_only);
  }

  AdParser(InputStream is, boolean body_only) {
    this(new InputStreamReader(is, defaultCharset), body_only);
  }

  AdParser(Reader r, boolean body_only) {
    this.r = (r instanceof BufferedReader) ? r : new BufferedReader(r);
    if (this.body_only = body_only) saved = '[';
  }

  // Utility methods
  // ---------------
  // Get the next character, throwing an unchecked exception on error.
  private char next() {
    try {
      int i = (saved != 0) ? saved : r.read();
      saved = 0;
      if (i <= -1) {
        if (body_only) return ']'; // Just a little hacky.
        throw eof;
      }
      return (char) i;
    } catch (RuntimeException e) {
      throw e;
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  // Peek at the next character, then save it.
  private char peek() {
    char c = next();
    return saved = c;
  }

  // Check if a string or range contains a character.
  private static boolean check(char c, String s) {
    return s.indexOf(c) >= 0;
  }

  private static boolean check(char c, char from, char to) {
    if (from > to) return check(c, to, from);
    return c >= from && c <= to;
  }

  // Discard characters from the given set. Return the first thing that
  // isn't part of the ignore set.
  private char discard() {
    // Discard whitespace by default.
    return discard(WS);
  }

  private char discard(String s) {
    char c;
    for (c = next(); check(c, s); c = next()) ;
    return c;
  }

  // Discard ignored characters as well as comments.
  private char discardIgnored() {
    return discardIgnored(WS);
  }

  private char discardIgnored(String s) {
    char c;
    do
      switch (c = discard(s)) {
        case '/':
          if (peek() != '/') return c;
        case '#':
          for (c = peek(); !check(c, "\r\n"); c = next()) ;
      }
    while (check(c, s));
    return c;
  }

  // Ignore all whitespace and check for a character in the given set.
  // If something else is found, throws a parse error.
  private char expect(String s) {
    return expect(s, WS);
  }

  private char expect(String s, String i) {
    char c = discardIgnored(i);
    if (!check(c, s)) throw new RuntimeException("unexpected character: " + c);
    return c;
  }

  // Lowercase a character assuming it's an A-Z letter.
  private static char low(char c) {
    return (char) ((short) c | (short) ' ');
  }

  // Parsing methods
  // ---------------
  public Ad parse() {
    try {
      return parseInto(new ParsedAd());
    } catch (RuntimeException e) {
      if (e == eof) return null;
      throw e;
    }
  }

  public Ad parseInto(Ad ad) {
    char open = expect(OB);

    for (int i = 0; ; i++) {
      char c = saved = discardIgnored(SEP + WS);

      // Check for end of ad.
      if (check(c, CB)) {
        next();
        return ad;
      }

      // Discard any extraneous separators or newlines.
      saved = discardIgnored(SEP + WS);

      // Read the first token.
      Object o = readValue();

      // Check if it's anonymous or not.
      // FIXME: These switch cases should not be hardcoded...
      switch (c = expect(EQ + SEP + CB, " \t\b")) {
        case ':': // Check for assignment.
        case '=':
          // Determine the key.
          String k = o.toString();
          o = findValue();
          // Insert into ad as key-value pair.
          if (o instanceof Atom) ad.putObject(k, ((Atom) o).eval());
          else ad.putObject(k, o);
          if (open > 0 && ad instanceof ParsedAd) ((ParsedAd) ad).setHint(open);
          open = 0;
          break;
        case '}': // Check for end and push char back if found.
        case ']':
        case ')':
        case '>':
          saved = c;
        case ',': // Check for separator.
        case ';':
        case '&':
        case ' ':
        case '\r':
        case '\n':
          // Insert into ad as list item.
          if (o instanceof Atom) ad.putObject(((Atom) o).eval());
          else ad.putObject(o);
          if (open > 0 && ad instanceof ParsedAd) ((ParsedAd) ad).setHint(open);
          open = 0;
      }
    }
  }

  // Find and unescape a string.
  private String readString() {
    StringBuilder sb = new StringBuilder();
    char c = next();
    if (c != '"') throw new RuntimeException("expecting start of string");
    while (true)
      switch (c = next()) {
        case '"':
          return sb.toString();
        case '\\':
          sb.append(readEscaped());
          continue;
        default:
          if (Character.isISOControl(c)) throw new RuntimeException("illegal character in string");
          sb.append(c);
      }
  }

  // Find an escaped character, assuming the \ has already been read.
  private char readEscaped() {
    switch (next()) {
      case '"':
        return '"';
      case '\\':
        return '\\';
      case '/':
        return '/';
      case 'b':
        return '\b';
      case 'f':
        return '\f';
      case 'n':
        return '\n';
      case 'r':
        return '\r';
      case 't':
        return '\t';
      case 'u':
        {
          int r = 0;
          for (int i = 0; i < 4; i++) {
            char c = next();
            if (check(c, '0', '9')) r = (r << 4) | (c - '0');
            else if (check(c = low(c), 'a', 'f')) r = (r << 4) | (c - 'a' + 10);
            else throw new RuntimeException("illegal escape sequence");
          }
          return (char) r;
        }
    }
    throw new RuntimeException("illegal escape sequence");
  }

  // An atom represents something that can be either an identifier or a
  // keyword. The string representation of this should be lowercased for
  // use as a key.
  private static class Atom {
    String s;

    Atom(String s) {
      this.s = s;
    }

    public String toString() {
      return s.toLowerCase();
    }

    public Object eval() {
      String sl = s.toLowerCase();
      if (sl.equals("false")) return Boolean.FALSE;
      if (sl.equals("true")) return Boolean.TRUE;
      if (sl.equals("null")) return null;
      return s;
    }
  }

  // Try to read an atom.
  private Atom readAtom() {
    StringBuilder sb = new StringBuilder();
    for (char c = peek(); validAtomPart(c); c = peek()) sb.append(next());
    return new Atom(sb.toString());
  }

  // Check if a character can start or be in an atom.
  private static boolean validAtomStart(char c) {
    return check(low(c), 'a', 'z') || c == '_';
  }

  private static boolean validAtomPart(char c) {
    return check(low(c), 'a', 'z') || c == '_' || check(c, '0', '9');
  }

  // Check if a string is a valid identifier.
  public static boolean checkIdentifier(String s) {
    for (char i = 0; i < s.length(); i++) {
      char c = s.charAt(i);
      if (i == 0 && !validAtomStart(c) || !validAtomPart(c)) return false;
    }
    return true;
  }

  // Find a value, and return it as a Java object.
  private Object findValue() {
    saved = discardIgnored();
    return readValue();
  }

  private Object readValue() {
    char c = peek();
    switch (c) {
      case '"':
        return readString();
      case '-':
        return readNumber();
    }
    if (check(c, '0', '9')) {
      return readNumber();
    }
    if (check(c, "{[(<")) {
      return parseInto(new Ad());
    }
    if (validAtomStart(c)) {
      return readAtom();
    }
    throw new RuntimeException("cannot parse value starting with: " + c);
  }

  private Number readNumber() {
    char c;
    boolean d = false;
    StringBuilder sb = new StringBuilder();
    while (true)
      switch (c = peek()) {
        case '.':
        case 'e':
        case 'E':
        case '+':
          d = true;
        case '-':
          sb.append(next());
          continue;
        default:
          if (check(c, '0', '9')) sb.append(next());
          else if (check(c, ",:; \n\r\t>}])")) {
            return d ? new BigDecimal(sb.toString()) : new BigInteger(sb.toString());
          } else throw new RuntimeException("unexpected character: " + c);
      }
  }
}
예제 #13
0
// 感觉如果没有看过 NIO 的详细内容是不会懂这部分
public class TypeServer extends TCPNIOServer {
  static String testString = "Thisisateststring";

  static class ClientInfo {
    ByteBuffer inBuf = ByteBuffer.allocateDirect(512);
    ByteBuffer outBuf = ByteBuffer.allocateDirect(512);
    boolean outputPending = false;
    SocketChannel channel;
  }

  Map allClients = new HashMap();
  Charset encoder = Charset.forName("UTF-8");

  protected void handleClient(SelectionKey key) throws IOException {
    SocketChannel sc = (SocketChannel) key.channel();
    ClientInfo ci = (ClientInfo) allClients.get(sc);
    if (ci == null) throw new IllegalStateException("Unknown client");
    if (key.isWritable()) send(sc, ci);
    if (key.isReadable())
      // 从一个客户端读进所有的可用数据
      recv(sc, ci);
  }

  private void recv(SocketChannel sc, ClientInfo ci) throws IOException {
    ci.channel.read(ci.inBuf);
    ByteBuffer tmpBuf = ci.inBuf.duplicate();
    tmpBuf.flip();
    int bytesProcessed = 0;
    boolean doneLoop = false;
    while (!doneLoop) {
      byte b;
      try {
        b = tmpBuf.get();
      } catch (BufferUnderflowException bue) {
        // Processed all data in buffer
        ci.inBuf.clear();
        doneLoop = true;
        break;
      }
      switch (b) {
        case TypeServerConstants.WELCOME:
          bytesProcessed++;
          break;
        case TypeServerConstants.GET_STRING_REQUEST:
          bytesProcessed++;
          if (ci.outputPending) {
            // Client is backed up. We can't append to
            // the byte buffer because it's in the wrong
            // state. We could allocate another buffer
            // here and change our send method to know
            // about multiple buffers, but we'll just
            // assume that the client is dead
            break;
          }
          ci.outBuf.put(TypeServerConstants.GET_STRING_RESPONSE);
          ByteBuffer strBuf = encoder.encode(testString);
          ci.outBuf.putShort((short) strBuf.remaining());
          ci.outBuf.put(strBuf);
          ci.outBuf.flip();
          send(sc, ci);
          break;
        case TypeServerConstants.GET_STRING_RESPONSE:
          int startPos = tmpBuf.position();
          try {
            int nBytes = tmpBuf.getInt();
            byte[] buf = new byte[nBytes];
            tmpBuf.get(buf);
            bytesProcessed += buf.length + 5;
            String s = new String(buf);
            // Send the string to the GUI
            break;
          } catch (BufferUnderflowException bue) {
            // Processed all available data
            ci.inBuf.position(ci.inBuf.position() + bytesProcessed);
            doneLoop = true;
          }
          break;
      }
    }
  }

  private void send(SocketChannel sc, ClientInfo ci) throws IOException {
    int len = ci.outBuf.remaining();
    int nBytes = sc.write(ci.outBuf);
    if (nBytes != len) {
      // Client not ready to receive data
      ci.outputPending = true;
      ci.channel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
    } else {
      ci.outBuf.clear();
      if (ci.outputPending) {
        ci.outputPending = false;
        ci.channel.register(selector, SelectionKey.OP_READ);
      }
    }
  }

  protected void registeredClient(SocketChannel sc) throws IOException {
    ClientInfo ci = new ClientInfo();
    ci.channel = sc;
    ci.outBuf.clear();
    ci.outBuf.put(TypeServerConstants.WELCOME);
    ci.outBuf.flip();
    allClients.put(sc, ci);
    send(sc, ci);
  }

  public static void main(String[] args) throws Exception {
    TypeServer ts = new TypeServer();
    ts.port = Integer.parseInt(args[0]);
    Thread t = new Thread(ts);
    t.start();
    System.out.println("Type server ready...Type CTRL-D to exit");
    while (System.in.read() > 0) ;
    ts.stopServer();
    t.join();
  }
}
예제 #14
0
/**
 * Java client for BaseX. Works with BaseX 7.0 and later
 *
 * <p>Documentation: http://docs.basex.org/wiki/Clients
 *
 * <p>(C) BaseX Team 2005-12, BSD License
 */
public class BaseXClient {
  /** UTF-8 charset. */
  static final Charset UTF8 = Charset.forName("UTF-8");
  /** Event notifications. */
  final Map<String, EventNotifier> notifiers = new HashMap<String, EventNotifier>();
  /** Output stream. */
  final OutputStream out;
  /** Socket. */
  final Socket socket;
  /** Cache. */
  final BufferedInputStream in;
  /** Command info. */
  String info;
  /** Socket event reference. */
  Socket esocket;
  /** Socket host name. */
  String ehost;

  /**
   * Constructor.
   *
   * @param host server name
   * @param port server port
   * @param user user name
   * @param pass password
   * @throws IOException Exception
   */
  public BaseXClient(final String host, final int port, final String user, final String pass)
      throws IOException {

    socket = new Socket();
    socket.connect(new InetSocketAddress(host, port), 5000);
    in = new BufferedInputStream(socket.getInputStream());
    out = socket.getOutputStream();
    ehost = host;

    // receive timestamp
    final String ts = receive();
    // send {Username}0 and hashed {Password/Timestamp}0
    send(user);
    send(md5(md5(pass) + ts));

    // receive success flag
    if (!ok()) throw new IOException("Access denied.");
  }

  /**
   * Executes a command and serializes the result to an output stream.
   *
   * @param cmd command
   * @param o output stream
   * @throws IOException Exception
   */
  public void execute(final String cmd, final OutputStream o) throws IOException {
    // send {Command}0
    send(cmd);
    receive(in, o);
    info = receive();
    if (!ok()) throw new IOException(info);
  }

  /**
   * Executes a command and returns the result.
   *
   * @param cmd command
   * @return result
   * @throws IOException Exception
   */
  public String execute(final String cmd) throws IOException {
    final ByteArrayOutputStream os = new ByteArrayOutputStream();
    execute(cmd, os);
    return new String(os.toByteArray(), UTF8);
  }

  /**
   * Creates a query object.
   *
   * @param query query string
   * @return query
   * @throws IOException Exception
   */
  public Query query(final String query) throws IOException {
    return new Query(query);
  }

  /**
   * Creates a database.
   *
   * @param name name of database
   * @param input xml input
   * @throws IOException I/O exception
   */
  public void create(final String name, final InputStream input) throws IOException {
    send(8, name, input);
  }

  /**
   * Adds a document to a database.
   *
   * @param path path to document
   * @param input xml input
   * @throws IOException I/O exception
   */
  public void add(final String path, final InputStream input) throws IOException {
    send(9, path, input);
  }

  /**
   * Replaces a document in a database.
   *
   * @param path path to document
   * @param input xml input
   * @throws IOException I/O exception
   */
  public void replace(final String path, final InputStream input) throws IOException {
    send(12, path, input);
  }

  /**
   * Stores a binary resource in a database.
   *
   * @param path path to document
   * @param input xml input
   * @throws IOException I/O exception
   */
  public void store(final String path, final InputStream input) throws IOException {
    send(13, path, input);
  }

  /**
   * Watches an event.
   *
   * @param name event name
   * @param notifier event notification
   * @throws IOException I/O exception
   */
  public void watch(final String name, final EventNotifier notifier) throws IOException {
    out.write(10);
    if (esocket == null) {
      final int eport = Integer.parseInt(receive());
      // initialize event socket
      esocket = new Socket();
      esocket.connect(new InetSocketAddress(ehost, eport), 5000);
      final OutputStream os = esocket.getOutputStream();
      receive(in, os);
      os.write(0);
      os.flush();
      final InputStream is = esocket.getInputStream();
      is.read();
      listen(is);
    }
    send(name);
    info = receive();
    if (!ok()) throw new IOException(info);
    notifiers.put(name, notifier);
  }

  /**
   * Unwatches an event.
   *
   * @param name event name
   * @throws IOException I/O exception
   */
  public void unwatch(final String name) throws IOException {
    out.write(11);
    send(name);
    info = receive();
    if (!ok()) throw new IOException(info);
    notifiers.remove(name);
  }

  /**
   * Returns command information.
   *
   * @return string info
   */
  public String info() {
    return info;
  }

  /**
   * Closes the session.
   *
   * @throws IOException Exception
   */
  public void close() throws IOException {
    send("exit");
    out.flush();
    if (esocket != null) esocket.close();
    socket.close();
  }

  /**
   * Checks the next success flag.
   *
   * @return value of check
   * @throws IOException Exception
   */
  boolean ok() throws IOException {
    out.flush();
    return in.read() == 0;
  }

  /**
   * Returns the next received string.
   *
   * @return String result or info
   * @throws IOException I/O exception
   */
  String receive() throws IOException {
    final ByteArrayOutputStream os = new ByteArrayOutputStream();
    receive(in, os);
    return new String(os.toByteArray(), UTF8);
  }

  /**
   * Sends a string to the server.
   *
   * @param s string to be sent
   * @throws IOException I/O exception
   */
  void send(final String s) throws IOException {
    out.write((s + '\0').getBytes(UTF8));
  }

  /**
   * Receives a string and writes it to the specified output stream.
   *
   * @param is input stream
   * @param os output stream
   * @throws IOException I/O exception
   */
  static void receive(final InputStream is, final OutputStream os) throws IOException {
    for (int b; (b = is.read()) > 0; ) {
      // read next byte if 0xFF is received
      os.write(b == 0xFF ? is.read() : b);
    }
  }

  /**
   * Sends a command, argument, and input.
   *
   * @param cmd command
   * @param path path to document
   * @param input xml input
   * @throws IOException I/O exception
   */
  private void send(final int cmd, final String path, final InputStream input) throws IOException {
    out.write(cmd);
    send(path);
    send(input);
  }

  /**
   * Starts the listener thread.
   *
   * @param is input stream
   */
  private void listen(final InputStream is) {
    final BufferedInputStream bi = new BufferedInputStream(is);
    new Thread() {
      @Override
      public void run() {
        try {
          while (true) {
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            receive(bi, os);
            final String name = new String(os.toByteArray(), UTF8);
            os = new ByteArrayOutputStream();
            receive(bi, os);
            final String data = new String(os.toByteArray(), UTF8);
            notifiers.get(name).notify(data);
          }
        } catch (final IOException ex) {
          // loop will be quit if no data can be received anymore
        }
      }
    }.start();
  }

  /**
   * Sends an input stream to the server.
   *
   * @param input xml input
   * @throws IOException I/O exception
   */
  private void send(final InputStream input) throws IOException {
    final BufferedInputStream bis = new BufferedInputStream(input);
    final BufferedOutputStream bos = new BufferedOutputStream(out);
    for (int b; (b = bis.read()) != -1; ) {
      // 0x00 and 0xFF will be prefixed by 0xFF
      if (b == 0x00 || b == 0xFF) bos.write(0xFF);
      bos.write(b);
    }
    bos.write(0);
    bos.flush();
    info = receive();
    if (!ok()) throw new IOException(info);
  }

  /**
   * Returns an MD5 hash.
   *
   * @param pw String
   * @return String
   */
  private static String md5(final String pw) {
    final StringBuilder sb = new StringBuilder();
    try {
      final MessageDigest md = MessageDigest.getInstance("MD5");
      md.update(pw.getBytes());
      for (final byte b : md.digest()) {
        final String s = Integer.toHexString(b & 0xFF);
        if (s.length() == 1) sb.append('0');
        sb.append(s);
      }
    } catch (final NoSuchAlgorithmException ex) {
      // should not occur
      ex.printStackTrace();
    }
    return sb.toString();
  }

  /** Inner class for iterative query execution. */
  public class Query {
    /** Query id. */
    private final String id;
    /** Cached results. */
    private ArrayList<byte[]> cache;
    /** Cache pointer. */
    private int pos;

    /**
     * Standard constructor.
     *
     * @param query query string
     * @throws IOException I/O exception
     */
    public Query(final String query) throws IOException {
      id = exec(0, query);
    }

    /**
     * Binds a value to an external variable.
     *
     * @param name name of variable
     * @param value value
     * @throws IOException I/O exception
     */
    public void bind(final String name, final String value) throws IOException {
      bind(name, value, "");
    }

    /**
     * Binds a value with the specified type to an external variable.
     *
     * @param name name of variable
     * @param value value
     * @param type type (can be an empty string)
     * @throws IOException I/O exception
     */
    public void bind(final String name, final String value, final String type) throws IOException {
      cache = null;
      exec(3, id + '\0' + name + '\0' + value + '\0' + type);
    }

    /**
     * Binds a value to the context item.
     *
     * @param value value
     * @throws IOException I/O exception
     */
    public void context(final String value) throws IOException {
      context(value, "");
    }

    /**
     * Binds a value with the specified type to the context item.
     *
     * @param value value
     * @param type type (can be an empty string)
     * @throws IOException I/O exception
     */
    public void context(final String value, final String type) throws IOException {
      cache = null;
      exec(14, id + '\0' + value + '\0' + type);
    }

    /**
     * Checks for the next item.
     *
     * @return result of check
     * @throws IOException I/O exception
     */
    public boolean more() throws IOException {
      if (cache == null) {
        out.write(4);
        send(id);
        cache = new ArrayList<byte[]>();
        final ByteArrayOutputStream os = new ByteArrayOutputStream();
        while (in.read() > 0) {
          receive(in, os);
          cache.add(os.toByteArray());
          os.reset();
        }
        if (!ok()) throw new IOException(receive());
        pos = 0;
      }
      if (pos < cache.size()) return true;
      cache = null;
      return false;
    }

    /**
     * Returns the next item.
     *
     * @return item string
     * @throws IOException I/O Exception
     */
    public String next() throws IOException {
      return more() ? new String(cache.set(pos++, null), UTF8) : null;
    }

    /**
     * Returns the whole result of the query.
     *
     * @return query result
     * @throws IOException I/O Exception
     */
    public String execute() throws IOException {
      return exec(5, id);
    }

    /**
     * Returns query info in a string.
     *
     * @return query info
     * @throws IOException I/O exception
     */
    public String info() throws IOException {
      return exec(6, id);
    }

    /**
     * Returns serialization parameters in a string.
     *
     * @return query info
     * @throws IOException I/O exception
     */
    public String options() throws IOException {
      return exec(7, id);
    }

    /**
     * Closes the query.
     *
     * @throws IOException I/O exception
     */
    public void close() throws IOException {
      exec(2, id);
    }

    /**
     * Executes the specified command.
     *
     * @param cmd command
     * @param arg argument
     * @return resulting string
     * @throws IOException I/O exception
     */
    private String exec(final int cmd, final String arg) throws IOException {
      out.write(cmd);
      send(arg);
      final String s = receive();
      if (!ok()) throw new IOException(receive());
      return s;
    }
  }

  /** Interface for event notifications. */
  public interface EventNotifier {
    /**
     * Invoked when a database event was fired.
     *
     * @param value event string
     */
    void notify(final String value);
  }
}