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
018package org.apache.commons.math3.linear;
019
020import java.text.FieldPosition;
021import java.text.NumberFormat;
022import java.text.ParsePosition;
023import java.util.ArrayList;
024import java.util.List;
025import java.util.Locale;
026
027import org.apache.commons.math3.exception.MathParseException;
028import 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 */
044public 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}