summaryrefslogtreecommitdiff
path: root/sources/server/CalendarFormatParser.java
blob: a1f3cb01ac0fa096a2a2324d1ddfaae266c05d63 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
/* NetCalendar 2006, 2009 (c) Dipl.-Inform. (FH) Paul C. Buetow
 * http://netcalendar.buetow.org - netcalendar@dev.buetow.org
 */

/**
 *
 */
package server;

import java.util.*;
import java.util.regex.*;
import java.text.*;
import java.io.*;

import shared.*;

/**
 * This class is needed for parsing the original UNIX calendar database format.
 * Its parsing the calendar database from the file-system and its caching it into the
 * memory. Then, the  CalendarDatabase class can be used to access the database.
 * Each calendar category has its own file. In each file all the calendar events of the
 * specific categories are stored.
 * @author Paul C. Buetow
 *
 */
public final class CalendarFormatParser {
    private int iCurrentYear;
    private Date dateCurrent;
    private String sWorkdir;
    private Vector vecCategories;
    private static final Pattern datePattern;
    private static final Pattern dateYearlyPattern;
    private static final Pattern dateElsePattern;
    private static final Pattern dateEndPattern;
    // private static final Pattern emptyLine;
    private static final String DATE_FORMAT = "M/d-y";
    private static final String EXTENDED_DATE_FORMAT = "M/d-y-H:m";

    // Those pattern will be used later, precompile here once to use often
    static {
        // Matches calendar date format like: "01/25		2006-14:23"
        datePattern = Pattern.compile("\\d{2}/\\d{2}\t\\d{4}-\\d{2}:\\d{2}");

        // Matches calendar date format like: "01/25		yearly-14:23"
        dateYearlyPattern = Pattern.compile("\\d{2}/\\d{2}\tyearly-\\d{2}:\\d{2}");

        // Matches calendar date format like: "01/25		Some free text here
        dateElsePattern = Pattern.compile("\\d{2}/\\d{2}\t");

        // Matches the end of the date string like: "arly-14:23 " or "2006-14:23"
        dateEndPattern = Pattern.compile(".{4}-\\d{2}:\\d{2}");
    }

    /**
     * Simple constructor. Creates a calendar format parser object and initializes some private members.
     */
    public CalendarFormatParser() {
        // Use current dir as workdir by default
        this.sWorkdir = ".";
        this.vecCategories = new Vector();

        // Events without a year specified will use the current year!
        GregorianCalendar cal = new GregorianCalendar();
        iCurrentYear = cal.get(Calendar.YEAR);

        dateCurrent = new Date();
    }

    /**
     * This method returns a vector of all found calendar categories after parsing.
     * @return Returns a Vector of all available CalendarCategory objects.
     */
    public Vector getCategories() {
        return vecCategories;
    }

    /**
     * This method sets the working directory. Its the "server_database_dir" variable defined in the current configuration by default.
     * @param sWorkdir Specifies the working directory.
     */
    public void setWorkdir(String sWorkdir) {
        this.sWorkdir = sWorkdir;
    }

    /**
     * Starts the parsing work of the calendar database files.
     */
    public void start() {
        lookForCategories();
        parseAllCategories();
        Main.execExternalCommand(Config.getStringValue("server_startup_command"));
    }

    /**
     * Parses for the available calendar categories.
     */
    private void lookForCategories() {
        File dir = new File(sWorkdir);
        File[] dirContent = dir.listFiles();

        for (int i = 0; i < dirContent.length; ++i)
            if (dirContent[i].isFile())
                // Ignore the 'calendar' file, only read the 'calendar.*' files
                if (!dirContent[i].getName().equals("calendar"))
                    vecCategories.add(new CalendarCategory(dirContent[i]));
    }

    /**
     * Parses all events of all available categories.
     */
    private void parseAllCategories() {
        Enumeration enumCategories = vecCategories.elements();
        while (enumCategories.hasMoreElements())
            parseCategory((CalendarCategory) enumCategories.nextElement());
    }

    /**
     * Parses all events of a category file.
     * @param category Specifies the calendar category to be parsed.
     */
    private void parseCategory(CalendarCategory category) {
        Vector vecEvents = new Vector();
        File file = category.getFile();

        try {
            BufferedReader in = new BufferedReader(new FileReader(file));
            String sLine;

            while ((sLine = in.readLine()) != null) {
                CalendarEvent event = new CalendarEvent(category);

                // Ignore empty lines!
                if (sLine.equals(""))
                    continue;

                setEventsDate(event, sLine);
                setEventsPlace(event, sLine);
                setEventsDescription(event, sLine);

                vecEvents.add(event);
            }

        } catch (Exception e) {
            Main.infoMessage("Error: " + e.toString());
        }

        category.setEvents(vecEvents);
        category.unsetFile();
    }

    /**
     * This method parses all known informations from a given calendar format line and saves them
     * into the given calendar event object.
     * @param event Specifies the calendar event to be modified.
     * @param sLine Specifies the single line of the category file to be parsed.
     */
    private void setEventsDate(CalendarEvent event, String sLine) {
        // Create a local copy because the string may be modified
        String sMyLine = new String(sLine).replaceAll(" ", "");
        String sDateFormat;
        boolean bValidDateFormat = false;

        // Check if its a yearly event
        Matcher matcher = dateYearlyPattern.matcher(sMyLine);
        if (matcher.find()) {
            sMyLine = sMyLine.replaceFirst("\tyearly", "-" + iCurrentYear);
            sDateFormat = EXTENDED_DATE_FORMAT;
            event.setYearly(true);
            bValidDateFormat = true;

        } else {
            // Event is not yearly, but check if its still using NetCalendars
            // extended format which contains the event's time (hours:minutes)
            matcher = datePattern.matcher(sMyLine);
            if (matcher.find()) {
                sMyLine = sMyLine.replaceFirst("\t", "-");
                sDateFormat = EXTENDED_DATE_FORMAT;
                event.setYearly(false);
                bValidDateFormat = true;

            } else {
                //matcher = dateElsePattern.matcher(sMyLine);
                //if (matcher.find()) {
                // Just use original Calendar format without any year and time
                // informations
                // Assume yearly
                sMyLine = sMyLine.replaceFirst("\t", "-" + iCurrentYear);
                sDateFormat = DATE_FORMAT;
                event.setYearly(true);
                bValidDateFormat = true;
                /*} else {
                	return;
                }*/
            }
        }

        // Create a new date object containing the events time informations
        SimpleDateFormat formatter = new SimpleDateFormat(sDateFormat);
        Date date = null;

        if (bValidDateFormat) {
            try {
                date = formatter.parse(sMyLine);

            } catch (ParseException e) {
                Main.infoMessage("Error: Calendar format parser error at category "
                                 + event.getCategoryName() + ": " + e.getMessage());
            }
        }

        // The event is yearly, but occured already this year, so increment the events
        // year by one!
        if (event.isYearly() && date.getTime() < dateCurrent.getTime()) {
            Calendar calendar = new GregorianCalendar();
            calendar.setTime(date);
            calendar.set(Calendar.YEAR, iCurrentYear + 1);
            date = calendar.getTime();
        }

        event.setDate(date);
    }

    /**
     * Parses a single calendar line for the place information.
     * @param event Specifies the calendar event to be modified.
     * @param sLine Specifies the single line of the category file to be parsed.
     */
    private void setEventsPlace(CalendarEvent event, String sLine) {
        int iPos = sLine.indexOf(";;");

        // No event! Return empty string!
        if (iPos < 0) {
            event.setPlace("");

        } else {
            event.setPlace(trim(sLine.substring(iPos + 2)));
        }
    }

    /**
     * Parses a single calendar line for the description information.
     * @param event Specifies the calendar event to be modified.
     * @param sLine Specifies the single line of the category file to be parsed.
     */
    private void setEventsDescription(CalendarEvent event, String sLine) {
        // We need a local copy because we may modify the string
        String sTmp = new String(sLine);

        // Check if there is a place string...
        int iPos = sLine.indexOf(";;");

        // ... if yes, dont include it!
        if (iPos >= 0)
            sTmp = sTmp.substring(0, iPos);

        // Remove the events date from the string
        Matcher matcher = dateEndPattern.matcher(sTmp);

        if (matcher.find()) {
            sTmp = sTmp.substring(matcher.start()+11);

        } else {
            sTmp = sTmp.substring(sTmp.indexOf("\t")+1);
        }

        event.setDescription(trim(sTmp));
    }

    /**
     * Its like String.trim() but also removes a ending newline.
     * @param sTrimString Specifies the String to be trimmed.
     * @return Returns a copy of the string, with leading and trailing whitespace omitted, also a trailing newline will be omitted.
     */
    private String trim(String sTrimString) {
        int iPos = sTrimString.indexOf("\n");

        if (iPos >= 0)
            return sTrimString.substring(0, iPos).trim();

        return sTrimString.trim();
    }
}