1 | /* | |
2 | * Copyright 2006 - 2013 | |
3 | * Stefan Balev <stefan.balev@graphstream-project.org> | |
4 | * Julien Baudry <julien.baudry@graphstream-project.org> | |
5 | * Antoine Dutot <antoine.dutot@graphstream-project.org> | |
6 | * Yoann Pign�� <yoann.pigne@graphstream-project.org> | |
7 | * Guilhelm Savin <guilhelm.savin@graphstream-project.org> | |
8 | * | |
9 | * This file is part of GraphStream <http://graphstream-project.org>. | |
10 | * | |
11 | * GraphStream is a library whose purpose is to handle static or dynamic | |
12 | * graph, create them from scratch, file or any source and display them. | |
13 | * | |
14 | * This program is free software distributed under the terms of two licenses, the | |
15 | * CeCILL-C license that fits European law, and the GNU Lesser General Public | |
16 | * License. You can use, modify and/ or redistribute the software under the terms | |
17 | * of the CeCILL-C license as circulated by CEA, CNRS and INRIA at the following | |
18 | * URL <http://www.cecill.info> or under the terms of the GNU LGPL as published by | |
19 | * the Free Software Foundation, either version 3 of the License, or (at your | |
20 | * option) any later version. | |
21 | * | |
22 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY | |
23 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A | |
24 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. | |
25 | * | |
26 | * You should have received a copy of the GNU Lesser General Public License | |
27 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
28 | * | |
29 | * The fact that you are presently reading this means that you have had | |
30 | * knowledge of the CeCILL-C and LGPL licenses and that you accept their terms. | |
31 | */ | |
32 | package org.graphstream.util.time; | |
33 | ||
34 | import java.text.ParseException; | |
35 | import java.util.Calendar; | |
36 | import java.util.LinkedList; | |
37 | import java.util.regex.Matcher; | |
38 | import java.util.regex.Pattern; | |
39 | ||
40 | import org.graphstream.util.time.ISODateComponent.TextComponent; | |
41 | ||
42 | /** | |
43 | * Scanner for date in ISO/IEC 9899:1999 format. The scanner takes a format and | |
44 | * then is able to parse timestamp in the given format. | |
45 | * | |
46 | * The <i>parse()</i> return a {@link java.util.Calendar} for convenience. | |
47 | * | |
48 | * Format of the scanner can be composed of %? directive which define components | |
49 | * of the time. These directives are listed below. For example, the format | |
50 | * "%F %T", which is equivalent to "%Y-%m-%d %H:%M:%S" can parse the following | |
51 | * timestamp: "2010-12-09 03:45:39"; | |
52 | * | |
53 | * <dl> | |
54 | * <dt>%a</dt> | |
55 | * <dd>locale's abbreviated weekday name</dd> | |
56 | * <dt>%A</dt> | |
57 | * <dd>locale's weekday name</dd> | |
58 | * <dt>%b</dt> | |
59 | * <dd>locale's abbreviated month name</dd> | |
60 | * <dt>%B</dt> | |
61 | * <dd>locale's month name</dd> | |
62 | * <dt>%c</dt> | |
63 | * <dd>locale's date and time representation</dd> | |
64 | * <dt>%C</dt> | |
65 | * <dd>two first digits of full year as an integer (00-99)</dd> | |
66 | * <dt>%d</dt> | |
67 | * <dd>day of the month (01-31)</dd> | |
68 | * <dt>%D</dt> | |
69 | * <dd>%m/%d/%y</dd> | |
70 | * <dt>%e</dt> | |
71 | * <dd>day of the month (1-31)</dd> | |
72 | * <dt>%F</dt> | |
73 | * <dd>%Y-%m-%d</dd> | |
74 | * <dt>%g</dt> | |
75 | * <dd>last 2 digits of the week-based year (00-99)</dd> | |
76 | * <dt>%G</dt> | |
77 | * <dd>"week-based year as a decimal number</dd> | |
78 | * <dt>%h</dt> | |
79 | * <dd>%b</dd> | |
80 | * <dt>%H</dt> | |
81 | * <dd>hour (24-hour clock) as a decimal number (00-23)</dd> | |
82 | * <dt>%I</dt> | |
83 | * <dd>hour (12-hour clock) as a decimal number (01-12)</dd> | |
84 | * <dt>%j</dt> | |
85 | * <dd>day of the year as a decimal number (001-366)</dd> | |
86 | * <dt>%k</dt> | |
87 | * <dd>milliseconds as a decimal number (001-999)</dd> | |
88 | * <dt>%K</dt> | |
89 | * <dd>milliseconds since the epoch</dd> | |
90 | * <dt>%m</dt> | |
91 | * <dd>month as a decimal number (01-12)</dd> | |
92 | * <dt>%M</dt> | |
93 | * <dd>minute as a decimal number (00-59)</dd> | |
94 | * <dt>%n</dt> | |
95 | * <dd>\n</dd> | |
96 | * <dt>%p</dt> | |
97 | * <dd>locale-s equivalent of the AM/PM</dd> | |
98 | * <dt>%r</dt> | |
99 | * <dd>locale's 12-hour clock time</dd> | |
100 | * <dt>%R</dt> | |
101 | * <dd>%H:%M</dd> | |
102 | * <dt>%S</dt> | |
103 | * <dd>second as a decimal number (00-60)</dd> | |
104 | * <dt>%t</dt> | |
105 | * <dd>\t</dd> | |
106 | * <dt>%T</dt> | |
107 | * <dd>%H:%M:%S</dd> | |
108 | * <dt>%u</dt> | |
109 | * <dd>ISO 8601 weekday as a decimal number (1-7)</dd> | |
110 | * <dt>%U</dt> | |
111 | * <dd>week number of the year as a decimal number (00-53)</dd> | |
112 | * <dt>%V</dt> | |
113 | * <dd>ISO 8601 week number as a decimal number (01-53)</dd> | |
114 | * <dt>%w</dt> | |
115 | * <dd>weekday as a decimal number (0-6)</dd> | |
116 | * <dt>%W</dt> | |
117 | * <dd>week number of the year as a decimal number (00-53)</dd> | |
118 | * <dt>%x</dt> | |
119 | * <dd>locale's date representation</dd> | |
120 | * <dt>%X</dt> | |
121 | * <dd>locale's time representation</dd> | |
122 | * <dt>%y</dt> | |
123 | * <dd>last 2 digits of the year as a decimal number (00-99)</dd> | |
124 | * <dt>%Y</dt> | |
125 | * <dd>year as a decimal number</dd> | |
126 | * <dt>%z</dt> | |
127 | * <dd>offset from UTC in the ISO 8601 format</dd> | |
128 | * <dt>%Z</dt> | |
129 | * <dd>locale's time zone name of abbreviation or empty</dd> | |
130 | * </dl> | |
131 | * | |
132 | * @author Guilhelm Savin | |
133 | */ | |
134 | public class ISODateIO { | |
135 | ||
136 | private static final ISODateComponent[] KNOWN_COMPONENTS = { | |
137 | ISODateComponent.ABBREVIATED_WEEKDAY_NAME, | |
138 | ISODateComponent.FULL_WEEKDAY_NAME, | |
139 | ISODateComponent.ABBREVIATED_MONTH_NAME, | |
140 | ISODateComponent.FULL_MONTH_NAME, | |
141 | ISODateComponent.LOCALE_DATE_AND_TIME, ISODateComponent.CENTURY, | |
142 | ISODateComponent.DAY_OF_MONTH_2_DIGITS, ISODateComponent.DATE, | |
143 | ISODateComponent.DAY_OF_MONTH, ISODateComponent.DATE_ISO8601, | |
144 | ISODateComponent.WEEK_BASED_YEAR_2_DIGITS, | |
145 | ISODateComponent.WEEK_BASED_YEAR_4_DIGITS, | |
146 | ISODateComponent.ABBREVIATED_MONTH_NAME_ALIAS, | |
147 | ISODateComponent.HOUR_OF_DAY, ISODateComponent.HOUR, | |
148 | ISODateComponent.DAY_OF_YEAR, ISODateComponent.MILLISECOND, | |
149 | ISODateComponent.EPOCH, ISODateComponent.MONTH, | |
150 | ISODateComponent.MINUTE, ISODateComponent.NEW_LINE, | |
151 | ISODateComponent.AM_PM, ISODateComponent.LOCALE_CLOCK_TIME_12_HOUR, | |
152 | ISODateComponent.HOUR_AND_MINUTE, ISODateComponent.SECOND, | |
153 | ISODateComponent.TABULATION, ISODateComponent.TIME_ISO8601, | |
154 | ISODateComponent.DAY_OF_WEEK_1_7, | |
155 | ISODateComponent.WEEK_OF_YEAR_FROM_SUNDAY, | |
156 | ISODateComponent.WEEK_NUMBER_ISO8601, | |
157 | ISODateComponent.DAY_OF_WEEK_0_6, | |
158 | ISODateComponent.WEEK_OF_YEAR_FROM_MONDAY, | |
159 | ISODateComponent.LOCALE_DATE_REPRESENTATION, | |
160 | ISODateComponent.LOCALE_TIME_REPRESENTATION, | |
161 | ISODateComponent.YEAR_2_DIGITS, ISODateComponent.YEAR_4_DIGITS, | |
162 | ISODateComponent.UTC_OFFSET, | |
163 | ISODateComponent.LOCALE_TIME_ZONE_NAME, ISODateComponent.PERCENT }; | |
164 | ||
165 | /** | |
166 | * List of components, build from a string format. Some of these components | |
167 | * can just be text. | |
168 | */ | |
169 | protected LinkedList<ISODateComponent> components; | |
170 | /** | |
171 | * The regular expression builds from the components. | |
172 | */ | |
173 | protected Pattern pattern; | |
174 | ||
175 | /** | |
176 | * Create a scanner with default format "%K". | |
177 | * | |
178 | * @throws ParseException | |
179 | */ | |
180 | public ISODateIO() throws ParseException { | |
181 | this("%K"); | |
182 | } | |
183 | ||
184 | /** | |
185 | * Create a new scanner with a given format. | |
186 | * | |
187 | * @param format | |
188 | * format of the scanner. | |
189 | * @throws ParseException | |
190 | * if bad directives found | |
191 | */ | |
192 | public ISODateIO(String format) throws ParseException { | |
193 |
1
1. |
setFormat(format); |
194 | } | |
195 | ||
196 | /** | |
197 | * Get the current pattern used to parse timestamp. | |
198 | * | |
199 | * @return a regular expression as a string | |
200 | */ | |
201 | public Pattern getPattern() { | |
202 |
1
1. getPattern : mutated return of Object value for org/graphstream/util/time/ISODateIO::getPattern to ( if (x != null) null else throw new RuntimeException ) → NO_COVERAGE |
return pattern; |
203 | } | |
204 | ||
205 | /** | |
206 | * Build a list of component from a string. | |
207 | * | |
208 | * @param format | |
209 | * format of the scanner | |
210 | * @return a list of components found in the string format | |
211 | * @throws ParseException | |
212 | * if invalid component found | |
213 | */ | |
214 | protected LinkedList<ISODateComponent> findComponents(String format) | |
215 | throws ParseException { | |
216 | LinkedList<ISODateComponent> components = new LinkedList<ISODateComponent>(); | |
217 | int offset = 0; | |
218 | ||
219 |
2
1. findComponents : changed conditional boundary → NO_COVERAGE 2. findComponents : negated conditional → NO_COVERAGE |
while (offset < format.length()) { |
220 |
1
1. findComponents : negated conditional → NO_COVERAGE |
if (format.charAt(offset) == '%') { |
221 | boolean found = false; | |
222 |
4
1. findComponents : changed conditional boundary → NO_COVERAGE 2. findComponents : Changed increment from 1 to -1 → NO_COVERAGE 3. findComponents : negated conditional → NO_COVERAGE 4. findComponents : negated conditional → NO_COVERAGE |
for (int i = 0; !found && i < KNOWN_COMPONENTS.length; i++) { |
223 | if (format.startsWith(KNOWN_COMPONENTS[i].getDirective(), | |
224 |
1
1. findComponents : negated conditional → NO_COVERAGE |
offset)) { |
225 | found = true; | |
226 |
1
1. findComponents : negated conditional → NO_COVERAGE |
if (KNOWN_COMPONENTS[i].isAlias()) { |
227 | LinkedList<ISODateComponent> sub = findComponents(KNOWN_COMPONENTS[i] | |
228 | .getReplacement()); | |
229 | components.addAll(sub); | |
230 | } else | |
231 |
1
1. findComponents : removed call to java/util/LinkedList::addLast → NO_COVERAGE |
components.addLast(KNOWN_COMPONENTS[i]); |
232 | ||
233 |
1
1. findComponents : Replaced integer addition with subtraction → NO_COVERAGE |
offset += KNOWN_COMPONENTS[i].getDirective().length(); |
234 | } | |
235 | } | |
236 |
1
1. findComponents : negated conditional → NO_COVERAGE |
if (!found) |
237 | throw new ParseException("unknown identifier", offset); | |
238 | } else { | |
239 | int from = offset; | |
240 |
3
1. findComponents : changed conditional boundary → NO_COVERAGE 2. findComponents : negated conditional → NO_COVERAGE 3. findComponents : negated conditional → NO_COVERAGE |
while (offset < format.length() && format.charAt(offset) != '%') |
241 |
1
1. findComponents : Changed increment from 1 to -1 → NO_COVERAGE |
offset++; |
242 |
1
1. findComponents : removed call to java/util/LinkedList::addLast → NO_COVERAGE |
components.addLast(new TextComponent(Pattern.quote(format |
243 | .substring(from, offset)))); | |
244 | } | |
245 | } | |
246 | ||
247 |
1
1. findComponents : mutated return of Object value for org/graphstream/util/time/ISODateIO::findComponents to ( if (x != null) null else throw new RuntimeException ) → NO_COVERAGE |
return components; |
248 | } | |
249 | ||
250 | /** | |
251 | * Build a regular expression from the components of the scanner. | |
252 | */ | |
253 | protected void buildRegularExpression() { | |
254 | String pattern = ""; | |
255 | ||
256 |
3
1. buildRegularExpression : changed conditional boundary → NO_COVERAGE 2. buildRegularExpression : Changed increment from 1 to -1 → NO_COVERAGE 3. buildRegularExpression : negated conditional → NO_COVERAGE |
for (int i = 0; i < components.size(); i++) { |
257 | Object c = components.get(i); | |
258 | String regexValue; | |
259 |
1
1. buildRegularExpression : negated conditional → NO_COVERAGE |
if (c instanceof ISODateComponent) |
260 | regexValue = ((ISODateComponent) c).getReplacement(); | |
261 | else | |
262 | regexValue = c.toString(); | |
263 | ||
264 | pattern += "(" + regexValue + ")"; | |
265 | } | |
266 | ||
267 | this.pattern = Pattern.compile(pattern); | |
268 | } | |
269 | ||
270 | /** | |
271 | * Set the format of this scanner. | |
272 | * | |
273 | * @param format | |
274 | * new format of the scanner | |
275 | * @throws ParseException | |
276 | * if an error is found in the new format | |
277 | */ | |
278 | public void setFormat(String format) throws ParseException { | |
279 | components = findComponents(format); | |
280 |
1
1. setFormat : removed call to org/graphstream/util/time/ISODateIO::buildRegularExpression → NO_COVERAGE |
buildRegularExpression(); |
281 | } | |
282 | ||
283 | /** | |
284 | * Parse a string which should be in the scanner format. If not, null is | |
285 | * returned. | |
286 | * | |
287 | * @param time | |
288 | * timestamp in the scanner format | |
289 | * @return a calendar modeling the time value or null if invalid format | |
290 | */ | |
291 | public Calendar parse(String time) { | |
292 | Calendar cal = Calendar.getInstance(); | |
293 | Matcher match = pattern.matcher(time); | |
294 | ||
295 |
1
1. parse : negated conditional → NO_COVERAGE |
if (match.matches()) { |
296 |
3
1. parse : changed conditional boundary → NO_COVERAGE 2. parse : Changed increment from 1 to -1 → NO_COVERAGE 3. parse : negated conditional → NO_COVERAGE |
for (int i = 0; i < components.size(); i++) |
297 |
2
1. parse : Replaced integer addition with subtraction → NO_COVERAGE 2. parse : removed call to org/graphstream/util/time/ISODateComponent::set → NO_COVERAGE |
components.get(i).set(match.group(i + 1), cal); |
298 | } else | |
299 |
1
1. parse : mutated return of Object value for org/graphstream/util/time/ISODateIO::parse to ( if (x != null) null else throw new RuntimeException ) → NO_COVERAGE |
return null; |
300 | ||
301 |
1
1. parse : mutated return of Object value for org/graphstream/util/time/ISODateIO::parse to ( if (x != null) null else throw new RuntimeException ) → NO_COVERAGE |
return cal; |
302 | } | |
303 | ||
304 | /** | |
305 | * Convert a calendar into a string according to the format of this object. | |
306 | * | |
307 | * @param calendar | |
308 | * the calendar to convert | |
309 | * @return a string modeling the calendar. | |
310 | */ | |
311 | public String toString(Calendar calendar) { | |
312 | StringBuffer buffer = new StringBuffer(); | |
313 | ||
314 |
3
1. toString : changed conditional boundary → NO_COVERAGE 2. toString : Changed increment from 1 to -1 → NO_COVERAGE 3. toString : negated conditional → NO_COVERAGE |
for (int i = 0; i < components.size(); i++) |
315 | buffer.append(components.get(i).get(calendar)); | |
316 | ||
317 |
1
1. toString : mutated return of Object value for org/graphstream/util/time/ISODateIO::toString to ( if (x != null) null else throw new RuntimeException ) → NO_COVERAGE |
return buffer.toString(); |
318 | } | |
319 | } | |
Mutations | ||
193 |
1.1 |
|
202 |
1.1 |
|
219 |
1.1 2.2 |
|
220 |
1.1 |
|
222 |
1.1 2.2 3.3 4.4 |
|
224 |
1.1 |
|
226 |
1.1 |
|
231 |
1.1 |
|
233 |
1.1 |
|
236 |
1.1 |
|
240 |
1.1 2.2 3.3 |
|
241 |
1.1 |
|
242 |
1.1 |
|
247 |
1.1 |
|
256 |
1.1 2.2 3.3 |
|
259 |
1.1 |
|
280 |
1.1 |
|
295 |
1.1 |
|
296 |
1.1 2.2 3.3 |
|
297 |
1.1 2.2 |
|
299 |
1.1 |
|
301 |
1.1 |
|
314 |
1.1 2.2 3.3 |
|
317 |
1.1 |