001/**
002 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
003 *   This file is part of the LDP4j Project:
004 *     http://www.ldp4j.org/
005 *
006 *   Center for Open Middleware
007 *     http://www.centeropenmiddleware.com/
008 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
009 *   Copyright (C) 2014 Center for Open Middleware.
010 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
011 *   Licensed under the Apache License, Version 2.0 (the "License");
012 *   you may not use this file except in compliance with the License.
013 *   You may obtain a copy of the License at
014 *
015 *             http://www.apache.org/licenses/LICENSE-2.0
016 *
017 *   Unless required by applicable law or agreed to in writing, software
018 *   distributed under the License is distributed on an "AS IS" BASIS,
019 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
020 *   See the License for the specific language governing permissions and
021 *   limitations under the License.
022 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
023 *   Artifact    : org.ldp4j.framework:ldp4j-application-api:0.2.0
024 *   Bundle      : ldp4j-application-api-0.2.0.jar
025 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
026 */
027package org.ldp4j.application.data;
028
029import static com.google.common.base.Preconditions.checkNotNull;
030
031import java.io.Serializable;
032import java.net.URI;
033import java.util.Calendar;
034import java.util.Date;
035import java.util.GregorianCalendar;
036import java.util.Locale;
037import java.util.Map.Entry;
038import java.util.TimeZone;
039import java.util.concurrent.TimeUnit;
040
041import javax.xml.datatype.XMLGregorianCalendar;
042
043import org.joda.time.DateTime;
044import org.joda.time.Duration;
045import org.joda.time.Period;
046import org.joda.time.chrono.ISOChronology;
047import org.joda.time.format.ISOPeriodFormat;
048
049import com.google.common.collect.ImmutableMap;
050
051public final class Literals {
052
053  public abstract static class DateTimeLiteralBuilder {
054
055    private DateTimeLiteralBuilder() {
056    }
057
058    public final DateTimeLiteral dateTime() {
059      return new ImmutableDateTimeLiteral(getDateTime(),Datatypes.DATE_TIME);
060    }
061
062    public final DateTimeLiteral date() {
063      return new ImmutableDateTimeLiteral(getDateTime(),Datatypes.DATE);
064    }
065
066    public final DateTimeLiteral time() {
067      return new ImmutableDateTimeLiteral(getDateTime(),Datatypes.TIME);
068    }
069
070    public final DateTimeLiteral year() {
071      return new ImmutableDateTimeLiteral(getDateTime(),Datatypes.GYEAR);
072    }
073
074    public final DateTimeLiteral month() {
075      return new ImmutableDateTimeLiteral(getDateTime(),Datatypes.GMONTH);
076    }
077
078    public final DateTimeLiteral day() {
079      return new ImmutableDateTimeLiteral(getDateTime(),Datatypes.GDAY);
080    }
081
082    public final DateTimeLiteral yearAndMonth() {
083      return new ImmutableDateTimeLiteral(getDateTime(),Datatypes.GYEARMONTH);
084    }
085
086    public final DateTimeLiteral monthAndDay() {
087      return new ImmutableDateTimeLiteral(getDateTime(),Datatypes.GMONTHDAY);
088    }
089
090    protected abstract DateTime getDateTime();
091
092  }
093
094  public static final class DateTimeDateTimeLiteralBuilder extends DateTimeLiteralBuilder {
095
096    private final DateTime dateTime;
097
098    private DateTimeDateTimeLiteralBuilder(DateTime dateTime) {
099      super();
100      this.dateTime = dateTime;
101    }
102
103    @Override
104    protected DateTime getDateTime() {
105      return dateTime;
106    }
107
108  }
109
110  public static final class XMLGregorianCalendarDateTimeLiteralBuilder extends DateTimeLiteralBuilder {
111
112    private final XMLGregorianCalendar calendar;
113
114    private TimeZone timezone;
115    private Locale locale;
116    private XMLGregorianCalendar defaults;
117
118    private XMLGregorianCalendarDateTimeLiteralBuilder(XMLGregorianCalendar calendar) {
119      super();
120      this.calendar = calendar;
121    }
122
123    public XMLGregorianCalendarDateTimeLiteralBuilder withTimeZone(TimeZone timezone) {
124      this.timezone = timezone;
125      return this;
126    }
127
128    public XMLGregorianCalendarDateTimeLiteralBuilder withLocale(Locale locale) {
129      this.locale = locale;
130      return this;
131    }
132
133    public XMLGregorianCalendarDateTimeLiteralBuilder withDefaults(XMLGregorianCalendar defaults) {
134      this.defaults = defaults;
135      return this;
136    }
137
138    @Override
139    protected DateTime getDateTime() {
140      return normalizeChronology(new DateTime(this.calendar.toGregorianCalendar(this.timezone, this.locale, this.defaults)));
141    }
142
143  }
144
145  private interface DateTimeLiteralBuilderFactory {
146
147    DateTimeLiteralBuilder createBuilder(Object value, URI datatype);
148
149  }
150
151  private interface DateTimeLiteralBuilderAdapter {
152
153    DateTimeLiteral adapt(DateTimeLiteralBuilder builder);
154
155  }
156
157  private static final ImmutableMap<Class<?>,DateTimeLiteralBuilderFactory> FACTORIES=
158    ImmutableMap.
159      <Class<?>,DateTimeLiteralBuilderFactory>builder().
160        put(
161          Date.class,
162          new DateTimeLiteralBuilderFactory() {
163            @Override
164            public DateTimeLiteralBuilder createBuilder(Object value, URI datatype) {
165              return of((Date)value);
166            }
167          }
168        ).
169        put(
170          GregorianCalendar.class,
171          new DateTimeLiteralBuilderFactory() {
172            @Override
173            public DateTimeLiteralBuilder createBuilder(Object value, URI datatype) {
174              return of((GregorianCalendar)value);
175            }
176          }
177        ).
178        put(
179          Calendar.class,
180          new DateTimeLiteralBuilderFactory() {
181            @Override
182            public DateTimeLiteralBuilder createBuilder(Object value, URI datatype) {
183              return of((Calendar)value);
184            }
185          }
186        ).
187        put(
188          DateTime.class,
189          new DateTimeLiteralBuilderFactory() {
190            @Override
191            public DateTimeLiteralBuilder createBuilder(Object value, URI datatype) {
192              return of((DateTime)value);
193            }
194          }
195        ).
196        put(
197          XMLGregorianCalendar.class,
198          new DateTimeLiteralBuilderFactory() {
199            @Override
200            public DateTimeLiteralBuilder createBuilder(Object value, URI datatype) {
201              return of((XMLGregorianCalendar)value);
202            }
203          }
204        ).
205        put(
206          String.class,
207          new DateTimeLiteralBuilderFactory() {
208            @Override
209            public DateTimeLiteralBuilder createBuilder(Object value, URI datatype) {
210              try {
211                return of(new DateTime(value));
212              } catch (Exception e) {
213                throw new DatatypeCohercionException(value,datatype,e);
214              }
215            }
216          }
217        ).
218        build();
219
220  private static final ImmutableMap<URI,DateTimeLiteralBuilderAdapter> ADAPTERS=
221    ImmutableMap.
222      <URI,DateTimeLiteralBuilderAdapter>builder().
223        put(
224          Datatypes.DATE_TIME,
225          new DateTimeLiteralBuilderAdapter() {
226            @Override
227            public DateTimeLiteral adapt(DateTimeLiteralBuilder builder) {
228              return builder.dateTime();
229            }
230          }
231        ).
232        put(
233          Datatypes.DATE,
234          new DateTimeLiteralBuilderAdapter() {
235            @Override
236            public DateTimeLiteral adapt(DateTimeLiteralBuilder builder) {
237              return builder.date();
238            }
239          }
240        ).
241        put(
242          Datatypes.TIME,
243          new DateTimeLiteralBuilderAdapter() {
244            @Override
245            public DateTimeLiteral adapt(DateTimeLiteralBuilder builder) {
246              return builder.time();
247            }
248          }
249        ).
250        put(
251          Datatypes.GYEAR,
252          new DateTimeLiteralBuilderAdapter() {
253            @Override
254            public DateTimeLiteral adapt(DateTimeLiteralBuilder builder) {
255              return builder.year();
256            }
257          }
258        ).
259        put(
260          Datatypes.GMONTH,
261          new DateTimeLiteralBuilderAdapter() {
262            @Override
263            public DateTimeLiteral adapt(DateTimeLiteralBuilder builder) {
264              return builder.month();
265            }
266          }
267        ).
268        put(
269          Datatypes.GDAY,
270          new DateTimeLiteralBuilderAdapter() {
271            @Override
272            public DateTimeLiteral adapt(DateTimeLiteralBuilder builder) {
273              return builder.day();
274            }
275          }
276        ).
277        put(
278          Datatypes.GYEARMONTH,
279          new DateTimeLiteralBuilderAdapter() {
280            @Override
281            public DateTimeLiteral adapt(DateTimeLiteralBuilder builder) {
282              return builder.yearAndMonth();
283            }
284          }
285        ).
286        put(
287          Datatypes.GMONTHDAY,
288          new DateTimeLiteralBuilderAdapter() {
289            @Override
290            public DateTimeLiteral adapt(DateTimeLiteralBuilder builder) {
291              return builder.monthAndDay();
292            }
293          }
294        ).
295        build();
296
297  private static final String DATATYPE_CANNOT_BE_NULL = "Datatype cannot be null";
298  private static final String LANGUAGE_CANNOT_BE_NULL = "Language cannot be null";
299  private static final String STRING_CANNOT_BE_NULL = "String cannot be null";
300  private static final String TIME_UNIT_CANNOT_BE_NULL = "Time unit cannot be null";
301  private static final String DATE_TIME_CANNOT_BE_NULL = "Date-time cannot be null";
302  private static final String LITERAL_VALUE_CANNOT_BE_NULL = "Literal value cannot be null";
303  private static final String CALENDAR_CANNOT_BE_NULL = "Calendar cannot be null";
304  private static final String TIME_CANNOT_BE_NULL = "Time cannot be null";
305  private static final String DATE_CANNOT_BE_NULL = "Date cannot be null";
306  private static final String DURATION_CANNOT_BE_NULL = "Duration cannot be null";
307
308
309  private static final Class<?>[] DATE_TIME_CLASSES={
310    java.sql.Date.class,
311    Date.class,
312    java.sql.Time.class,
313    DateTime.class,
314    GregorianCalendar.class,
315    Calendar.class,
316    XMLGregorianCalendar.class
317  };
318
319  private static final Class<?>[] DURATION_CLASSES={
320    Duration.class,
321    javax.xml.datatype.Duration.class
322  };
323
324  private Literals() {
325  }
326
327  private static boolean isDateTime(Object obj) {
328    return isInstanceOf(obj,Literals.DATE_TIME_CLASSES);
329  }
330
331  private static boolean isDuration(Object obj) {
332    return isInstanceOf(obj,Literals.DURATION_CLASSES);
333  }
334
335  private static boolean isInstanceOf(Object obj, Class<?>[] classes) {
336    for(Class<?> clazz:classes) {
337      if(clazz.isInstance(obj)) {
338        return true;
339      }
340    }
341    return false;
342  }
343
344  private static DateTime normalizeChronology(DateTime dateTime) {
345    return dateTime.withChronology(ISOChronology.getInstance());
346  }
347
348  private static <T> DurationLiteral coherceDuration(T value) {
349    DurationLiteral duration=null;
350    if(value instanceof Duration) {
351      duration=of((Duration)value);
352    } else if(value instanceof javax.xml.datatype.Duration) {
353      duration=of((javax.xml.datatype.Duration)value);
354    } else if(value instanceof String) {
355      try {
356        Period period = ISOPeriodFormat.standard().parsePeriod((String)value);
357        duration=of(period.toStandardDuration());
358      } catch (Exception e) {
359        throw new DatatypeCohercionException(value,Datatypes.DURATION,e);
360      }
361    } else {
362      throw new DatatypeCohercionException(value,Datatypes.DURATION);
363    }
364    return duration;
365  }
366
367  private static DateTimeLiteral coherceDateTime(Object value, URI datatype) throws AssertionError {
368    DateTimeLiteral dateTime=null;
369    if(value instanceof java.sql.Date) {
370      dateTime=of((java.sql.Date)value);
371    } else if(value instanceof java.sql.Time) {
372      dateTime=of((java.sql.Time)value);
373    } else {
374      dateTime=coherceVariableDateTime(value,datatype);
375    }
376    return dateTime;
377  }
378
379  private static DateTimeLiteral coherceVariableDateTime(Object value, URI datatype) {
380    return Literals.ADAPTERS.get(datatype).adapt(getBuilder(value,datatype));
381  }
382
383  private static DateTimeLiteralBuilder getBuilder(Object value, URI datatype) {
384    for(Entry<Class<?>,DateTimeLiteralBuilderFactory> entry:Literals.FACTORIES.entrySet()) {
385      if(entry.getKey().isInstance(value)) {
386        return entry.getValue().createBuilder(value, datatype);
387      }
388    }
389    throw new DatatypeCohercionException(value,datatype);
390  }
391
392  public static Literal<String> of(String value) {
393    checkNotNull(value,STRING_CANNOT_BE_NULL);
394    return new ImmutableLiteral<String>(value);
395  }
396
397  public static DurationLiteral of(javax.xml.datatype.Duration duration) {
398    checkNotNull(duration,DURATION_CANNOT_BE_NULL);
399    Period period = ISOPeriodFormat.standard().parsePeriod(duration.toString());
400    return new ImmutableDurationLiteral(period.toStandardDuration(),Datatypes.DURATION);
401  }
402
403  public static DurationLiteral of(Duration duration) {
404    checkNotNull(duration,DURATION_CANNOT_BE_NULL);
405    return new ImmutableDurationLiteral(duration,Datatypes.DURATION);
406  }
407
408  public static DateTimeDateTimeLiteralBuilder of(Date date) {
409    checkNotNull(date,DATE_CANNOT_BE_NULL);
410    return new DateTimeDateTimeLiteralBuilder(new DateTime(date));
411  }
412
413  public static DateTimeLiteral of(java.sql.Date date) {
414    checkNotNull(date,DATE_CANNOT_BE_NULL);
415    return new ImmutableDateTimeLiteral(new DateTime(date), Datatypes.DATE);
416  }
417
418  public static DateTimeLiteral of(java.sql.Time time) {
419    checkNotNull(time,TIME_CANNOT_BE_NULL);
420    return new ImmutableDateTimeLiteral(new DateTime(time.getTime()), Datatypes.TIME);
421  }
422
423  public static DateTimeDateTimeLiteralBuilder of(Calendar calendar) {
424    checkNotNull(calendar,CALENDAR_CANNOT_BE_NULL);
425    return new DateTimeDateTimeLiteralBuilder(normalizeChronology(new DateTime(calendar)));
426  }
427
428  public static DateTimeDateTimeLiteralBuilder of(GregorianCalendar calendar) {
429    checkNotNull(calendar,CALENDAR_CANNOT_BE_NULL);
430    return new DateTimeDateTimeLiteralBuilder(normalizeChronology(new DateTime(calendar)));
431  }
432
433  public static XMLGregorianCalendarDateTimeLiteralBuilder of(XMLGregorianCalendar calendar) {
434    checkNotNull(calendar,CALENDAR_CANNOT_BE_NULL);
435    return new XMLGregorianCalendarDateTimeLiteralBuilder(calendar);
436  }
437
438  public static DateTimeDateTimeLiteralBuilder of(DateTime dateTime) {
439    checkNotNull(dateTime,DATE_TIME_CANNOT_BE_NULL);
440    return new DateTimeDateTimeLiteralBuilder(dateTime);
441  }
442
443  public static DurationLiteral duration(long time, TimeUnit unit) {
444    checkNotNull(time,TIME_CANNOT_BE_NULL);
445    checkNotNull(unit,TIME_UNIT_CANNOT_BE_NULL);
446    return of(new Duration(TimeUnit.MILLISECONDS.convert(time, unit)));
447  }
448
449  public static Literal<? extends Serializable> newLiteral(Object value) {
450    checkNotNull(value,LITERAL_VALUE_CANNOT_BE_NULL);
451    Literal<? extends Serializable> result=null;
452    if(isDuration(value)) {
453      result=coherceDuration(value);
454    } else if(isDateTime(value)) {
455      result=coherceDateTime(value,Datatypes.DATE_TIME);
456    } else if(value instanceof Serializable) {
457      result=new ImmutableLiteral<Serializable>((Serializable)value);
458    } else {
459      result=new ImmutableTypedLiteral<String>(value.toString(),Datatypes.STRING);
460    }
461    return result;
462  }
463
464  public static TypedLiteral<? extends Serializable> newTypedLiteral(Object value, URI datatype) {
465    checkNotNull(value,LITERAL_VALUE_CANNOT_BE_NULL);
466    checkNotNull(datatype,DATATYPE_CANNOT_BE_NULL);
467    TypedLiteral<? extends Serializable> result=null;
468    if(Datatypes.isDuration(datatype)) {
469      result=coherceDuration(value);
470    } else if(Datatypes.isTemporal(datatype)) {
471      result=coherceDateTime(value, datatype);
472    } else if(value instanceof Serializable){
473      result=new ImmutableTypedLiteral<Serializable>((Serializable)value,datatype);
474    } else {
475      result=new ImmutableTypedLiteral<String>(value.toString(),Datatypes.STRING);
476    }
477    return result;
478  }
479
480  public static LanguageLiteral newLanguageLiteral(String value, String language) {
481    checkNotNull(value,STRING_CANNOT_BE_NULL);
482    checkNotNull(language,LANGUAGE_CANNOT_BE_NULL);
483    return new ImmutableLanguageLiteral(value,language);
484  }
485
486}