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 */ 017package org.apache.commons.math3.util; 018 019import java.text.FieldPosition; 020import java.text.NumberFormat; 021import java.text.ParsePosition; 022import java.util.Locale; 023 024/** 025 * Base class for formatters of composite objects (complex numbers, vectors ...). 026 * 027 */ 028public class CompositeFormat { 029 030 /** 031 * Class contains only static methods. 032 */ 033 private CompositeFormat() {} 034 035 /** 036 * Create a default number format. The default number format is based on 037 * {@link NumberFormat#getInstance()} with the only customizing that the 038 * maximum number of fraction digits is set to 10. 039 * @return the default number format. 040 */ 041 public static NumberFormat getDefaultNumberFormat() { 042 return getDefaultNumberFormat(Locale.getDefault()); 043 } 044 045 /** 046 * Create a default number format. The default number format is based on 047 * {@link NumberFormat#getInstance(java.util.Locale)} with the only 048 * customizing that the maximum number of fraction digits is set to 10. 049 * @param locale the specific locale used by the format. 050 * @return the default number format specific to the given locale. 051 */ 052 public static NumberFormat getDefaultNumberFormat(final Locale locale) { 053 final NumberFormat nf = NumberFormat.getInstance(locale); 054 nf.setMaximumFractionDigits(10); 055 return nf; 056 } 057 058 /** 059 * Parses <code>source</code> until a non-whitespace character is found. 060 * 061 * @param source the string to parse 062 * @param pos input/output parsing parameter. On output, <code>pos</code> 063 * holds the index of the next non-whitespace character. 064 */ 065 public static void parseAndIgnoreWhitespace(final String source, 066 final ParsePosition pos) { 067 parseNextCharacter(source, pos); 068 pos.setIndex(pos.getIndex() - 1); 069 } 070 071 /** 072 * Parses <code>source</code> until a non-whitespace character is found. 073 * 074 * @param source the string to parse 075 * @param pos input/output parsing parameter. 076 * @return the first non-whitespace character. 077 */ 078 public static char parseNextCharacter(final String source, 079 final ParsePosition pos) { 080 int index = pos.getIndex(); 081 final int n = source.length(); 082 char ret = 0; 083 084 if (index < n) { 085 char c; 086 do { 087 c = source.charAt(index++); 088 } while (Character.isWhitespace(c) && index < n); 089 pos.setIndex(index); 090 091 if (index < n) { 092 ret = c; 093 } 094 } 095 096 return ret; 097 } 098 099 /** 100 * Parses <code>source</code> for special double values. These values 101 * include Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY. 102 * 103 * @param source the string to parse 104 * @param value the special value to parse. 105 * @param pos input/output parsing parameter. 106 * @return the special number. 107 */ 108 private static Number parseNumber(final String source, final double value, 109 final ParsePosition pos) { 110 Number ret = null; 111 112 StringBuilder sb = new StringBuilder(); 113 sb.append('('); 114 sb.append(value); 115 sb.append(')'); 116 117 final int n = sb.length(); 118 final int startIndex = pos.getIndex(); 119 final int endIndex = startIndex + n; 120 if (endIndex < source.length() && 121 source.substring(startIndex, endIndex).compareTo(sb.toString()) == 0) { 122 ret = Double.valueOf(value); 123 pos.setIndex(endIndex); 124 } 125 126 return ret; 127 } 128 129 /** 130 * Parses <code>source</code> for a number. This method can parse normal, 131 * numeric values as well as special values. These special values include 132 * Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY. 133 * 134 * @param source the string to parse 135 * @param format the number format used to parse normal, numeric values. 136 * @param pos input/output parsing parameter. 137 * @return the parsed number. 138 */ 139 public static Number parseNumber(final String source, final NumberFormat format, 140 final ParsePosition pos) { 141 final int startIndex = pos.getIndex(); 142 Number number = format.parse(source, pos); 143 final int endIndex = pos.getIndex(); 144 145 // check for error parsing number 146 if (startIndex == endIndex) { 147 // try parsing special numbers 148 final double[] special = { 149 Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY 150 }; 151 for (int i = 0; i < special.length; ++i) { 152 number = parseNumber(source, special[i], pos); 153 if (number != null) { 154 break; 155 } 156 } 157 } 158 159 return number; 160 } 161 162 /** 163 * Parse <code>source</code> for an expected fixed string. 164 * @param source the string to parse 165 * @param expected expected string 166 * @param pos input/output parsing parameter. 167 * @return true if the expected string was there 168 */ 169 public static boolean parseFixedstring(final String source, 170 final String expected, 171 final ParsePosition pos) { 172 173 final int startIndex = pos.getIndex(); 174 final int endIndex = startIndex + expected.length(); 175 if ((startIndex >= source.length()) || 176 (endIndex > source.length()) || 177 (source.substring(startIndex, endIndex).compareTo(expected) != 0)) { 178 // set index back to start, error index should be the start index 179 pos.setIndex(startIndex); 180 pos.setErrorIndex(startIndex); 181 return false; 182 } 183 184 // the string was here 185 pos.setIndex(endIndex); 186 return true; 187 } 188 189 /** 190 * Formats a double value to produce a string. In general, the value is 191 * formatted using the formatting rules of <code>format</code>. There are 192 * three exceptions to this: 193 * <ol> 194 * <li>NaN is formatted as '(NaN)'</li> 195 * <li>Positive infinity is formatted as '(Infinity)'</li> 196 * <li>Negative infinity is formatted as '(-Infinity)'</li> 197 * </ol> 198 * 199 * @param value the double to format. 200 * @param format the format used. 201 * @param toAppendTo where the text is to be appended 202 * @param pos On input: an alignment field, if desired. On output: the 203 * offsets of the alignment field 204 * @return the value passed in as toAppendTo. 205 */ 206 public static StringBuffer formatDouble(final double value, final NumberFormat format, 207 final StringBuffer toAppendTo, 208 final FieldPosition pos) { 209 if( Double.isNaN(value) || Double.isInfinite(value) ) { 210 toAppendTo.append('('); 211 toAppendTo.append(value); 212 toAppendTo.append(')'); 213 } else { 214 format.format(value, toAppendTo, pos); 215 } 216 return toAppendTo; 217 } 218}