    001    /*
    002     * Licensed to the Apache Software Foundation (ASF) under one or more
    003     * contributor license agreements.  See the NOTICE file distributed with
    004     * this work for additional information regarding copyright ownership.
    005     * The ASF licenses this file to You under the Apache License, Version 2.0
    006     * (the "License"); you may not use this file except in compliance with
    007     * the License.  You may obtain a copy of the License at
    008     *
    009     *      http://www.apache.org/licenses/LICENSE-2.0
    010     *
    011     * Unless required by applicable law or agreed to in writing, software
    012     * distributed under the License is distributed on an "AS IS" BASIS,
    013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014     * See the License for the specific language governing permissions and
    015     * limitations under the License.
    016     */
    017
    018    package org.apache.commons.math.geometry;
    019
    020    import java.text.FieldPosition;
    021    import java.text.NumberFormat;
    022    import java.text.ParseException;
    023    import java.text.ParsePosition;
    024    import java.util.Locale;
    025
    026    import org.apache.commons.math.MathRuntimeException;
    027    import org.apache.commons.math.exception.util.LocalizedFormats;
    028    import org.apache.commons.math.util.CompositeFormat;
    029
    030    /**
    031     * Formats a 3D vector in components list format "{x; y; z}".
    032     * <p>The prefix and suffix "{" and "}" and the separator "; " can be replaced by
    033     * any user-defined strings. The number format for components can be configured.</p>
    034     * <p>White space is ignored at parse time, even if it is in the prefix, suffix
    035     * or separator specifications. So even if the default separator does include a space
    036     * character that is used at format time, both input string "{1;1;1}" and
    037     * " { 1 ; 1 ; 1 } " will be parsed without error and the same vector will be
    038     * returned. In the second case, however, the parse position after parsing will be
    039     * just after the closing curly brace, i.e. just before the trailing space.</p>
    040     *
    041     * @version $Revision: 1003886 $ $Date: 2010-10-02 23:04:44 +0200 (sam. 02 oct. 2010) $
    042     */
    043    public class Vector3DFormat extends CompositeFormat {
    044
    045        /** Serializable version identifier */
    046        private static final long serialVersionUID = -5447606608652576301L;
    047
    048        /** The default prefix: "{". */
    049        private static final String DEFAULT_PREFIX = "{";
    050
    051        /** The default suffix: "}". */
    052        private static final String DEFAULT_SUFFIX = "}";
    053
    054        /** The default separator: ", ". */
    055        private static final String DEFAULT_SEPARATOR = "; ";
    056
    057        /** Prefix. */
    058        private final String prefix;
    059
    060        /** Suffix. */
    061        private final String suffix;
    062
    063        /** Separator. */
    064        private final String separator;
    065
    066        /** Trimmed prefix. */
    067        private final String trimmedPrefix;
    068
    069        /** Trimmed suffix. */
    070        private final String trimmedSuffix;
    071
    072        /** Trimmed separator. */
    073        private final String trimmedSeparator;
    074
    075        /** The format used for components. */
    076        private final NumberFormat format;
    077
    078        /**
    079         * Create an instance with default settings.
    080         * <p>The instance uses the default prefix, suffix and separator:
    081         * "{", "}", and "; " and the default number format for components.</p>
    082         */
    083        public Vector3DFormat() {
    084            this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, getDefaultNumberFormat());
    085        }
    086
    087        /**
    088         * Create an instance with a custom number format for components.
    089         * @param format the custom format for components.
    090         */
    091        public Vector3DFormat(final NumberFormat format) {
    092            this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, format);
    093        }
    094
    095        /**
    096         * Create an instance with custom prefix, suffix and separator.
    097         * @param prefix prefix to use instead of the default "{"
    098         * @param suffix suffix to use instead of the default "}"
    099         * @param separator separator to use instead of the default "; "
    100         */
    101        public Vector3DFormat(final String prefix, final String suffix,
    102                              final String separator) {
    103            this(prefix, suffix, separator, getDefaultNumberFormat());
    104        }
    105
    106        /**
    107         * Create an instance with custom prefix, suffix, separator and format
    108         * for components.
    109         * @param prefix prefix to use instead of the default "{"
    110         * @param suffix suffix to use instead of the default "}"
    111         * @param separator separator to use instead of the default "; "
    112         * @param format the custom format for components.
    113         */
    114        public Vector3DFormat(final String prefix, final String suffix,
    115                              final String separator, final NumberFormat format) {
    116            this.prefix      = prefix;
    117            this.suffix      = suffix;
    118            this.separator   = separator;
    119            trimmedPrefix    = prefix.trim();
    120            trimmedSuffix    = suffix.trim();
    121            trimmedSeparator = separator.trim();
    122            this.format      = format;
    123        }
    124
    125        /**
    126         * Get the set of locales for which 3D vectors formats are available.
    127         * <p>This is the same set as the {@link NumberFormat} set.</p>
    128         * @return available 3D vector format locales.
    129         */
    130        public static Locale[] getAvailableLocales() {
    131            return NumberFormat.getAvailableLocales();
    132        }
    133
    134        /**
    135         * Get the format prefix.
    136         * @return format prefix.
    137         */
    138        public String getPrefix() {
    139            return prefix;
    140        }
    141
    142        /**
    143         * Get the format suffix.
    144         * @return format suffix.
    145         */
    146        public String getSuffix() {
    147            return suffix;
    148        }
    149
    150        /**
    151         * Get the format separator between components.
    152         * @return format separator.
    153         */
    154        public String getSeparator() {
    155            return separator;
    156        }
    157
    158        /**
    159         * Get the components format.
    160         * @return components format.
    161         */
    162        public NumberFormat getFormat() {
    163            return format;
    164        }
    165
    166        /**
    167         * Returns the default 3D vector format for the current locale.
    168         * @return the default 3D vector format.
    169         */
    170        public static Vector3DFormat getInstance() {
    171            return getInstance(Locale.getDefault());
    172        }
    173
    174        /**
    175         * Returns the default 3D vector format for the given locale.
    176         * @param locale the specific locale used by the format.
    177         * @return the 3D vector format specific to the given locale.
    178         */
    179        public static Vector3DFormat getInstance(final Locale locale) {
    180            return new Vector3DFormat(getDefaultNumberFormat(locale));
    181        }
    182
    183        /**
    184         * This static method calls {@link #format(Object)} on a default instance of
    185         * Vector3DFormat.
    186         *
    187         * @param v Vector3D object to format
    188         * @return A formatted vector
    189         */
    190        public static String formatVector3D(Vector3D v) {
    191            return getInstance().format(v);
    192        }
    193
    194        /**
    195         * Formats a {@link Vector3D} object to produce a string.
    196         * @param vector the object to format.
    197         * @param toAppendTo where the text is to be appended
    198         * @param pos On input: an alignment field, if desired. On output: the
    199         *            offsets of the alignment field
    200         * @return the value passed in as toAppendTo.
    201         */
    202        public StringBuffer format(Vector3D vector, StringBuffer toAppendTo,
    203                                   FieldPosition pos) {
    204
    205            pos.setBeginIndex(0);
    206            pos.setEndIndex(0);
    207
    208            // format prefix
    209            toAppendTo.append(prefix);
    210
    211            // format components
    212            formatDouble(vector.getX(), format, toAppendTo, pos);
    213            toAppendTo.append(separator);
    214            formatDouble(vector.getY(), format, toAppendTo, pos);
    215            toAppendTo.append(separator);
    216            formatDouble(vector.getZ(), format, toAppendTo, pos);
    217
    218            // format suffix
    219            toAppendTo.append(suffix);
    220
    221            return toAppendTo;
    222
    223        }
    224
    225        /**
    226         * Formats a object to produce a string.
    227         * <p><code>obj</code> must be a  {@link Vector3D} object. Any other type of
    228         * object will result in an {@link IllegalArgumentException} being thrown.</p>
    229         * @param obj the object to format.
    230         * @param toAppendTo where the text is to be appended
    231         * @param pos On input: an alignment field, if desired. On output: the
    232         *            offsets of the alignment field
    233         * @return the value passed in as toAppendTo.
    234         * @see java.text.Format#format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition)
    235         * @throws IllegalArgumentException is <code>obj</code> is not a valid type.
    236         */
    237        @Override
    238        public StringBuffer format(Object obj, StringBuffer toAppendTo,
    239                                   FieldPosition pos) {
    240
    241            if (obj instanceof Vector3D) {
    242                return format( (Vector3D)obj, toAppendTo, pos);
    243            }
    244
    245            throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.CANNOT_FORMAT_INSTANCE_AS_3D_VECTOR,
    246                                                                      obj.getClass().getName());
    247
    248        }
    249
    250        /**
    251         * Parses a string to produce a {@link Vector3D} object.
    252         * @param source the string to parse
    253         * @return the parsed {@link Vector3D} object.
    254         * @exception ParseException if the beginning of the specified string
    255         *            cannot be parsed.
    256         */
    257        public Vector3D parse(String source) throws ParseException {
    258            ParsePosition parsePosition = new ParsePosition(0);
    259            Vector3D result = parse(source, parsePosition);
    260            if (parsePosition.getIndex() == 0) {
    261                throw MathRuntimeException.createParseException(
    262                        parsePosition.getErrorIndex(),
    263                        LocalizedFormats.UNPARSEABLE_3D_VECTOR, source);
    264            }
    265            return result;
    266        }
    267
    268        /**
    269         * Parses a string to produce a {@link Vector3D} object.
    270         * @param source the string to parse
    271         * @param pos input/ouput parsing parameter.
    272         * @return the parsed {@link Vector3D} object.
    273         */
    274        public Vector3D parse(String source, ParsePosition pos) {
    275            int initialIndex = pos.getIndex();
    276
    277            // parse prefix
    278            parseAndIgnoreWhitespace(source, pos);
    279            if (!parseFixedstring(source, trimmedPrefix, pos)) {
    280                return null;
    281            }
    282
    283            // parse X component
    284            parseAndIgnoreWhitespace(source, pos);
    285            Number x = parseNumber(source, format, pos);
    286            if (x == null) {
    287                // invalid abscissa
    288                // set index back to initial, error index should already be set
    289                pos.setIndex(initialIndex);
    290                return null;
    291            }
    292
    293            // parse Y component
    294            parseAndIgnoreWhitespace(source, pos);
    295            if (!parseFixedstring(source, trimmedSeparator, pos)) {
    296                return null;
    297            }
    298            parseAndIgnoreWhitespace(source, pos);
    299            Number y = parseNumber(source, format, pos);
    300            if (y == null) {
    301                // invalid ordinate
    302                // set index back to initial, error index should already be set
    303                pos.setIndex(initialIndex);
    304                return null;
    305            }
    306
    307            // parse Z component
    308            parseAndIgnoreWhitespace(source, pos);
    309            if (!parseFixedstring(source, trimmedSeparator, pos)) {
    310                return null;
    311            }
    312            parseAndIgnoreWhitespace(source, pos);
    313            Number z = parseNumber(source, format, pos);
    314            if (z == null) {
    315                // invalid height
    316                // set index back to initial, error index should already be set
    317                pos.setIndex(initialIndex);
    318                return null;
    319            }
    320
    321            // parse suffix
    322            parseAndIgnoreWhitespace(source, pos);
    323            if (!parseFixedstring(source, trimmedSuffix, pos)) {
    324                return null;
    325            }
    326
    327            return new Vector3D(x.doubleValue(), y.doubleValue(), z.doubleValue());
    328
    329        }
    330
    331        /**
    332         * Parses a string to produce a object.
    333         * @param source the string to parse
    334         * @param pos input/ouput parsing parameter.
    335         * @return the parsed object.
    336         * @see java.text.Format#parseObject(java.lang.String, java.text.ParsePosition)
    337         */
    338        @Override
    339        public Object parseObject(String source, ParsePosition pos) {
    340            return parse(source, pos);
    341        }
    342
    343    }