1 /*
   2  * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.swing.text;
  27 
  28 import java.io.Serializable;
  29 
  30 /**
  31  * A TabSet is comprised of many TabStops. It offers methods for locating the
  32  * closest TabStop to a given position and finding all the potential TabStops.
  33  * It is also immutable.
  34  * <p>
  35  * <strong>Warning:</strong>
  36  * Serialized objects of this class will not be compatible with
  37  * future Swing releases. The current serialization support is
  38  * appropriate for short term storage or RMI between applications running
  39  * the same version of Swing.  As of 1.4, support for long term storage
  40  * of all JavaBeans<sup><font size="-2">TM</font></sup>
  41  * has been added to the <code>java.beans</code> package.
  42  * Please see {@link java.beans.XMLEncoder}.
  43  *
  44  * @author  Scott Violet
  45  */
  46 public class TabSet implements Serializable
  47 {
  48     /** TabStops this TabSet contains. */
  49     private TabStop[]              tabs;
  50     /**
  51      * Since this class is immutable the hash code could be
  52      * calculated once. MAX_VALUE means that it was not initialized
  53      * yet. Hash code shouldn't has MAX_VALUE value.
  54      */
  55     private int hashCode = Integer.MAX_VALUE;
  56 
  57     /**
  58      * Creates and returns an instance of TabSet. The array of Tabs
  59      * passed in must be sorted in ascending order.
  60      */
  61     public TabSet(TabStop[] tabs) {
  62         // PENDING(sky): If this becomes a problem, make it sort.
  63         if(tabs != null) {
  64             int          tabCount = tabs.length;
  65 
  66             this.tabs = new TabStop[tabCount];
  67             System.arraycopy(tabs, 0, this.tabs, 0, tabCount);
  68         }
  69         else
  70             this.tabs = null;
  71     }
  72 
  73     /**
  74      * Returns the number of Tab instances the receiver contains.
  75      */
  76     public int getTabCount() {
  77         return (tabs == null) ? 0 : tabs.length;
  78     }
  79 
  80     /**
  81      * Returns the TabStop at index <code>index</code>. This will throw an
  82      * IllegalArgumentException if <code>index</code> is outside the range
  83      * of tabs.
  84      */
  85     public TabStop getTab(int index) {
  86         int          numTabs = getTabCount();
  87 
  88         if(index < 0 || index >= numTabs)
  89             throw new IllegalArgumentException(index +
  90                                               " is outside the range of tabs");
  91         return tabs[index];
  92     }
  93 
  94     /**
  95      * Returns the Tab instance after <code>location</code>. This will
  96      * return null if there are no tabs after <code>location</code>.
  97      */
  98     public TabStop getTabAfter(float location) {
  99         int     index = getTabIndexAfter(location);
 100 
 101         return (index == -1) ? null : tabs[index];
 102     }
 103 
 104     /**
 105      * @return the index of the TabStop <code>tab</code>, or -1 if
 106      * <code>tab</code> is not contained in the receiver.
 107      */
 108     public int getTabIndex(TabStop tab) {
 109         for(int counter = getTabCount() - 1; counter >= 0; counter--)
 110             // should this use .equals?
 111             if(getTab(counter) == tab)
 112                 return counter;
 113         return -1;
 114     }
 115 
 116     /**
 117      * Returns the index of the Tab to be used after <code>location</code>.
 118      * This will return -1 if there are no tabs after <code>location</code>.
 119      */
 120     public int getTabIndexAfter(float location) {
 121         int     current, min, max;
 122 
 123         min = 0;
 124         max = getTabCount();
 125         while(min != max) {
 126             current = (max - min) / 2 + min;
 127             if(location > tabs[current].getPosition()) {
 128                 if(min == current)
 129                     min = max;
 130                 else
 131                     min = current;
 132             }
 133             else {
 134                 if(current == 0 || location > tabs[current - 1].getPosition())
 135                     return current;
 136                 max = current;
 137             }
 138         }
 139         // no tabs after the passed in location.
 140         return -1;
 141     }
 142 
 143     /**
 144      * Indicates whether this <code>TabSet</code> is equal to another one.
 145      * @param o the <code>TabSet</code> instance which this instance
 146      *  should be compared to.
 147      * @return <code>true</code> if <code>o</code> is the instance of
 148      * <code>TabSet</code>, has the same number of <code>TabStop</code>s
 149      * and they are all equal, <code>false</code> otherwise.
 150      *
 151      * @since 1.5
 152      */
 153     public boolean equals(Object o) {
 154         if (o == this) {
 155             return true;
 156         }
 157         if (o instanceof TabSet) {
 158             TabSet ts = (TabSet) o;
 159             int count = getTabCount();
 160             if (ts.getTabCount() != count) {
 161                 return false;
 162             }
 163             for (int i=0; i < count; i++) {
 164                 TabStop ts1 = getTab(i);
 165                 TabStop ts2 = ts.getTab(i);
 166                 if ((ts1 == null && ts2 != null) ||
 167                         (ts1 != null && !getTab(i).equals(ts.getTab(i)))) {
 168                     return false;
 169                 }
 170             }
 171             return true;
 172         }
 173         return false;
 174     }
 175 
 176     /**
 177      * Returns a hashcode for this set of TabStops.
 178      * @return  a hashcode value for this set of TabStops.
 179      *
 180      * @since 1.5
 181      */
 182     public int hashCode() {
 183         if (hashCode == Integer.MAX_VALUE) {
 184             hashCode = 0;
 185             int len = getTabCount();
 186             for (int i = 0; i < len; i++) {
 187                 TabStop ts = getTab(i);
 188                 hashCode ^= ts != null ? getTab(i).hashCode() : 0;
 189             }
 190             if (hashCode == Integer.MAX_VALUE) {
 191                 hashCode -= 1;
 192             }
 193         }
 194         return hashCode;
 195     }
 196 
 197     /**
 198      * Returns the string representation of the set of tabs.
 199      */
 200     public String toString() {
 201         int            tabCount = getTabCount();
 202         StringBuilder buffer = new StringBuilder("[ ");
 203 
 204         for(int counter = 0; counter < tabCount; counter++) {
 205             if(counter > 0)
 206                 buffer.append(" - ");
 207             buffer.append(getTab(counter).toString());
 208         }
 209         buffer.append(" ]");
 210         return buffer.toString();
 211     }
 212 }