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