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 {@code nxm} matrix in components list format 032 * "{{a<sub>0</sub><sub>0</sub>,a<sub>0</sub><sub>1</sub>, ..., 033 * a<sub>0</sub><sub>m-1</sub>},{a<sub>1</sub><sub>0</sub>, 034 * a<sub>1</sub><sub>1</sub>, ..., a<sub>1</sub><sub>m-1</sub>},{...},{ 035 * a<sub>n-1</sub><sub>0</sub>, a<sub>n-1</sub><sub>1</sub>, ..., 036 * a<sub>n-1</sub><sub>m-1</sub>}}". 037 * <p>The prefix and suffix "{" and "}", the row prefix and suffix "{" and "}", 038 * the row separator "," and the column separator "," can be replaced by any 039 * user-defined strings. The number format for components can be configured.</p> 040 * 041 * <p>White space is ignored at parse time, even if it is in the prefix, suffix 042 * or separator specifications. So even if the default separator does include a space 043 * character that is used at format time, both input string "{{1,1,1}}" and 044 * " { { 1 , 1 , 1 } } " will be parsed without error and the same matrix will be 045 * returned. In the second case, however, the parse position after parsing will be 046 * just after the closing curly brace, i.e. just before the trailing space.</p> 047 * 048 * <p><b>Note:</b> the grouping functionality of the used {@link NumberFormat} is 049 * disabled to prevent problems when parsing (e.g. 1,345.34 would be a valid number 050 * but conflicts with the default column separator).</p> 051 * 052 * @since 3.1 053 * @version $Id: RealMatrixFormat.java 1364793 2012-07-23 20:46:28Z tn $ 054 */ 055 public class RealMatrixFormat { 056 057 /** The default prefix: "{". */ 058 private static final String DEFAULT_PREFIX = "{"; 059 /** The default suffix: "}". */ 060 private static final String DEFAULT_SUFFIX = "}"; 061 /** The default row prefix: "{". */ 062 private static final String DEFAULT_ROW_PREFIX = "{"; 063 /** The default row suffix: "}". */ 064 private static final String DEFAULT_ROW_SUFFIX = "}"; 065 /** The default row separator: ",". */ 066 private static final String DEFAULT_ROW_SEPARATOR = ","; 067 /** The default column separator: ",". */ 068 private static final String DEFAULT_COLUMN_SEPARATOR = ","; 069 /** Prefix. */ 070 private final String prefix; 071 /** Suffix. */ 072 private final String suffix; 073 /** Row prefix. */ 074 private final String rowPrefix; 075 /** Row suffix. */ 076 private final String rowSuffix; 077 /** Row separator. */ 078 private final String rowSeparator; 079 /** Column separator. */ 080 private final String columnSeparator; 081 /** The format used for components. */ 082 private final NumberFormat format; 083 084 /** 085 * Create an instance with default settings. 086 * <p>The instance uses the default prefix, suffix and row/column separator: 087 * "[", "]", ";" and ", " and the default number format for components.</p> 088 */ 089 public RealMatrixFormat() { 090 this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_ROW_PREFIX, DEFAULT_ROW_SUFFIX, 091 DEFAULT_ROW_SEPARATOR, DEFAULT_COLUMN_SEPARATOR, CompositeFormat.getDefaultNumberFormat()); 092 } 093 094 /** 095 * Create an instance with a custom number format for components. 096 * @param format the custom format for components. 097 */ 098 public RealMatrixFormat(final NumberFormat format) { 099 this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_ROW_PREFIX, DEFAULT_ROW_SUFFIX, 100 DEFAULT_ROW_SEPARATOR, DEFAULT_COLUMN_SEPARATOR, format); 101 } 102 103 /** 104 * Create an instance with custom prefix, suffix and separator. 105 * @param prefix prefix to use instead of the default "{" 106 * @param suffix suffix to use instead of the default "}" 107 * @param rowPrefix row prefix to use instead of the default "{" 108 * @param rowSuffix row suffix to use instead of the default "}" 109 * @param rowSeparator tow separator to use instead of the default ";" 110 * @param columnSeparator column separator to use instead of the default ", " 111 */ 112 public RealMatrixFormat(final String prefix, final String suffix, 113 final String rowPrefix, final String rowSuffix, 114 final String rowSeparator, final String columnSeparator) { 115 this(prefix, suffix, rowPrefix, rowSuffix, rowSeparator, columnSeparator, 116 CompositeFormat.getDefaultNumberFormat()); 117 } 118 119 /** 120 * Create an instance with custom prefix, suffix, separator and format 121 * for components. 122 * @param prefix prefix to use instead of the default "{" 123 * @param suffix suffix to use instead of the default "}" 124 * @param rowPrefix row prefix to use instead of the default "{" 125 * @param rowSuffix row suffix to use instead of the default "}" 126 * @param rowSeparator tow separator to use instead of the default ";" 127 * @param columnSeparator column separator to use instead of the default ", " 128 * @param format the custom format for components. 129 */ 130 public RealMatrixFormat(final String prefix, final String suffix, 131 final String rowPrefix, final String rowSuffix, 132 final String rowSeparator, final String columnSeparator, 133 final NumberFormat format) { 134 this.prefix = prefix; 135 this.suffix = suffix; 136 this.rowPrefix = rowPrefix; 137 this.rowSuffix = rowSuffix; 138 this.rowSeparator = rowSeparator; 139 this.columnSeparator = columnSeparator; 140 this.format = format; 141 // disable grouping to prevent parsing problems 142 this.format.setGroupingUsed(false); 143 } 144 145 /** 146 * Get the set of locales for which real vectors formats are available. 147 * <p>This is the same set as the {@link NumberFormat} set.</p> 148 * @return available real vector format locales. 149 */ 150 public static Locale[] getAvailableLocales() { 151 return NumberFormat.getAvailableLocales(); 152 } 153 154 /** 155 * Get the format prefix. 156 * @return format prefix. 157 */ 158 public String getPrefix() { 159 return prefix; 160 } 161 162 /** 163 * Get the format suffix. 164 * @return format suffix. 165 */ 166 public String getSuffix() { 167 return suffix; 168 } 169 170 /** 171 * Get the format prefix. 172 * @return format prefix. 173 */ 174 public String getRowPrefix() { 175 return rowPrefix; 176 } 177 178 /** 179 * Get the format suffix. 180 * @return format suffix. 181 */ 182 public String getRowSuffix() { 183 return rowSuffix; 184 } 185 186 /** 187 * Get the format separator between rows of the matrix. 188 * @return format separator for rows. 189 */ 190 public String getRowSeparator() { 191 return rowSeparator; 192 } 193 194 /** 195 * Get the format separator between components. 196 * @return format separator between components. 197 */ 198 public String getColumnSeparator() { 199 return columnSeparator; 200 } 201 202 /** 203 * Get the components format. 204 * @return components format. 205 */ 206 public NumberFormat getFormat() { 207 return format; 208 } 209 210 /** 211 * Returns the default real vector format for the current locale. 212 * @return the default real vector format. 213 */ 214 public static RealMatrixFormat getInstance() { 215 return getInstance(Locale.getDefault()); 216 } 217 218 /** 219 * Returns the default real vector format for the given locale. 220 * @param locale the specific locale used by the format. 221 * @return the real vector format specific to the given locale. 222 */ 223 public static RealMatrixFormat getInstance(final Locale locale) { 224 return new RealMatrixFormat(CompositeFormat.getDefaultNumberFormat(locale)); 225 } 226 227 /** 228 * This method calls {@link #format(RealMatrix,StringBuffer,FieldPosition)}. 229 * 230 * @param m RealMatrix object to format. 231 * @return a formatted matrix. 232 */ 233 public String format(RealMatrix m) { 234 return format(m, new StringBuffer(), new FieldPosition(0)).toString(); 235 } 236 237 /** 238 * Formats a {@link RealMatrix} object to produce a string. 239 * @param matrix the object to format. 240 * @param toAppendTo where the text is to be appended 241 * @param pos On input: an alignment field, if desired. On output: the 242 * offsets of the alignment field 243 * @return the value passed in as toAppendTo. 244 */ 245 public StringBuffer format(RealMatrix matrix, StringBuffer toAppendTo, 246 FieldPosition pos) { 247 248 pos.setBeginIndex(0); 249 pos.setEndIndex(0); 250 251 // format prefix 252 toAppendTo.append(prefix); 253 254 // format rows 255 final int rows = matrix.getRowDimension(); 256 for (int i = 0; i < rows; ++i) { 257 toAppendTo.append(rowPrefix); 258 for (int j = 0; j < matrix.getColumnDimension(); ++j) { 259 if (j > 0) { 260 toAppendTo.append(columnSeparator); 261 } 262 CompositeFormat.formatDouble(matrix.getEntry(i, j), format, toAppendTo, pos); 263 } 264 toAppendTo.append(rowSuffix); 265 if (i < rows - 1) { 266 toAppendTo.append(rowSeparator); 267 } 268 } 269 270 // format suffix 271 toAppendTo.append(suffix); 272 273 return toAppendTo; 274 } 275 276 /** 277 * Parse a string to produce a {@link RealMatrix} object. 278 * 279 * @param source String to parse. 280 * @return the parsed {@link RealMatrix} object. 281 * @throws MathParseException if the beginning of the specified string 282 * cannot be parsed. 283 */ 284 public RealMatrix parse(String source) { 285 final ParsePosition parsePosition = new ParsePosition(0); 286 final RealMatrix result = parse(source, parsePosition); 287 if (parsePosition.getIndex() == 0) { 288 throw new MathParseException(source, 289 parsePosition.getErrorIndex(), 290 Array2DRowRealMatrix.class); 291 } 292 return result; 293 } 294 295 /** 296 * Parse a string to produce a {@link RealMatrix} object. 297 * 298 * @param source String to parse. 299 * @param pos input/ouput parsing parameter. 300 * @return the parsed {@link RealMatrix} object. 301 */ 302 public RealMatrix parse(String source, ParsePosition pos) { 303 int initialIndex = pos.getIndex(); 304 305 final String trimmedPrefix = prefix.trim(); 306 final String trimmedSuffix = suffix.trim(); 307 final String trimmedRowPrefix = rowPrefix.trim(); 308 final String trimmedRowSuffix = rowSuffix.trim(); 309 final String trimmedColumnSeparator = columnSeparator.trim(); 310 final String trimmedRowSeparator = rowSeparator.trim(); 311 312 // parse prefix 313 CompositeFormat.parseAndIgnoreWhitespace(source, pos); 314 if (!CompositeFormat.parseFixedstring(source, trimmedPrefix, pos)) { 315 return null; 316 } 317 318 // parse components 319 List<List<Number>> matrix = new ArrayList<List<Number>>(); 320 List<Number> rowComponents = new ArrayList<Number>(); 321 for (boolean loop = true; loop;){ 322 323 if (!rowComponents.isEmpty()) { 324 CompositeFormat.parseAndIgnoreWhitespace(source, pos); 325 if (!CompositeFormat.parseFixedstring(source, trimmedColumnSeparator, pos)) { 326 if (trimmedRowSuffix.length() != 0 && 327 !CompositeFormat.parseFixedstring(source, trimmedRowSuffix, pos)) { 328 return null; 329 } else { 330 CompositeFormat.parseAndIgnoreWhitespace(source, pos); 331 if (CompositeFormat.parseFixedstring(source, trimmedRowSeparator, pos)) { 332 matrix.add(rowComponents); 333 rowComponents = new ArrayList<Number>(); 334 continue; 335 } else { 336 loop = false; 337 } 338 } 339 } 340 } else { 341 CompositeFormat.parseAndIgnoreWhitespace(source, pos); 342 if (trimmedRowPrefix.length() != 0 && 343 !CompositeFormat.parseFixedstring(source, trimmedRowPrefix, pos)) { 344 return null; 345 } 346 } 347 348 if (loop) { 349 CompositeFormat.parseAndIgnoreWhitespace(source, pos); 350 Number component = CompositeFormat.parseNumber(source, format, pos); 351 if (component != null) { 352 rowComponents.add(component); 353 } else { 354 if (rowComponents.isEmpty()) { 355 loop = false; 356 } else { 357 // invalid component 358 // set index back to initial, error index should already be set 359 pos.setIndex(initialIndex); 360 return null; 361 } 362 } 363 } 364 365 } 366 367 if (!rowComponents.isEmpty()) { 368 matrix.add(rowComponents); 369 } 370 371 // parse suffix 372 CompositeFormat.parseAndIgnoreWhitespace(source, pos); 373 if (!CompositeFormat.parseFixedstring(source, trimmedSuffix, pos)) { 374 return null; 375 } 376 377 // do not allow an empty matrix 378 if (matrix.isEmpty()) { 379 pos.setIndex(initialIndex); 380 return null; 381 } 382 383 // build vector 384 double[][] data = new double[matrix.size()][]; 385 int row = 0; 386 for (List<Number> rowList : matrix) { 387 data[row] = new double[rowList.size()]; 388 for (int i = 0; i < rowList.size(); i++) { 389 data[row][i] = rowList.get(i).doubleValue(); 390 } 391 row++; 392 } 393 return MatrixUtils.createRealMatrix(data); 394 } 395 }