/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.sis.measure;

import java.util.Locale;
import java.text.Format;
import java.text.FieldPosition;
import java.text.NumberFormat;
import java.text.ParsePosition;
import javax.measure.Quantity;
import javax.measure.Unit;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.internal.util.FinalFieldSetter;


/**
 * Parses and formats numbers with units of measurement.
 *
 * @author  Martin Desruisseaux (Geomatys)
 * @version 1.1
 *
 * @see NumberFormat
 * @see UnitFormat
 *
 * @since 1.1
 * @module
 */
public class QuantityFormat extends Format {
    /**
     * For cross-version compatibility.
     */
    private static final long serialVersionUID = 1014042719969477503L;

    /**
     * The default separator used between numerical value and its unit of measurement.
     * Current value is narrow no-break space (U+202F).
     */
    public static final char SEPARATOR = '\u202F';

    /**
     * The format for parsing and formatting the number part.
     */
    protected final NumberFormat numberFormat;

    /**
     * The format for parsing and formatting the unit of measurement part.
     */
    protected final UnitFormat unitFormat;

    /**
     * Creates a new instance for the given locale.
     *
     * @param  locale  the locale for the quantity format.
     */
    public QuantityFormat(final Locale locale) {
        ArgumentChecks.ensureNonNull("locale", locale);
        numberFormat = NumberFormat.getNumberInstance(locale);
        unitFormat   = new UnitFormat(locale);
    }

    /**
     * Creates a new instance using the given number and unit formats.
     *
     * @param  numberFormat  the format for parsing and formatting the number part.
     * @param  unitFormat    the format for parsing and formatting the unit of measurement part.
     */
    public QuantityFormat(final NumberFormat numberFormat, final UnitFormat unitFormat) {
        ArgumentChecks.ensureNonNull("numberFormat", numberFormat);
        ArgumentChecks.ensureNonNull("unitFormat",   unitFormat);
        this.numberFormat = numberFormat;
        this.unitFormat   = unitFormat;
    }

    /**
     * Formats the specified quantity in the given buffer.
     * The given object shall be an {@link Quantity} instance.
     *
     * @param  quantity    the quantity to format.
     * @param  toAppendTo  where to format the quantity.
     * @param  pos         where to store the position of a formatted field, or {@code null} if none.
     * @return the given {@code toAppendTo} argument, for method calls chaining.
     */
    @Override
    public StringBuffer format(final Object quantity, StringBuffer toAppendTo, FieldPosition pos) {
        final Quantity<?> q = (Quantity<?>) quantity;
        if (pos == null) pos = new FieldPosition(0);
        toAppendTo = numberFormat.format(q.getValue(), toAppendTo, pos).append(SEPARATOR);   // Narrow no-break space.
        toAppendTo = unitFormat.format(q.getUnit(), toAppendTo, pos);
        return toAppendTo;
    }

    /**
     * Parses text from a string to produce a quantity, or returns {@code null} if the parsing failed.
     *
     * @param  source  the text, part of which should be parsed.
     * @param  pos     index and error index information.
     * @return a unit parsed from the string, or {@code null} in case of error.
     */
    @Override
    public Object parseObject(final String source, final ParsePosition pos) {
        final Number value = numberFormat.parse(source, pos);
        if (value != null) {
            final Unit<?> unit = unitFormat.parse(source, pos);
            if (unit != null) {
                return Quantities.create(value.doubleValue(), unit);
            }
        }
        return null;
    }

    /**
     * Returns a clone of this format.
     *
     * @return a clone of this format.
     */
    @Override
    public QuantityFormat clone() {
        final QuantityFormat clone = (QuantityFormat) super.clone();
        try {
            FinalFieldSetter.set(QuantityFormat.class, "numberFormat", "unitFormat",
                                 clone, numberFormat.clone(), unitFormat.clone());
        } catch (ReflectiveOperationException e) {
            throw FinalFieldSetter.cloneFailure(e);
        }
        return clone;
    }
}

@@ -1031,16 +1031,152 @@
 e.Unit;%0A
+import javax.measure.format.ParserException;%0Aimport org.apache.sis.internal.system.Loggers;%0Aimport org.apache.sis.util.logging.Logging;%0A
 import o
@@ -1210,16 +1210,16 @@
 Checks;%0A
-
 import o
@@ -1265,16 +1265,67 @@
 etter;%0A%0A
+import static java.util.logging.Logger.getLogger;%0A%0A
 %0A/**%0A * 
@@ -3205,17 +3205,16 @@
 all be a
-n
  %7B@link 
@@ -4241,20 +4241,24 @@
 eturn a 
-un
+quant
 it
+y
  parsed 
@@ -4400,32 +4400,74 @@
 Position pos) %7B%0A
+        final int start = pos.getIndex();%0A
         final Nu
@@ -4533,32 +4533,54 @@
 alue != null) %7B%0A
+            try %7B%0A    
             fina
@@ -4635,24 +4635,28 @@
             
+    
 if (unit != 
@@ -4675,24 +4675,28 @@
             
+  
+  
 return Quant
@@ -4740,33 +4740,292 @@
 t);%0A            
-%7D
+    %7D%0A            %7D catch (ParserException e) %7B%0A                Logging.ignorableException(getLogger(Loggers.MEASURE), QuantityFormat.class, %22parseObject%22, e);%0A            %7D%0A            pos.setIndex(start);        // By %60Format.parseObject(%E2%80%A6)%60 method contract.
 %0A        %7D%0A     

Make `QuantityFormat` more compliant with specification.