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

  1. /*
  2.  * @(#)StyledString.java    1.38 98/03/18
  3.  *
  4.  * Copyright 1997, 1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14.  
  15. /*
  16.  * (C) Copyright Taligent, Inc. 1996 - 1997, All Rights Reserved
  17.  * (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved
  18.  *
  19.  * The original version of this source code and documentation is
  20.  * copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
  21.  * of IBM. These materials are provided under terms of a License
  22.  * Agreement between Taligent and Sun. This technology is protected
  23.  * by multiple US and International patents.
  24.  *
  25.  * This notice and attribution to Taligent may not be removed.
  26.  * Taligent is a registered trademark of Taligent, Inc.
  27.  *
  28.  */
  29.  
  30. package java.awt.font;
  31.  
  32. import java.awt.Font;
  33. import java.awt.Shape;
  34. import java.awt.geom.GeneralPath;
  35. import java.awt.geom.Rectangle2D;
  36. import java.awt.geom.Point2D;
  37. import java.text.AttributedCharacterIterator;
  38. import java.text.AttributeSet;
  39.  
  40. /**
  41.  * StyledString is a simple, immutable storage model for styled
  42.  * text.  It supports substring and concatenation operations with
  43.  * other StyledStrings.
  44.  *
  45.  */
  46.  
  47.  
  48. public final class StyledString {
  49.     private float ascent;
  50.     private float descent;
  51.     private float leading;
  52.     private int length;
  53.  
  54.     /*
  55.      * package access
  56.      * indices are start of each attr range, one more value than
  57.      * ranges
  58.      */
  59.     char[] chars;
  60.     AttributeSet[] attrs;
  61.     int[] indices;
  62.  
  63.     /**
  64.      * Constructs a new StyledString with no characters.
  65.      */
  66.     public StyledString(){
  67.         initEmpty();
  68.     }
  69.  
  70.     /**
  71.      * Allocates a new styled string containing the same characters
  72.      * as the string argument, all styled with the specified attributes.
  73.      */
  74.     public StyledString(String str, AttributeSet attributes) {
  75.     init(str, attributes);
  76.     }
  77.  
  78.     /**
  79.      * Allocates a new styled string containing the same characters
  80.      * as the first string argument, all styled with an attribute
  81.      * that has the specified name and value.
  82.      * REMIND jk . new api
  83.      */
  84.     private StyledString(String str, String name, Object value) {
  85.         init(str, new TextAttributeSet(name, value));
  86.     }
  87.  
  88.     /* package, used by StyledStringBuffer */
  89.     StyledString(char[] chars, AttributeSet[] attrs, int[] indices) {
  90.         this.chars = chars;
  91.         this.attrs = attrs;
  92.         this.indices = indices;
  93.         ascent = -1;
  94.     }
  95.  
  96.     /**
  97.      * Allocates a new styled string containing the same characters
  98.      * and attributes as the iterator argument.
  99.      * REMIND jk . new api
  100.      */
  101.     /*private StyledString(AttributedCharacterIterator text) {
  102.         if (text.getEndIndex() == text.getBeginIndex()) {
  103.             initEmpty();
  104.         } else {
  105.             StyledStringBuffer buffer = new StyledStringBuffer(text);
  106.             buffer.copyInto(this);
  107.             ascent = -1;
  108.         }
  109.     }*/
  110.  
  111.     /**
  112.      * Allocates a new styled string containing the same characters
  113.      * and attributes currently contained in the styled string buffer
  114.      * argument.
  115.      *
  116.      * @param buffer a <code>StringBuffer</code>.
  117.      */
  118.     /*StyledString(StyledStringBuffer buffer) {
  119.         if (buffer.getLength() == 0) {
  120.             initEmpty();
  121.         } else {
  122.             buffer.copyInto(this);
  123.             ascent = -1;
  124.         }
  125.     }*/
  126.  
  127.     /**
  128.      * Constructs a new StyledString from the given String using the given Font.
  129.      * @param str the String object.
  130.      * @param font the Font object.
  131.      */
  132.     public StyledString( String str, Font font ){
  133.         TextAttributeSet attributes =
  134.             new TextAttributeSet(font.getRequestedAttributes());
  135.         attributes.add(TextAttributeSet.FONT, font);
  136.         init(str, attributes);
  137.     }
  138.  
  139.     private void init(String str, AttributeSet attributes) {
  140.         if (str.length() == 0) {
  141.             initEmpty();
  142.         } else {
  143.             this.chars = str.toCharArray();
  144.             this.attrs = new AttributeSet[] { attributes };
  145.             this.indices = new int[] { 0, chars.length };
  146.         }
  147.         length = 0;
  148.         ascent = -1;
  149.     }
  150.  
  151.     private void initEmpty() {
  152.         chars = new char[0];
  153.         indices = new int[] { 0 };
  154.         length = 0;
  155.         ascent = -1;
  156.     }
  157.  
  158.     /**
  159.      * Returns a new styled string that is a substring of this
  160.      * styled string. The substring begins at the specified
  161.      * <code>beginIndex</code> and extends to the character at
  162.      * index <code>endIndex - 1</code>.
  163.      *
  164.      * @param      beginIndex   the beginning index, inclusive.
  165.      * @param      endIndex     the ending index, exclusive.
  166.      * @return     the specified substring.
  167.      * @exception  StringIndexOutOfBoundsException  if the
  168.      * <code>beginIndex</code> or the <code>endIndex</code> is
  169.      * out of range.
  170.      */
  171.     public StyledString substring(int beginIndex, int endIndex) {
  172.     if (beginIndex < 0) {
  173.         throw new StringIndexOutOfBoundsException(beginIndex);
  174.     }
  175.     if (endIndex > chars.length) {
  176.         throw new StringIndexOutOfBoundsException(endIndex);
  177.     }
  178.     if (endIndex < beginIndex) {
  179.         throw new StringIndexOutOfBoundsException(endIndex -
  180.                 beginIndex);
  181.     }
  182.  
  183.     // check for empty string, which can cause array access errors
  184.         if (length() == 0) {
  185.             return this;
  186.         } else if (endIndex == beginIndex) {
  187.             return new StyledString();
  188.         }
  189.  
  190.     int startSegment = findSegmentContaining(beginIndex);
  191.     int endSegment = findSegmentContaining(endIndex);
  192.     if (endIndex == indices[endSegment]) {
  193.         --endSegment;
  194.         }
  195.  
  196.     int len = endIndex - beginIndex;
  197.     int attrLen = endSegment - startSegment + 1;
  198.  
  199.         char[] newChars = new char[len];
  200.         System.arraycopy(chars, beginIndex, newChars, 0, len);
  201.  
  202.         AttributeSet[] newAttrs = new AttributeSet[attrLen];
  203.         System.arraycopy(attrs, startSegment, newAttrs, 0, attrLen);
  204.  
  205.         int[] newIndices = new int[attrLen+1];
  206.  
  207.         newIndices[0] = 0;
  208.         for (int i = 1; i < attrLen; i++) {
  209.             newIndices[i] = indices[startSegment + i] - beginIndex;
  210.         }
  211.         newIndices[attrLen] = endIndex - beginIndex;
  212.  
  213.         return new StyledString(newChars, newAttrs, newIndices);
  214.     }
  215.  
  216.     private void loadMetrics(){
  217.         if(ascent == -1) {
  218.             for (int i=0;i<attrs.length;i++){
  219.                 Font theFont = (Font) attrs[i].get(TextAttributeSet.FONT);
  220.                 ascent = Math.max(ascent,theFont.getAscent());
  221.                 descent = Math.max(descent,theFont.getDescent());
  222.                 leading = Math.max(leading,theFont.getLeading());
  223.             }
  224.         }
  225.     }
  226.  
  227.  
  228.     /**
  229.      * Returns the largest ascent of any font represented in this StyledString.
  230.      * The units are user space coordinates.
  231.      */
  232.     public float getAscent(){
  233.         loadMetrics();
  234.         return ascent;
  235.     }
  236.  
  237.     /**
  238.      * Returns the largest descent of any font represented in this StyledString.
  239.      * The units are user space coordinates.
  240.      */
  241.     public float getDescent(){
  242.         loadMetrics();
  243.         return descent;
  244.     }
  245.  
  246.     /**
  247.      * Returns the largest leading of any font represented in this StyledString.
  248.      * The units are user space coordinates.
  249.      */
  250.     public float getLeading(){
  251.         loadMetrics();
  252.         return leading;
  253.     }
  254.  
  255.     /**
  256.      * Internal method to map a position to the index of the style
  257.      * run containing the character at that position.
  258.      */
  259.     int findSegmentContaining(int pos) {
  260.         /*
  261.          * Cache the last index on the assumption that most segment
  262.          * queries are local. If this isn't the case then we can go
  263.          * to a binary search, but this is simpler.
  264.          */
  265.         while (pos > indices[segmentIndexCache++]) {}
  266.         while (pos < indices[--segmentIndexCache]) {}
  267.         return segmentIndexCache;
  268.     }
  269.  
  270.     // cache for findSegmentContaining
  271.     private int segmentIndexCache = 0;
  272.  
  273.     /**
  274.      * Return the index of the first character of the run of
  275.      * characters containing index sharing equal attributes.
  276.      *
  277.      * @param      index the index of the character.  Index may be
  278.      * equal to length, in which case the return value is the length
  279.      * of the styled string.
  280.      * @return     the index of the first character in the run
  281.      * @exception  StringIndexOutOfBoundsException if the index is
  282.      * out of range.
  283.      * @see #getRunLimit
  284.      */
  285.     public int getRunStart(int index) {
  286.     if (index < 0 || index > chars.length) {
  287.         throw new StringIndexOutOfBoundsException(index);
  288.     } else if (index == chars.length) {
  289.         return index;
  290.     }
  291.     int seg = findSegmentContaining(index);
  292.  
  293.     return indices[seg];
  294.     }
  295.  
  296.     /**
  297.      * Return the index past the last character of the run of
  298.      * characters containing index sharing equal attributes.
  299.      *
  300.      * @param      index the index of the character.  Index may be
  301.      * equal to length, in which case the return value is the length
  302.      * of the styled string.
  303.      * @return     the index past the last character in the run
  304.      * @exception  StringIndexOutOfBoundsException  if the index is
  305.      * out of range.
  306.      * @see #getRunStart
  307.      */
  308.     public int getRunLimit(int index) {
  309.     if (index < 0 || index > chars.length) {
  310.         throw new StringIndexOutOfBoundsException(index);
  311.     } else if (index == chars.length) {
  312.         return index;
  313.     }
  314.     int seg = findSegmentContaining(index);
  315.  
  316.     return indices[seg+1];
  317.     }
  318.  
  319.     /**
  320.      * Returns the advance vector for the end position of this StyledString.
  321.      */
  322.     public Point2D getAdvanceVector(){
  323.         float totalAdvance=0;
  324.         GlyphSet glyphs;
  325.         int start = 0;
  326.         int end;
  327.         for (int i=0;i<attrs.length;i++){
  328.             end = indices[i+1];
  329.             Font theFont = (Font) attrs[i].get(TextAttributeSet.FONT);
  330.             String theString = new String(chars, start, end - start);
  331.             glyphs = theFont.getGlyphSet(theString);
  332.             totalAdvance += glyphs.getAdvance();
  333.             start = end + 1;
  334.         }
  335.         return new Point2D.Float(totalAdvance,0);
  336.     }
  337.  
  338.     /**
  339.      * Returns the bounding box of this StyledString.
  340.      */
  341.     public Rectangle2D getBounds2D(){
  342.         return new Rectangle2D.Double(0.0, 0.0, getAdvanceVector().getX(),
  343.                                      getAscent() + getDescent());
  344.     }
  345.  
  346.     /**
  347.      * Returns an array of GlyphSet objects associated with this StyledString.
  348.      * REMIND jk . ask ccc to remove this api
  349.      */
  350.     public GlyphSet[] getGlyphSets(){
  351.         GlyphSet[] glyphs = new GlyphSet[attrs.length];
  352.  
  353.         int start = 0;
  354.         int end;
  355.         for (int i=0;i<attrs.length;i++){
  356.             end = indices[i+1];
  357.             Font theFont = (Font) attrs[i].get(TextAttributeSet.FONT);
  358.             String theString = new String(chars, start, end - start);
  359.             glyphs[i] = theFont.getGlyphSet(theString);
  360.             start = end + 1;
  361.         }
  362.         return glyphs;
  363.     }
  364.  
  365.     /**
  366.      * Returns the Shape object for this StyledString, which is the union
  367.      * of all the outlines for glyphs in the StyledString.
  368.      */
  369.     public Shape getStringOutline(){
  370.         Font theFont;
  371.         int start = 0;
  372.         int end;
  373.         GeneralPath resultShape = new GeneralPath(GeneralPath.EVEN_ODD);
  374.         for (int i=0;i<attrs.length;i++){
  375.             end = indices[i+1];
  376.             theFont = (Font) attrs[i].get(TextAttributeSet.FONT);
  377.             String theString = new String(chars, start, end - start);
  378.             resultShape.append(theFont.getOutline(theFont.
  379.                                                   getGlyphSet(theString),
  380.                                                   null, 0, getAscent() +
  381.                                                   getDescent()), false);
  382.             start = end + 1;
  383.         }
  384.         return resultShape;
  385.     }
  386.  
  387.     /**
  388.      * Return a String containing the characters in this object.
  389.      *
  390.      * @return  a string containing the characters in this object.
  391.      */
  392.     public String toString(){
  393.     return new String(chars);
  394.     }
  395.  
  396.     /**
  397.      * Concatenates the specified characters and attributes
  398.      * to the end of this styled string.
  399.      * <p>
  400.      * If the length of the argument string is <code>0</code>, then this
  401.      * object is returned.
  402.      *
  403.      * @param   str the <code>String</code> that is concatenated to the
  404.      *          end of this <code>StyledString</code>.
  405.      * @param   attributes the attributes for the new characters
  406.      * @return  a styled string that represents the concatenation of this
  407.      *          object's characters and attributes followed by the string
  408.      *          argument's characters.
  409.      * REMIND jk new api
  410.      */
  411.     private StyledString concat(String str, AttributeSet attributes) {
  412.         return concat(new StyledString(str, attributes));
  413.     }
  414.  
  415.     /**
  416.      * Concatenates the specified styled text to the end of this string.
  417.      * <p>
  418.      * If the length of the argument text is <code>0</code>, then this
  419.      * object is returned.
  420.      *
  421.      * @param   iter  the text and attributes that is concatenated to
  422.      * the end of this <code>StyledString</code>.
  423.      * @return  a styled string that represents the concatenation of
  424.      * this object's characters and attributes followed by the iterator's
  425.      * characters and attributes.
  426.      * REMIND jk new api
  427.      */
  428.     /*private StyledString concat(AttributedCharacterIterator iter) {
  429.     return concat(new StyledString(iter));
  430.     }*/
  431.  
  432.     /**
  433.      * Concatenates the specified styled string to the end of this string.
  434.      * <p>
  435.      * If the length of the argument string is <code>0</code>, then this
  436.      * object is returned.
  437.      *
  438.      * @param   str   the <code>StyledString</code> that is concatenated
  439.      * to the end of this <code>StyledString</code>.
  440.      * @return  a styled string that represents the concatenation of this
  441.      * object's characters and attributes followed by the string argument's
  442.      * characters and attributes.
  443.      */
  444.     public StyledString concat(StyledString text){
  445.     if (text.length() == 0) {
  446.         return this;
  447.     } else if (length() == 0) {
  448.         return text;
  449.     } else {
  450.         // concatenate text
  451.         char[] newChars = new char[chars.length + text.chars.length];
  452.         System.arraycopy(chars, 0, newChars, 0, chars.length);
  453.         System.arraycopy(text.chars, 0, newChars, chars.length,
  454.                 text.chars.length);
  455.  
  456.         boolean merge = attrs[attrs.length - 1].equals(text.attrs[0]);
  457.  
  458.             AttributeSet[] newAttrs = new AttributeSet[attrs.length +
  459.                 text.attrs.length - (merge ? 1 : 0)];
  460.         System.arraycopy(attrs, 0, newAttrs, 0, attrs.length);
  461.         System.arraycopy(text.attrs, merge ? 1 : 0, newAttrs,
  462.                 attrs.length, newAttrs.length - attrs.length);
  463.  
  464.         int[] newIndices = new int[newAttrs.length + 1];
  465.         System.arraycopy(indices, 0, newIndices, 0, indices.length);
  466.  
  467.         int i = indices.length - (merge ? 1 : 0);
  468.         int j = 1;
  469.         int d = indices[indices.length - 1];
  470.         while (i < newIndices.length) {
  471.             newIndices[i++] = d + text.indices[j++];
  472.         }
  473.             return new StyledString(newChars, newAttrs, newIndices);
  474.     }
  475.     }
  476.  
  477.     /**
  478.      * Returns the character at the specified index. An index ranges
  479.      * from <code>0</code> to <code>length() - 1</code>.
  480.      *
  481.      * @param      index   the index of the character.
  482.      * @return     the character at the specified index of this string.
  483.      *             The first character is at index <code>0</code>.
  484.      * @exception  StringIndexOutOfBoundsException  if the index is out of
  485.      *               range.
  486.      */
  487.     public char charAt(int index) {
  488.     if (index < 0 || index >= chars.length) {
  489.         throw new StringIndexOutOfBoundsException(index);
  490.     }
  491.     return chars[index];
  492.     }
  493.  
  494.     /**
  495.      * Returns the Font associated with the character at the specified index.
  496.      * An index ranges from 0 to length() - 1.
  497.      * @exception java.lang.StringIndexOutOfBoundsException if the index
  498.      * is not in the range 0 to length()-1.
  499.      */
  500.     public Font getFontAt(int index) {
  501.     if (index < 0 || index > chars.length) {
  502.         throw new StringIndexOutOfBoundsException(index);
  503.         }
  504.         int seg = findSegmentContaining(index);
  505.         return (Font) attrs[seg].get(TextAttributeSet.FONT);
  506.     }
  507.  
  508.     /**
  509.      * Returns the length of this styled string.
  510.      * The length is equal to the number of Unicode characters in
  511.      * the styled string.
  512.      *
  513.      * @return  the length of the sequence of characters
  514.      * represented by this object.
  515.      */
  516.     public int length() {
  517.     return chars.length;
  518.     }
  519.  
  520.     /**
  521.      * Return a hashcode
  522.      * REMIND jk .new api
  523.      */
  524.     /*public int hashCode() {
  525.     return chars.length ^ indices.length << 8;
  526.     }*/
  527.  
  528.     /**
  529.      * Return true if the strings have the same characters and
  530.      * attribute runs.
  531.      * REMIND jk .new api
  532.      */
  533.     public boolean equals(Object rhs) {
  534.         if (rhs == this) {
  535.             return true;
  536.         } else if (rhs == null) {
  537.             return false;
  538.         } else {
  539.             try {
  540.                 StyledString other = (StyledString)rhs;
  541.  
  542.                 if (chars.length != other.chars.length ||
  543.                     indices.length != other.indices.length) {
  544.                     return false;
  545.                 }
  546.  
  547.             for (int i = 0; i < chars.length; i++) {
  548.                 if (chars[i] != other.chars[i]) {
  549.                     return false;
  550.                 }
  551.             }
  552.  
  553.                 for (int i = 0; i < indices.length; i++) {
  554.                     if (indices[i] != other.indices[i]) {
  555.                         return false;
  556.                     }
  557.                 }
  558.  
  559.                 if (attrs != null && other.attrs != null) {
  560.                     for (int i = 0; i < attrs.length; i++) {
  561.                         if (!attrs[i].equals(other.attrs[i])) {
  562.                             return false;
  563.                         }
  564.                     }
  565.                 }
  566.  
  567.                 return true;
  568.             }
  569.             catch (ClassCastException e) {
  570.                 return false;
  571.             }
  572.         }
  573.     }
  574.     /*
  575.      * other new taligent calls. need to know if I want them.
  576.      */
  577.     public AttributedCharacterIterator createIterator() {
  578.         return new StyledStringIterator(this);
  579.     }
  580.  
  581.     public AttributedCharacterIterator createIterator(int start, int limit, int pos) {
  582.         return new StyledStringIterator(this, start, limit, pos);
  583.     }
  584.  
  585.  
  586.     /**
  587.      * Return an attribute set describing the attributes on the
  588.      * character at index.
  589.      *
  590.      * @param      index   the index of the character.
  591.      * @return     the attributes of the character at index.
  592.      * @exception  StringIndexOutOfBoundsException if the index is
  593.      * out of range.
  594.      */
  595.     public AttributeSet getAttributesAt(int index) {
  596.     if (index < 0 || index >= chars.length) {
  597.         throw new StringIndexOutOfBoundsException(index);
  598.     }
  599.     int seg = findSegmentContaining(index);
  600.  
  601.     return attrs[seg];
  602.     }
  603.  
  604.     /**
  605.      * Copies characters from this string into the destination character array.
  606.      * <p>
  607.      * The first character to be copied is at index <code>srcBegin</code>;
  608.      * the last character to be copied is at index <code>srcEnd-1</code>
  609.      * (thus the total number of characters to be copied is
  610.      * <code>srcEnd-srcBegin</code>). The characters are copied into the
  611.      * subarray of <code>dst</code> starting at index <code>dstBegin</code>
  612.      * and ending at index:
  613.      * <p><blockquote><pre>
  614.      *     dstbegin + (srcEnd-srcBegin) - 1
  615.      * </pre></blockquote>
  616.      *
  617.      * @param      srcBegin   index of the first character in the string
  618.      *                        to copy.
  619.      * @param      srcEnd     index after the last character in the string
  620.      *                        to copy.
  621.      * @param      dst        the destination array.
  622.      * @param      dstBegin   the start offset in the destination array.
  623.      * @exception StringIndexOutOfBoundsException If srcBegin or srcEnd is out
  624.      *              of range, or if srcBegin is greater than the srcEnd.
  625.      */
  626.     public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) {
  627.         if (srcBegin < 0) {
  628.             throw new StringIndexOutOfBoundsException(srcBegin);
  629.         }
  630.         if (srcEnd > chars.length) {
  631.             throw new StringIndexOutOfBoundsException(srcEnd);
  632.         }
  633.         if (srcEnd < srcBegin) {
  634.             throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
  635.         }
  636.         System.arraycopy(chars, srcBegin, dst, dstBegin, srcEnd - srcBegin);
  637.     }
  638.  
  639.   /* this exposes internals and must remain package private as long as it
  640.      does not copy the char array. */
  641.     char[] getChars() {
  642.         return chars;
  643.     }
  644.  
  645. }
  646.  
  647.  
  648.