Home | History | Annotate | Download | only in geometry
      1 /*
      2  * Licensed to the Apache Software Foundation (ASF) under one or more
      3  * contributor license agreements.  See the NOTICE file distributed with
      4  * this work for additional information regarding copyright ownership.
      5  * The ASF licenses this file to You under the Apache License, Version 2.0
      6  * (the "License"); you may not use this file except in compliance with
      7  * the License.  You may obtain a copy of the License at
      8  *
      9  *      http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 package org.apache.commons.math.geometry;
     19 
     20 import java.text.FieldPosition;
     21 import java.text.NumberFormat;
     22 import java.text.ParseException;
     23 import java.text.ParsePosition;
     24 import java.util.Locale;
     25 
     26 import org.apache.commons.math.MathRuntimeException;
     27 import org.apache.commons.math.exception.util.LocalizedFormats;
     28 import org.apache.commons.math.util.CompositeFormat;
     29 
     30 /**
     31  * Formats a 3D vector in components list format "{x; y; z}".
     32  * <p>The prefix and suffix "{" and "}" and the separator "; " can be replaced by
     33  * any user-defined strings. The number format for components can be configured.</p>
     34  * <p>White space is ignored at parse time, even if it is in the prefix, suffix
     35  * or separator specifications. So even if the default separator does include a space
     36  * character that is used at format time, both input string "{1;1;1}" and
     37  * " { 1 ; 1 ; 1 } " will be parsed without error and the same vector will be
     38  * returned. In the second case, however, the parse position after parsing will be
     39  * just after the closing curly brace, i.e. just before the trailing space.</p>
     40  *
     41  * @version $Revision: 1003886 $ $Date: 2010-10-02 23:04:44 +0200 (sam. 02 oct. 2010) $
     42  */
     43 public class Vector3DFormat extends CompositeFormat {
     44 
     45     /** Serializable version identifier */
     46     private static final long serialVersionUID = -5447606608652576301L;
     47 
     48     /** The default prefix: "{". */
     49     private static final String DEFAULT_PREFIX = "{";
     50 
     51     /** The default suffix: "}". */
     52     private static final String DEFAULT_SUFFIX = "}";
     53 
     54     /** The default separator: ", ". */
     55     private static final String DEFAULT_SEPARATOR = "; ";
     56 
     57     /** Prefix. */
     58     private final String prefix;
     59 
     60     /** Suffix. */
     61     private final String suffix;
     62 
     63     /** Separator. */
     64     private final String separator;
     65 
     66     /** Trimmed prefix. */
     67     private final String trimmedPrefix;
     68 
     69     /** Trimmed suffix. */
     70     private final String trimmedSuffix;
     71 
     72     /** Trimmed separator. */
     73     private final String trimmedSeparator;
     74 
     75     /** The format used for components. */
     76     private final NumberFormat format;
     77 
     78     /**
     79      * Create an instance with default settings.
     80      * <p>The instance uses the default prefix, suffix and separator:
     81      * "{", "}", and "; " and the default number format for components.</p>
     82      */
     83     public Vector3DFormat() {
     84         this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, getDefaultNumberFormat());
     85     }
     86 
     87     /**
     88      * Create an instance with a custom number format for components.
     89      * @param format the custom format for components.
     90      */
     91     public Vector3DFormat(final NumberFormat format) {
     92         this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, format);
     93     }
     94 
     95     /**
     96      * Create an instance with custom prefix, suffix and separator.
     97      * @param prefix prefix to use instead of the default "{"
     98      * @param suffix suffix to use instead of the default "}"
     99      * @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 }
    344