tec.uom.se.format.EBNFUnitFormat Maven / Gradle / Ivy
/*
* Units of Measurement Implementation for Java SE
* Copyright (c) 2005-2018, Jean-Marie Dautelle, Werner Keil, Otavio Santana.
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions
* and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of JSR-363 nor the names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package tec.uom.se.format;
import tec.uom.se.AbstractUnit;
import tec.uom.se.internal.format.TokenException;
import tec.uom.se.internal.format.TokenMgrError;
import tec.uom.se.internal.format.UnitFormatParser;
import tec.uom.se.unit.AnnotatedUnit;
import javax.measure.Quantity;
import javax.measure.Unit;
import javax.measure.format.ParserException;
import java.io.IOException;
import java.io.StringReader;
import java.text.ParsePosition;
import java.util.Locale;
import java.util.ResourceBundle;
/**
*
* This class represents the local neutral format.
*
*
* Here is the grammar for Units in Extended Backus-Naur Form (EBNF)
*
* Note that the grammar has been left-factored to be suitable for use by a top-down parser generator such as JavaCC
*
*
*
* Lexical Entities:
*
*
* <sign>
* :=
* "+" | "-"
*
*
* <digit>
* :=
* "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
*
*
* <superscript_digit>
* :=
* "⁰" | "¹" | "²" | "³" | "⁴" | "⁵" | "⁶" | "⁷" | "⁸" | "⁹"
*
*
* <integer>
* :=
* (<digit>)+
*
*
* <number>
* :=
* (<sign>)? (<digit>)* (".")? (<digit>)+ (("e" | "E") (<sign>)? (<digit>)+)?
*
*
* <exponent>
* :=
* ( "^" ( <sign> )? <integer> )
* | ( "^(" (<sign>)? <integer> ( "/" (<sign>)? <integer> )? ")" )
* | ( <superscript_digit> )+
*
*
* <initial_char>
* :=
* ? Any Unicode character excluding the following: ASCII control & whitespace (\u0000 - \u0020), decimal digits '0'-'9', '('
* (\u0028), ')' (\u0029), '*' (\u002A), '+' (\u002B), '-' (\u002D), '.' (\u002E), '/' (\u005C), ':' (\u003A), '^'
* (\u005E), '²' (\u00B2), '³' (\u00B3), '·' (\u00B7), '¹' (\u00B9), '⁰' (\u2070), '⁴' (\u2074), '⁵' (\u2075), '⁶'
* (\u2076), '⁷' (\u2077), '⁸' (\u2078), '⁹' (\u2079) ?
*
*
* <unit_identifier>
* :=
* <initial_char> ( <initial_char> | <digit> )*
*
*
* Non-Terminals:
*
*
* <unit_expr>
* :=
* <compound_expr>
*
*
* <compound_expr>
* :=
* <add_expr> ( ":" <add_expr> )*
*
*
* <add_expr>
* :=
* ( <number> <sign> )? <mul_expr> ( <sign> <number> )?
*
*
* <mul_expr>
* :=
* <exponent_expr> ( ( ( "*" | "·" ) <exponent_expr> ) | ( "/" <exponent_expr> ) )*
*
*
* <exponent_expr>
* :=
* ( <atomic_expr> ( <exponent> )? )
* | (<integer> "^" <atomic_expr>)
* | ( ( "log" ( <integer> )? ) | "ln" ) "(" <add_expr> ")" )
*
*
* <atomic_expr>
* :=
* <number>
* | <unit_identifier>
* | ( "(" <add_expr> ")" )
*
*
*
* @author Eric Russell
* @author Werner Keil
* @version 1.0.3, $Date: 2017-03-18 $
* @since 1.0
*/
public class EBNFUnitFormat extends AbstractUnitFormat {
// ////////////////////////////////////////////////////
// Class variables //
// ////////////////////////////////////////////////////
/**
*
*/
// private static final long serialVersionUID = 8968559300292910840L;
/**
* Name of the resource bundle
*/
private static final String BUNDLE_NAME = "tec.uom.se.format.messages"; //$NON-NLS-1$
/**
* Default locale instance. If the default locale is changed after the class is initialized, this instance will no longer be used.
*/
private static final EBNFUnitFormat DEFAULT_INSTANCE = new EBNFUnitFormat();
/**
* Returns the instance for the current default locale (non-ascii characters are allowed)
*/
public static EBNFUnitFormat getInstance() {
return DEFAULT_INSTANCE;
}
/** Returns an instance for the given symbol map. */
public static EBNFUnitFormat getInstance(SymbolMap symbols) {
return new EBNFUnitFormat(symbols);
}
// //////////////////////
// Instance variables //
// //////////////////////
/**
* The symbol map used by this instance to map between {@link org.unitsofmeasure.Unit Unit}s and String
s, etc...
*/
private final transient SymbolMap symbolMap;
// ////////////////
// Constructors //
// ////////////////
/**
* Base constructor.
*
*/
EBNFUnitFormat() {
this(SymbolMap.of(ResourceBundle.getBundle(BUNDLE_NAME, Locale.ROOT)));
}
/**
* Private constructor.
*
* @param symbols
* the symbol mapping.
*/
private EBNFUnitFormat(SymbolMap symbols) {
symbolMap = symbols;
}
// //////////////////////
// Instance methods //
// //////////////////////
/**
* Get the symbol map used by this instance to map between {@link org.unitsofmeasure.Unit Unit}s and String
s, etc...
*
* @return SymbolMap the current symbol map
*/
protected SymbolMap getSymbols() {
return symbolMap;
}
// //////////////
// Formatting //
// //////////////
public Appendable format(Unit unit, Appendable appendable) throws IOException {
EBNFHelper.formatInternal(unit, appendable, symbolMap);
if (unit instanceof AnnotatedUnit) {
AnnotatedUnit annotatedUnit = (AnnotatedUnit) unit;
if (annotatedUnit.getAnnotation() != null) {
appendable.append('{');
appendable.append(annotatedUnit.getAnnotation());
appendable.append('}');
}
}
return appendable;
}
public boolean isLocaleSensitive() {
return false;
}
@Override
protected Unit> parse(CharSequence csq, ParsePosition cursor) throws ParserException {
// Parsing reads the whole character sequence from the parse position.
int start = cursor != null ? cursor.getIndex() : 0;
int end = csq.length();
if (end <= start) {
return AbstractUnit.ONE;
}
String source = csq.subSequence(start, end).toString().trim();
if (source.length() == 0) {
return AbstractUnit.ONE;
}
try {
UnitFormatParser parser = new UnitFormatParser(symbolMap, new StringReader(source));
Unit result = parser.parseUnit();
if (cursor != null)
cursor.setIndex(end);
return result;
} catch (TokenException e) {
if (e.currentToken != null) {
cursor.setErrorIndex(start + e.currentToken.endColumn);
} else {
cursor.setErrorIndex(start);
}
throw new ParserException(e);
} catch (TokenMgrError e) {
cursor.setErrorIndex(start);
throw new IllegalArgumentException(e.getMessage());
}
}
@Override
protected Unit parse(CharSequence csq, int index) throws IllegalArgumentException {
return parse(csq, new ParsePosition(index));
}
public Unit parse(CharSequence csq) throws ParserException {
return parse(csq, 0);
}
}