// Copyright (C) 2006 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.ical.values;
import com.google.ical.util.DTBuilder;
import com.google.ical.util.TimeUtils;
import java.text.ParseException;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* static functions for parsing ical values.
*
* @author mikesamuel+svn@gmail.com (Mike Samuel)
*/
public final class IcalParseUtil {
private static final Pattern DATE_VALUE = Pattern.compile(
"(\\d{4,})(\\d\\d)(\\d\\d)" +
"(?:T([0-1]\\d|2[0-3])([0-5]\\d)([0-5]\\d)(Z)?)?");
/** parses a date of the form yyyymmdd or yyyymmdd'T'hhMMss */
public static DateValue parseDateValue(String s) throws ParseException {
return parseDateValue(s, null);
}
/**
* parses a date of the form yyyymmdd or yyyymmdd'T'hhMMss converting from
* the given timezone to UTC.
*/
public static DateValue parseDateValue(String s, TimeZone tzid)
throws ParseException {
Matcher m = DATE_VALUE.matcher(s);
if (!m.matches()) { throw new ParseException(s, 0); }
int year = Integer.parseInt(m.group(1)),
month = Integer.parseInt(m.group(2)),
day = Integer.parseInt(m.group(3));
if (null != m.group(4)) {
int hour = Integer.parseInt(m.group(4)),
minute = Integer.parseInt(m.group(5)),
second = Integer.parseInt(m.group(6));
boolean utc = null != m.group(7);
DateValue dv = new DTBuilder(
year, month, day, hour, minute, second).toDateTime();
if (!utc && null != tzid) {
dv = TimeUtils.toUtc(dv, tzid);
}
return dv;
} else {
return new DTBuilder(year, month, day).toDate();
}
}
/**
* parse a period value of the form <start>/<end>.
* This does not yet recognize the <start>/<duration> form.
*/
public static PeriodValue parsePeriodValue(String s) throws ParseException {
return parsePeriodValue(s, null);
}
/**
* parse a period value of the form <start>/<end>, converting
* from the given timezone to UTC.
* This does not yet recognize the <start>/<duration> form.
*/
public static PeriodValue parsePeriodValue(String s, TimeZone tzid)
throws ParseException {
int sep = s.indexOf('/');
if (sep < 0) { throw new ParseException(s, s.length()); }
DateValue start = parseDateValue(s.substring(0, sep), tzid),
end = parseDateValue(s.substring(sep + 1), tzid);
if ((start instanceof TimeValue) != (end instanceof TimeValue)) {
throw new ParseException(s, 0);
}
try {
return PeriodValueImpl.create(start, end);
} catch (IllegalArgumentException ex) {
throw (ParseException) new ParseException(s, sep + 1).initCause(ex);
}
}
/**
* unfolds ical content lines as per RFC 2445 section 4.1.
*
* <h3>4.1 Content Lines</h3>
*
* <p>The iCalendar object is organized into individual lines of text, called
* content lines. Content lines are delimited by a line break, which is a CRLF
* sequence (US-ASCII decimal 13, followed by US-ASCII decimal 10).
*
* <p>Lines of text SHOULD NOT be longer than 75 octets, excluding the line
* break. Long content lines SHOULD be split into a multiple line
* representations using a line "folding" technique. That is, a long line can
* be split between any two characters by inserting a CRLF immediately
* followed by a single linear white space character (i.e., SPACE, US-ASCII
* decimal 32 or HTAB, US-ASCII decimal 9). Any sequence of CRLF followed
* immediately by a single linear white space character is ignored (i.e.,
* removed) when processing the content type.
*/
public static String unfoldIcal(String foldedContentLines) {
return IGNORABLE_ICAL_WHITESPACE.matcher(foldedContentLines).replaceAll("");
}
private static final Pattern IGNORABLE_ICAL_WHITESPACE =
Pattern.compile("(?:\\r\\n?|\\n)[ \t]");
private IcalParseUtil() {
// uninstantiable.
}
}