1 /*
   2  * Copyright (c) 1998, 2015, 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
  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 @SuppressWarnings("serial") // Same-version serialization only
  47 public class TabSet implements Serializable
  48 {
  49     /** TabStops this TabSet contains. */
  50     private TabStop[]              tabs;
  51     /**
  52      * Since this class is immutable the hash code could be
  53      * calculated once. MAX_VALUE means that it was not initialized
  54      * yet. Hash code shouldn't has MAX_VALUE value.
  55      */
  56     private int hashCode = Integer.MAX_VALUE;
  57 
  58     /**
  59      * Creates and returns an instance of TabSet. The array of Tabs
  60      * passed in must be sorted in ascending order.
  61      * @param tabs the TabStops to initialize the TabSet
  62      */
  63     public TabSet(TabStop[] tabs) {
  64         // PENDING(sky): If this becomes a problem, make it sort.
  65         if(tabs != null) {
  66             int          tabCount = tabs.length;
  67 
  68             this.tabs = new TabStop[tabCount];
  69             System.arraycopy(tabs, 0, this.tabs, 0, tabCount);
  70         }
  71         else
  72             this.tabs = null;
  73     }
  74 
  75     /**
  76      * Returns the number of Tab instances the receiver contains.
  77      * @return the number of Tab instances the receiver contains
  78      */
  79     public int getTabCount() {
  80         return (tabs == null) ? 0 : tabs.length;
  81     }
  82 
  83     /**
  84      * Returns the TabStop at index <code>index</code>. This will throw an
  85      * IllegalArgumentException if <code>index</code> is outside the range
  86      * of tabs.
  87      * @param index which TapStop to return
  88      * @return the TabStop at index {@code index}
  89      */
  90     public TabStop getTab(int index) {
  91         int          numTabs = getTabCount();
  92 
  93         if(index < 0 || index >= numTabs)
  94             throw new IllegalArgumentException(index +
  95                                               " is outside the range of tabs");
  96         return tabs[index];
  97     }
  98 
  99     /**
 100      * Returns the Tab instance after <code>location</code>. This will
 101      * return null if there are no tabs after <code>location</code>.
 102      * @param location location to find a Tab after
 103      * @return the Tab instance after {@code location}
 104      */
 105     public TabStop getTabAfter(float location) {
 106         int     index = getTabIndexAfter(location);
 107 
 108         return (index == -1) ? null : tabs[index];
 109     }
 110 
 111     /**
 112      * Returns the index of the TabStop <code>tab</code>, or -1 if
 113      * <code>tab</code> is not contained in the receiver.
 114      * @param tab the TabStop to find
 115      * @return the index of the TabStop <code>tab</code>, or -1 if
 116      * <code>tab</code> is not contained in the receiver.
 117      */
 118     public int getTabIndex(TabStop tab) {
 119         for(int counter = getTabCount() - 1; counter >= 0; counter--)
 120             // should this use .equals?
 121             if(getTab(counter) == tab)
 122                 return counter;
 123         return -1;
 124     }
 125 
 126     /**
 127      * Returns the index of the Tab to be used after <code>location</code>.
 128      * This will return -1 if there are no tabs after <code>location</code>.
 129      * @param location location to find a Tab after
 130      * @return the index of the Tab to be used after <code>location</code>
 131      */
 132     public int getTabIndexAfter(float location) {
 133         int     current, min, max;
 134 
 135         min = 0;
 136         max = getTabCount();
 137         while(min != max) {
 138             current = (max - min) / 2 + min;
 139             if(location > tabs[current].getPosition()) {
 140                 if(min == current)
 141                     min = max;
 142                 else
 143                     min = current;
 144             }
 145             else {
 146                 if(current == 0 || location > tabs[current - 1].getPosition())
 147                     return current;
 148                 max = current;
 149             }
 150         }
 151         // no tabs after the passed in location.
 152         return -1;
 153     }
 154 
 155     /**
 156      * Indicates whether this <code>TabSet</code> is equal to another one.
 157      * @param o the <code>TabSet</code> instance which this instance
 158      *  should be compared to.
 159      * @return <code>true</code> if <code>o</code> is the instance of
 160      * <code>TabSet</code>, has the same number of <code>TabStop</code>s
 161      * and they are all equal, <code>false</code> otherwise.
 162      *
 163      * @since 1.5
 164      */
 165     public boolean equals(Object o) {
 166         if (o == this) {
 167             return true;
 168         }
 169         if (o instanceof TabSet) {
 170             TabSet ts = (TabSet) o;
 171             int count = getTabCount();
 172             if (ts.getTabCount() != count) {
 173                 return false;
 174             }
 175             for (int i=0; i < count; i++) {
 176                 TabStop ts1 = getTab(i);
 177                 TabStop ts2 = ts.getTab(i);
 178                 if ((ts1 == null && ts2 != null) ||
 179                         (ts1 != null && !getTab(i).equals(ts.getTab(i)))) {
 180                     return false;
 181                 }
 182             }
 183             return true;
 184         }
 185         return false;
 186     }
 187 
 188     /**
 189      * Returns a hashcode for this set of TabStops.
 190      * @return  a hashcode value for this set of TabStops.
 191      *
 192      * @since 1.5
 193      */
 194     public int hashCode() {
 195         if (hashCode == Integer.MAX_VALUE) {
 196             hashCode = 0;
 197             int len = getTabCount();
 198             for (int i = 0; i < len; i++) {
 199                 TabStop ts = getTab(i);
 200                 hashCode ^= ts != null ? getTab(i).hashCode() : 0;
 201             }
 202             if (hashCode == Integer.MAX_VALUE) {
 203                 hashCode -= 1;
 204             }
 205         }
 206         return hashCode;
 207     }
 208 
 209     /**
 210      * Returns the string representation of the set of tabs.
 211      */
 212     public String toString() {
 213         int            tabCount = getTabCount();
 214         StringBuilder buffer = new StringBuilder("[ ");
 215 
 216         for(int counter = 0; counter < tabCount; counter++) {
 217             if(counter > 0)
 218                 buffer.append(" - ");
 219             buffer.append(getTab(counter).toString());
 220         }
 221         buffer.append(" ]");
 222         return buffer.toString();
 223     }
 224 }