001: /*
002: *
003: * Copyright (c) 2007, Sun Microsystems, Inc.
004: *
005: * All rights reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * * Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: * * Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in the
015: * documentation and/or other materials provided with the distribution.
016: * * Neither the name of Sun Microsystems nor the names of its contributors
017: * may be used to endorse or promote products derived from this software
018: * without specific prior written permission.
019: *
020: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
021: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
022: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
023: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
024: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
025: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
026: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
027: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
028: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
029: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
030: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
031: */
032: package example.stock;
033:
034: /**
035: * <p>This is a utility class that is used to parse data obtained from either
036: * the quote server or the database and break it into fields for easy use
037: * rather than spreading the parsing code around to a thousand different places
038: * through out the program.</p>
039: */
040: public final class Stock {
041: /**
042: * Name of the Stock
043: */
044: private static String name;
045:
046: /**
047: * Time of the last trade
048: */
049: private static String time;
050:
051: /**
052: * Price of the Stock
053: */
054: private static int price;
055:
056: /**
057: * $ change
058: */
059: private static int change;
060:
061: /**
062: * 52-week high
063: */
064: private static int high;
065:
066: /**
067: * 52-week low
068: */
069: private static int low;
070:
071: /**
072: * opening price on the day
073: */
074: private static int open;
075:
076: /**
077: * previous high
078: */
079: private static int prev;
080:
081: /**
082: * <p>Takes a <code>String</code> from the quote server or database and
083: * parses the string into each field. We first have to split it into
084: * small strings and then parse each string that should be a number.
085: *
086: * @param quoteString the <code>String</code> to parse into the fields
087: * @throws <code>NumberFormatException</code> is thrown if
088: * data that is not of the correct format is passed in -- where
089: * the number parts of the string cannot be converted to <code>
090: * Integers</code> because there are non-numeric characters
091: * in positions where numbers are expected
092: * @throws <code>StringIndexOutOfBoundsException</code> is
093: * thrown if data that is not of the correct format is passed
094: * in -- where the delimiters between the fields are not in
095: * the right spots, or the data is not of the correct length
096: */
097: public static void parse(String quoteString)
098: throws NumberFormatException,
099: StringIndexOutOfBoundsException {
100: // get our starting index
101: int index = quoteString.indexOf('"');
102:
103: if (index == -1) {
104: name = quoteString;
105:
106: return;
107: }
108:
109: // split the string up into it's fields
110: name = quoteString.substring(++index, (index = quoteString
111: .indexOf('"', index)));
112: index += 3;
113: time = quoteString.substring(index, (index = quoteString
114: .indexOf('-', index)) - 1);
115: index += 5;
116:
117: String Sprice = quoteString.substring(index,
118: (index = quoteString.indexOf('<', index)));
119: index += 6;
120:
121: String Schange = quoteString.substring(index,
122: (index = quoteString.indexOf(',', index)));
123: index += 2;
124:
125: String Slow = quoteString.substring(index, (index = quoteString
126: .indexOf(' ', index)));
127: index += 3;
128:
129: String Shigh = quoteString.substring(index,
130: (index = quoteString.indexOf('"', index)));
131: index += 2;
132:
133: String Sopen = quoteString.substring(index,
134: (index = quoteString.indexOf(',', index)));
135: ++index;
136:
137: String Sprev = quoteString.substring(index, quoteString
138: .length() - 2);
139:
140: // convert the strings that should be numbers into ints
141: price = makeInt(Sprice);
142: // remove the '+' sign if it exists
143: Schange = (Schange.indexOf('+') == -1) ? Schange : Schange
144: .substring(1, Schange.length());
145: change = makeInt(Schange);
146: prev = makeInt(Sprev);
147:
148: if ("N/A".equals(Slow)) {
149: low = prev;
150: } else {
151: low = makeInt(Slow);
152: }
153:
154: if ("N/A".equals(Shigh)) {
155: high = prev;
156: } else {
157: high = makeInt(Shigh);
158: }
159:
160: if ("N/A".equals(Sopen)) {
161: open = prev;
162: } else {
163: open = makeInt(Sopen);
164: }
165: }
166:
167: /**
168: * <p>Take a <code>String</code> representation of an int and the number of
169: * decimal places that the <code>String</code> carries and make an <code>
170: * int</code> out of it</p>
171: * <p>Since there is no floating point support in MIDP/CLDC, we have to
172: * convert the decimal numbers into <code>Integer</code>s.
173: * We do this by:</p>
174: * <li> Looking at only the first 7 significant characters which, because
175: * of the decimal, means the first 6 numbers from left to right.</li>
176: * <li> Looking at a maximum of 4 decimal places</li>
177: * <li> We remove the decimal character (if there is one) and concatenate
178: * the numbers before and after the decimal so that we can convert
179: * to an integer and manipulate the value as a number</li>
180: * <li> After doing this for each number, we have
181: * <code>int</code> values but no
182: * notion of where the decimal place was. To
183: * alleviate this, we make
184: * sure that each number has EXACTLY 4 decimal place holders. Therefore,
185: * we can divide by 10000 to put the decimal place back in the same
186: * spot.<BR>
187: *<pre>
188: * Example: 100 -> 1000000 -> /10000 = 100
189: * Example: 345.67 -> 34567 -> 3456700 -> /10000 = 345.67
190: * Example: 3.4526 -> 34526 -> /10000 = 3.4526
191: *</pre></li>
192: *
193: * @return the int value of the string
194: * @param length
195: * @param source the <code>String</code> value to convert
196: * to an <code>int</code>
197: */
198: public static int makeInt(String str) throws NumberFormatException,
199: StringIndexOutOfBoundsException {
200: // Make sure to remove any whitespace chars that might be present.
201: String source = str.trim();
202:
203: // cut the entire string down to 6 characters
204: if (source.length() > 7) {
205: source = source.substring(0, 6);
206: }
207:
208: // cut the string down to 4 decimal places
209: while ((source.length() - (source.indexOf('.') + 1)) > 4) {
210: source = source.substring(0, source.length() - 1);
211: }
212:
213: // convert to an int
214: int value = (source.indexOf('.') == -1) ? Integer.valueOf(
215: source).intValue() : Integer.valueOf(
216: new String(source.substring(0, source.indexOf('.'))
217: + source.substring(source.indexOf('.') + 1,
218: source.length()))).intValue();
219:
220: // offset to 4 decimal placeholders
221: int length = (source.indexOf('.') == -1) ? 0 : source
222: .substring(source.indexOf('.') + 1, source.length())
223: .length();
224:
225: if (length < 4) {
226: int diff = 4 - length;
227:
228: while (diff-- > 0) {
229: value *= 10;
230: }
231: }
232:
233: return value;
234: }
235:
236: /**
237: * <p>Return the name of the stock</p>
238: *
239: * @return name (ticker symbol) of the stock
240: * @param quoteString <code>String</code> to parse for the field data
241: */
242: public static String getName(String quoteString) {
243: parse(quoteString);
244:
245: return name;
246: }
247:
248: /**
249: * <p>Return the time of the last trade</p>
250: *
251: * @return time of the last trade of the stock
252: * @param quoteString <code>String</code> to parse for the field data
253: */
254: public static String getTime(String quoteString) {
255: parse(quoteString);
256:
257: return time;
258: }
259:
260: /**
261: * <p>Return the price of the last trade of the stock</p>
262: *
263: * @return price of the last trade of the stock
264: * @param quoteString <code>String</code> to parse for the field data
265: */
266: public static int getPrice(String quoteString) {
267: parse(quoteString);
268:
269: return price;
270: }
271:
272: /**
273: * <p>Return the $ change in the stock</p>
274: *
275: * @return $ change in the stock today
276: * @param quoteString <code>String</code> to parse for the field data
277: */
278: public static int getChange(String quoteString) {
279: parse(quoteString);
280:
281: return change;
282: }
283:
284: /**
285: * <p>Return the 52-week high for the stock</p>
286: *
287: * @return 52-week high of the stock
288: * @param quoteString <code>String</code> to parse for the field data
289: */
290: public static int getHigh(String quoteString) {
291: parse(quoteString);
292:
293: return high;
294: }
295:
296: /**
297: * <p>Return the 52-week low of the stock</p>
298: *
299: * @return 52-week low of the stock
300: * @param quoteString <code>String</code> to parse for the field data
301: */
302: public static int getLow(String quoteString) {
303: parse(quoteString);
304:
305: return low;
306: }
307:
308: /**
309: * <p>Return the opening price of the stock</p>
310: *
311: * @return opening price of the stock today
312: * @param quoteString <code>String</code> to parse for the field data
313: */
314: public static int getOpen(String quoteString) {
315: parse(quoteString);
316:
317: return open;
318: }
319:
320: /**
321: * <p>Return the previous high for the stock</p>
322: *
323: * @return previous high for the stock
324: * @param quoteString <code>String</code> to parse for the field data
325: */
326: public static int getPrevious(String quoteString) {
327: parse(quoteString);
328:
329: return prev;
330: }
331:
332: /**
333: * <p>Convert an <code>int</code> into a <code>String</code>
334: * with the decimal placed back in</p>
335: *
336: * @return The <code>String</code> value of the int
337: * @param intNum the <code>int</code> value to convert
338: * to a <code>String</code>
339: */
340: public static String convert(int intNum) {
341: String s = String.valueOf(intNum);
342: String pre = s.substring(0, ((s.length() < 4) ? s.length() : (s
343: .length() - 4)));
344: String suf = s.substring(((s.length() == pre.length()) ? 0 : (s
345: .length() - 4)), s.length());
346:
347: if (Integer.valueOf(suf).intValue() == 0) {
348: return pre;
349: }
350:
351: while (Integer.valueOf(
352: suf.substring(suf.length() - 1, suf.length()))
353: .intValue() == 0) {
354: suf = suf.substring(0, suf.length() - 1);
355: }
356:
357: return (pre + "." + suf);
358: }
359:
360: /**
361: * <p>String representation of price with decimal placed
362: * back in the correct spot</p>
363: *
364: * @return current stock price
365: * @param quoteString <code>String</code> to parse for the field data
366: */
367: public static String getStringPrice(String quoteString) {
368: parse(quoteString);
369:
370: return convert(price);
371: }
372:
373: /**
374: * <p>String representation of change with decimal placed
375: * back in the correct spot</p>
376: *
377: * @return change in stock price today
378: * @param quoteString <code>String</code> to parse for the field data
379: */
380: public static String getStringChange(String quoteString) {
381: parse(quoteString);
382:
383: return convert(change);
384: }
385:
386: /**
387: * <p>String representation of the 52-week high with decimal
388: * placed back in the correct spot</p>
389: *
390: * @return 52-week high
391: * @param quoteString <code>String</code> to parse for the field data
392: */
393: public static String getStringHigh(String quoteString) {
394: parse(quoteString);
395:
396: return convert(high);
397: }
398:
399: /**
400: * <p>String representation of the 52-week low with decimal
401: * placed back in the correct spot</p>
402: *
403: * @return 52-week low
404: * @param quoteString <code>String</code> to parse for the field data
405: */
406: public static String getStringLow(String quoteString) {
407: parse(quoteString);
408:
409: return convert(low);
410: }
411:
412: /**
413: * <p>String representation of the opening price with
414: * decimal placed back in the correct spot</p>
415: *
416: * @return opening stock price
417: * @param quoteString <code>String</code> to parse for the field data
418: */
419: public static String getStringOpen(String quoteString) {
420: parse(quoteString);
421:
422: return convert(open);
423: }
424:
425: /**
426: * <p>String representation of previous with decimal
427: * placed back in the correct spot</p>
428: *
429: * @return previous high for the stock
430: * @param quoteString <code>String</code> to parse for the field data
431: */
432: public static String getStringPrevious(String quoteString) {
433: parse(quoteString);
434:
435: return convert(prev);
436: }
437: }
|