001/*
002 * Units of Measurement Reference Implementation
003 * Copyright (c) 2005-2017, Jean-Marie Dautelle, Werner Keil, V2COM.
004 *
005 * All rights reserved.
006 *
007 * Redistribution and use in source and binary forms, with or without modification,
008 * are permitted provided that the following conditions are met:
009 *
010 * 1. Redistributions of source code must retain the above copyright notice,
011 *    this list of conditions and the following disclaimer.
012 *
013 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions
014 *    and the following disclaimer in the documentation and/or other materials provided with the distribution.
015 *
016 * 3. Neither the name of JSR-363 nor the names of its contributors may be used to endorse or promote products
017 *    derived from this software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
020 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
021 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
022 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
023 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
026 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030/*
031 * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved
032 * (C) Copyright IBM Corp. 1996 - All Rights Reserved
033 *
034 *   The original version of this source code and documentation is copyrighted
035 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
036 * materials are provided under terms of a License Agreement between Taligent
037 * and Sun. This technology is protected by multiple US and International
038 * patents. This notice and attribution to Taligent may not be removed.
039 *   Taligent is a registered trademark of Taligent, Inc.
040 *
041 */
042
043package tec.units.ri.internal.format.l10n;
044
045/**
046 * <code>FieldPosition</code> is a simple class used by <code>Format</code> and its subclasses to identify fields in formatted output. Fields can be
047 * identified in two ways:
048 * <ul>
049 * <li>By an integer constant, whose names typically end with <code>_FIELD</code>. The constants are defined in the various subclasses of
050 * <code>Format</code>.
051 * <li>By a <code>Format.Field</code> constant, see <code>ERA_FIELD</code> and its friends in <code>DateFormat</code> for an example.
052 * </ul>
053 * <p>
054 * <code>FieldPosition</code> keeps track of the position of the field within the formatted output with two indices: the index of the first character
055 * of the field and the index of the last character of the field.
056 *
057 * <p>
058 * One version of the <code>format</code> method in the various <code>Format</code> classes requires a <code>FieldPosition</code> object as an
059 * argument. You use this <code>format</code> method to perform partial formatting or to get information about the formatted output (such as the
060 * position of a field).
061 *
062 * <p>
063 * If you are interested in the positions of all attributes in the formatted string use the <code>Format</code> method
064 * <code>formatToCharacterIterator</code>.
065 *
066 * @author Mark Davis
067 * @see Format
068 */
069public class FieldPosition {
070
071  /**
072   * Input: Desired field to determine start and end offsets for. The meaning depends on the subclass of Format.
073   */
074  int field = 0;
075
076  /**
077   * Output: End offset of field in text. If the field does not occur in the text, 0 is returned.
078   */
079  int endIndex = 0;
080
081  /**
082   * Output: Start offset of field in text. If the field does not occur in the text, 0 is returned.
083   */
084  int beginIndex = 0;
085
086  /**
087   * Desired field this FieldPosition is for.
088   */
089  private Format.Field attribute;
090
091  /**
092   * Creates a FieldPosition object for the given field. Fields are identified by constants, whose names typically end with _FIELD, in the various
093   * subclasses of Format.
094   *
095   * @see NumberFormat#INTEGER_FIELD
096   * @see NumberFormat#FRACTION_FIELD
097   */
098  public FieldPosition(int field) {
099    this.field = field;
100  }
101
102  /**
103   * Creates a FieldPosition object for the given field constant. Fields are identified by constants defined in the various <code>Format</code>
104   * subclasses. This is equivalent to calling <code>new FieldPosition(attribute, -1)</code>.
105   *
106   * @param attribute
107   *          Format.Field constant identifying a field
108   * @since 1.4
109   */
110  FieldPosition(Format.Field attribute) {
111    this(attribute, -1);
112  }
113
114  /**
115   * Creates a <code>FieldPosition</code> object for the given field. The field is identified by an attribute constant from one of the
116   * <code>Field</code> subclasses as well as an integer field ID defined by the <code>Format</code> subclasses. <code>Format</code> subclasses that
117   * are aware of <code>Field</code> should give precedence to <code>attribute</code> and ignore <code>fieldID</code> if <code>attribute</code> is not
118   * null. However, older <code>Format</code> subclasses may not be aware of <code>Field</code> and rely on <code>fieldID</code>. If the field has no
119   * corresponding integer constant, <code>fieldID</code> should be -1.
120   *
121   * @param attribute
122   *          Format.Field constant identifying a field
123   * @param fieldID
124   *          integer constantce identifying a field
125   * @since 1.4
126   */
127  public FieldPosition(Format.Field attribute, int fieldID) {
128    this.attribute = attribute;
129    this.field = fieldID;
130  }
131
132  /**
133   * Returns the field identifier as an attribute constant from one of the <code>Field</code> subclasses. May return null if the field is specified
134   * only by an integer field ID.
135   *
136   * @return Identifier for the field
137   * @since 1.4
138   */
139  public Format.Field getFieldAttribute() {
140    return attribute;
141  }
142
143  /**
144   * Retrieves the field identifier.
145   */
146  public int getField() {
147    return field;
148  }
149
150  /**
151   * Retrieves the index of the first character in the requested field.
152   */
153  public int getBeginIndex() {
154    return beginIndex;
155  }
156
157  /**
158   * Retrieves the index of the character following the last character in the requested field.
159   */
160  public int getEndIndex() {
161    return endIndex;
162  }
163
164  /**
165   * Sets the begin index. For use by subclasses of Format.
166   * 
167   * @since 1.2
168   */
169  public void setBeginIndex(int bi) {
170    beginIndex = bi;
171  }
172
173  /**
174   * Sets the end index. For use by subclasses of Format.
175   * 
176   * @since 1.2
177   */
178  public void setEndIndex(int ei) {
179    endIndex = ei;
180  }
181
182  /**
183   * Returns a <code>Format.FieldDelegate</code> instance that is associated with the FieldPosition. When the delegate is notified of the same field
184   * the FieldPosition is associated with, the begin/end will be adjusted.
185   */
186  Format.FieldDelegate getFieldDelegate() {
187    return new Delegate();
188  }
189
190  /**
191   * Overrides equals
192   */
193  public boolean equals(Object obj) {
194    if (obj == null)
195      return false;
196    if (!(obj instanceof FieldPosition))
197      return false;
198    FieldPosition other = (FieldPosition) obj;
199    if (attribute == null) {
200      if (other.attribute != null) {
201        return false;
202      }
203    } else if (!attribute.equals(other.attribute)) {
204      return false;
205    }
206    return (beginIndex == other.beginIndex && endIndex == other.endIndex && field == other.field);
207  }
208
209  /**
210   * Returns a hash code for this FieldPosition.
211   * 
212   * @return a hash code value for this object
213   */
214  public int hashCode() {
215    return (field << 24) | (beginIndex << 16) | endIndex;
216  }
217
218  /**
219   * Return a string representation of this FieldPosition.
220   * 
221   * @return a string representation of this object
222   */
223  public String toString() {
224    return getClass().getName() + "[field=" + field + ",attribute=" + attribute + ",beginIndex=" + beginIndex + ",endIndex=" + endIndex + ']';
225  }
226
227  /**
228   * Return true if the receiver wants a <code>Format.Field</code> value and <code>attribute</code> is equal to it.
229   */
230  private boolean matchesField(Format.Field attribute) {
231    if (this.attribute != null) {
232      return this.attribute.equals(attribute);
233    }
234    return false;
235  }
236
237  /**
238   * Return true if the receiver wants a <code>Format.Field</code> value and <code>attribute</code> is equal to it, or true if the receiver represents
239   * an inteter constant and <code>field</code> equals it.
240   */
241  private boolean matchesField(Format.Field attribute, int field) {
242    if (this.attribute != null) {
243      return this.attribute.equals(attribute);
244    }
245    return (field == this.field);
246  }
247
248  /**
249   * An implementation of FieldDelegate that will adjust the begin/end of the FieldPosition if the arguments match the field of the FieldPosition.
250   */
251  private class Delegate implements Format.FieldDelegate {
252    /**
253     * Indicates whether the field has been encountered before. If this is true, and <code>formatted</code> is invoked, the begin/end are not updated.
254     */
255    private boolean encounteredField;
256
257    public void formatted(Format.Field attr, Object value, int start, int end, StringBuffer buffer) {
258      if (!encounteredField && matchesField(attr)) {
259        setBeginIndex(start);
260        setEndIndex(end);
261        encounteredField = (start != end);
262      }
263    }
264
265    public void formatted(int fieldID, Format.Field attr, Object value, int start, int end, StringBuffer buffer) {
266      if (!encounteredField && matchesField(attr, fieldID)) {
267        setBeginIndex(start);
268        setEndIndex(end);
269        encounteredField = (start != end);
270      }
271    }
272  }
273}