001/*
002 * Units of Measurement Reference Implementation
003 * Copyright (c) 2005-2020, Units of Measurement project.
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.MeasurementException;
038import javax.measure.Quantity;
039import javax.measure.Unit;
040import javax.measure.format.MeasurementParseException;
041import javax.measure.format.QuantityFormat;
042import tech.units.indriya.ComparableQuantity;
043import tech.units.indriya.quantity.CompoundQuantity;
044import tech.uom.lib.common.function.Parser;
045
046/**
047 * <p>
048 * This class provides the interface for formatting and parsing {@link Quantity quantities}.
049 * </p>
050 *
051 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
052 * @author <a href="mailto:werner@units.tech">Werner Keil</a>
053 * @version 1.6, $Date: 2019-04-13 $
054 * @since 1.0
055 * 
056 */
057@SuppressWarnings("rawtypes")
058public abstract class AbstractQuantityFormat extends Format implements QuantityFormat, Parser<CharSequence, Quantity> {
059    /**
060     * The default delimiter.
061     */
062    protected static final String DEFAULT_DELIMITER = " ";
063
064    /**
065     *
066     */
067    private static final long serialVersionUID = -4628006924354248662L;
068
069    /**
070     * Formats the specified quantity into an <code>Appendable</code>.
071     *
072     * @param quantity
073     *            the quantity to format.
074     * @param dest
075     *            the appendable destination.
076     * @return the specified <code>Appendable</code>.
077     * @throws IOException
078     *             if an I/O exception occurs.
079     */
080    public abstract Appendable format(Quantity<?> quantity, Appendable dest) throws IOException;
081
082    /**
083     * Parses a portion of the specified <code>CharSequence</code> from the specified position to produce an object. If parsing succeeds, then the
084     * index of the <code>cursor</code> argument is updated to the index after the last character used.
085     *
086     * @param csq
087     *            the <code>CharSequence</code> to parse.
088     * @param cursor
089     *            the cursor holding the current parsing index.
090     * @return the object parsed from the specified character sub-sequence.
091     * @throws IllegalArgumentException
092     *             if any problem occurs while parsing the specified character sequence (e.g. illegal syntax).
093     */
094    public abstract Quantity<?> parse(CharSequence csq, ParsePosition cursor) throws IllegalArgumentException, MeasurementParseException;
095
096    /**
097     * Parses a portion of the specified <code>CharSequence</code> from the specified position to produce an object. If parsing succeeds, then the
098     * index of the <code>cursor</code> argument is updated to the index after the last character used.
099     *
100     * @param csq
101     *            the <code>CharSequence</code> to parse.
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 Quantity<?> 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
111     * index 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    protected abstract Quantity<?> 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 CompoundQuantity<?>) {
126            return formatCompound((CompoundQuantity<?>) obj, toAppendTo);
127        } else {
128            if (!(obj instanceof ComparableQuantity<?>))
129                throw new IllegalArgumentException("obj: Not an instance of Quantity");
130            if ((toAppendTo == null) || (pos == null))
131                throw new NullPointerException();
132            return (StringBuffer) format((ComparableQuantity<?>) obj, toAppendTo);
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     * Formats an object to produce a string. This is equivalent to <blockquote> {@link #format(Unit, StringBuilder) format}<code>(unit,
147     *         new StringBuilder()).toString();</code> </blockquote>
148     *
149     * @param quantity
150     *          The quantity to format
151     * @return Formatted string.
152     */
153    public final String format(Quantity<?> quantity) {
154      if (quantity instanceof ComparableQuantity) return format((ComparableQuantity<?>) quantity, new StringBuffer()).toString();
155
156      try {
157        return (this.format(quantity, new StringBuffer())).toString();
158      } catch (IOException ex) {
159        throw new MeasurementException(ex); // Should never happen.
160      }
161    }
162    
163    /**
164     * Convenience method equivalent to {@link #format(ComparableQuantity, Appendable)} except it does not raise an IOException.
165     *
166     * @param quantity
167     *            the quantity to format.
168     * @param dest
169     *            the appendable destination.
170     * @return the specified <code>StringBuilder</code>.
171     */
172    protected final StringBuffer format(ComparableQuantity<?> quantity, StringBuffer dest) {
173        try {
174            return (StringBuffer) this.format(quantity, (Appendable) dest);
175        } catch (IOException ex) {
176            throw new RuntimeException(ex); // Should not happen.
177        }
178    }
179    
180    /**
181     * Convenience method equivalent to {@link #format(CompoundQuantity, Appendable)} except it does not raise an IOException.
182     *
183     * @param comp
184     *            the composite quantity to format.
185     * @param dest
186     *            the appendable destination.
187     * @return the specified <code>StringBuilder</code>.
188     */
189    protected abstract StringBuffer formatCompound(CompoundQuantity<?> comp, StringBuffer dest);
190}