package com.dbxml.util;

/*
 * dbXML - Native XML Database
 * Copyright (C) 1999-2004  The dbXML Group, L.L.C.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * $Id: UTF8.java,v 1.10 2004/08/22 04:18:43 bradford Exp $
 */

import com.dbxml.db.core.data.Value;
import java.io.IOException;
import java.io.Writer;

/**
 * UTF8 Utility Class
 */

public final class UTF8 {
   private static final byte[] EmptyBytes = new byte[0];

   private UTF8() {
   }

   public static byte[] toUTF8(String str) {
      if ( str != null )
         return toUTF8(str.toCharArray());
      else
         return EmptyBytes;
   }

   public static byte[] toUTF8(char[] charr, int pos, int len) {
      int size = len;
      for ( int i = pos; i < pos + len; i++ ) {
         char c = charr[i];
         if ( c > 0x07FF )
            size += 2;
         else if ( c == 0x0000 || c > 0x007F )
            size++;
      }

      byte[] buf = new byte[size];

      if ( size == len ) {
         for ( int i = pos, count = 0; i < pos + len; i++, count++ )
            buf[count] = (byte)charr[i];
      }
      else {
         for ( int i = pos, count = 0; i < pos + len; i++ ) {
            char c = charr[i];
            if ( (c >= 0x0001) && (c <= 0x007F) )
               buf[count++] = (byte)c;
            else if ( c > 0x07FF ) {
               buf[count++] = (byte)(0xE0 | ((c >> 12) & 0x0F));
               buf[count++] = (byte)(0x80 | ((c >> 6) & 0x3F));
               buf[count++] = (byte)(0x80 | ((c >> 0) & 0x3F));
            }
            else {
               buf[count++] = (byte)(0xC0 | ((c >> 6) & 0x1F));
               buf[count++] = (byte)(0x80 | ((c >> 0) & 0x3F));
            }
         }
      }

      return buf;
   }

   public static byte[] toUTF8(char[] charr) {
      return toUTF8(charr, 0, charr.length);
   }

   public static String toString(byte[] bytearr) {
      CharArrayInfo info = toCharArrayInfo(bytearr);
      return new String(info.ch, 0, info.len);
   }

   public static String toString(byte[] bytearr, int pos, int len) {
      CharArrayInfo info = toCharArrayInfo(bytearr, pos, len);
      return new String(info.ch, 0, info.len);
   }

   public static String toString(Value value) {
      int pos = value.getOffset();
      int len = value.getLength();
      byte[] b = value.getRawData();
      return toString(b, pos, len);
   }

   public static void toWriter(byte[] bytearr, Writer writer) throws IOException {
      CharArrayInfo info = toCharArrayInfo(bytearr);
      writer.write(info.ch, 0, info.len);
   }

   public static void toWriter(byte[] bytearr, int pos, int len, Writer writer) throws IOException {
      CharArrayInfo info = toCharArrayInfo(bytearr, pos, len);
      writer.write(info.ch, 0, info.len);
   }

   public static void toWriter(Value value, Writer writer) throws IOException {
      CharArrayInfo info = toCharArrayInfo(value);
      writer.write(info.ch, 0, info.len);
   }

   public static char[] toCharArray(byte[] bytearr) {
      return toCharArray(bytearr, 0, bytearr.length);
   }

   public static char[] toCharArray(byte[] bytearr, int pos, int len) {
      CharArrayInfo info = toCharArrayInfo(bytearr, pos, len);
      if ( info.len == info.ch.length )
         return info.ch;
      else {
         char[] ch = new char[info.len];
         System.arraycopy(info.ch, 0, ch, 0, ch.length);
         return ch;
      }
   }

   public static char[] toCharArray(Value value) {
      int pos = value.getOffset();
      int len = value.getLength();
      byte[] b = value.getRawData();
      return toCharArray(b, pos, len);
   }

   private static CharArrayInfo toCharArrayInfo(byte[] bytearr) {
      return toCharArrayInfo(bytearr, 0, bytearr.length);
   }

   public static CharArrayInfo toCharArrayInfo(Value value) {
      int pos = value.getOffset();
      int len = value.getLength();
      byte[] b = value.getRawData();
      return toCharArrayInfo(b, pos, len);
   }

   private static CharArrayInfo toCharArrayInfo(byte[] bytearr, int pos, int len) {
      char str[] = new char[len];
      int count = 0;
      int strlen = 0;

      while ( count < len ) {
         int c = (int)bytearr[pos + count] & 0xff;
         int char2, char3;
         switch ( c >> 4 ) {
            case 0:
            case 1:
            case 2:
            case 3:
            case 4:
            case 5:
            case 6:
            case 7:
               count++;
               str[strlen++] = (char)c;
               break;
            case 12:
            case 13:
               count += 2;
               char2 = bytearr[(pos + count) - 1];
               str[strlen++] = (char)(((c & 0x1F) << 6) | (char2 & 0x3F));
               break;
            case 14:
               count += 3;
               char2 = bytearr[(pos + count) - 2];
               char3 = bytearr[(pos + count) - 1];
               str[strlen++] = (char)(((c & 0x0F) << 12) |
                  ((char2 & 0x3F) << 6) |
                  ((char3 & 0x3F) << 0));
               break;
         }
      }
      return new CharArrayInfo(str, strlen);
   }


   /**
    * CharArrayInfo
    */

   public static class CharArrayInfo {
      public char[] ch;
      public int len;

      public CharArrayInfo(char[] ch, int len) {
         this.ch = ch;
         this.len = len;
      }
   }
}

