home *** CD-ROM | disk | FTP | other *** search
/ Java 1.2 How-To / JavaHowTo.iso / 3rdParty / jbuilder / unsupported / JDK1.2beta3 / SOURCE / SRC.ZIP / java / text / SimpleTextBoundary.java < prev    next >
Encoding:
Java Source  |  1998-03-20  |  11.4 KB  |  372 lines

  1. /*
  2.  * @(#)SimpleTextBoundary.java    1.17 98/03/18
  3.  *
  4.  * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved
  5.  * (C) Copyright IBM Corp. 1996 - All Rights Reserved
  6.  *
  7.  * Portions copyright (c) 1996-1998 Sun Microsystems, Inc. All Rights Reserved.
  8.  *
  9.  *   The original version of this source code and documentation is copyrighted
  10.  * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
  11.  * materials are provided under terms of a License Agreement between Taligent
  12.  * and Sun. This technology is protected by multiple US and International
  13.  * patents. This notice and attribution to Taligent may not be removed.
  14.  *   Taligent is a registered trademark of Taligent, Inc.
  15.  *
  16.  * Permission to use, copy, modify, and distribute this software
  17.  * and its documentation for NON-COMMERCIAL purposes and without
  18.  * fee is hereby granted provided that this copyright notice
  19.  * appears in all copies. Please refer to the file "copyright.html"
  20.  * for further important copyright and licensing information.
  21.  *
  22.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
  23.  * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  24.  * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  25.  * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
  26.  * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
  27.  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
  28.  *
  29.  */
  30.  
  31. package java.text;
  32.  
  33. import java.io.IOException;
  34. import java.io.ObjectOutputStream;
  35. import java.io.ObjectInputStream;
  36.  
  37. /**
  38.  * SimpleTextBoundary is an implementation of the BreakIterator
  39.  * protocol.  SimpleTextBoundary uses a state machine to compute breaks.
  40.  * There are currently several subclasses of SimpleTextBoundary that
  41.  * compute breaks for sentences, words, lines, and characters.  They are
  42.  * accessable through static functions of SimpleTextBoundary.
  43.  * @see BreakIterator
  44.  */
  45.  
  46. final class SimpleTextBoundary extends BreakIterator
  47. {
  48.  
  49.     private int pos;
  50.     private CharacterIterator text;
  51.     private transient TextBoundaryData data;
  52.  
  53.     // internally, the not-a-Unicode value is used as a sentinel value meaning
  54.     // "the end of the string" for the purposes of looking up an appropriate
  55.     // state transition when you've run off the end of the string
  56.     private static final char END_OF_STRING = '\uffff';
  57.  
  58.     /**
  59.      * Create a SimpleTextBoundary using the specified tables. Currently,
  60.      * the table format is private.
  61.      * @param data data used for boundary determination
  62.      */
  63.     protected SimpleTextBoundary(TextBoundaryData data)
  64.     {
  65.         this.data = data;
  66.         text = new StringCharacterIterator("");
  67.         pos = text.getBeginIndex();
  68.     }
  69.  
  70.     /**
  71.      * Compares the equality of two SimpleTextBoundary objects.
  72.      * @param obj the SimpleTextBoundary object to be compared with.
  73.      * @return true if the given obj is the same as this
  74.      * SimpleTextBoundary object; false otherwise.
  75.      */
  76.     public boolean equals(Object obj)
  77.     {
  78.         if (this == obj)
  79.             return true;
  80.         if (!(obj instanceof SimpleTextBoundary))
  81.             return false;
  82.  
  83.         SimpleTextBoundary that = (SimpleTextBoundary) obj;
  84.  
  85.         // The data classes are final and sharable. Only the
  86.         // class type needs to be compared.
  87.         if (this.data.getClass() != that.data.getClass())
  88.             return false;
  89.         if (this.hashCode() != that.hashCode())
  90.             return false;
  91.         if (pos != that.pos)
  92.             return false;
  93.         if (!text.equals(that.text))
  94.             return false;
  95.         return true;
  96.     }
  97.  
  98.     /**
  99.      * Compute a hashcode for this enumeration
  100.      * @return A hash code
  101.      */
  102.     public int hashCode()
  103.     {
  104.         return getClass().hashCode() ^ text.hashCode();
  105.     }
  106.  
  107.     /**
  108.      * Overrides Cloneable
  109.      */
  110.     public Object clone()
  111.     {
  112.         try {
  113.             SimpleTextBoundary other = (SimpleTextBoundary) super.clone();
  114.             other.text = (CharacterIterator) text.clone();
  115.             // The data classes are final and sharable.
  116.             // They don't need to be cloned.
  117.             return other;
  118.         } catch (InternalError e) {
  119.             throw new InternalError();
  120.         }
  121.     }
  122.  
  123.     /**
  124.      * Write the BreakIterator out to a stream
  125.      */
  126.     private void writeObject(ObjectOutputStream stream) throws IOException
  127.     {
  128.         stream.defaultWriteObject();
  129.  
  130.         // write out just the class name of the break tables.  Right now, the
  131.         // break tables are internal and immutable, so all we need to recreate
  132.         // them is the class.  We write the name instead of the Class object
  133.         // because the Class object couldn't be serialized, and because writing
  134.         // the name allows for future changes
  135.         stream.writeUTF(data.getClass().getName());
  136.     }
  137.  
  138.     /**
  139.      * Read the BreakIterator in from a stream
  140.      */
  141.     private void readObject(ObjectInputStream stream) throws IOException
  142.     {
  143.         try {
  144.             stream.defaultReadObject();
  145.  
  146.             // read in the class name of the break table, create a Class object
  147.             // from it, and use that Class object to instantiate the break table
  148.             // itself.  Since the break tables are currently immutable and
  149.             // internal, all we need to deserialize a break table is the class.
  150.             // We use the class name rather than the Class object itself because
  151.             // you can't serialize a Class object for a class which isn't itself
  152.             // serializable, and because writing a name allows for future changes
  153.             data = (TextBoundaryData)(Class.forName(stream.readUTF()).newInstance());
  154.         }
  155.         catch (Exception e) {
  156.             throw new IOException("Deserialization failure: " + e.toString());
  157.         }
  158.     }
  159.  
  160.     /**
  161.      * Get the text being scanned by the enumeration
  162.      * @return the text being scanned by the enumeration
  163.      */
  164.     public CharacterIterator getText()
  165.     {
  166.         return text;
  167.     }
  168.  
  169.     /**
  170.      * Set a new text string for enumeration.  The position of the
  171.      * enumerator is reset to first().
  172.      * @param newText new text to scan.
  173.      */
  174.     public void setText(String newText)
  175.     {
  176.         text = new StringCharacterIterator(newText);
  177.         pos = text.getBeginIndex();
  178.     }
  179.  
  180.     /**
  181.      * Set a new text to scan.  The position is reset to first().
  182.      * @param newText new text to scan.
  183.      */
  184.     public void setText(CharacterIterator newText)
  185.     {
  186.         text = newText;
  187.         pos = text.getBeginIndex();
  188.     }
  189.  
  190.     /**
  191.      * Return the first boundary. The iterator's current position is set
  192.      * to the first boundary.
  193.      */
  194.     public int first()
  195.     {
  196.         pos = text.getBeginIndex();
  197.         return pos;
  198.     }
  199.  
  200.     /**
  201.      * Return the last boundary. The iterator's current position is set
  202.      * to the last boundary.
  203.      */
  204.     public int last()
  205.     {
  206.         pos = text.getEndIndex();
  207.         return pos;
  208.     }
  209.  
  210.     /**
  211.      * Return the nth boundary from the current boundary
  212.      * @param index which boundary to return.  A value of 0
  213.      * does nothing.
  214.      * @return the nth boundary from the current position.
  215.      */
  216.     public int next(int increment)
  217.     {
  218.         int result = current();
  219.         if (increment < 0) {
  220.             for (int i = increment; (i < 0) && (result != DONE); ++i) {
  221.                 result = previous();
  222.             }
  223.         }
  224.         else {
  225.             for(int i = increment; (i > 0) && (result != DONE); --i) {
  226.                 result = next();
  227.             }
  228.         }
  229.         return result;
  230.     }
  231.  
  232.     /**
  233.      * Return the boundary preceding the last boundary
  234.      */
  235.     public int previous()
  236.     {
  237.         if (pos > text.getBeginIndex()) {
  238.             int startBoundary = pos;
  239.             pos = previousSafePosition(pos-1);
  240.             int prev = pos;
  241.             int next = next();
  242.             while (next < startBoundary && next != DONE) {
  243.                 prev = next;
  244.                 next = next();
  245.             }
  246.             pos = prev;
  247.             return pos;
  248.         }
  249.         else {
  250.             return DONE;
  251.         }
  252.     }
  253.  
  254.     /**
  255.      * Return the next text boundary
  256.      * @return the character offset of the text boundary or DONE if all
  257.      * boundaries have been returned.
  258.      */
  259.     public int next()
  260.     {
  261.         int result = pos;
  262.         if (pos < text.getEndIndex()) {
  263.             pos = nextPosition(pos);
  264.             result = pos;
  265.         }
  266.         else {
  267.             result = DONE;
  268.         }
  269.         return result;
  270.     }
  271.  
  272.     /**
  273.      * Return the first boundary after the specified offset
  274.      * @param offset the offset to start
  275.      * @return int the first boundary after offset
  276.      */
  277.     public int following(int offset)
  278.     {
  279.         if (offset < text.getBeginIndex() || offset >= text.getEndIndex())
  280.             throw new IllegalArgumentException(
  281.               "nextBoundaryAt offset out of bounds");
  282.         pos = previousSafePosition(offset);
  283.         int result;
  284.         do {
  285.             result = next();
  286.         } while (result <= offset && result != DONE);
  287.         return result;
  288.     }
  289.  
  290.     /**
  291.      * Return the last boundary preceding the specified offset
  292.      * @param offset the offset to start
  293.      * @return the last boundary before offset
  294.      */
  295. // function made package private pending API-change approval
  296.     /*public*/ int preceding(int offset)
  297.     {
  298.         if (offset < text.getBeginIndex() || offset >= text.getEndIndex())
  299.             throw new IllegalArgumentException("preceding() offset out of bounds");
  300.         if (offset == text.getBeginIndex())
  301.             return BreakIterator.DONE;
  302.         pos = previousSafePosition(offset);
  303.         int curr = pos;
  304.         int last;
  305.         do {
  306.             last = curr;
  307.             curr = next();
  308.         } while (curr < offset && curr != BreakIterator.DONE);
  309.         return last;
  310.     }
  311.  
  312.     /**
  313.      * Return the boundary last returned by previous or next
  314.      * @return int the boundary last returned by previous or next
  315.      */
  316.     public int current()
  317.     {
  318.         return pos;
  319.     }
  320.  
  321.     //.................................................
  322.     //utility functions.  These functions don't change the current position.
  323.     private int previousSafePosition(int offset)
  324.     {
  325.         int result = text.getBeginIndex();
  326.         int state = data.backward().initialState();
  327.  
  328.         if (offset == result)
  329.             ++offset;
  330.         for (char c = text.setIndex(offset - 1);
  331.              c != CharacterIterator.DONE && !data.backward().isEndState(state);
  332.              c = text.previous()) {
  333.  
  334.             state = data.backward().get(state, mappedChar(c));
  335.             if (data.backward().isMarkState(state)) {
  336.                 result = text.getIndex();
  337.             }
  338.         }
  339.         return result;
  340.     }
  341.  
  342.     private int nextPosition(int offset)
  343.     {
  344.         int getEndIndex = text.getEndIndex();
  345.         int state = data.forward().initialState();
  346.  
  347.         for (char c = text.setIndex(offset);
  348.              c != CharacterIterator.DONE && !data.forward().isEndState(state);
  349.              c = text.next()) {
  350.             state = data.forward().get(state, mappedChar(c));
  351.             if (data.forward().isMarkState(state)) {
  352.                 getEndIndex = text.getIndex();
  353.             }
  354.         }
  355.         if (data.forward().isEndState(state))
  356.             return getEndIndex;
  357.         else {
  358.             state = data.forward().get(state, mappedChar(END_OF_STRING));
  359.             if (data.forward().isMarkState(state))
  360.                 return text.getEndIndex();
  361.             else
  362.                 return getEndIndex;
  363.         }
  364.     }
  365.  
  366.     protected int mappedChar(char c)
  367.     {
  368.         return data.map().mappedChar(c);
  369.     }
  370.  
  371. }
  372.