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}