Text.java
上传用户:quxuerui
上传日期:2018-01-08
资源大小:41811k
文件大小:19k
源码类别:

网格计算

开发平台:

Java

  1. /**
  2.  * Licensed to the Apache Software Foundation (ASF) under one
  3.  * or more contributor license agreements.  See the NOTICE file
  4.  * distributed with this work for additional information
  5.  * regarding copyright ownership.  The ASF licenses this file
  6.  * to you under the Apache License, Version 2.0 (the
  7.  * "License"); you may not use this file except in compliance
  8.  * with the License.  You may obtain a copy of the License at
  9.  *
  10.  *     http://www.apache.org/licenses/LICENSE-2.0
  11.  *
  12.  * Unless required by applicable law or agreed to in writing, software
  13.  * distributed under the License is distributed on an "AS IS" BASIS,
  14.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15.  * See the License for the specific language governing permissions and
  16.  * limitations under the License.
  17.  */
  18. package org.apache.hadoop.io;
  19. import java.io.IOException;
  20. import java.io.DataInput;
  21. import java.io.DataOutput;
  22. import java.nio.ByteBuffer;
  23. import java.nio.CharBuffer;
  24. import java.nio.charset.CharacterCodingException;
  25. import java.nio.charset.Charset;
  26. import java.nio.charset.CharsetDecoder;
  27. import java.nio.charset.CharsetEncoder;
  28. import java.nio.charset.CodingErrorAction;
  29. import java.nio.charset.MalformedInputException;
  30. import java.text.CharacterIterator;
  31. import java.text.StringCharacterIterator;
  32. import org.apache.commons.logging.Log;
  33. import org.apache.commons.logging.LogFactory;
  34. /** This class stores text using standard UTF8 encoding.  It provides methods
  35.  * to serialize, deserialize, and compare texts at byte level.  The type of
  36.  * length is integer and is serialized using zero-compressed format.  <p>In
  37.  * addition, it provides methods for string traversal without converting the
  38.  * byte array to a string.  <p>Also includes utilities for
  39.  * serializing/deserialing a string, coding/decoding a string, checking if a
  40.  * byte array contains valid UTF8 code, calculating the length of an encoded
  41.  * string.
  42.  */
  43. public class Text extends BinaryComparable
  44.     implements WritableComparable<BinaryComparable> {
  45.   private static final Log LOG= LogFactory.getLog(Text.class);
  46.   
  47.   private static ThreadLocal<CharsetEncoder> ENCODER_FACTORY =
  48.     new ThreadLocal<CharsetEncoder>() {
  49.       protected CharsetEncoder initialValue() {
  50.         return Charset.forName("UTF-8").newEncoder().
  51.                onMalformedInput(CodingErrorAction.REPORT).
  52.                onUnmappableCharacter(CodingErrorAction.REPORT);
  53.     }
  54.   };
  55.   
  56.   private static ThreadLocal<CharsetDecoder> DECODER_FACTORY =
  57.     new ThreadLocal<CharsetDecoder>() {
  58.     protected CharsetDecoder initialValue() {
  59.       return Charset.forName("UTF-8").newDecoder().
  60.              onMalformedInput(CodingErrorAction.REPORT).
  61.              onUnmappableCharacter(CodingErrorAction.REPORT);
  62.     }
  63.   };
  64.   
  65.   private static final byte [] EMPTY_BYTES = new byte[0];
  66.   
  67.   private byte[] bytes;
  68.   private int length;
  69.   public Text() {
  70.     bytes = EMPTY_BYTES;
  71.   }
  72.   /** Construct from a string. 
  73.    */
  74.   public Text(String string) {
  75.     set(string);
  76.   }
  77.   /** Construct from another text. */
  78.   public Text(Text utf8) {
  79.     set(utf8);
  80.   }
  81.   /** Construct from a byte array.
  82.    */
  83.   public Text(byte[] utf8)  {
  84.     set(utf8);
  85.   }
  86.   
  87.   /**
  88.    * Returns the raw bytes; however, only data up to {@link #getLength()} is
  89.    * valid.
  90.    */
  91.   public byte[] getBytes() {
  92.     return bytes;
  93.   }
  94.   /** Returns the number of bytes in the byte array */ 
  95.   public int getLength() {
  96.     return length;
  97.   }
  98.   
  99.   /**
  100.    * Returns the Unicode Scalar Value (32-bit integer value)
  101.    * for the character at <code>position</code>. Note that this
  102.    * method avoids using the converter or doing String instatiation
  103.    * @return the Unicode scalar value at position or -1
  104.    *          if the position is invalid or points to a
  105.    *          trailing byte
  106.    */
  107.   public int charAt(int position) {
  108.     if (position > this.length) return -1; // too long
  109.     if (position < 0) return -1; // duh.
  110.       
  111.     ByteBuffer bb = (ByteBuffer)ByteBuffer.wrap(bytes).position(position);
  112.     return bytesToCodePoint(bb.slice());
  113.   }
  114.   
  115.   public int find(String what) {
  116.     return find(what, 0);
  117.   }
  118.   
  119.   /**
  120.    * Finds any occurence of <code>what</code> in the backing
  121.    * buffer, starting as position <code>start</code>. The starting
  122.    * position is measured in bytes and the return value is in
  123.    * terms of byte position in the buffer. The backing buffer is
  124.    * not converted to a string for this operation.
  125.    * @return byte position of the first occurence of the search
  126.    *         string in the UTF-8 buffer or -1 if not found
  127.    */
  128.   public int find(String what, int start) {
  129.     try {
  130.       ByteBuffer src = ByteBuffer.wrap(this.bytes,0,this.length);
  131.       ByteBuffer tgt = encode(what);
  132.       byte b = tgt.get();
  133.       src.position(start);
  134.           
  135.       while (src.hasRemaining()) {
  136.         if (b == src.get()) { // matching first byte
  137.           src.mark(); // save position in loop
  138.           tgt.mark(); // save position in target
  139.           boolean found = true;
  140.           int pos = src.position()-1;
  141.           while (tgt.hasRemaining()) {
  142.             if (!src.hasRemaining()) { // src expired first
  143.               tgt.reset();
  144.               src.reset();
  145.               found = false;
  146.               break;
  147.             }
  148.             if (!(tgt.get() == src.get())) {
  149.               tgt.reset();
  150.               src.reset();
  151.               found = false;
  152.               break; // no match
  153.             }
  154.           }
  155.           if (found) return pos;
  156.         }
  157.       }
  158.       return -1; // not found
  159.     } catch (CharacterCodingException e) {
  160.       // can't get here
  161.       e.printStackTrace();
  162.       return -1;
  163.     }
  164.   }  
  165.   /** Set to contain the contents of a string. 
  166.    */
  167.   public void set(String string) {
  168.     try {
  169.       ByteBuffer bb = encode(string, true);
  170.       bytes = bb.array();
  171.       length = bb.limit();
  172.     }catch(CharacterCodingException e) {
  173.       throw new RuntimeException("Should not have happened " + e.toString()); 
  174.     }
  175.   }
  176.   /** Set to a utf8 byte array
  177.    */
  178.   public void set(byte[] utf8) {
  179.     set(utf8, 0, utf8.length);
  180.   }
  181.   
  182.   /** copy a text. */
  183.   public void set(Text other) {
  184.     set(other.getBytes(), 0, other.getLength());
  185.   }
  186.   /**
  187.    * Set the Text to range of bytes
  188.    * @param utf8 the data to copy from
  189.    * @param start the first position of the new string
  190.    * @param len the number of bytes of the new string
  191.    */
  192.   public void set(byte[] utf8, int start, int len) {
  193.     setCapacity(len, false);
  194.     System.arraycopy(utf8, start, bytes, 0, len);
  195.     this.length = len;
  196.   }
  197.   /**
  198.    * Append a range of bytes to the end of the given text
  199.    * @param utf8 the data to copy from
  200.    * @param start the first position to append from utf8
  201.    * @param len the number of bytes to append
  202.    */
  203.   public void append(byte[] utf8, int start, int len) {
  204.     setCapacity(length + len, true);
  205.     System.arraycopy(utf8, start, bytes, length, len);
  206.     length += len;
  207.   }
  208.   /**
  209.    * Clear the string to empty.
  210.    */
  211.   public void clear() {
  212.     length = 0;
  213.   }
  214.   /*
  215.    * Sets the capacity of this Text object to <em>at least</em>
  216.    * <code>len</code> bytes. If the current buffer is longer,
  217.    * then the capacity and existing content of the buffer are
  218.    * unchanged. If <code>len</code> is larger
  219.    * than the current capacity, the Text object's capacity is
  220.    * increased to match.
  221.    * @param len the number of bytes we need
  222.    * @param keepData should the old data be kept
  223.    */
  224.   private void setCapacity(int len, boolean keepData) {
  225.     if (bytes == null || bytes.length < len) {
  226.       byte[] newBytes = new byte[len];
  227.       if (bytes != null && keepData) {
  228.         System.arraycopy(bytes, 0, newBytes, 0, length);
  229.       }
  230.       bytes = newBytes;
  231.     }
  232.   }
  233.    
  234.   /** 
  235.    * Convert text back to string
  236.    * @see java.lang.Object#toString()
  237.    */
  238.   public String toString() {
  239.     try {
  240.       return decode(bytes, 0, length);
  241.     } catch (CharacterCodingException e) { 
  242.       throw new RuntimeException("Should not have happened " + e.toString()); 
  243.     }
  244.   }
  245.   
  246.   /** deserialize 
  247.    */
  248.   public void readFields(DataInput in) throws IOException {
  249.     int newLength = WritableUtils.readVInt(in);
  250.     setCapacity(newLength, false);
  251.     in.readFully(bytes, 0, newLength);
  252.     length = newLength;
  253.   }
  254.   /** Skips over one Text in the input. */
  255.   public static void skip(DataInput in) throws IOException {
  256.     int length = WritableUtils.readVInt(in);
  257.     WritableUtils.skipFully(in, length);
  258.   }
  259.   /** serialize
  260.    * write this object to out
  261.    * length uses zero-compressed encoding
  262.    * @see Writable#write(DataOutput)
  263.    */
  264.   public void write(DataOutput out) throws IOException {
  265.     WritableUtils.writeVInt(out, length);
  266.     out.write(bytes, 0, length);
  267.   }
  268.   /** Returns true iff <code>o</code> is a Text with the same contents.  */
  269.   public boolean equals(Object o) {
  270.     if (o instanceof Text)
  271.       return super.equals(o);
  272.     return false;
  273.   }
  274.   public int hashCode() {
  275.     return super.hashCode();
  276.   }
  277.   /** A WritableComparator optimized for Text keys. */
  278.   public static class Comparator extends WritableComparator {
  279.     public Comparator() {
  280.       super(Text.class);
  281.     }
  282.     public int compare(byte[] b1, int s1, int l1,
  283.                        byte[] b2, int s2, int l2) {
  284.       int n1 = WritableUtils.decodeVIntSize(b1[s1]);
  285.       int n2 = WritableUtils.decodeVIntSize(b2[s2]);
  286.       return compareBytes(b1, s1+n1, l1-n1, b2, s2+n2, l2-n2);
  287.     }
  288.   }
  289.   static {
  290.     // register this comparator
  291.     WritableComparator.define(Text.class, new Comparator());
  292.   }
  293.   /// STATIC UTILITIES FROM HERE DOWN
  294.   /**
  295.    * Converts the provided byte array to a String using the
  296.    * UTF-8 encoding. If the input is malformed,
  297.    * replace by a default value.
  298.    */
  299.   public static String decode(byte[] utf8) throws CharacterCodingException {
  300.     return decode(ByteBuffer.wrap(utf8), true);
  301.   }
  302.   
  303.   public static String decode(byte[] utf8, int start, int length) 
  304.     throws CharacterCodingException {
  305.     return decode(ByteBuffer.wrap(utf8, start, length), true);
  306.   }
  307.   
  308.   /**
  309.    * Converts the provided byte array to a String using the
  310.    * UTF-8 encoding. If <code>replace</code> is true, then
  311.    * malformed input is replaced with the
  312.    * substitution character, which is U+FFFD. Otherwise the
  313.    * method throws a MalformedInputException.
  314.    */
  315.   public static String decode(byte[] utf8, int start, int length, boolean replace) 
  316.     throws CharacterCodingException {
  317.     return decode(ByteBuffer.wrap(utf8, start, length), replace);
  318.   }
  319.   
  320.   private static String decode(ByteBuffer utf8, boolean replace) 
  321.     throws CharacterCodingException {
  322.     CharsetDecoder decoder = DECODER_FACTORY.get();
  323.     if (replace) {
  324.       decoder.onMalformedInput(
  325.           java.nio.charset.CodingErrorAction.REPLACE);
  326.       decoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
  327.     }
  328.     String str = decoder.decode(utf8).toString();
  329.     // set decoder back to its default value: REPORT
  330.     if (replace) {
  331.       decoder.onMalformedInput(CodingErrorAction.REPORT);
  332.       decoder.onUnmappableCharacter(CodingErrorAction.REPORT);
  333.     }
  334.     return str;
  335.   }
  336.   /**
  337.    * Converts the provided String to bytes using the
  338.    * UTF-8 encoding. If the input is malformed,
  339.    * invalid chars are replaced by a default value.
  340.    * @return ByteBuffer: bytes stores at ByteBuffer.array() 
  341.    *                     and length is ByteBuffer.limit()
  342.    */
  343.   public static ByteBuffer encode(String string)
  344.     throws CharacterCodingException {
  345.     return encode(string, true);
  346.   }
  347.   /**
  348.    * Converts the provided String to bytes using the
  349.    * UTF-8 encoding. If <code>replace</code> is true, then
  350.    * malformed input is replaced with the
  351.    * substitution character, which is U+FFFD. Otherwise the
  352.    * method throws a MalformedInputException.
  353.    * @return ByteBuffer: bytes stores at ByteBuffer.array() 
  354.    *                     and length is ByteBuffer.limit()
  355.    */
  356.   public static ByteBuffer encode(String string, boolean replace)
  357.     throws CharacterCodingException {
  358.     CharsetEncoder encoder = ENCODER_FACTORY.get();
  359.     if (replace) {
  360.       encoder.onMalformedInput(CodingErrorAction.REPLACE);
  361.       encoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
  362.     }
  363.     ByteBuffer bytes = 
  364.       encoder.encode(CharBuffer.wrap(string.toCharArray()));
  365.     if (replace) {
  366.       encoder.onMalformedInput(CodingErrorAction.REPORT);
  367.       encoder.onUnmappableCharacter(CodingErrorAction.REPORT);
  368.     }
  369.     return bytes;
  370.   }
  371.   /** Read a UTF8 encoded string from in
  372.    */
  373.   public static String readString(DataInput in) throws IOException {
  374.     int length = WritableUtils.readVInt(in);
  375.     byte [] bytes = new byte[length];
  376.     in.readFully(bytes, 0, length);
  377.     return decode(bytes);
  378.   }
  379.   /** Write a UTF8 encoded string to out
  380.    */
  381.   public static int writeString(DataOutput out, String s) throws IOException {
  382.     ByteBuffer bytes = encode(s);
  383.     int length = bytes.limit();
  384.     WritableUtils.writeVInt(out, length);
  385.     out.write(bytes.array(), 0, length);
  386.     return length;
  387.   }
  388.   ////// states for validateUTF8
  389.   
  390.   private static final int LEAD_BYTE = 0;
  391.   private static final int TRAIL_BYTE_1 = 1;
  392.   private static final int TRAIL_BYTE = 2;
  393.   /** 
  394.    * Check if a byte array contains valid utf-8
  395.    * @param utf8 byte array
  396.    * @throws MalformedInputException if the byte array contains invalid utf-8
  397.    */
  398.   public static void validateUTF8(byte[] utf8) throws MalformedInputException {
  399.     validateUTF8(utf8, 0, utf8.length);     
  400.   }
  401.   
  402.   /**
  403.    * Check to see if a byte array is valid utf-8
  404.    * @param utf8 the array of bytes
  405.    * @param start the offset of the first byte in the array
  406.    * @param len the length of the byte sequence
  407.    * @throws MalformedInputException if the byte array contains invalid bytes
  408.    */
  409.   public static void validateUTF8(byte[] utf8, int start, int len)
  410.     throws MalformedInputException {
  411.     int count = start;
  412.     int leadByte = 0;
  413.     int length = 0;
  414.     int state = LEAD_BYTE;
  415.     while (count < start+len) {
  416.       int aByte = ((int) utf8[count] & 0xFF);
  417.       switch (state) {
  418.       case LEAD_BYTE:
  419.         leadByte = aByte;
  420.         length = bytesFromUTF8[aByte];
  421.         switch (length) {
  422.         case 0: // check for ASCII
  423.           if (leadByte > 0x7F)
  424.             throw new MalformedInputException(count);
  425.           break;
  426.         case 1:
  427.           if (leadByte < 0xC2 || leadByte > 0xDF)
  428.             throw new MalformedInputException(count);
  429.           state = TRAIL_BYTE_1;
  430.           break;
  431.         case 2:
  432.           if (leadByte < 0xE0 || leadByte > 0xEF)
  433.             throw new MalformedInputException(count);
  434.           state = TRAIL_BYTE_1;
  435.           break;
  436.         case 3:
  437.           if (leadByte < 0xF0 || leadByte > 0xF4)
  438.             throw new MalformedInputException(count);
  439.           state = TRAIL_BYTE_1;
  440.           break;
  441.         default:
  442.           // too long! Longest valid UTF-8 is 4 bytes (lead + three)
  443.           // or if < 0 we got a trail byte in the lead byte position
  444.           throw new MalformedInputException(count);
  445.         } // switch (length)
  446.         break;
  447.       case TRAIL_BYTE_1:
  448.         if (leadByte == 0xF0 && aByte < 0x90)
  449.           throw new MalformedInputException(count);
  450.         if (leadByte == 0xF4 && aByte > 0x8F)
  451.           throw new MalformedInputException(count);
  452.         if (leadByte == 0xE0 && aByte < 0xA0)
  453.           throw new MalformedInputException(count);
  454.         if (leadByte == 0xED && aByte > 0x9F)
  455.           throw new MalformedInputException(count);
  456.         // falls through to regular trail-byte test!!
  457.       case TRAIL_BYTE:
  458.         if (aByte < 0x80 || aByte > 0xBF)
  459.           throw new MalformedInputException(count);
  460.         if (--length == 0) {
  461.           state = LEAD_BYTE;
  462.         } else {
  463.           state = TRAIL_BYTE;
  464.         }
  465.         break;
  466.       } // switch (state)
  467.       count++;
  468.     }
  469.   }
  470.   /**
  471.    * Magic numbers for UTF-8. These are the number of bytes
  472.    * that <em>follow</em> a given lead byte. Trailing bytes
  473.    * have the value -1. The values 4 and 5 are presented in
  474.    * this table, even though valid UTF-8 cannot include the
  475.    * five and six byte sequences.
  476.    */
  477.   static final int[] bytesFromUTF8 =
  478.   { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  479.     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  480.     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  481.     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  482.     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  483.     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  484.     0, 0, 0, 0, 0, 0, 0,
  485.     // trail bytes
  486.     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  487.     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  488.     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  489.     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1,
  490.     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  491.     1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3,
  492.     3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 };
  493.   /**
  494.    * Returns the next code point at the current position in
  495.    * the buffer. The buffer's position will be incremented.
  496.    * Any mark set on this buffer will be changed by this method!
  497.    */
  498.   public static int bytesToCodePoint(ByteBuffer bytes) {
  499.     bytes.mark();
  500.     byte b = bytes.get();
  501.     bytes.reset();
  502.     int extraBytesToRead = bytesFromUTF8[(b & 0xFF)];
  503.     if (extraBytesToRead < 0) return -1; // trailing byte!
  504.     int ch = 0;
  505.     switch (extraBytesToRead) {
  506.     case 5: ch += (bytes.get() & 0xFF); ch <<= 6; /* remember, illegal UTF-8 */
  507.     case 4: ch += (bytes.get() & 0xFF); ch <<= 6; /* remember, illegal UTF-8 */
  508.     case 3: ch += (bytes.get() & 0xFF); ch <<= 6;
  509.     case 2: ch += (bytes.get() & 0xFF); ch <<= 6;
  510.     case 1: ch += (bytes.get() & 0xFF); ch <<= 6;
  511.     case 0: ch += (bytes.get() & 0xFF);
  512.     }
  513.     ch -= offsetsFromUTF8[extraBytesToRead];
  514.     return ch;
  515.   }
  516.   
  517.   static final int offsetsFromUTF8[] =
  518.   { 0x00000000, 0x00003080,
  519.     0x000E2080, 0x03C82080, 0xFA082080, 0x82082080 };
  520.   /**
  521.    * For the given string, returns the number of UTF-8 bytes
  522.    * required to encode the string.
  523.    * @param string text to encode
  524.    * @return number of UTF-8 bytes required to encode
  525.    */
  526.   public static int utf8Length(String string) {
  527.     CharacterIterator iter = new StringCharacterIterator(string);
  528.     char ch = iter.first();
  529.     int size = 0;
  530.     while (ch != CharacterIterator.DONE) {
  531.       if ((ch >= 0xD800) && (ch < 0xDC00)) {
  532.         // surrogate pair?
  533.         char trail = iter.next();
  534.         if ((trail > 0xDBFF) && (trail < 0xE000)) {
  535.           // valid pair
  536.           size += 4;
  537.         } else {
  538.           // invalid pair
  539.           size += 3;
  540.           iter.previous(); // rewind one
  541.         }
  542.       } else if (ch < 0x80) {
  543.         size++;
  544.       } else if (ch < 0x800) {
  545.         size += 2;
  546.       } else {
  547.         // ch < 0x10000, that is, the largest char value
  548.         size += 3;
  549.       }
  550.       ch = iter.next();
  551.     }
  552.     return size;
  553.   }
  554. }