001/* 002 * CREDIT SUISSE IS WILLING TO LICENSE THIS SPECIFICATION TO YOU ONLY UPON THE 003 * CONDITION THAT YOU ACCEPT ALL OF THE TERMS CONTAINED IN THIS AGREEMENT. 004 * PLEASE READ THE TERMS AND CONDITIONS OF THIS AGREEMENT CAREFULLY. BY 005 * DOWNLOADING THIS SPECIFICATION, YOU ACCEPT THE TERMS AND CONDITIONS OF THE 006 * AGREEMENT. IF YOU ARE NOT WILLING TO BE BOUND BY IT, SELECT THE "DECLINE" 007 * BUTTON AT THE BOTTOM OF THIS PAGE. Specification: JSR-354 Money and Currency 008 * API ("Specification") Copyright (c) 2012-2013, Credit Suisse All rights 009 * reserved. 010 */ 011package org.javamoney.moneta.format; 012 013import java.text.ParsePosition; 014 015import javax.money.CurrencyUnit; 016 017/** 018 * Context passed along to each {@link FormatToken} in-line, when parsing an 019 * input stream using a {@link ItemFormatBuilder}. It allows to inspect the next 020 * tokens, the whole input String, or just the current input substring, based on 021 * the current parsing position etc. 022 * <p> 023 * This class is mutable and intended for use by a single thread. A new instance 024 * is created for each parse. 025 */ 026// TODO ItemFormatBuilder does not exist any more. Fix JavaDoc 027public final class ParseContext { 028 /** The current position of parsing. */ 029 private int index; 030 /** The error index position. */ 031 private int errorIndex = -1; 032 /** The full input. */ 033 private CharSequence originalInput; 034 035 private CurrencyUnit parsedCurrency; 036 037 private Number parsedNumber; 038 039 /** 040 * Creates a new {@link ParseContext} with the given input. 041 * 042 * @param text 043 * The test to be parsed. 044 */ 045 public ParseContext(CharSequence text) { 046 if (text == null) { 047 throw new IllegalArgumentException("test is required"); 048 } 049 this.originalInput = text; 050 } 051 052 /** 053 * Method allows to determine if the item being parsed is available from the 054 * {@link ParseContext}. 055 * 056 * @return true, if the item is available. 057 */ 058 public boolean isComplete() { 059 return parsedNumber != null; 060 } 061 062 /** 063 * Get the parsed item. 064 * 065 * @return the item parsed. 066 */ 067 public Number getParsedNumber() { 068 if (!isComplete()) { 069 throw new IllegalStateException("Parsing is not yet complete."); 070 } 071 return parsedNumber; 072 } 073 074 /** 075 * Consumes the given token. If the current residual text to be parsed 076 * starts with the parsing index is increased by {@code token.size()}. 077 * 078 * @param token 079 * The token expected. 080 * @return true, if the token could be consumed and the index was increased 081 * by {@code token.size()}. 082 */ 083 public boolean consume(String token) { 084 if (getInput().toString().startsWith(token)) { 085 index += token.length(); 086 return true; 087 } 088 return false; 089 } 090 091 /** 092 * Tries to consume one single character. 093 * 094 * @param c 095 * the next character being expected. 096 * @return true, if the character matched and the index could be increased 097 * by one. 098 */ 099 public boolean consume(char c) { 100 if (originalInput.charAt(index) == c) { 101 index++; 102 return true; 103 } 104 return false; 105 } 106 107 /** 108 * Skips all whitespaces until a non whitespace character is occurring. If 109 * the next character is not whitespace this method does nothing. 110 * 111 * @see Character#isWhitespace(char) 112 * 113 * @return the new parse index after skipping any whitespaces. 114 */ 115 public int skipWhitespace() { 116 for (int i = index; i < originalInput.length(); i++) { 117 if (Character.isWhitespace(originalInput.charAt(i))) { 118 index++; 119 } else { 120 break; 121 } 122 } 123 return index; 124 } 125 126 /** 127 * Gets the error index. 128 * 129 * @return the error index, negative if no error 130 */ 131 public int getErrorIndex() { 132 return errorIndex; 133 } 134 135 /** 136 * Sets the error index. 137 * 138 * @param index 139 * the error index 140 */ 141 public void setErrorIndex(int index) { 142 this.errorIndex = index; 143 } 144 145 /** 146 * Sets the error index from the current index. 147 */ 148 public void setError() { 149 this.errorIndex = index; 150 } 151 152 /** 153 * Gets the current parse position. 154 * 155 * @return the current parse position within the input. 156 */ 157 public int getIndex() { 158 return index; 159 } 160 161 /** 162 * Gets the residual input text starting from the current parse position. 163 * 164 * @return the residual input text 165 */ 166 public CharSequence getInput() { 167 return originalInput.subSequence(index, originalInput.length() - 1); 168 } 169 170 /** 171 * Gets the full input text. 172 * 173 * @return the full input. 174 */ 175 public String getOriginalInput() { 176 return originalInput.toString(); 177 } 178 179 /** 180 * Resets this instance; this will reset the parsing position, the error 181 * index and also all containing results. 182 */ 183 public void reset() { 184 this.index = 0; 185 this.errorIndex = -1; 186 this.parsedNumber = null; 187 this.parsedCurrency = null; 188 } 189 190 /** 191 * Add a result to the results of this context. 192 * 193 * @param key 194 * The result key 195 * @param value 196 * The result value 197 */ 198 public void setParsedNumber(Number number) { 199 this.parsedNumber = number; 200 } 201 202 /** 203 * Add a result to the results of this context. 204 * 205 * @param key 206 * The result key 207 * @param value 208 * The result value 209 */ 210 public void setParsedCurrency(CurrencyUnit currency) { 211 this.parsedCurrency = currency; 212 } 213 214 /** 215 * Checks if the parse has found an error. 216 * 217 * @return whether a parse error has occurred 218 */ 219 public boolean isError() { 220 return errorIndex >= 0; 221 } 222 223 /** 224 * Checks if the text has been fully parsed such that there is no more text 225 * to parse. 226 * 227 * @return true if fully parsed 228 */ 229 public boolean isFullyParsed() { 230 return index == this.originalInput.length(); 231 } 232 233 /** 234 * This method skips all whitespaces and returns the full text, until 235 * another whitespace area or the end of the input is reached. The method 236 * will not update any index pointers. 237 * 238 * @return the next token found, or null. 239 */ 240 public String lookupNextToken() { 241 skipWhitespace(); 242 int start = index; 243 for (int end = index; end < originalInput.length(); end++) { 244 if (Character.isWhitespace(originalInput.charAt(end))) { 245 if (end > start) { 246 return originalInput.subSequence(start, end).toString(); 247 } 248 return null; 249 } 250 } 251 return null; 252 } 253 254 /** 255 * Converts the indexes to a parse position. 256 * 257 * @return the parse position, never null 258 */ 259 public ParsePosition toParsePosition() { 260 return new ParsePosition(index); 261 } 262 263 /* 264 * (non-Javadoc) 265 * 266 * @see java.lang.Object#toString() 267 */ 268 @Override 269 public String toString() { 270 return "ParseContext [index=" + index + ", errorIndex=" + errorIndex 271 + ", originalInput='" + originalInput + "', parsedNumber=" 272 + parsedNumber + "', parsedCurrency=" + parsedCurrency 273 + "]"; 274 } 275 276 public CurrencyUnit getParsedCurrency() { 277 return parsedCurrency; 278 } 279 280}