1 /*
2 * jSDP: A Java implementation of SDP protocol Copyright (C) 2007 Claudio Di
3 * Vita
4 */
5 package net.sourceforge.jsdp;
6
7 import java.util.Date;
8 import java.util.Enumeration;
9 import java.util.Hashtable;
10
11 import net.sourceforge.jsdp.util.TypedTime;
12 import net.sourceforge.jsdp.util.ZoneAdjustment;
13
14 /**
15 * A <tt>TimeZone</tt> represents a <b>z=<i><field value></i></b>
16 * field contained in a SDP message. The timezone field allows the sender to
17 * specify a list of adjustment times and offsets from the base time.
18 *
19 * @since 0.1.0
20 *
21 * @version 1.0
22 *
23 * @author <a href="mailto:cdivita@users.sourceforge.net">Claudio Di Vita</a>
24 */
25 public class TimeZone implements Field {
26
27 /** The class Stream Unique Identifier, SUID */
28 private static final long serialVersionUID = -87926730273093503L;
29
30 /** Indicates if the field will be output as a typed time or a number */
31 protected boolean isTyped;
32
33 /** The map of timezone adjustments */
34 protected Hashtable adjustments;
35
36 /**
37 * Creates a new <tt>TimeZone</tt>.
38 */
39 protected TimeZone() {
40
41 super();
42
43 adjustments = new Hashtable(3);
44 }
45
46 /**
47 * Creates a new <tt>TimeZone</tt>.
48 *
49 * @param time the adjustment time
50 *
51 * @param offset the offset
52 *
53 * @throws SDPException if the NTP representation of the adjustment time is
54 * negative
55 */
56 public TimeZone(final Date time, final long offset) throws SDPException {
57
58 this(new ZoneAdjustment(time, offset));
59 }
60
61 /**
62 * Creates a new <tt>TimeZone</tt>.
63 *
64 * @param adjustment the time zone adjustment
65 */
66 public TimeZone(final ZoneAdjustment adjustment) {
67
68 this();
69
70 addZoneAdjustment(adjustment);
71 this.isTyped = true;
72 }
73
74 /**
75 * Parse an input string and constructs the equivalent timezone field.
76 *
77 * @param field the string to parse
78 *
79 * @return a new <tt>TimeZone</tt> instance
80 *
81 * @throws SDPParseException if an error occurs while parsing
82 */
83 public static TimeZone parse(final String field) throws SDPParseException {
84
85 if (!field.startsWith("z=")) {
86 throw new SDPParseException("The string \"" + field + "\" isn't a timezone field");
87 }
88
89 String[] values = field.substring(2).split(" ");
90
91 if (values.length < 2 || (values.length % 2 != 0)) {
92 throw new SDPParseException("The string \"" + field + "\" isn't a valid timezone field");
93 }
94
95 TimeZone z = new TimeZone();
96
97 for (int i = 0; i < values.length; i += 2) {
98 try {
99 z.addZoneAdjustment(new ZoneAdjustment(Long.parseLong(values[i]), TypedTime.getTime(values[i + 1])));
100 }
101 catch (Exception parseException) {
102 throw new SDPParseException("The string \"" + field + "\" isn't a valid timezone field", parseException);
103 }
104 }
105
106 return z;
107 }
108
109 /**
110 * Add a zone adjustment to the field.
111 *
112 * @param time the adjustment time
113 * @param offset the offset
114 *
115 * @throws SDPException if the NTP representation of the adjustment time is
116 * negative
117 */
118 public void addZoneAdjustment(final Date time, final long offset) throws SDPException {
119
120 adjustments.put(time, new ZoneAdjustment(time, offset));
121 }
122
123 /**
124 * Add a zone adjustment to the field.
125 *
126 * @param adjustment the time zone adjustment
127 */
128 public void addZoneAdjustment(final ZoneAdjustment adjustment) {
129
130 adjustments.put(new Date(adjustment.getTime()), adjustment);
131 }
132
133 /**
134 * Returns a clone of this field.
135 *
136 * @return a clone of this field
137 */
138 public Object clone() {
139
140 TimeZone field = new TimeZone();
141
142 field.isTyped = this.isTyped;
143 field.adjustments = (Hashtable) this.adjustments.clone();
144
145 return field;
146 }
147
148 /**
149 * Returns the type character for the field.
150 *
151 * @return the field type character: <tt>z</t>
152 */
153 public char getType() {
154
155 return Field.TIMEZONE_FIELD;
156 }
157
158 /*
159 * <p>Returns a Map of adjustment times, where :</p>
160 *
161 * <ul> <li>key = {@link java.util.Date}</li> <li>value =
162 * {@link ZoneAdjustment}</li> </ul>
163 *
164 * @return The time zone adjustments
165 */
166 /*
167 * public Map getZoneAdjustments() {
168 *
169 * return adjustments; }
170 */
171
172 /**
173 * Returns whether the field will be output as a typed time or a number.
174 *
175 * <p>
176 * Typed time is formatted as an number followed by a unit character: the
177 * unit indicates an appropriate multiplier for the number. The following
178 * unit types are allowed:
179 * </p>
180 * <ul>
181 * <li><tt><b>d</b></tt> - days (86400 seconds)</li>
182 * <li><tt><b>h</b></tt> - hours (3600 seconds)</li>
183 * <li><tt><b>m</b></tt> - minutes (60 seconds)</li>
184 * <li><tt><b>s</b></tt> - seconds (1 second)</li>
185 * </ul>
186 *
187 * @return <tt>true</tt> if the field will be output as a typed time,
188 * <tt>false</tt> if as a number
189 */
190 public boolean isTypedTime() {
191
192 return isTyped;
193 }
194
195 /**
196 * Sets whether the field will be output as a typed time or a number.
197 *
198 * <p>
199 * Typed time is formatted as an number followed by a unit character: the
200 * unit indicates an appropriate multiplier for the number. The following
201 * unit types are allowed:
202 * </p>
203 * <ul>
204 * <li><tt><b>d</b></tt> - days (86400 seconds)</li>
205 * <li><tt><b>h</b></tt> - hours (3600 seconds)</li>
206 * <li><tt><b>m</b></tt> - minutes (60 seconds)</li>
207 * <li><tt><b>s</b></tt> - seconds (1 second)</li>
208 * </ul>
209 */
210 public void setTypedTime(final boolean typedTime) {
211
212 this.isTyped = typedTime;
213 }
214
215 /**
216 * Returns a string representation of the field. The representation has the
217 * form: <b>z=<i><time></i> <i><offset></i>+
218 *
219 * @return The string representation of the field
220 */
221 public String toString() {
222
223 StringBuffer result = new StringBuffer(getType() + "=");
224 ZoneAdjustment adjustment;
225 Enumeration values = adjustments.elements();
226 while (values.hasMoreElements()) {
227
228 adjustment = (ZoneAdjustment) values.nextElement();
229
230 result.append(adjustment.getTime() + " ");
231 if (this.isTyped) {
232 result.append(TypedTime.toString(adjustment.getOffset()) + " ");
233 }
234 else {
235 result.append(adjustment.getOffset() + " ");
236 }
237 }
238 result.deleteCharAt(result.length() - 1);
239
240 return result.toString();
241 }
242 }