Source Code Cross Referenced for VCalendar10Format.java in  » 6.0-JDK-Modules » j2me » com » sun » kvem » midp » pim » formats » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » 6.0 JDK Modules » j2me » com.sun.kvem.midp.pim.formats 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         *   
0003:         *
0004:         * Copyright  1990-2007 Sun Microsystems, Inc. All Rights Reserved.
0005:         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
0006:         * 
0007:         * This program is free software; you can redistribute it and/or
0008:         * modify it under the terms of the GNU General Public License version
0009:         * 2 only, as published by the Free Software Foundation.
0010:         * 
0011:         * This program is distributed in the hope that it will be useful, but
0012:         * WITHOUT ANY WARRANTY; without even the implied warranty of
0013:         * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014:         * General Public License version 2 for more details (a copy is
0015:         * included at /legal/license.txt).
0016:         * 
0017:         * You should have received a copy of the GNU General Public License
0018:         * version 2 along with this work; if not, write to the Free Software
0019:         * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0020:         * 02110-1301 USA
0021:         * 
0022:         * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
0023:         * Clara, CA 95054 or visit www.sun.com if you need additional
0024:         * information or have any questions.
0025:         */
0026:
0027:        package com.sun.kvem.midp.pim.formats;
0028:
0029:        import com.sun.kvem.midp.pim.AbstractPIMItem;
0030:        import com.sun.kvem.midp.pim.AbstractPIMList;
0031:        import com.sun.kvem.midp.pim.EventImpl;
0032:        import com.sun.kvem.midp.pim.LineReader;
0033:        import com.sun.kvem.midp.pim.PIMFormat;
0034:        import com.sun.kvem.midp.pim.PIMHandler;
0035:        import com.sun.kvem.midp.pim.ToDoImpl;
0036:        import com.sun.kvem.midp.pim.UnsupportedPIMFormatException;
0037:        import java.io.IOException;
0038:        import java.io.InputStream;
0039:        import java.io.OutputStream;
0040:        import java.io.OutputStreamWriter;
0041:        import java.io.Writer;
0042:        import java.util.Date;
0043:        import java.util.Enumeration;
0044:        import java.util.Vector;
0045:        import javax.microedition.pim.*;
0046:
0047:        /**
0048:         * Implementation of PIMEncoding for VCalendar/1.0.
0049:         *
0050:         */
0051:        public class VCalendar10Format extends EndMatcher implements  PIMFormat {
0052:
0053:            /** List of the weekday masks from RepeatRule */
0054:            private static final int[] DAYS_OF_WEEK = { RepeatRule.SUNDAY,
0055:                    RepeatRule.MONDAY, RepeatRule.TUESDAY,
0056:                    RepeatRule.WEDNESDAY, RepeatRule.THURSDAY,
0057:                    RepeatRule.FRIDAY, RepeatRule.SATURDAY };
0058:
0059:            /**
0060:             * List of vCalendar weekday codes. This sequence is parallel to
0061:             * DAYS_OF_WEEK.
0062:             */
0063:            private static final String[] DAYS_OF_WEEK_CODES = { "SU", "MO",
0064:                    "TU", "WE", "TH", "FR", "SA" };
0065:
0066:            /** List of the week in month masks from RepeatRule */
0067:            private static final int[] WEEKS_OF_MONTH = { RepeatRule.FIRST,
0068:                    RepeatRule.SECOND, RepeatRule.THIRD, RepeatRule.FOURTH,
0069:                    RepeatRule.FIFTH, RepeatRule.LAST, RepeatRule.SECONDLAST,
0070:                    RepeatRule.THIRDLAST, RepeatRule.FOURTHLAST,
0071:                    RepeatRule.FIFTHLAST };
0072:
0073:            /**
0074:             * List of vCalendar week of month codes. This sequence is
0075:             * parallel to WEEKS_OF_MONTH.
0076:             */
0077:            private static final String[] WEEKS_OF_MONTH_CODES = { "1+", "2+",
0078:                    "3+", "4+", "5+", "1-", "2-", "3-", "4-", "5-", };
0079:
0080:            /** List of month in year masks from RepeatRule */
0081:            private static final int[] MONTHS_IN_YEAR = { 0,
0082:                    RepeatRule.JANUARY, RepeatRule.FEBRUARY, RepeatRule.MARCH,
0083:                    RepeatRule.APRIL, RepeatRule.MAY, RepeatRule.JUNE,
0084:                    RepeatRule.JULY, RepeatRule.AUGUST, RepeatRule.SEPTEMBER,
0085:                    RepeatRule.OCTOBER, RepeatRule.NOVEMBER,
0086:                    RepeatRule.DECEMBER };
0087:
0088:            /**
0089:             * VCalendar 1.0 formatter.
0090:             */
0091:            public VCalendar10Format() {
0092:                super ("VCALENDAR");
0093:            }
0094:
0095:            /**
0096:             * Gets the code name of this encoding (e.g. "VCARD/2.1").
0097:             * @return the encoding name
0098:             */
0099:            public String getName() {
0100:                return "VCALENDAR/1.0";
0101:            }
0102:
0103:            /**
0104:             * Checks to see if a given PIM list type is supported by this encoding.
0105:             * @param pimListType int representing the PIM list type to check
0106:             * @return true if the type can be read and written by this encoding,
0107:             * false otherwise
0108:             */
0109:            public boolean isTypeSupported(int pimListType) {
0110:                return pimListType == PIM.TODO_LIST
0111:                        || pimListType == PIM.EVENT_LIST;
0112:            }
0113:
0114:            /**
0115:             * Serializes a PIMItem.
0116:             * @param out Stream to which serialized data is written
0117:             * @param encoding Character encoding to use for serialized data
0118:             * @param pimItem The item to write to the stream
0119:             * @throws IOException if a write error occurs
0120:             */
0121:            public void encode(OutputStream out, String encoding,
0122:                    PIMItem pimItem) throws IOException {
0123:
0124:                Writer w = new OutputStreamWriter(out, encoding);
0125:                w.write("BEGIN:VCALENDAR\r\n");
0126:                w.write("VERSION:1.0\r\n");
0127:                if (pimItem instanceof  Event) {
0128:                    encode(w, (Event) pimItem);
0129:                } else if (pimItem instanceof  ToDo) {
0130:                    encode(w, (ToDo) pimItem);
0131:                }
0132:                w.write("END:VCALENDAR\r\n");
0133:                w.flush();
0134:            }
0135:
0136:            /**
0137:             * Serializes a vEvent.
0138:             * @param w output stream 
0139:             * @param event the event to be encoded
0140:             * @throws IOException if an error occurs while encoding 
0141:             */
0142:            private void encode(Writer w, Event event) throws IOException {
0143:                w.write("BEGIN:VEVENT\r\n");
0144:                // write known fields
0145:                int[] fields = event.getFields();
0146:                for (int i = 0; i < fields.length; i++) {
0147:                    int valueCount = event.countValues(fields[i]);
0148:                    for (int j = 0; j < valueCount; j++) {
0149:                        writeValue(w, event, fields[i], j);
0150:                    }
0151:                }
0152:                // write categories
0153:                String categories = FormatSupport.join(event.getCategories(),
0154:                        ",");
0155:                if (categories.length() > 0) {
0156:                    w.write("CATEGORIES:");
0157:                    w.write(categories);
0158:                    w.write("\r\n");
0159:                }
0160:                // write repeat rule
0161:                RepeatRule rule = event.getRepeat();
0162:                if (rule != null) {
0163:                    String s = encodeRepeatRule(rule, 0);
0164:                    if (s != null) {
0165:                        w.write("RRULE:");
0166:                        w.write(s);
0167:                        w.write("\r\n");
0168:                    }
0169:                    Enumeration exDates = rule.getExceptDates();
0170:                    if (exDates.hasMoreElements()) {
0171:                        w.write("EXDATE;VALUE=DATE:");
0172:                        while (exDates.hasMoreElements()) {
0173:                            long time = ((Date) exDates.nextElement())
0174:                                    .getTime();
0175:                            w
0176:                                    .write(PIMHandler.getInstance()
0177:                                            .composeDate1(time));
0178:                            if (exDates.hasMoreElements()) {
0179:                                w.write(",");
0180:                            }
0181:                        }
0182:                        w.write("\r\n");
0183:                    }
0184:                }
0185:                w.write("END:VEVENT\r\n");
0186:            }
0187:
0188:            /**
0189:             * Serializes a vToDo.
0190:             * @param w output stream target
0191:             * @param todo item to be encoded
0192:             * @throws IOException if an error occurs while encoding
0193:             */
0194:            private void encode(Writer w, ToDo todo) throws IOException {
0195:                w.write("BEGIN:VTODO\r\n");
0196:                // write known fields
0197:                int[] fields = todo.getFields();
0198:                for (int i = 0; i < fields.length; i++) {
0199:                    int valueCount = todo.countValues(fields[i]);
0200:                    for (int j = 0; j < valueCount; j++) {
0201:                        writeValue(w, todo, fields[i], j);
0202:                    }
0203:                }
0204:                // write categories
0205:                String categories = FormatSupport.join(todo.getCategories(),
0206:                        ",");
0207:                if (categories.length() > 0) {
0208:                    w.write("CATEGORIES:");
0209:                    w.write(categories);
0210:                    w.write("\r\n");
0211:                }
0212:                w.write("END:VTODO\r\n");
0213:            }
0214:
0215:            /**
0216:             * Serializes one line of a vEvent.
0217:             * @param w output stream target
0218:             * @param event element to be processed
0219:             * @param field component of element to output
0220:             * @param index offset in field to process
0221:             * @throws IOException if an error occurs while writing
0222:             */
0223:            private void writeValue(Writer w, Event event, int field, int index)
0224:                    throws IOException {
0225:
0226:                switch (field) {
0227:                case Event.CLASS: {
0228:                    int iValue = event.getInt(field, index);
0229:                    String sValue = VEventSupport.getClassType(iValue);
0230:                    if (sValue != null) {
0231:                        w.write("CLASS:");
0232:                        w.write(sValue);
0233:                        w.write("\r\n");
0234:                    }
0235:                    break;
0236:                }
0237:                case Event.ALARM: {
0238:                    int iValue = event.getInt(field, index);
0239:                    // subtract Event.ALARM from Event.START
0240:                    try {
0241:                        long startTime = event.getDate(Event.START, 0);
0242:                        w.write("DALARM:");
0243:                        w.write(PIMHandler.getInstance().composeDateTime(
0244:                                startTime - iValue * 1000));
0245:                        w.write("\r\n");
0246:                    } catch (IOException e) {
0247:                        // don't write a DALARM field
0248:                    }
0249:                    break;
0250:                }
0251:                case Event.LOCATION:
0252:                case Event.NOTE:
0253:                case Event.SUMMARY:
0254:                case Event.UID: {
0255:                    String sValue = event.getString(field, index);
0256:                    if (sValue != null) {
0257:                        String property = VEventSupport.getFieldLabel(field);
0258:                        w.write(property);
0259:                        w.write(":");
0260:                        w.write(sValue);
0261:                        w.write("\r\n");
0262:                    }
0263:                    break;
0264:                }
0265:                case Event.END:
0266:                case Event.REVISION:
0267:                case Event.START: {
0268:                    long date = event.getDate(field, index);
0269:                    w.write(VEventSupport.getFieldLabel(field));
0270:                    w.write(":");
0271:                    w.write(PIMHandler.getInstance().composeDateTime(date));
0272:                    w.write("\r\n");
0273:                    break;
0274:                }
0275:                }
0276:
0277:            }
0278:
0279:            /** 
0280:             * Serializes one line of a vToDo.
0281:             * @param w output stream target
0282:             * @param todo element to be processed
0283:             * @param field component of element to output
0284:             * @param index offset in field to process
0285:             * @throws IOException if an error occurs while writing
0286:             *
0287:             */
0288:            private void writeValue(Writer w, ToDo todo, int field, int index)
0289:                    throws IOException {
0290:
0291:                switch (field) {
0292:                case ToDo.CLASS: {
0293:                    int iValue = todo.getInt(field, index);
0294:                    String sValue = VToDoSupport.getClassType(iValue);
0295:                    if (sValue != null) {
0296:                        w.write("CLASS:");
0297:                        w.write(sValue);
0298:                        w.write("\r\n");
0299:                    }
0300:                    break;
0301:                }
0302:                case ToDo.NOTE:
0303:                case ToDo.SUMMARY:
0304:                case ToDo.UID: {
0305:                    String sValue = todo.getString(field, index);
0306:                    if (sValue != null) {
0307:                        String property = VToDoSupport.getFieldLabel(field);
0308:                        w.write(property);
0309:                        w.write(":");
0310:                        w.write(sValue);
0311:                        w.write("\r\n");
0312:                    }
0313:                    break;
0314:                }
0315:                case ToDo.DUE:
0316:                case ToDo.COMPLETION_DATE:
0317:                case ToDo.REVISION: {
0318:                    long date = todo.getDate(field, index);
0319:                    w.write(VToDoSupport.getFieldLabel(field));
0320:                    w.write(":");
0321:                    w.write(PIMHandler.getInstance().composeDateTime(date));
0322:                    w.write("\r\n");
0323:                    break;
0324:                }
0325:                case ToDo.COMPLETED: {
0326:                    w.write("STATUS:COMPLETED\r\n");
0327:                    break;
0328:                }
0329:                case ToDo.PRIORITY: {
0330:                    w.write(VToDoSupport.getFieldLabel(field));
0331:                    w.write(":");
0332:                    w.write(String.valueOf(todo.getInt(field, index)));
0333:                    w.write("\r\n");
0334:                    break;
0335:                }
0336:                }
0337:
0338:            }
0339:
0340:            /**
0341:             * Returns a VCalendar representation of a repeating rule, or
0342:             * null if the rule cannot be encoded.
0343:             *
0344:             * For more details please see The Electronic Calendaring and Scheduling
0345:             * Exchange Format Version 1.0
0346:             *
0347:             * @param rule data to be encoded
0348:             * @param startFreq start frequency value (0 on start)
0349:             * @return encoded rule
0350:             */
0351:            private String encodeRepeatRule(RepeatRule rule, int startFreq) {
0352:                StringBuffer sb = new StringBuffer();
0353:                int[] fields = rule.getFields();
0354:                FormatSupport.sort(fields);
0355:                if (!FormatSupport.contains(fields, RepeatRule.FREQUENCY)) {
0356:                    return null;
0357:                }
0358:                int frequency;
0359:                if (startFreq != 0) {
0360:                    frequency = startFreq;
0361:                } else {
0362:                    frequency = rule.getInt(RepeatRule.FREQUENCY);
0363:                }
0364:                int interval = 1; // default value according to JSR75 spec
0365:                if (FormatSupport.contains(fields, RepeatRule.INTERVAL)
0366:                        && (startFreq == 0)) {
0367:                    interval = rule.getInt(RepeatRule.INTERVAL);
0368:                }
0369:                String encodedCount = " #0"; // forever
0370:                if (FormatSupport.contains(fields, RepeatRule.COUNT)
0371:                        && (startFreq == 0)) {
0372:                    encodedCount = " #" + rule.getInt(RepeatRule.COUNT);
0373:                }
0374:                // enddate - ISO 8601 clause 5.4.1
0375:                String encodedEndDate = "";
0376:                if (FormatSupport.contains(fields, RepeatRule.END)) {
0377:                    encodedEndDate = " "
0378:                            + PIMHandler.getInstance().composeDateTime(
0379:                                    rule.getDate(RepeatRule.END));
0380:                }
0381:                switch (frequency) {
0382:                case RepeatRule.DAILY: {
0383:                    // D<interval> [<duration>]
0384:                    sb.append(FormatSupport.DAILY);
0385:                    sb.append(interval);
0386:                    sb.append(encodedCount);
0387:                    break;
0388:                }
0389:                case RepeatRule.WEEKLY: {
0390:                    // W<interval> <weekday> [<duration>]
0391:                    sb.append(FormatSupport.WEEKLY);
0392:                    sb.append(interval);
0393:                    if (FormatSupport.contains(fields, RepeatRule.DAY_IN_WEEK)) {
0394:                        sb.append(encodeRepeatRuleDaysInWeek(rule));
0395:                    }
0396:                    sb.append(encodedCount);
0397:                    break;
0398:                }
0399:                case RepeatRule.MONTHLY: {
0400:                    sb.append(FormatSupport.MONTHLY);
0401:                    if (FormatSupport.contains(fields, RepeatRule.DAY_IN_MONTH)) {
0402:                        // MD<interval> <daynumber> [<duration>]
0403:                        sb.append(FormatSupport.DAY_IN_MONTH);
0404:                        sb.append(interval);
0405:                        sb.append(" ");
0406:                        sb.append(rule.getInt(RepeatRule.DAY_IN_MONTH));
0407:                        sb.append(encodedCount);
0408:                    } else if (FormatSupport.contains(fields,
0409:                            RepeatRule.WEEK_IN_MONTH)) {
0410:                        // MP<interval> {<1>|<2>}{<+>|<->} [<duration>] [weekly|daily]
0411:                        sb.append(FormatSupport.WEEK_IN_MONTH);
0412:                        sb.append(interval);
0413:                        sb.append(encodeRepeatRuleWeeksInMonth(fields, rule));
0414:                        sb.append(encodedCount);
0415:                        if (FormatSupport.contains(fields,
0416:                                RepeatRule.DAY_IN_WEEK)) {
0417:                            sb
0418:                                    .append(" "
0419:                                            + encodeRepeatRule(rule,
0420:                                                    RepeatRule.WEEKLY));
0421:                        }
0422:                    }
0423:                    break;
0424:                }
0425:                case RepeatRule.YEARLY: {
0426:                    sb.append(FormatSupport.YEARLY);
0427:                    if (FormatSupport.contains(fields, RepeatRule.DAY_IN_YEAR)) {
0428:                        sb.append(FormatSupport.DAY_IN_YEAR);
0429:                        sb.append(interval);
0430:                        sb.append(" ");
0431:                        sb.append(rule.getInt(RepeatRule.DAY_IN_YEAR));
0432:                        sb.append(encodedCount);
0433:                    } else if (FormatSupport.contains(fields,
0434:                            RepeatRule.MONTH_IN_YEAR)) {
0435:                        sb.append(FormatSupport.MONTH_IN_YEAR);
0436:                        sb.append(interval);
0437:                        sb.append(encodeRepeatRuleMonthsInYear(fields, rule));
0438:                        sb.append(encodedCount);
0439:                        if (FormatSupport.contains(fields,
0440:                                RepeatRule.DAY_IN_MONTH)
0441:                                || FormatSupport.contains(fields,
0442:                                        RepeatRule.WEEK_IN_MONTH)) {
0443:                            sb
0444:                                    .append(" "
0445:                                            + encodeRepeatRule(rule,
0446:                                                    RepeatRule.MONTHLY));
0447:                        }
0448:                    }
0449:                    break;
0450:                }
0451:                default:
0452:                    return null;
0453:                }
0454:                if (startFreq == 0) {
0455:                    sb.append(encodedEndDate);
0456:                }
0457:                return sb.toString();
0458:            }
0459:
0460:            /** 
0461:             * Returns a string representation of a weekly rule.
0462:             * @param rule data to be encoded
0463:             * @return encoded rule
0464:             */
0465:            private String encodeRepeatRuleDaysInWeek(RepeatRule rule) {
0466:                StringBuffer sb = new StringBuffer();
0467:                int daysInWeek = rule.getInt(RepeatRule.DAY_IN_WEEK);
0468:                for (int i = 0; i < DAYS_OF_WEEK.length; i++) {
0469:                    if ((daysInWeek & DAYS_OF_WEEK[i]) != 0) {
0470:                        sb.append(" ");
0471:                        sb.append(DAYS_OF_WEEK_CODES[i]);
0472:                    }
0473:                }
0474:                return sb.toString();
0475:            }
0476:
0477:            /**
0478:             * Returns a string representation of a monthly rule with a weekly
0479:             * parameter.
0480:             * @param fields data to be processed
0481:             * @param rule to encode
0482:             * @return encoded rule
0483:             */
0484:            private String encodeRepeatRuleWeeksInMonth(int[] fields,
0485:                    RepeatRule rule) {
0486:                StringBuffer sb = new StringBuffer();
0487:                int weeksInMonth = rule.getInt(RepeatRule.WEEK_IN_MONTH);
0488:                for (int i = 0; i < WEEKS_OF_MONTH.length; i++) {
0489:                    if ((weeksInMonth & WEEKS_OF_MONTH[i]) != 0) {
0490:                        sb.append(" ");
0491:                        sb.append(WEEKS_OF_MONTH_CODES[i]);
0492:                    }
0493:                }
0494:                return sb.toString();
0495:            }
0496:
0497:            /**
0498:             * Returns a string representation of a yearly rule with a monthly
0499:             * parameter.
0500:             * @param fields data to be processed
0501:             * @param rule to encode
0502:             * @return encoded rule
0503:             */
0504:            private String encodeRepeatRuleMonthsInYear(int[] fields,
0505:                    RepeatRule rule) {
0506:                StringBuffer sb = new StringBuffer();
0507:                int monthsInYear = rule.getInt(RepeatRule.MONTH_IN_YEAR);
0508:                for (int i = 0; i < MONTHS_IN_YEAR.length; i++) {
0509:                    if ((monthsInYear & MONTHS_IN_YEAR[i]) != 0) {
0510:                        sb.append(" ");
0511:                        sb.append(i);
0512:                    }
0513:                }
0514:                return sb.toString();
0515:            }
0516:
0517:            /**
0518:             * Constructs one or more PIMItems from serialized data.
0519:             * @param in Stream containing serialized data
0520:             * @param encoding Character encoding of the stream
0521:             * @param list PIMList to which items should be added, or null if the items
0522:             * should not be part of a list
0523:             * @throws UnsupportedPIMFormatException if the serialized
0524:             * data cannot be interpreted by this encoding.
0525:             * @return a non-empty array of PIMItems containing the objects described
0526:             * in the serialized data, or null if no items are available
0527:             * @throws IOException if a read error occurs
0528:             */
0529:            public PIMItem[] decode(InputStream in, String encoding,
0530:                    PIMList list) throws IOException {
0531:
0532:                LineReader r = new LineReader(in, encoding, this );
0533:                String line = r.readLine();
0534:                if (line == null) {
0535:                    return null;
0536:                }
0537:                if (!line.toUpperCase().equals("BEGIN:VCALENDAR")) {
0538:                    throw new UnsupportedPIMFormatException(
0539:                            "Not a vCalendar object");
0540:                }
0541:                Vector items = new Vector();
0542:                for (AbstractPIMItem item; (item = decode(r, list)) != null;) {
0543:                    items.addElement(item);
0544:                }
0545:                if (items.size() == 0) {
0546:                    return null;
0547:                }
0548:                AbstractPIMItem[] a = new AbstractPIMItem[items.size()];
0549:                items.copyInto(a);
0550:                return a;
0551:            }
0552:
0553:            /**
0554:             * Constructs a single PIMItem from serialized data.
0555:             * @param in LineReader containing serialized data
0556:             * @param list PIM list to which the item belongs
0557:             * @throws UnsupportedPIMFormatException if the serialized data cannot be
0558:             * interpreted by this encoding.
0559:             * @return an unserialized Event, or null if no data was available
0560:             */
0561:            private AbstractPIMItem decode(LineReader in, PIMList list)
0562:                    throws IOException {
0563:
0564:                while (true) {
0565:                    String line = in.readLine();
0566:                    if (line == null) {
0567:                        return null;
0568:                    }
0569:                    FormatSupport.DataElement element = FormatSupport
0570:                            .parseObjectLine(line);
0571:                    if (element.propertyName.equals("BEGIN")) {
0572:                        if (element.data.toUpperCase().equals("VEVENT")) {
0573:                            return decodeEvent(in, list);
0574:                        } else if (element.data.toUpperCase().equals("VTODO")) {
0575:                            return decodeToDo(in, list);
0576:                        } else {
0577:                            throw new UnsupportedPIMFormatException(
0578:                                    "Bad argument to BEGIN: " + element.data);
0579:                        }
0580:                    } else if (element.propertyName.equals("END")) {
0581:                        if (element.data.toUpperCase().equals("VCALENDAR")) {
0582:                            return null;
0583:                        } else {
0584:                            throw new UnsupportedPIMFormatException(
0585:                                    "Bad argument to END: " + element.data);
0586:                        }
0587:                    } else if (element.propertyName.equals("PRODID")) {
0588:                        // ignore product ID
0589:                    } else if (element.propertyName.equals("VERSION")) {
0590:                        // check version, then keep reading
0591:                        if (!element.data.equals("1.0")) {
0592:                            throw new UnsupportedPIMFormatException(
0593:                                    "vCalendar version '" + element.data
0594:                                            + "' is not supported");
0595:                        }
0596:                    } else if (element.propertyName.equals("CATEGORIES")) {
0597:                        // what should I do with this? this seems to be the wrong place
0598:                        // to put the field.
0599:                    } else {
0600:                        throw new UnsupportedPIMFormatException(
0601:                                "Unrecognized item: " + line);
0602:                    }
0603:                }
0604:            }
0605:
0606:            /** 
0607:             * Reads and decodes a single vEvent.
0608:             * @param in encoded event reader stream
0609:             * @param list PIM list to which the item belongs
0610:             * @return event reader implementation handle
0611:             * @throws IOException if a reading error occurs
0612:             */
0613:            private EventImpl decodeEvent(LineReader in, PIMList list)
0614:                    throws IOException {
0615:                EventImpl event = new EventImpl((AbstractPIMList) list);
0616:                String line;
0617:                while ((line = in.readLine()) != null) {
0618:                    FormatSupport.DataElement element = FormatSupport
0619:                            .parseObjectLine(line);
0620:                    if (element.propertyName.equals("END")) {
0621:                        // patch DALARM values
0622:                        int alarmValues = event.countValues(Event.ALARM);
0623:                        if (alarmValues > 0
0624:                                && event.countValues(Event.START) > 0) {
0625:                            int startTime = (int) (event
0626:                                    .getDate(Event.START, 0) / 1000);
0627:                            for (int i = 0, j = 0; i < alarmValues; i++, j++) {
0628:                                int alarmTime = event.getInt(Event.ALARM, i);
0629:                                if (alarmTime * 1000 < startTime) {
0630:                                    event.setInt(Event.ALARM, i,
0631:                                            Event.ATTR_NONE, startTime
0632:                                                    - alarmTime);
0633:                                } else {
0634:                                    event.removeValue(Event.ALARM, i);
0635:                                    alarmValues--;
0636:                                    i--;
0637:                                }
0638:                            }
0639:                        }
0640:                        return event;
0641:                    } else if (element.propertyName.equals("VERSION")) {
0642:                        if (!element.data.equals("1.0")) {
0643:                            throw new UnsupportedPIMFormatException("Version "
0644:                                    + element.data + " is not supported");
0645:                        }
0646:                    } else if (element.propertyName.equals("CATEGORIES")) {
0647:                        String[] categories = FormatSupport.split(element.data,
0648:                                ',', 0);
0649:                        for (int j = 0; j < categories.length; j++) {
0650:                            try {
0651:                                event.addToCategory(categories[j]);
0652:                            } catch (PIMException e) {
0653:                                // cannot add item
0654:                            }
0655:                        }
0656:                    } else if (element.propertyName.equals("RRULE")) {
0657:                        RepeatRule rule = new RepeatRule();
0658:                        if (!decodeRepeatRule(rule, element.data, true)) {
0659:                            throw new IOException(
0660:                                    "Empty or invalid RepeatRule data");
0661:                        }
0662:                        event.setRepeat(rule);
0663:                    } else if (element.propertyName.equals("EXDATE")) {
0664:                        RepeatRule rule = event.getRepeat();
0665:                        if (rule != null) {
0666:                            decodeExDates(rule, element.data);
0667:                            event.setRepeat(rule);
0668:                        }
0669:                    } else {
0670:                        importData(event, element.propertyName,
0671:                                element.attributes, element.data);
0672:                    }
0673:                }
0674:                throw new IOException("Unterminated vEvent");
0675:            }
0676:
0677:            /**
0678:             * Decodes except dates.
0679:             *
0680:             * For more details please see The Electronic Calendaring and Scheduling
0681:             * Exchange Format Version 1.0
0682:             *
0683:             * @param rule repeat rule instance
0684:             * @param data string contains encoded dates
0685:             * separated by ","
0686:             *
0687:             */
0688:            private void decodeExDates(RepeatRule rule, String data)
0689:                    throws IOException {
0690:                Parser parser = new Parser(data);
0691:                long date;
0692:                while (parser.hasNextDate()) {
0693:                    date = PIMHandler.getInstance().parseDate(
0694:                            parser.getEndDate());
0695:                    parser.setPos(parser.getPos()
0696:                            + parser.getEndDate().length());
0697:                    rule.addExceptDate(date);
0698:                    if (!parser.hasMoreChars()) {
0699:                        break;
0700:                    }
0701:                    parser.matchSkip(','); // separator
0702:                }
0703:            }
0704:
0705:            /**
0706:             * Decodes repeat rule.
0707:             *
0708:             * For more details please see The Electronic Calendaring and Scheduling
0709:             * Exchange Format Version 1.0
0710:             *
0711:             * @param rule repeat rule instance
0712:             * @param data string contains encoded repeat rule
0713:             * @param isTop true on top recursive level else false
0714:             * @return true if repeat rule was successfully decoded, false otherwise
0715:             */
0716:            private boolean decodeRepeatRule(RepeatRule rule, String data,
0717:                    boolean isTop) {
0718:                boolean res = true;
0719:                Parser parser = new Parser(data);
0720:
0721:                char sym;
0722:                try {
0723:                    parser.skipBlank();
0724:                    sym = parser.readChar();
0725:                    int interval;
0726:                    switch (sym) {
0727:                    case FormatSupport.DAILY:
0728:                        // D<interval> [<duration>]
0729:                        interval = parser.readInt();
0730:                        if (isTop) {
0731:                            rule.setInt(RepeatRule.FREQUENCY, RepeatRule.DAILY);
0732:                            rule.setInt(RepeatRule.INTERVAL, interval);
0733:                        }
0734:                        setRepeatRuleCount(parser, rule, isTop);
0735:                        break;
0736:                    case FormatSupport.WEEKLY:
0737:                        // W<interval> <weekday> [<duration>]
0738:                        interval = parser.readInt();
0739:                        if (isTop) {
0740:                            rule
0741:                                    .setInt(RepeatRule.FREQUENCY,
0742:                                            RepeatRule.WEEKLY);
0743:                            rule.setInt(RepeatRule.INTERVAL, interval);
0744:                        }
0745:                        rule.setInt(RepeatRule.DAY_IN_WEEK,
0746:                                decodeDaysInWeek(parser));
0747:                        setRepeatRuleCount(parser, rule, isTop);
0748:                        break;
0749:                    case FormatSupport.MONTHLY:
0750:                        if (isTop) {
0751:                            rule.setInt(RepeatRule.FREQUENCY,
0752:                                    RepeatRule.MONTHLY);
0753:                        }
0754:                        sym = parser.readChar();
0755:                        switch (sym) {
0756:                        case FormatSupport.DAY_IN_MONTH:
0757:                            // MD<interval> <daynumber> [<duration>]
0758:                            interval = parser.readInt();
0759:                            if (isTop) {
0760:                                rule.setInt(RepeatRule.INTERVAL, interval);
0761:                            }
0762:                            parser.skipBlank();
0763:                            rule.setInt(RepeatRule.DAY_IN_MONTH, parser
0764:                                    .readInt());
0765:                            setRepeatRuleCount(parser, rule, isTop);
0766:                            break;
0767:                        case FormatSupport.WEEK_IN_MONTH:
0768:                            interval = parser.readInt();
0769:                            if (isTop) {
0770:                                rule.setInt(RepeatRule.INTERVAL, interval);
0771:                            }
0772:                            parser.skipBlank();
0773:                            rule.setInt(RepeatRule.WEEK_IN_MONTH,
0774:                                    decodeWeeksInMonth(parser, rule));
0775:                            setRepeatRuleCount(parser, rule, isTop);
0776:                            parser.skipBlank();
0777:                            if (parser.hasMoreChars()) { // parse next rule
0778:                                res = decodeRepeatRule(rule, parser
0779:                                        .getRemainder(), false);
0780:                            }
0781:                            break;
0782:                        default:
0783:                            res = false;
0784:                        }
0785:                        break;
0786:                    case FormatSupport.YEARLY:
0787:                        if (isTop) {
0788:                            rule
0789:                                    .setInt(RepeatRule.FREQUENCY,
0790:                                            RepeatRule.YEARLY);
0791:                        }
0792:                        sym = parser.readChar();
0793:                        switch (sym) {
0794:                        case FormatSupport.DAY_IN_YEAR:
0795:                            interval = parser.readInt();
0796:                            if (isTop) {
0797:                                rule.setInt(RepeatRule.INTERVAL, interval);
0798:                            }
0799:                            parser.skipBlank();
0800:                            rule.setInt(RepeatRule.DAY_IN_YEAR, parser
0801:                                    .readInt());
0802:                            setRepeatRuleCount(parser, rule, isTop);
0803:                            break;
0804:                        case FormatSupport.MONTH_IN_YEAR:
0805:                            interval = parser.readInt();
0806:                            if (isTop) {
0807:                                rule.setInt(RepeatRule.INTERVAL, interval);
0808:                            }
0809:                            rule.setInt(RepeatRule.MONTH_IN_YEAR,
0810:                                    decodeMonthsInYear(parser, rule));
0811:                            setRepeatRuleCount(parser, rule, isTop);
0812:                            parser.skipBlank();
0813:                            if (parser.hasMoreChars()) { // parse next rule
0814:                                res = decodeRepeatRule(rule, parser
0815:                                        .getRemainder(), false);
0816:                            }
0817:                            break;
0818:                        default:
0819:                            res = false;
0820:                        }
0821:                    }
0822:                } catch (IOException ex) {
0823:                    res = false;
0824:                } catch (NumberFormatException ex) {
0825:                    res = false;
0826:                } catch (IllegalArgumentException ex) {
0827:                    res = false;
0828:                } catch (FieldEmptyException ex) {
0829:                    res = false;
0830:                }
0831:
0832:                return res;
0833:            }
0834:
0835:            /**
0836:             * Decodes days in a week.
0837:             *
0838:             * @param parser input data parser
0839:             * @return day-in-a-week value
0840:             * @throws IOException if a reading error occurs or wrong format
0841:             */
0842:            private int decodeDaysInWeek(Parser parser) throws IOException {
0843:                int daysInWeek = 0;
0844:                while (true) { // decode days
0845:                    parser.skipBlank();
0846:                    if (!parser.isNextMatchStr(DAYS_OF_WEEK_CODES)) {
0847:                        break;
0848:                    }
0849:                    String dayStr = parser.readId();
0850:                    int i;
0851:                    for (i = 0; i < DAYS_OF_WEEK_CODES.length; i++) {
0852:                        if (DAYS_OF_WEEK_CODES[i].equals(dayStr)) {
0853:                            daysInWeek |= DAYS_OF_WEEK[i];
0854:                            break;
0855:                        }
0856:                    }
0857:                    if (i == DAYS_OF_WEEK_CODES.length) {
0858:                        throw new IOException("Wrong format"); // not found
0859:                    }
0860:                }
0861:                return daysInWeek;
0862:            }
0863:
0864:            /**
0865:             * Decodes weeks in a month.
0866:             *
0867:             * @param parser input data parser
0868:             * @param rule RepeatRule for setting fields
0869:             * @return week-in-a-month value
0870:             * @throws IOException if a reading error occurs or wrong format
0871:             */
0872:            private int decodeWeeksInMonth(Parser parser, RepeatRule rule)
0873:                    throws IOException {
0874:                int weeksInMonth = 0;
0875:                while (true) { // decode weeks
0876:                    parser.skipBlank();
0877:                    if (!parser.isNextMatchStr(WEEKS_OF_MONTH_CODES)) {
0878:                        break;
0879:                    }
0880:                    String weekStr = parser.readId();
0881:                    int i;
0882:                    for (i = 0; i < WEEKS_OF_MONTH_CODES.length; i++) {
0883:                        if (WEEKS_OF_MONTH_CODES[i].equals(weekStr)) {
0884:                            weeksInMonth |= WEEKS_OF_MONTH[i];
0885:                            break;
0886:                        }
0887:                    }
0888:                    if (i == WEEKS_OF_MONTH_CODES.length) {
0889:                        throw new IOException("Wrong format"); // not found
0890:                    }
0891:                }
0892:                return weeksInMonth;
0893:            }
0894:
0895:            /**
0896:             * Decodes months in a year.
0897:             *
0898:             * @param parser input data parser
0899:             * @param rule RepeatRule for setting fields
0900:             * @return month-in-a-year value
0901:             * @throws IOException if a reading error occurs or wrong format
0902:             */
0903:            private int decodeMonthsInYear(Parser parser, RepeatRule rule)
0904:                    throws IOException {
0905:                int monthsInYear = 0;
0906:                int monthNum;
0907:                while (true) { // decode monthes
0908:                    parser.skipBlank();
0909:                    if (!parser.isNextInt()) {
0910:                        break;
0911:                    }
0912:                    monthNum = parser.readInt();
0913:                    if (monthNum >= MONTHS_IN_YEAR.length) {
0914:                        throw new IOException("Wrong month number");
0915:                    }
0916:                    monthsInYear |= MONTHS_IN_YEAR[monthNum];
0917:                }
0918:                return monthsInYear;
0919:            }
0920:
0921:            /**
0922:             * Puts duration (#value) and end date (yyyymmddThhmmss(Z)/yyyyMMdd)
0923:             * to COUNT and END fields of the repeat rule.
0924:             *
0925:             * @param parser input data parser
0926:             * @param rule repeat rule object for setting
0927:             * @param isSetCount set COUNT field else don't set
0928:             * @throws IOException if a reading error occurs or wrong format
0929:             */
0930:            private void setRepeatRuleCount(Parser parser, RepeatRule rule,
0931:                    boolean isSetCount) throws IOException {
0932:                // parse duration
0933:                parser.skipBlank();
0934:                if (parser.match('#')) {
0935:                    parser.skip();
0936:                    int count = parser.readInt();
0937:                    if (isSetCount && count > 0) {
0938:                        rule.setInt(RepeatRule.COUNT, count);
0939:                    }
0940:                }
0941:                // parse end date
0942:                parser.skipBlank();
0943:                if (parser.hasNextDate()) {
0944:                    int dateLen = parser.getEndDate().length();
0945:                    // end date is either date in yyyyMMdd format or
0946:                    // date/time in yyyymmddThhmmss(Z).
0947:                    long date = (dateLen < 15) ? PIMHandler.getInstance()
0948:                            .parseDate(parser.getEndDate()) : PIMHandler
0949:                            .getInstance().parseDateTime(parser.getEndDate());
0950:                    rule.setDate(RepeatRule.END, date);
0951:                    parser.setPos(parser.getPos() + dateLen);
0952:                }
0953:            }
0954:
0955:            /**
0956:             * Decodes one line of a vEvent.
0957:             * @param event data to be processed
0958:             * @param propertyName property key name
0959:             * @param attributes fields to be processed
0960:             * @param data string to be processed
0961:             */
0962:            private void importData(Event event, String propertyName,
0963:                    String[] attributes, String data) {
0964:                int field = VEventSupport.getFieldCode(propertyName);
0965:                switch (field) {
0966:                case Event.SUMMARY:
0967:                case Event.LOCATION:
0968:                case Event.NOTE:
0969:                case Event.UID: {
0970:                    String sdata = FormatSupport.parseString(attributes, data);
0971:                    event.addString(field, Event.ATTR_NONE, sdata);
0972:                    break;
0973:                }
0974:                case Event.END:
0975:                case Event.REVISION:
0976:                case Event.START: {
0977:                    long date = PIMHandler.getInstance().parseDateTime(data);
0978:                    event.addDate(field, Event.ATTR_NONE, date);
0979:                    break;
0980:                }
0981:                case Event.CLASS: {
0982:                    String sdata = FormatSupport.parseString(attributes, data);
0983:                    int c = VEventSupport.getClassCode(sdata);
0984:                    event.addInt(Event.CLASS, Event.ATTR_NONE, c);
0985:                    break;
0986:                }
0987:                case Event.ALARM: {
0988:                    String[] s = FormatSupport.parseStringArray(attributes,
0989:                            data);
0990:                    if (s.length > 0) {
0991:                        long alarmTime = PIMHandler.getInstance()
0992:                                .parseDateTime(s[0]);
0993:                        event.addInt(Event.ALARM, Event.ATTR_NONE,
0994:                                (int) (alarmTime / 1000));
0995:                    }
0996:                    break;
0997:                }
0998:                }
0999:            }
1000:
1001:            /**
1002:             * Reads and decodes a single vToDo.
1003:             * @param in input stream
1004:             * @param list PIM list to which the item belongs
1005:             * @throws IOException if an error occurs reading
1006:             * @return ToDo implementation handler
1007:             */
1008:            private ToDoImpl decodeToDo(LineReader in, PIMList list)
1009:                    throws IOException {
1010:                ToDoImpl todo = new ToDoImpl((AbstractPIMList) list);
1011:                String line;
1012:                while ((line = in.readLine()) != null) {
1013:                    FormatSupport.DataElement element = FormatSupport
1014:                            .parseObjectLine(line);
1015:                    if (element.propertyName.equals("END")) {
1016:                        return todo;
1017:                    } else if (element.propertyName.equals("VERSION")) {
1018:                        if (!element.data.equals("1.0")) {
1019:                            throw new UnsupportedPIMFormatException("Version "
1020:                                    + element.data + " is not supported");
1021:                        }
1022:                    } else if (element.propertyName.equals("CATEGORIES")) {
1023:                        String[] categories = FormatSupport.split(element.data,
1024:                                ',', 0);
1025:                        for (int j = 0; j < categories.length; j++) {
1026:                            try {
1027:                                todo.addToCategory(categories[j]);
1028:                            } catch (PIMException e) {
1029:                                // cannot add item to category
1030:                            }
1031:                        }
1032:                    } else {
1033:                        importData(todo, element.propertyName,
1034:                                element.attributes, element.data);
1035:                    }
1036:                }
1037:                throw new IOException("Unterminated vToDo");
1038:            }
1039:
1040:            /** 
1041:             * Decodes one line of a vToDo.
1042:             * @param todo element to fill 
1043:             * @param propertyName key to property value
1044:             * @param attributes fields to import
1045:             * @param data string containing input data
1046:             */
1047:            private void importData(ToDo todo, String propertyName,
1048:                    String[] attributes, String data) {
1049:
1050:                int field = VToDoSupport.getFieldCode(propertyName);
1051:                switch (field) {
1052:                case ToDo.SUMMARY:
1053:                case ToDo.NOTE:
1054:                case ToDo.UID: {
1055:                    String sdata = FormatSupport.parseString(attributes, data);
1056:                    todo.addString(field, ToDo.ATTR_NONE, sdata);
1057:                    break;
1058:                }
1059:                case ToDo.COMPLETION_DATE:
1060:                    todo.addBoolean(ToDo.COMPLETED, ToDo.ATTR_NONE, true);
1061:                    // fall through
1062:                case ToDo.DUE:
1063:                case ToDo.REVISION: {
1064:                    long date = PIMHandler.getInstance().parseDateTime(data);
1065:                    todo.addDate(field, ToDo.ATTR_NONE, date);
1066:                    break;
1067:                }
1068:                case ToDo.CLASS: {
1069:                    String sdata = FormatSupport.parseString(attributes, data);
1070:                    int c = VToDoSupport.getClassCode(sdata);
1071:                    todo.addInt(ToDo.CLASS, ToDo.ATTR_NONE, c);
1072:                    break;
1073:                }
1074:                case ToDo.PRIORITY: {
1075:                    try {
1076:                        int i = Integer.parseInt(data);
1077:                        todo.addInt(ToDo.PRIORITY, ToDo.ATTR_NONE, i);
1078:                    } catch (NumberFormatException e) {
1079:                        // ignore this field
1080:                    }
1081:                    break;
1082:                }
1083:                }
1084:            }
1085:
1086:        }
1087:
1088:        /**
1089:         * A simple parser for encoded RepeatRule data.
1090:         */
1091:        class Parser {
1092:
1093:            /** Source data buffer. */
1094:            String s;
1095:
1096:            /** Current position in source data buffer. */
1097:            private int index;
1098:
1099:            /** Saved end date. */
1100:            private String endDate = "";
1101:
1102:            /** 
1103:             * Constructor.
1104:             *
1105:             * @param s string for parsing 
1106:             */
1107:            Parser(String s) {
1108:                this .s = s;
1109:                index = 0;
1110:            }
1111:
1112:            /**
1113:             * Checks that current position is valid.
1114:             * @throws IOException if position is out of buffer
1115:             */
1116:            private void checkBound() throws IOException {
1117:                checkBound(0);
1118:            }
1119:
1120:            /**
1121:             * Checks that input position is valid.
1122:             * @param off offset from current position
1123:             * @throws IOException if position + offset is out of buffer
1124:             */
1125:            private void checkBound(int off) throws IOException {
1126:                int bound = index + off;
1127:                if (bound < 0 || bound >= s.length()) {
1128:                    throw new IOException("Out of bound");
1129:                }
1130:            }
1131:
1132:            /**
1133:             * Reads next char from buffer.
1134:             * @return next char from buffer
1135:             * @throws IOException if position is out of buffer
1136:             */
1137:            char readChar() throws IOException {
1138:                checkBound();
1139:                return s.charAt(index++);
1140:            }
1141:
1142:            /**
1143:             * Reads next char from buffer without changing the pointer.
1144:             * @return next char from buffer
1145:             * @throws IOException if position is out of buffer
1146:             */
1147:            char nextChar() throws IOException {
1148:                return nextChar(0);
1149:            }
1150:
1151:            /**
1152:             * Reads next char by given position 
1153:             * from buffer without changing the pointer.
1154:             * @param off offset from current position
1155:             * @return next char from buffer
1156:             * @throws IOException if position + offset is out of buffer
1157:             */
1158:            char nextChar(int off) throws IOException {
1159:                checkBound(off);
1160:                return s.charAt(index + off);
1161:            }
1162:
1163:            /**
1164:             * Checks that next chars contain a date
1165:             * in format yyyymmddThhmmss.
1166:             *
1167:             * For more details please see RFC 2445
1168:             *
1169:             * @return true when next chars contain a date else false
1170:             * If the date exists then it is saved in endDate member
1171:             */
1172:            boolean hasNextDate() { // Date format yyyymmddThhmmss
1173:                endDate = "";
1174:                if (!hasMoreChars()) {
1175:                    return false;
1176:                }
1177:                String strDate = getRemainder();
1178:                int dateLength = 8; // yyyymmdd
1179:                if (strDate.length() < dateLength) {
1180:                    return false;
1181:                }
1182:                if (strDate.length() > 14 && strDate.charAt(8) == 'T') {
1183:                    dateLength = 15; // yyyymmddThhmmss
1184:                    // yyyymmddThhmmssZ - absolute time
1185:                    if (strDate.length() > 15 && strDate.charAt(15) == 'Z') {
1186:                        dateLength = 16;
1187:                    }
1188:                }
1189:                strDate = strDate.substring(0, dateLength);
1190:                try {
1191:                    int year = Integer.parseInt(strDate.substring(0, 4));
1192:                    if (year < 1970) {
1193:                        return false;
1194:                    }
1195:                    int month = Integer.parseInt(strDate.substring(4, 6));
1196:                    if ((month < 1) || (month > 12)) {
1197:                        return false;
1198:                    }
1199:                    int day = Integer.parseInt(strDate.substring(6, 8));
1200:                    if ((day < 1) || (day > 31)) {
1201:                        return false;
1202:                    }
1203:                    // Don't check time
1204:                } catch (NumberFormatException ex) {
1205:                    return false;
1206:                }
1207:                endDate = strDate;
1208:                return true;
1209:            }
1210:
1211:            /**
1212:             * Gets a date that saved by hasNextDate method.
1213:             *
1214:             * @return saved date
1215:             */
1216:            String getEndDate() {
1217:                return endDate;
1218:            }
1219:
1220:            /**
1221:             * Reads next integer value from buffer. 
1222:             *
1223:             * @return integer value
1224:             * @throws IOException if chars from current position don't contain
1225:             * integer value
1226:             */
1227:            int readInt() throws IOException {
1228:                checkBound();
1229:                StringBuffer sb = new StringBuffer();
1230:                for (; index < s.length() && Character.isDigit(s.charAt(index)); index++) {
1231:                    sb.append(s.charAt(index));
1232:                }
1233:                if (sb.length() == 0) {
1234:                    throw new IOException("No digital chars");
1235:                }
1236:                return Integer.parseInt(sb.toString());
1237:            }
1238:
1239:            /**
1240:             * Checks that the next char is equal to given char.
1241:             * @param sym char for comparing
1242:             * @return true when next char is equal to given char
1243:             */
1244:            boolean match(char sym) {
1245:                if (!hasMoreChars()) {
1246:                    return false;
1247:                }
1248:                return sym == s.charAt(index);
1249:            }
1250:
1251:            /**
1252:             * Checks that the buffer contains chars from current
1253:             * position.
1254:             * @return true when buffer has unparsed chars
1255:             */
1256:            boolean hasMoreChars() {
1257:                return index < s.length();
1258:            }
1259:
1260:            /**
1261:             * Skips the current position.
1262:             */
1263:            void skip() {
1264:                index++;
1265:            }
1266:
1267:            /**
1268:             * Skips next spaces and tabs.
1269:             */
1270:            void skipBlank() {
1271:                while (hasMoreChars() && (match(' ') || match('\t'))) {
1272:                    skip();
1273:                }
1274:            }
1275:
1276:            /**
1277:             * Checks that the next symbol is equal to given one.
1278:             * 
1279:             * @throws IOException if next symbol is different from input one
1280:             * @param sym the symbol to be checked
1281:             */
1282:            void matchSkip(char sym) throws IOException {
1283:                if (!match(sym)) {
1284:                    throw new IOException("No symbol " + sym);
1285:                }
1286:                index++;
1287:            }
1288:
1289:            /**
1290:             * Gets the remainder of parsed buffer.
1291:             * @return part of buffer from current position
1292:             */
1293:            String getRemainder() {
1294:                String retValue = null;
1295:                if (index < s.length()) {
1296:                    retValue = s.substring(index);
1297:                }
1298:                return retValue;
1299:            }
1300:
1301:            /**
1302:             * Gets the current position.
1303:             * @return current position
1304:             */
1305:            int getPos() {
1306:                return index;
1307:            }
1308:
1309:            /**
1310:             * Sets the given position.
1311:             * @param pos position for setting
1312:             */
1313:            void setPos(int pos) {
1314:                index = pos;
1315:            }
1316:
1317:            /**
1318:             * Checks that next ID from buffer could be
1319:             * found in input string array.
1320:             * @param arrStr array for searching
1321:             * @return true when next ID is found in the input array else false
1322:             */
1323:            boolean isNextMatchStr(String[] arrStr) {
1324:                if (!hasMoreChars()) {
1325:                    return false;
1326:                }
1327:                int pos = index;
1328:                String nextId;
1329:                try {
1330:                    nextId = readId();
1331:                } catch (IOException ex) {
1332:                    return false;
1333:                }
1334:                index = pos;
1335:                if ((nextId == null) || (nextId.length() == 0)) {
1336:                    return false;
1337:                }
1338:                for (int i = 0; i < arrStr.length; i++) {
1339:                    if (arrStr[i].equals(nextId)) {
1340:                        return true;
1341:                    }
1342:                }
1343:                return false;
1344:            }
1345:
1346:            /**
1347:             * Checks that next ID from buffer contains
1348:             * digit symbols only.
1349:             * @return true when next ID contains digit symbols only
1350:             */
1351:            boolean isNextInt() {
1352:                if (!hasMoreChars()) {
1353:                    return false;
1354:                }
1355:                int pos = index;
1356:                String nextId;
1357:                try {
1358:                    nextId = readId();
1359:                } catch (IOException ex) {
1360:                    return false;
1361:                }
1362:                index = pos;
1363:                if ((nextId == null) || (nextId.length() == 0)) {
1364:                    return false;
1365:                }
1366:                return Character.isDigit(nextId.charAt(0));
1367:            }
1368:
1369:            /**
1370:             * Gets next ID from buffer.
1371:             * @return next ID
1372:             * @throws IOException if position is out of buffer bounds
1373:             */
1374:            String readId() throws IOException {
1375:                checkBound();
1376:                String id = getRemainder();
1377:                int index = id.indexOf(' ');
1378:                if (index > -1) {
1379:                    id = id.substring(0, index);
1380:                }
1381:                setPos(getPos() + id.length());
1382:                return id;
1383:            }
1384:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.