001/*
002 * Units of Measurement Reference Implementation
003 * Copyright (c) 2005-2018, Jean-Marie Dautelle, Werner Keil, Otavio Santana.
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-385, Indriya nor the names of their 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 */
030package tech.units.indriya.format;
031
032import java.io.IOException;
033import java.text.FieldPosition;
034import java.text.Format;
035import java.text.ParsePosition;
036
037import javax.measure.Quantity;
038import javax.measure.format.MeasurementParseException;
039import javax.measure.format.QuantityFormat;
040import tech.units.indriya.AbstractQuantity;
041import tech.units.indriya.ComparableQuantity;
042import tech.uom.lib.common.function.Parser;
043
044/**
045 * <p>
046 * This class provides the interface for formatting and parsing {@link Quantity quantities}.
047 * </p>
048 *
049 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
050 * @author <a href="mailto:werner@uom.technology">Werner Keil</a>
051 * @version 1.1, $Date: 2018-04-06 $
052 * @since 1.0
053 * 
054 */
055@SuppressWarnings("rawtypes")
056public abstract class AbstractQuantityFormat extends Format implements QuantityFormat, Parser<CharSequence, ComparableQuantity> {
057  // TODO for later, see https://github.com/unitsofmeasurement/indriya/issues/17
058  // * <p>
059  // * Instances of this class should be able to format quantities stated in {@link CompoundUnit}. See {@link #formatCompound formatCompound(...)}.
060  // * </p>
061
062  /**
063   *
064   */
065  private static final long serialVersionUID = -4628006924354248662L;
066
067  /**
068   * Formats the specified quantity into an <code>Appendable</code>.
069   *
070   * @param quantity
071   *          the quantity to format.
072   * @param dest
073   *          the appendable destination.
074   * @return the specified <code>Appendable</code>.
075   * @throws IOException
076   *           if an I/O exception occurs.
077   */
078  public abstract Appendable format(Quantity<?> quantity, Appendable dest) throws IOException;
079
080  /**
081   * Parses a portion of the specified <code>CharSequence</code> from the specified position to produce an object. If parsing succeeds, then the index
082   * of the <code>cursor</code> argument is updated to the index after the last character used.
083   *
084   * @param csq
085   *          the <code>CharSequence</code> to parse.
086   * @param cursor
087   *          the cursor holding the current parsing index.
088   * @return the object parsed from the specified character sub-sequence.
089   * @throws IllegalArgumentException
090   *           if any problem occurs while parsing the specified character sequence (e.g. illegal syntax).
091   */
092  public abstract ComparableQuantity<?> parse(CharSequence csq, ParsePosition cursor) throws IllegalArgumentException, MeasurementParseException;
093
094  /**
095   * Parses a portion of the specified <code>CharSequence</code> from the specified position to produce an object. If parsing succeeds, then the index
096   * of the <code>cursor</code> argument is updated to the index after the last character used.
097   *
098   * @param csq
099   *          the <code>CharSequence</code> to parse.
100   * @param cursor
101   *          the cursor holding the current parsing index.
102   * @return the object parsed from the specified character sub-sequence.
103   * @throws IllegalArgumentException
104   *           if any problem occurs while parsing the specified character sequence (e.g. illegal syntax).
105   */
106  @Override
107  public abstract ComparableQuantity<?> parse(CharSequence csq) throws MeasurementParseException;
108
109  /**
110   * Parses a portion of the specified <code>CharSequence</code> from the specified position to produce an object. If parsing succeeds, then the index
111   * of the <code>cursor</code> argument is updated to the index after the last character used.
112   * 
113   * @param csq
114   *          the <code>CharSequence</code> to parse.
115   * @param index
116   *          the current parsing index.
117   * @return the object parsed from the specified character sub-sequence.
118   * @throws IllegalArgumentException
119   *           if any problem occurs while parsing the specified character sequence (e.g. illegal syntax).
120   */
121  abstract ComparableQuantity<?> parse(CharSequence csq, int index) throws IllegalArgumentException, MeasurementParseException;
122
123  @Override
124  public final StringBuffer format(Object obj, final StringBuffer toAppendTo, FieldPosition pos) {
125    if (!(obj instanceof AbstractQuantity<?>))
126      throw new IllegalArgumentException("obj: Not an instance of Quantity");
127    if ((toAppendTo == null) || (pos == null))
128      throw new NullPointerException();
129    try {
130      return (StringBuffer) format((AbstractQuantity<?>) obj, toAppendTo);
131    } catch (IOException ex) {
132      throw new Error(ex); // Cannot happen.
133    }
134  }
135
136  @Override
137  public final Quantity<?> parseObject(String source, ParsePosition pos) {
138    try {
139      return parse(source, pos);
140    } catch (IllegalArgumentException | MeasurementParseException e) {
141      return null;
142    }
143  }
144
145  /**
146   * Convenience method equivalent to {@link #format(AbstractQuantity, Appendable)} except it does not raise an IOException.
147   *
148   * @param quantity
149   *          the quantity to format.
150   * @param dest
151   *          the appendable destination.
152   * @return the specified <code>StringBuilder</code>.
153   */
154  public final StringBuilder format(AbstractQuantity<?> quantity, StringBuilder dest) {
155    try {
156      return (StringBuilder) this.format(quantity, (Appendable) dest);
157    } catch (IOException ex) {
158      throw new RuntimeException(ex); // Should not happen.
159    }
160  }
161}