blob: 0855885ef9172845c88a57d17aa885f668154f4b [file] [log] [blame]
Nils Diewald92729ce2014-10-06 16:00:17 +00001package de.ids_mannheim.korap.query.wrap;
2
3import org.apache.lucene.search.spans.SpanQuery;
Nils Diewaldbe5943e2014-10-21 19:35:34 +00004import de.ids_mannheim.korap.util.QueryException;
Akrona7b936d2016-03-04 13:40:54 +01005import de.ids_mannheim.korap.query.SpanFocusQuery;
6import de.ids_mannheim.korap.query.SpanClassQuery;
7import de.ids_mannheim.korap.query.SpanWithinQuery;
Nils Diewald92729ce2014-10-06 16:00:17 +00008
Nils Diewald0339d462015-02-26 14:53:56 +00009// TODO: Add warnings and errors - using KrillQuery
Nils Diewald92729ce2014-10-06 16:00:17 +000010
11/**
Nils Diewald5dfbd502015-01-29 03:54:36 +000012 * A wrapper base class for Lucene SpanQueries,
13 * that add certain information,
14 * necessary for the correct and optimized
15 * deserialization of nested queries.
Nils Diewaldbb33da22015-03-04 16:24:25 +000016 *
Nils Diewald5dfbd502015-01-29 03:54:36 +000017 * This class is meant to be extended by
18 * wrapper classes.
Nils Diewaldbb33da22015-03-04 16:24:25 +000019 *
Nils Diewald6535c522015-02-26 17:45:24 +000020 * <strong>Warning</strong>: SpanQueryWrapper
Nils Diewaldbb33da22015-03-04 16:24:25 +000021 * will probably be merged with {@link KrillQuery} in near future. Use
22 * of this API is on your own risk.
23 *
Nils Diewald5dfbd502015-01-29 03:54:36 +000024 * @author diewald
Nils Diewald92729ce2014-10-06 16:00:17 +000025 */
26public class SpanQueryWrapper {
Nils Diewald92729ce2014-10-06 16:00:17 +000027
Nils Diewald5dfbd502015-01-29 03:54:36 +000028 // Boundaries, e.g. for repetitions
29 protected int min = 1, max = 1;
30
31 // Class number
Nils Diewaldbe5943e2014-10-21 19:35:34 +000032 protected byte number = (byte) 0;
Nils Diewaldbe5943e2014-10-21 19:35:34 +000033
Nils Diewald5dfbd502015-01-29 03:54:36 +000034 // Boolean properties
Nils Diewaldbb33da22015-03-04 16:24:25 +000035 protected boolean hasClass = false, isNull = true, isOptional = false,
36 isNegative = false, isEmpty = false, isExtended = false,
37 isExtendedToTheRight = false, maybeUnsorted = false,
38 retrieveNode = false;
39
Nils Diewaldbe5943e2014-10-21 19:35:34 +000040
Nils Diewald5dfbd502015-01-29 03:54:36 +000041 /**
42 * Serialize the wrapped query and return a SpanQuery.
43 * This should be overwritten.
Nils Diewaldbb33da22015-03-04 16:24:25 +000044 *
Nils Diewald5dfbd502015-01-29 03:54:36 +000045 * @return A {@link SpanQuery} object.
46 * @throws QueryException
47 */
Akrona7b936d2016-03-04 13:40:54 +010048 public SpanQuery toFragmentQuery () throws QueryException {
49 System.err.println("||||||||||||||||||||||||||");
Nils Diewald5dfbd502015-01-29 03:54:36 +000050 return (SpanQuery) null;
Nils Diewald92729ce2014-10-06 16:00:17 +000051 };
52
Nils Diewald5dfbd502015-01-29 03:54:36 +000053
Akrona7b936d2016-03-04 13:40:54 +010054
55 /**
56 * Serialize the wrapped query and return a SpanQuery.
57 * This will be the final query and may be rewritten.
58 *
59 * @return A {@link SpanQuery} object.
60 * @throws QueryException
61 */
62 public SpanQuery toQuery () throws QueryException {
63
64 if (this.isNull() || this.isEmpty())
65 return null;
66
67 // Wrap the query in a <base/s=t>, if it's extended to the right
68 if (this.isExtendedToTheRight()) {
Akron6759b042016-04-28 01:25:00 +020069 return new SpanFocusQuery(new SpanWithinQuery("base/s:t",
70 new SpanClassQuery(this.toFragmentQuery(), (byte) 254)),
71 (byte) 254);
Akrona7b936d2016-03-04 13:40:54 +010072 };
73
74 return this.toFragmentQuery();
75 };
76
Akron6759b042016-04-28 01:25:00 +020077
Nils Diewald5dfbd502015-01-29 03:54:36 +000078 /**
79 * Boolean value indicating that the wrapped query
80 * is optional.
Nils Diewaldbb33da22015-03-04 16:24:25 +000081 *
Nils Diewald5dfbd502015-01-29 03:54:36 +000082 * For example the segment denoting an adjective
83 * in the following Poliqarp expression is optional.
Nils Diewaldbb33da22015-03-04 16:24:25 +000084 *
Nils Diewald5dfbd502015-01-29 03:54:36 +000085 * <blockquote><pre>
Nils Diewaldbb33da22015-03-04 16:24:25 +000086 * the [pos=ADJ]? tree
Nils Diewald5dfbd502015-01-29 03:54:36 +000087 * </pre></blockquote>
Nils Diewaldbb33da22015-03-04 16:24:25 +000088 *
Nils Diewald5dfbd502015-01-29 03:54:36 +000089 * @return <tt>true</tt> in case the wrapped query is
90 * optional and <tt>false</tt> in case it is
91 * mandatory.
92 */
Nils Diewald92729ce2014-10-06 16:00:17 +000093 public boolean isOptional () {
Nils Diewald5dfbd502015-01-29 03:54:36 +000094 return this.isOptional;
Nils Diewald92729ce2014-10-06 16:00:17 +000095 };
96
Nils Diewald5dfbd502015-01-29 03:54:36 +000097
98 /**
99 * Boolean value indicating that the wrapped query is
100 * <tt>null</tt>, meaning it doesn't match anything at
101 * all.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000102 *
Nils Diewald5dfbd502015-01-29 03:54:36 +0000103 * For example the segment denoting an adjective
104 * in the following Poliqarp expression doen't match
105 * anything.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000106 *
Nils Diewald5dfbd502015-01-29 03:54:36 +0000107 * <blockquote><pre>
Nils Diewaldbb33da22015-03-04 16:24:25 +0000108 * the [pos=ADJ]{0} tree
Nils Diewald5dfbd502015-01-29 03:54:36 +0000109 * </pre></blockquote>
Nils Diewaldbb33da22015-03-04 16:24:25 +0000110 *
Nils Diewald5dfbd502015-01-29 03:54:36 +0000111 * @return <tt>true</tt> in case the wrapped query can't
112 * match anything, otherwise <tt>false</tt>.
113 */
Nils Diewald92729ce2014-10-06 16:00:17 +0000114 public boolean isNull () {
Nils Diewald5dfbd502015-01-29 03:54:36 +0000115 if (this.getMin() == 0 && this.getMax() == 0)
116 return true;
117 return this.isNull;
Nils Diewald92729ce2014-10-06 16:00:17 +0000118 };
119
Nils Diewald5dfbd502015-01-29 03:54:36 +0000120
121 /**
122 * Boolean value indicating that the wrapped query matches
123 * in case the condition of the query is not true.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000124 *
Nils Diewald5dfbd502015-01-29 03:54:36 +0000125 * For example the segment denoting an adjective
126 * in the following Poliqarp expression is negative.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000127 *
Nils Diewald5dfbd502015-01-29 03:54:36 +0000128 * <blockquote><pre>
Nils Diewaldbb33da22015-03-04 16:24:25 +0000129 * the [pos!=ADJ]
Nils Diewald5dfbd502015-01-29 03:54:36 +0000130 * </pre></blockquote>
Nils Diewaldbb33da22015-03-04 16:24:25 +0000131 *
Nils Diewald5dfbd502015-01-29 03:54:36 +0000132 * @return <tt>true</tt> in case the wrapped query is
133 * negative, otherwise <tt>false</tt>.
134 */
Nils Diewald92729ce2014-10-06 16:00:17 +0000135 public boolean isNegative () {
Nils Diewald5dfbd502015-01-29 03:54:36 +0000136 return this.isNegative;
Nils Diewald92729ce2014-10-06 16:00:17 +0000137 };
138
Nils Diewaldbb33da22015-03-04 16:24:25 +0000139
Nils Diewald5dfbd502015-01-29 03:54:36 +0000140 /**
141 * Boolean value indicating that the wrapped query has
142 * no further condition for matching and therefore
143 * matches everywhere.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000144 *
Nils Diewald5dfbd502015-01-29 03:54:36 +0000145 * For example the empty segment in the following
146 * Poliqarp expression matches without any condition.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000147 *
Nils Diewald5dfbd502015-01-29 03:54:36 +0000148 * <blockquote><pre>
Nils Diewaldbb33da22015-03-04 16:24:25 +0000149 * the []
Nils Diewald5dfbd502015-01-29 03:54:36 +0000150 * </pre></blockquote>
Nils Diewaldbb33da22015-03-04 16:24:25 +0000151 *
Nils Diewald5dfbd502015-01-29 03:54:36 +0000152 * @return <tt>true</tt> in case the wrapped query is
153 * empty, otherwise <tt>false</tt>.
154 */
Nils Diewaldbe5943e2014-10-21 19:35:34 +0000155 public boolean isEmpty () {
Nils Diewald5dfbd502015-01-29 03:54:36 +0000156 return this.isEmpty;
Nils Diewaldbe5943e2014-10-21 19:35:34 +0000157 };
158
Nils Diewald5dfbd502015-01-29 03:54:36 +0000159
160 /**
161 * Boolean value indicating that the wrapped query
162 * is extended by subquery.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000163 *
Nils Diewald5dfbd502015-01-29 03:54:36 +0000164 * For example the segment denoting an adjective may
165 * be wrapped as having an extension to the left.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000166 *
Nils Diewald5dfbd502015-01-29 03:54:36 +0000167 * <blockquote><pre>
Nils Diewaldbb33da22015-03-04 16:24:25 +0000168 * []{3,4}[base=tree]
Nils Diewald5dfbd502015-01-29 03:54:36 +0000169 * </pre></blockquote>
Nils Diewaldbb33da22015-03-04 16:24:25 +0000170 *
Nils Diewald5dfbd502015-01-29 03:54:36 +0000171 * @return <tt>true</tt> in case the wrapped query is
172 * extended, otherwise <tt>false</tt>.
173 */
Nils Diewald8db8f922014-10-24 17:43:13 +0000174 public boolean isExtended () {
Nils Diewald5dfbd502015-01-29 03:54:36 +0000175 return this.isExtended;
Nils Diewald8db8f922014-10-24 17:43:13 +0000176 };
177
Nils Diewald5dfbd502015-01-29 03:54:36 +0000178
Akrona7b936d2016-03-04 13:40:54 +0100179 public SpanQueryWrapper isExtended (boolean extended) {
180 this.isExtended = extended;
181 return this;
182 };
183
184
185
Nils Diewald5dfbd502015-01-29 03:54:36 +0000186 /**
187 * Boolean value indicating that the wrapped query
188 * is extended by a subquery to the right.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000189 *
Nils Diewald5dfbd502015-01-29 03:54:36 +0000190 * For example the segment denoting the lemma tree
191 * may be wrapped as being extended to the right
192 * in the following Poliqarp expression.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000193 *
Nils Diewald5dfbd502015-01-29 03:54:36 +0000194 * <blockquote><pre>
Nils Diewaldbb33da22015-03-04 16:24:25 +0000195 * [base=tree][]{3,4}
Nils Diewald5dfbd502015-01-29 03:54:36 +0000196 * </pre></blockquote>
Nils Diewaldbb33da22015-03-04 16:24:25 +0000197 *
Nils Diewald5dfbd502015-01-29 03:54:36 +0000198 * This information is necessary to ensure a match
199 * is valid even at the end of a document.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000200 *
Nils Diewald5dfbd502015-01-29 03:54:36 +0000201 * @return <tt>true</tt> in case the wrapped query is
202 * extended to the right, otherwise <tt>false</tt>.
203 */
Nils Diewald3b07c372014-10-22 21:59:17 +0000204 public boolean isExtendedToTheRight () {
Nils Diewald5dfbd502015-01-29 03:54:36 +0000205 return this.isExtendedToTheRight;
Nils Diewald3b07c372014-10-22 21:59:17 +0000206 };
207
Nils Diewald5dfbd502015-01-29 03:54:36 +0000208
Akrona7b936d2016-03-04 13:40:54 +0100209 public SpanQueryWrapper isExtendedToTheRight (boolean extended) {
210 this.isExtendedToTheRight = extended;
211 return this;
212 };
213
214
Nils Diewald5dfbd502015-01-29 03:54:36 +0000215 /**
216 * Check, if the wrapped query can be used as an
217 * anchor query in a sequence, i.e. a query that
218 * has a condition that must be positively evaluated.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000219 *
Nils Diewald5dfbd502015-01-29 03:54:36 +0000220 * Wrapped queries with positive conditions are neither
221 * negative, optional, nor empty.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000222 *
Nils Diewald5dfbd502015-01-29 03:54:36 +0000223 * This is the opposite of {@link #maybeExtension}.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000224 *
Nils Diewald5dfbd502015-01-29 03:54:36 +0000225 * @return <tt>true</tt> in case the wrapped query
226 * can be used as an anchor in a sequence,
227 * otherwise <tt>false</tt>.
228 * @see SpanSequenceQueryWrapper
229 */
Nils Diewaldbe5943e2014-10-21 19:35:34 +0000230 public boolean maybeAnchor () {
Nils Diewaldbb33da22015-03-04 16:24:25 +0000231 if (this.isNegative())
232 return false;
233 if (this.isOptional())
234 return false;
235 if (this.isEmpty()) {
236 return false;
237 }
Nils Diewald5dfbd502015-01-29 03:54:36 +0000238 return true;
Nils Diewaldbe5943e2014-10-21 19:35:34 +0000239 };
240
Nils Diewald5dfbd502015-01-29 03:54:36 +0000241
242 /**
243 * Check, if the wrapped query can't be used as an
244 * anchor query in a sequence, meaning it has to be
245 * constructed as an extension to an anchor query.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000246 *
Nils Diewald5dfbd502015-01-29 03:54:36 +0000247 * Wrapped queries with negative conditions are either
248 * negative, optional, or empty.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000249 *
Nils Diewald5dfbd502015-01-29 03:54:36 +0000250 * This is the opposite of {@link #maybeAnchor}.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000251 *
Nils Diewald5dfbd502015-01-29 03:54:36 +0000252 * @return <tt>true</tt> in case the wrapped query
253 * has to be used as an extension in a sequence,
254 * otherwise <tt>false</tt>.
255 * @see SpanSequenceQueryWrapper
256 */
Nils Diewaldbe5943e2014-10-21 19:35:34 +0000257 public boolean maybeExtension () {
Nils Diewald5dfbd502015-01-29 03:54:36 +0000258 return !this.maybeAnchor();
Nils Diewaldbe5943e2014-10-21 19:35:34 +0000259 };
260
Nils Diewald5dfbd502015-01-29 03:54:36 +0000261
262 /**
Nils Diewald0981c212015-02-13 20:47:10 +0000263 * Check, if the wrapped query may need to be sorted
264 * on focussing on a specific class.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000265 *
Nils Diewald0981c212015-02-13 20:47:10 +0000266 * Normally spans are always sorted, but in case of
267 * a wrapped relation query, classed operands may
268 * be in arbitrary order. When focussing on these
269 * classes, the span has to me reordered.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000270 *
Nils Diewald0981c212015-02-13 20:47:10 +0000271 * @return <tt>true</tt> in case the wrapped query
272 * has to be sorted on focussing,
273 * otherwise <tt>false</tt>.
274 */
275 public boolean maybeUnsorted () {
276 return this.maybeUnsorted;
277 };
278
279
280 /**
Nils Diewald5dfbd502015-01-29 03:54:36 +0000281 * Get the minimum number of repetitions of the
282 * wrapped query.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000283 *
Nils Diewald5dfbd502015-01-29 03:54:36 +0000284 * @return The minimum number of repetions.
285 * @see SpanRepetitionQueryWrapper
286 */
Nils Diewaldbe5943e2014-10-21 19:35:34 +0000287 public int getMin () {
Nils Diewald5dfbd502015-01-29 03:54:36 +0000288 return this.min;
Nils Diewald92729ce2014-10-06 16:00:17 +0000289 };
290
Nils Diewaldbe5943e2014-10-21 19:35:34 +0000291
Nils Diewald5dfbd502015-01-29 03:54:36 +0000292 /**
293 * Set the minimum number of repetitions of the
294 * wrapped query.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000295 *
296 * @param min
297 * The minimum number of repetions.
Nils Diewald5dfbd502015-01-29 03:54:36 +0000298 * @return The {@link SpanQueryWrapper} object for chaining.
299 */
Nils Diewaldbe5943e2014-10-21 19:35:34 +0000300 public SpanQueryWrapper setMin (int min) {
Nils Diewald5dfbd502015-01-29 03:54:36 +0000301 this.min = min;
302 return this;
Nils Diewaldbe5943e2014-10-21 19:35:34 +0000303 };
304
Nils Diewald5dfbd502015-01-29 03:54:36 +0000305
306 /**
307 * Get the maximum number of repetitions of the
308 * wrapped query.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000309 *
Nils Diewald5dfbd502015-01-29 03:54:36 +0000310 * @return The maximum number of repetions.
311 * @see SpanRepetitionQueryWrapper
312 */
313 public int getMax () {
314 return this.max;
315 };
316
317
318 /**
319 * Set the maximum number of repetitions of the
320 * wrapped query.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000321 *
322 * @param max
323 * The maximum number of repetions.
Nils Diewald5dfbd502015-01-29 03:54:36 +0000324 * @return The {@link SpanQueryWrapper} object for chaining.
325 */
Nils Diewaldbe5943e2014-10-21 19:35:34 +0000326 public SpanQueryWrapper setMax (int max) {
Nils Diewald5dfbd502015-01-29 03:54:36 +0000327 this.max = max;
328 return this;
Nils Diewaldbe5943e2014-10-21 19:35:34 +0000329 };
330
331
Nils Diewald5dfbd502015-01-29 03:54:36 +0000332 /**
Nils Diewaldb9dd4132015-02-16 16:32:41 +0000333 * Make the query request node information in addition to
334 * span information.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000335 *
336 * @param retrieve
337 * Boolean value saying the wrapper
338 * has or has not to respect node information.
Nils Diewaldb9dd4132015-02-16 16:32:41 +0000339 * @return The {@link SpanQueryWrapper} object for chaining.
340 */
341 public SpanQueryWrapper retrieveNode (boolean retrieve) {
342 this.retrieveNode = retrieve;
343 return this;
344 };
345
346
347 /**
Nils Diewald5dfbd502015-01-29 03:54:36 +0000348 * Boolean value indicating that a wrapped query
349 * has a class. This is especially relevant for classed
350 * extension queries.
351 */
Nils Diewaldbe5943e2014-10-21 19:35:34 +0000352 public boolean hasClass () {
Nils Diewald5dfbd502015-01-29 03:54:36 +0000353 return this.hasClass;
Nils Diewaldbe5943e2014-10-21 19:35:34 +0000354 };
355
Nils Diewaldbe5943e2014-10-21 19:35:34 +0000356
Nils Diewald5dfbd502015-01-29 03:54:36 +0000357 /**
358 * Get the class number, if set.
359 * Returns <tt>0</tt> in case no class is set.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000360 *
Nils Diewald5dfbd502015-01-29 03:54:36 +0000361 * @return The class number.
362 */
Nils Diewaldbe5943e2014-10-21 19:35:34 +0000363 public byte getClassNumber () {
Nils Diewald5dfbd502015-01-29 03:54:36 +0000364 return this.number;
Nils Diewaldbe5943e2014-10-21 19:35:34 +0000365 };
366
Nils Diewald5dfbd502015-01-29 03:54:36 +0000367
368 /**
369 * Set the class number.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000370 *
371 * @param number
372 * The class number as a byte value.
Nils Diewald5dfbd502015-01-29 03:54:36 +0000373 * @return The {@link SpanQueryWrapper} object for chaining.
374 */
Nils Diewaldbe5943e2014-10-21 19:35:34 +0000375 public SpanQueryWrapper setClassNumber (byte number) {
Nils Diewald5dfbd502015-01-29 03:54:36 +0000376 this.hasClass = true;
377 this.number = number;
378 return this;
Nils Diewaldbe5943e2014-10-21 19:35:34 +0000379 };
380
Nils Diewald5dfbd502015-01-29 03:54:36 +0000381
382 /**
383 * Set the class number.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000384 *
385 * @param number
386 * The class number as a short value.
Nils Diewald5dfbd502015-01-29 03:54:36 +0000387 * @return The {@link SpanQueryWrapper} object for chaining.
388 */
Nils Diewaldbe5943e2014-10-21 19:35:34 +0000389 public SpanQueryWrapper setClassNumber (short number) {
Nils Diewald5dfbd502015-01-29 03:54:36 +0000390 return this.setClassNumber((byte) number);
Nils Diewaldbe5943e2014-10-21 19:35:34 +0000391 };
392
Nils Diewald5dfbd502015-01-29 03:54:36 +0000393
394 /**
395 * Set the class number.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000396 *
397 * @param number
398 * The class number as an integer value.
Nils Diewald5dfbd502015-01-29 03:54:36 +0000399 * @return The {@link SpanQueryWrapper} object for chaining.
400 */
Nils Diewaldbe5943e2014-10-21 19:35:34 +0000401 public SpanQueryWrapper setClassNumber (int number) {
Nils Diewald5dfbd502015-01-29 03:54:36 +0000402 return this.setClassNumber((byte) number);
Nils Diewaldbe5943e2014-10-21 19:35:34 +0000403 };
404
Nils Diewald5dfbd502015-01-29 03:54:36 +0000405
406 /**
407 * Serialize the wrapped query to a string representation.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000408 *
Nils Diewald5dfbd502015-01-29 03:54:36 +0000409 * This is meant to be overwritten.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000410 *
Nils Diewald5dfbd502015-01-29 03:54:36 +0000411 * @return A string containg the query representation.
412 */
Nils Diewaldbe5943e2014-10-21 19:35:34 +0000413 public String toString () {
Nils Diewaldbb33da22015-03-04 16:24:25 +0000414 String string = "" + (this.isNull() ? "isNull" : "notNull") + "-"
415 + (this.isEmpty() ? "isEmpty" : "notEmpty") + "-"
416 + (this.isOptional() ? "isOptional" : "notOptional");
Nils Diewald5dfbd502015-01-29 03:54:36 +0000417 return string;
Nils Diewaldbe5943e2014-10-21 19:35:34 +0000418 };
Nils Diewald92729ce2014-10-06 16:00:17 +0000419};