1f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling// =================================================================================================
2f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling// ADOBE SYSTEMS INCORPORATED
3f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling// Copyright 2006 Adobe Systems Incorporated
4f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling// All Rights Reserved
5f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling//
6f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling// NOTICE:  Adobe permits you to use, modify, and distribute this file in accordance with the terms
7f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling// of the Adobe license agreement accompanying it.
8f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling// =================================================================================================
9f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
10f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingpackage com.adobe.xmp.impl;
11f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
12f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport java.text.DecimalFormat;
13f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport java.text.DecimalFormatSymbols;
14f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport java.util.Locale;
15f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport java.util.SimpleTimeZone;
16f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
17f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.XMPDateTime;
18f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.XMPError;
19f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.XMPException;
20f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
21f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
22f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling/**
23f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Converts between ISO 8601 Strings and <code>Calendar</code> with millisecond resolution.
24f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling *
25f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @since   16.02.2006
26f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */
27f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingpublic final class ISO8601Converter
28f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling{
29f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/** Hides public constructor */
30f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	private ISO8601Converter()
31f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
32f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// EMPTY
33f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
34f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
35f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
36f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
37f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Converts an ISO 8601 string to an <code>XMPDateTime</code>.
38f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
39f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Parse a date according to ISO 8601 and
40f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * http://www.w3.org/TR/NOTE-datetime:
41f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <ul>
42f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>YYYY
43f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>YYYY-MM
44f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>YYYY-MM-DD
45f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>YYYY-MM-DDThh:mmTZD
46f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>YYYY-MM-DDThh:mm:ssTZD
47f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>YYYY-MM-DDThh:mm:ss.sTZD
48f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * </ul>
49f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
50f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Data fields:
51f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <ul>
52f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>YYYY = four-digit year
53f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>MM = two-digit month (01=January, etc.)
54f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>DD = two-digit day of month (01 through 31)
55f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>hh = two digits of hour (00 through 23)
56f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>mm = two digits of minute (00 through 59)
57f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>ss = two digits of second (00 through 59)
58f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>s = one or more digits representing a decimal fraction of a second
59f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>TZD = time zone designator (Z or +hh:mm or -hh:mm)
60f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * </ul>
61f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
62f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Note that ISO 8601 does not seem to allow years less than 1000 or greater
63f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * than 9999. We allow any year, even negative ones. The year is formatted
64f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * as "%.4d".
65f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <p>
66f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <em>Note:</em> Tolerate missing TZD, assume is UTC. Photoshop 8 writes
67f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * dates like this for exif:GPSTimeStamp.<br>
68f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <em>Note:</em> Tolerate missing date portion, in case someone foolishly
69f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * writes a time-only value that way.
70f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
71f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param iso8601String a date string that is ISO 8601 conform.
72f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @return Returns a <code>Calendar</code>.
73f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @throws XMPException Is thrown when the string is non-conform.
74f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
75f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	public static XMPDateTime parse(String iso8601String) throws XMPException
76f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
77f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		return parse(iso8601String, new XMPDateTimeImpl());
78f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
79f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
80f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
81f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
82f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param iso8601String a date string that is ISO 8601 conform.
83f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param binValue an existing XMPDateTime to set with the parsed date
84f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @return Returns an XMPDateTime-object containing the ISO8601-date.
85f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @throws XMPException Is thrown when the string is non-conform.
86f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
87f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	public static XMPDateTime parse(String iso8601String, XMPDateTime binValue) throws XMPException
88f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
89f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		ParameterAsserts.assertNotNull(iso8601String);
90f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
91f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		ParseState input = new ParseState(iso8601String);
92f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		int value;
93f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
94f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		boolean timeOnly =
95f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			 input.ch(0) == 'T'  ||
96f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			(input.length() >= 2  &&  input.ch(1) == ':'  ||
97f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			(input.length() >= 3  &&  input.ch(2) == ':'));
98f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
99f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (!timeOnly)
100f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
101f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (input.ch(0) == '-')
102f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
103f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				input.skip();
104f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
105f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
106f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
107f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			// Extract the year.
108f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			value = input.gatherInt("Invalid year in date string", 9999);
109f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (input.hasNext()  &&  input.ch() != '-')
110f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
111f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				throw new XMPException("Invalid date string, after year", XMPError.BADVALUE);
112f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
113f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
114f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (input.ch(0) == '-')
115f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
116f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				value = -value;
117f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
118f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			binValue.setYear(value);
119f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (!input.hasNext())
120f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
121f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				return binValue;
122f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
123f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			input.skip();
124f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
125f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
126f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			// Extract the month.
127f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			value = input.gatherInt("Invalid month in date string", 12);
128f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (input.hasNext()  &&  input.ch() != '-')
129f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
130f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				throw new XMPException("Invalid date string, after month", XMPError.BADVALUE);
131f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
132f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			binValue.setMonth(value);
133f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (!input.hasNext())
134f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
135f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				return binValue;
136f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
137f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			input.skip();
138f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
139f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
140f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			// Extract the day.
141f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			value = input.gatherInt("Invalid day in date string", 31);
142f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (input.hasNext()  &&  input.ch() != 'T')
143f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
144f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				throw new XMPException("Invalid date string, after day", XMPError.BADVALUE);
145f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
146f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			binValue.setDay(value);
147f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (!input.hasNext())
148f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
149f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				return binValue;
150f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
151f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
152f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		else
153f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
154f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			// set default day and month in the year 0000
155f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			binValue.setMonth(1);
156f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			binValue.setDay(1);
157f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
158f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
159f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (input.ch() == 'T')
160f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
161f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			input.skip();
162f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
163f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		else if (!timeOnly)
164f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
165f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			throw new XMPException("Invalid date string, missing 'T' after date",
166f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					XMPError.BADVALUE);
167f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
168f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
169f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
170f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// Extract the hour.
171f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		value = input.gatherInt("Invalid hour in date string", 23);
172f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (input.ch() != ':')
173f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
174f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			throw new XMPException("Invalid date string, after hour", XMPError.BADVALUE);
175f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
176f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		binValue.setHour(value);
177f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
178f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// Don't check for done, we have to work up to the time zone.
179f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		input.skip();
180f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
181f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
182f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// Extract the minute.
183f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		value = input.gatherInt("Invalid minute in date string", 59);
184f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (input.hasNext()  &&
185f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			input.ch() != ':' && input.ch() != 'Z' && input.ch() != '+' && input.ch() != '-')
186f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
187f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			throw new XMPException("Invalid date string, after minute", XMPError.BADVALUE);
188f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
189f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		binValue.setMinute(value);
190f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
191f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (input.ch() == ':')
192f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
193f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			input.skip();
194f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			value = input.gatherInt("Invalid whole seconds in date string", 59);
195f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (input.hasNext()  &&  input.ch() != '.'  &&  input.ch() != 'Z'  &&
196f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				input.ch() != '+' && input.ch() != '-')
197f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
198f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				throw new XMPException("Invalid date string, after whole seconds",
199f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						XMPError.BADVALUE);
200f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
201f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			binValue.setSecond(value);
202f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (input.ch() == '.')
203f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
204f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				input.skip();
205f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				int digits = input.pos();
206f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				value = input.gatherInt("Invalid fractional seconds in date string", 999999999);
207f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				if (input.ch() != 'Z'  &&  input.ch() != '+'  &&  input.ch() != '-')
208f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				{
209f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					throw new XMPException("Invalid date string, after fractional second",
210f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling							XMPError.BADVALUE);
211f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				}
212f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				digits = input.pos() - digits;
213f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				for (; digits > 9; --digits)
214f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				{
215f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					value = value / 10;
216f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				}
217f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				for (; digits < 9; ++digits)
218f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				{
219f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					value = value * 10;
220f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				}
221f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				binValue.setNanoSecond(value);
222f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
223f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
224f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
225f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		int tzSign = 0;
226f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		int tzHour = 0;
227f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		int tzMinute = 0;
228f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (input.ch() == 'Z')
229f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
230f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			input.skip();
231f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
232f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		else if (input.hasNext())
233f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
234f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (input.ch() == '+')
235f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
236f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				tzSign = 1;
237f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
238f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			else if (input.ch() == '-')
239f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
240f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				tzSign = -1;
241f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
242f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			else
243f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
244f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				throw new XMPException("Time zone must begin with 'Z', '+', or '-'",
245f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						XMPError.BADVALUE);
246f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
247f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
248f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			input.skip();
249f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			// Extract the time zone hour.
250f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			tzHour = input.gatherInt("Invalid time zone hour in date string", 23);
251f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (input.ch() != ':')
252f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
253f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				throw new XMPException("Invalid date string, after time zone hour",
254f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						XMPError.BADVALUE);
255f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
256f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			input.skip();
257f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
258f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			// Extract the time zone minute.
259f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			tzMinute = input.gatherInt("Invalid time zone minute in date string", 59);
260f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
261f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
262f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// create a corresponding TZ and set it time zone
263f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		int offset = (tzHour * 3600 * 1000 + tzMinute * 60 * 1000) * tzSign;
264f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		binValue.setTimeZone(new SimpleTimeZone(offset, ""));
265f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
266f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
267f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (input.hasNext())
268f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
269f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			throw new XMPException(
270f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				"Invalid date string, extra chars at end", XMPError.BADVALUE);
271f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
272f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
273f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		return binValue;
274f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
275f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
276f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
277f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
278f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Converts a <code>Calendar</code> into an ISO 8601 string.
279f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Format a date according to ISO 8601 and http://www.w3.org/TR/NOTE-datetime:
280f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <ul>
281f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>YYYY
282f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>YYYY-MM
283f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>YYYY-MM-DD
284f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>YYYY-MM-DDThh:mmTZD
285f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>YYYY-MM-DDThh:mm:ssTZD
286f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>YYYY-MM-DDThh:mm:ss.sTZD
287f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * </ul>
288f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
289f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Data fields:
290f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <ul>
291f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>YYYY = four-digit year
292f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>MM	 = two-digit month (01=January, etc.)
293f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>DD	 = two-digit day of month (01 through 31)
294f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>hh	 = two digits of hour (00 through 23)
295f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>mm	 = two digits of minute (00 through 59)
296f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>ss	 = two digits of second (00 through 59)
297f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>s	 = one or more digits representing a decimal fraction of a second
298f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>TZD	 = time zone designator (Z or +hh:mm or -hh:mm)
299f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * </ul>
300f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <p>
301f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <em>Note:</em> ISO 8601 does not seem to allow years less than 1000 or greater than 9999.
302f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * We allow any year, even negative ones. The year is formatted as "%.4d".<p>
303f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <em>Note:</em> Fix for bug 1269463 (silently fix out of range values) included in parsing.
304f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * The quasi-bogus "time only" values from Photoshop CS are not supported.
305f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
306f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param dateTime an XMPDateTime-object.
307f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @return Returns an ISO 8601 string.
308f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
309f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	public static String render(XMPDateTime dateTime)
310f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
311f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		StringBuffer buffer = new StringBuffer();
312f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
313f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// year is rendered in any case, even 0000
314f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		DecimalFormat df = new DecimalFormat("0000", new DecimalFormatSymbols(Locale.ENGLISH));
315f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		buffer.append(df.format(dateTime.getYear()));
316f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (dateTime.getMonth() == 0)
317f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
318f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			return buffer.toString();
319f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
320f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
321f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// month
322f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		df.applyPattern("'-'00");
323f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		buffer.append(df.format(dateTime.getMonth()));
324f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (dateTime.getDay() == 0)
325f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
326f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			return buffer.toString();
327f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
328f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
329f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// day
330f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		buffer.append(df.format(dateTime.getDay()));
331f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
332f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// time, rendered if any time field is not zero
333f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (dateTime.getHour() != 0  ||
334f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			dateTime.getMinute() != 0  ||
335f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			dateTime.getSecond() != 0  ||
336f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			dateTime.getNanoSecond() != 0  ||
337f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			(dateTime.getTimeZone() != null  &&  dateTime.getTimeZone().getRawOffset() != 0))
338f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
339f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			// hours and minutes
340f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			buffer.append('T');
341f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			df.applyPattern("00");
342f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			buffer.append(df.format(dateTime.getHour()));
343f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			buffer.append(':');
344f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			buffer.append(df.format(dateTime.getMinute()));
345f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
346f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			// seconds and nanoseconds
347f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (dateTime.getSecond() != 0 || dateTime.getNanoSecond() != 0)
348f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
349f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				double seconds = dateTime.getSecond() + dateTime.getNanoSecond() / 1e9d;
350f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
351f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				df.applyPattern(":00.#########");
352f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				buffer.append(df.format(seconds));
353f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
354f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
355f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			// time zone
356f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (dateTime.getTimeZone() != null)
357f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
358f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				// used to calculate the time zone offset incl. Daylight Savings
359f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				long timeInMillis = dateTime.getCalendar().getTimeInMillis();
360f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				int offset = dateTime.getTimeZone().getOffset(timeInMillis);
361f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				if (offset == 0)
362f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				{
363f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					// UTC
364f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					buffer.append('Z');
365f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				}
366f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				else
367f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				{
368f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					int thours = offset / 3600000;
369f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					int tminutes = Math.abs(offset % 3600000 / 60000);
370f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					df.applyPattern("+00;-00");
371f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					buffer.append(df.format(thours));
372f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					df.applyPattern(":00");
373f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					buffer.append(df.format(tminutes));
374f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				}
375f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
376f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
377f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		return buffer.toString();
378f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
379f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
380f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
381f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling}
382f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
383f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
384f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling/**
385f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @since   22.08.2006
386f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */
387f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingclass ParseState
388f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling{
389f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/** */
390f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	private String str;
391f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/** */
392f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	private int pos = 0;
393f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
394f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
395f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
396f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param str initializes the parser container
397f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
398f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	public ParseState(String str)
399f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
400f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		this.str = str;
401f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
402f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
403f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
404f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
405f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @return Returns the length of the input.
406f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
407f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	public int length()
408f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
409f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		return str.length();
410f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
411f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
412f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
413f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
414f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @return Returns whether there are more chars to come.
415f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
416f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	public boolean hasNext()
417f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
418f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		return pos < str.length();
419f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
420f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
421f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
422f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
423f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param index index of char
424f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @return Returns char at a certain index.
425f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
426f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	public char ch(int index)
427f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
428f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		return index < str.length() ?
429f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			str.charAt(index) :
430f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			0x0000;
431f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
432f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
433f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
434f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
435f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @return Returns the current char or 0x0000 if there are no more chars.
436f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
437f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	public char ch()
438f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
439f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		return pos < str.length() ?
440f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			str.charAt(pos) :
441f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			0x0000;
442f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
443f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
444f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
445f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
446f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Skips the next char.
447f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
448f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	public void skip()
449f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
450f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		pos++;
451f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
452f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
453f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
454f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
455f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @return Returns the current position.
456f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
457f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	public int pos()
458f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
459f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		return pos;
460f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
461f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
462f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
463f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
464f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Parses a integer from the source and sets the pointer after it.
465f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param errorMsg Error message to put in the exception if no number can be found
466f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param maxValue the max value of the number to return
467f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @return Returns the parsed integer.
468f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @throws XMPException Thrown if no integer can be found.
469f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
470f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	public int gatherInt(String errorMsg, int maxValue) throws XMPException
471f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
472f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		int value = 0;
473f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		boolean success = false;
474f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		char ch = ch(pos);
475f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		while ('0' <= ch  &&  ch <= '9')
476f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
477f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			value = (value * 10) + (ch - '0');
478f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			success = true;
479f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			pos++;
480f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			ch = ch(pos);
481f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
482f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
483f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (success)
484f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
485f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (value > maxValue)
486f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
487f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				return maxValue;
488f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
489f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			else if (value < 0)
490f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
491f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				return 0;
492f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
493f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			else
494f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
495f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				return value;
496f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
497f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
498f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		else
499f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
500f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			throw new XMPException(errorMsg, XMPError.BADVALUE);
501f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
502f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
503f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling}
504f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
505f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
506