    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.math3.linear;
    019
    020    import java.text.FieldPosition;
    021    import java.text.NumberFormat;
    022    import java.text.ParsePosition;
    023    import java.util.ArrayList;
    024    import java.util.List;
    025    import java.util.Locale;
    026
    027    import org.apache.commons.math3.exception.MathParseException;
    028    import org.apache.commons.math3.util.CompositeFormat;
    029
    030    /**
    031     * Formats a vector in components list format "{v0; v1; ...; vk-1}".
    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 $Id: RealVectorFormat.java 1416643 2012-12-03 19:37:14Z tn $
    042     * @since 2.0
    043     */
    044    public class RealVectorFormat {
    045
    046        /** The default prefix: "{". */
    047        private static final String DEFAULT_PREFIX = "{";
    048        /** The default suffix: "}". */
    049        private static final String DEFAULT_SUFFIX = "}";
    050        /** The default separator: ", ". */
    051        private static final String DEFAULT_SEPARATOR = "; ";
    052        /** Prefix. */
    053        private final String prefix;
    054        /** Suffix. */
    055        private final String suffix;
    056        /** Separator. */
    057        private final String separator;
    058        /** Trimmed prefix. */
    059        private final String trimmedPrefix;
    060        /** Trimmed suffix. */
    061        private final String trimmedSuffix;
    062        /** Trimmed separator. */
    063        private final String trimmedSeparator;
    064        /** The format used for components. */
    065        private final NumberFormat format;
    066
    067        /**
    068         * Create an instance with default settings.
    069         * <p>The instance uses the default prefix, suffix and separator:
    070         * "{", "}", and "; " and the default number format for components.</p>
    071         */
    072        public RealVectorFormat() {
    073            this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR,
    074                 CompositeFormat.getDefaultNumberFormat());
    075        }
    076
    077        /**
    078         * Create an instance with a custom number format for components.
    079         * @param format the custom format for components.
    080         */
    081        public RealVectorFormat(final NumberFormat format) {
    082            this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, format);
    083        }
    084
    085        /**
    086         * Create an instance with custom prefix, suffix and separator.
    087         * @param prefix prefix to use instead of the default "{"
    088         * @param suffix suffix to use instead of the default "}"
    089         * @param separator separator to use instead of the default "; "
    090         */
    091        public RealVectorFormat(final String prefix, final String suffix,
    092                                final String separator) {
    093            this(prefix, suffix, separator,
    094                 CompositeFormat.getDefaultNumberFormat());
    095        }
    096
    097        /**
    098         * Create an instance with custom prefix, suffix, separator and format
    099         * for components.
    100         * @param prefix prefix to use instead of the default "{"
    101         * @param suffix suffix to use instead of the default "}"
    102         * @param separator separator to use instead of the default "; "
    103         * @param format the custom format for components.
    104         */
    105        public RealVectorFormat(final String prefix, final String suffix,
    106                                final String separator, final NumberFormat format) {
    107            this.prefix      = prefix;
    108            this.suffix      = suffix;
    109            this.separator   = separator;
    110            trimmedPrefix    = prefix.trim();
    111            trimmedSuffix    = suffix.trim();
    112            trimmedSeparator = separator.trim();
    113            this.format      = format;
    114        }
    115
    116        /**
    117         * Get the set of locales for which real vectors formats are available.
    118         * <p>This is the same set as the {@link NumberFormat} set.</p>
    119         * @return available real vector format locales.
    120         */
    121        public static Locale[] getAvailableLocales() {
    122            return NumberFormat.getAvailableLocales();
    123        }
    124
    125        /**
    126         * Get the format prefix.
    127         * @return format prefix.
    128         */
    129        public String getPrefix() {
    130            return prefix;
    131        }
    132
    133        /**
    134         * Get the format suffix.
    135         * @return format suffix.
    136         */
    137        public String getSuffix() {
    138            return suffix;
    139        }
    140
    141        /**
    142         * Get the format separator between components.
    143         * @return format separator.
    144         */
    145        public String getSeparator() {
    146            return separator;
    147        }
    148
    149        /**
    150         * Get the components format.
    151         * @return components format.
    152         */
    153        public NumberFormat getFormat() {
    154            return format;
    155        }
    156
    157        /**
    158         * Returns the default real vector format for the current locale.
    159         * @return the default real vector format.
    160         */
    161        public static RealVectorFormat getInstance() {
    162            return getInstance(Locale.getDefault());
    163        }
    164
    165        /**
    166         * Returns the default real vector format for the given locale.
    167         * @param locale the specific locale used by the format.
    168         * @return the real vector format specific to the given locale.
    169         */
    170        public static RealVectorFormat getInstance(final Locale locale) {
    171            return new RealVectorFormat(CompositeFormat.getDefaultNumberFormat(locale));
    172        }
    173
    174        /**
    175         * This method calls {@link #format(RealVector,StringBuffer,FieldPosition)}.
    176         *
    177         * @param v RealVector object to format.
    178         * @return a formatted vector.
    179         */
    180        public String format(RealVector v) {
    181            return format(v, new StringBuffer(), new FieldPosition(0)).toString();
    182        }
    183
    184        /**
    185         * Formats a {@link RealVector} object to produce a string.
    186         * @param vector the object to format.
    187         * @param toAppendTo where the text is to be appended
    188         * @param pos On input: an alignment field, if desired. On output: the
    189         *            offsets of the alignment field
    190         * @return the value passed in as toAppendTo.
    191         */
    192        public StringBuffer format(RealVector vector, StringBuffer toAppendTo,
    193                                   FieldPosition pos) {
    194
    195            pos.setBeginIndex(0);
    196            pos.setEndIndex(0);
    197
    198            // format prefix
    199            toAppendTo.append(prefix);
    200
    201            // format components
    202            for (int i = 0; i < vector.getDimension(); ++i) {
    203                if (i > 0) {
    204                    toAppendTo.append(separator);
    205                }
    206                CompositeFormat.formatDouble(vector.getEntry(i), format, toAppendTo, pos);
    207            }
    208
    209            // format suffix
    210            toAppendTo.append(suffix);
    211
    212            return toAppendTo;
    213        }
    214
    215        /**
    216         * Parse a string to produce a {@link RealVector} object.
    217         *
    218         * @param source String to parse.
    219         * @return the parsed {@link RealVector} object.
    220         * @throws MathParseException if the beginning of the specified string
    221         * cannot be parsed.
    222         */
    223        public ArrayRealVector parse(String source) {
    224            final ParsePosition parsePosition = new ParsePosition(0);
    225            final ArrayRealVector result = parse(source, parsePosition);
    226            if (parsePosition.getIndex() == 0) {
    227                throw new MathParseException(source,
    228                                             parsePosition.getErrorIndex(),
    229                                             ArrayRealVector.class);
    230            }
    231            return result;
    232        }
    233
    234        /**
    235         * Parse a string to produce a {@link RealVector} object.
    236         *
    237         * @param source String to parse.
    238         * @param pos input/ouput parsing parameter.
    239         * @return the parsed {@link RealVector} object.
    240         */
    241        public ArrayRealVector parse(String source, ParsePosition pos) {
    242            int initialIndex = pos.getIndex();
    243
    244            // parse prefix
    245            CompositeFormat.parseAndIgnoreWhitespace(source, pos);
    246            if (!CompositeFormat.parseFixedstring(source, trimmedPrefix, pos)) {
    247                return null;
    248            }
    249
    250            // parse components
    251            List<Number> components = new ArrayList<Number>();
    252            for (boolean loop = true; loop;){
    253
    254                if (!components.isEmpty()) {
    255                    CompositeFormat.parseAndIgnoreWhitespace(source, pos);
    256                    if (!CompositeFormat.parseFixedstring(source, trimmedSeparator, pos)) {
    257                        loop = false;
    258                    }
    259                }
    260
    261                if (loop) {
    262                    CompositeFormat.parseAndIgnoreWhitespace(source, pos);
    263                    Number component = CompositeFormat.parseNumber(source, format, pos);
    264                    if (component != null) {
    265                        components.add(component);
    266                    } else {
    267                        // invalid component
    268                        // set index back to initial, error index should already be set
    269                        pos.setIndex(initialIndex);
    270                        return null;
    271                    }
    272                }
    273
    274            }
    275
    276            // parse suffix
    277            CompositeFormat.parseAndIgnoreWhitespace(source, pos);
    278            if (!CompositeFormat.parseFixedstring(source, trimmedSuffix, pos)) {
    279                return null;
    280            }
    281
    282            // build vector
    283            double[] data = new double[components.size()];
    284            for (int i = 0; i < data.length; ++i) {
    285                data[i] = components.get(i).doubleValue();
    286            }
    287            return new ArrayRealVector(data, false);
    288        }
    289    }