001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.acacia.client.widgets.serialdate; 029 030import org.opencms.acacia.shared.A_CmsSerialDateValue; 031import org.opencms.acacia.shared.I_CmsSerialDateValue; 032import org.opencms.gwt.client.util.CmsDebugLog; 033import org.opencms.util.CmsUUID; 034 035import java.util.Collection; 036import java.util.Date; 037import java.util.HashSet; 038import java.util.SortedSet; 039import java.util.TreeSet; 040 041import com.google.gwt.json.client.JSONArray; 042import com.google.gwt.json.client.JSONBoolean; 043import com.google.gwt.json.client.JSONObject; 044import com.google.gwt.json.client.JSONParser; 045import com.google.gwt.json.client.JSONString; 046import com.google.gwt.json.client.JSONValue; 047 048/** 049 * Client-side implementation of {@link I_CmsSerialDateValue}. 050 * The implementation additionally has setters for the various values of the serial date specification. 051 */ 052public class CmsSerialDateValue extends A_CmsSerialDateValue implements I_CmsObservableSerialDateValue { 053 054 /** The list of value change observers. */ 055 Collection<I_CmsSerialDateValueChangeObserver> m_valueChangeObservers = new HashSet<>(); 056 057 /** Default constructor, setting the default state of the the serial date widget. */ 058 public CmsSerialDateValue() { 059 setDefaultValue(); 060 } 061 062 /** 063 * @see org.opencms.acacia.client.widgets.serialdate.I_CmsObservableSerialDateValue#registerValueChangeObserver(org.opencms.acacia.client.widgets.serialdate.I_CmsSerialDateValueChangeObserver) 064 */ 065 public void registerValueChangeObserver(I_CmsSerialDateValueChangeObserver obs) { 066 067 m_valueChangeObservers.add(obs); 068 069 } 070 071 /** 072 * Set the value as provided. 073 * @param value the serial date value as JSON string. 074 */ 075 public final void setValue(String value) { 076 077 if ((null == value) || value.isEmpty()) { 078 setDefaultValue(); 079 } else { 080 try { 081 tryToSetParsedValue(value); 082 } catch (@SuppressWarnings("unused") Exception e) { 083 CmsDebugLog.consoleLog("Could not set invalid serial date value: " + value); 084 setDefaultValue(); 085 } 086 } 087 notifyOnValueChange(); 088 } 089 090 /** 091 * Convert the information from the wrapper to a JSON object. 092 * @return the serial date information as JSON. 093 */ 094 public JSONValue toJson() { 095 096 JSONObject result = new JSONObject(); 097 if (null != getStart()) { 098 result.put(JsonKey.START, dateToJson(getStart())); 099 } 100 if (null != getEnd()) { 101 result.put(JsonKey.END, dateToJson(getEnd())); 102 } 103 if (isWholeDay()) { 104 result.put(JsonKey.WHOLE_DAY, JSONBoolean.getInstance(true)); 105 } 106 JSONObject pattern = patternToJson(); 107 result.put(JsonKey.PATTERN, pattern); 108 SortedSet<Date> exceptions = getExceptions(); 109 if (exceptions.size() > 0) { 110 result.put(JsonKey.EXCEPTIONS, datesToJsonArray(exceptions)); 111 } 112 switch (getEndType()) { 113 case DATE: 114 result.put(JsonKey.SERIES_ENDDATE, dateToJson(getSeriesEndDate())); 115 break; 116 case TIMES: 117 result.put(JsonKey.SERIES_OCCURRENCES, new JSONString(String.valueOf(getOccurrences()))); 118 break; 119 case SINGLE: 120 default: 121 break; 122 } 123 if (!isCurrentTillEnd()) { 124 result.put(JsonKey.CURRENT_TILL_END, JSONBoolean.getInstance(false)); 125 } 126 if (getParentSeriesId() != null) { 127 result.put(JsonKey.PARENT_SERIES, new JSONString(getParentSeriesId().getStringValue())); 128 } 129 return result; 130 } 131 132 /** 133 * @see java.lang.Object#toString() 134 */ 135 @Override 136 public String toString() { 137 138 JSONValue json = toJson(); 139 return json.toString(); 140 } 141 142 /** 143 * @see org.opencms.acacia.client.widgets.serialdate.I_CmsObservableSerialDateValue#unregisterValueChangeObserver(org.opencms.acacia.client.widgets.serialdate.I_CmsSerialDateValueChangeObserver) 144 */ 145 public void unregisterValueChangeObserver(I_CmsSerialDateValueChangeObserver obs) { 146 147 m_valueChangeObservers.remove(obs); 148 149 } 150 151 /** 152 * Converts a collection of dates to a JSON array with the long representation of the dates as strings. 153 * @param dates the list to convert. 154 * @return JSON array with long values of dates as string 155 */ 156 private JSONValue datesToJsonArray(Collection<Date> dates) { 157 158 if (null != dates) { 159 JSONArray result = new JSONArray(); 160 for (Date d : dates) { 161 result.set(result.size(), dateToJson(d)); 162 } 163 return result; 164 } 165 return null; 166 } 167 168 /** 169 * Convert a date to the String representation we use in the JSON. 170 * @param d the date to convert 171 * @return the String representation we use in the JSON. 172 */ 173 private JSONValue dateToJson(Date d) { 174 175 return null != d ? new JSONString(Long.toString(d.getTime())) : null; 176 } 177 178 /** 179 * Notifies all observers on value changes. 180 */ 181 private void notifyOnValueChange() { 182 183 for (I_CmsSerialDateValueChangeObserver obs : m_valueChangeObservers) { 184 obs.onValueChange(); 185 } 186 187 } 188 189 /** 190 * Generates the JSON object storing the pattern information. 191 * @return the JSON object storing the pattern information. 192 */ 193 private JSONObject patternToJson() { 194 195 JSONObject pattern = new JSONObject(); 196 if (null != getPatternType()) { 197 pattern.put(JsonKey.PATTERN_TYPE, new JSONString(getPatternType().toString())); 198 switch (getPatternType()) { 199 case DAILY: 200 if (isEveryWorkingDay()) { 201 pattern.put(JsonKey.PATTERN_EVERYWORKINGDAY, JSONBoolean.getInstance(true)); 202 } else { 203 pattern.put(JsonKey.PATTERN_INTERVAL, new JSONString(String.valueOf(getInterval()))); 204 } 205 break; 206 case WEEKLY: 207 pattern.put(JsonKey.PATTERN_INTERVAL, new JSONString(String.valueOf(getInterval()))); 208 pattern.put(JsonKey.PATTERN_WEEKDAYS, toJsonStringList(getWeekDays())); 209 break; 210 case MONTHLY: 211 pattern.put(JsonKey.PATTERN_INTERVAL, new JSONString(String.valueOf(getInterval()))); 212 if (null != getWeekDay()) { 213 pattern.put(JsonKey.PATTERN_WEEKS_OF_MONTH, toJsonStringList(getWeeksOfMonth())); 214 pattern.put(JsonKey.PATTERN_WEEKDAYS, toJsonStringList(getWeekDays())); 215 } else { 216 pattern.put(JsonKey.PATTERN_DAY_OF_MONTH, new JSONString(String.valueOf(getDayOfMonth()))); 217 } 218 break; 219 case YEARLY: 220 pattern.put(JsonKey.PATTERN_MONTH, new JSONString(String.valueOf(getMonth()))); 221 if (null != getWeekDay()) { 222 pattern.put(JsonKey.PATTERN_WEEKS_OF_MONTH, toJsonStringList(getWeeksOfMonth())); 223 pattern.put(JsonKey.PATTERN_WEEKDAYS, toJsonStringList(getWeekDays())); 224 } else { 225 pattern.put(JsonKey.PATTERN_DAY_OF_MONTH, new JSONString(String.valueOf(getDayOfMonth()))); 226 } 227 break; 228 case INDIVIDUAL: 229 pattern.put(JsonKey.PATTERN_DATES, datesToJsonArray(getIndividualDates())); 230 break; 231 case NONE: 232 default: 233 break; 234 } 235 } 236 return pattern; 237 } 238 239 /** 240 * Extracts the dates from a JSON array. 241 * @param json the JSON array where the dates are stored in. 242 * @return list of the extracted dates. 243 */ 244 private SortedSet<Date> readDates(JSONValue json) { 245 246 JSONArray array = null == json ? null : json.isArray(); 247 if (null != array) { 248 SortedSet<Date> result = new TreeSet<>(); 249 for (int i = 0; i < array.size(); i++) { 250 Date d = readOptionalDate(array.get(i)); 251 if (null != d) { 252 result.add(d); 253 } 254 } 255 return result; 256 } 257 return new TreeSet<>(); 258 } 259 260 /** 261 * Read an optional boolean value form a JSON value. 262 * @param val the JSON value that should represent the boolean. 263 * @return the boolean from the JSON or null if reading the boolean fails. 264 */ 265 private Boolean readOptionalBoolean(JSONValue val) { 266 267 JSONBoolean b = null == val ? null : val.isBoolean(); 268 if (b != null) { 269 return Boolean.valueOf(b.booleanValue()); 270 } 271 return null; 272 } 273 274 /** 275 * Read an optional Date value form a JSON value. 276 * @param val the JSON value that should represent the Date as long value in a string. 277 * @return the Date from the JSON or null if reading the date fails. 278 */ 279 private Date readOptionalDate(JSONValue val) { 280 281 JSONString str = null == val ? null : val.isString(); 282 if (str != null) { 283 try { 284 return new Date(Long.parseLong(str.stringValue())); 285 } catch (@SuppressWarnings("unused") NumberFormatException e) { 286 // do nothing - return the default value 287 } 288 } 289 return null; 290 } 291 292 /** 293 * Read an optional int value form a JSON value. 294 * @param val the JSON value that should represent the int. 295 * @return the int from the JSON or 0 reading the int fails. 296 */ 297 private int readOptionalInt(JSONValue val) { 298 299 JSONString str = null == val ? null : val.isString(); 300 if (str != null) { 301 try { 302 return Integer.valueOf(str.stringValue()).intValue(); 303 } catch (@SuppressWarnings("unused") NumberFormatException e) { 304 // Do nothing, return default value 305 } 306 } 307 return 0; 308 } 309 310 /** 311 * Read an optional month value form a JSON value. 312 * @param val the JSON value that should represent the month. 313 * @return the month from the JSON or null if reading the month fails. 314 */ 315 private Month readOptionalMonth(JSONValue val) { 316 317 String str = readOptionalString(val); 318 if (null != str) { 319 try { 320 return Month.valueOf(str); 321 } catch (@SuppressWarnings("unused") IllegalArgumentException e) { 322 // Do nothing -return the default value 323 } 324 } 325 return null; 326 } 327 328 /** 329 * Read an optional string value form a JSON value. 330 * @param val the JSON value that should represent the string. 331 * @return the string from the JSON or null if reading the string fails. 332 */ 333 private String readOptionalString(JSONValue val) { 334 335 JSONString str = null == val ? null : val.isString(); 336 if (str != null) { 337 return str.stringValue(); 338 } 339 return null; 340 } 341 342 /** 343 * Read an optional uuid stored as JSON string. 344 * @param val the JSON value to read the uuid from. 345 * @return the uuid, or <code>null</code> if the uuid can not be read. 346 */ 347 private CmsUUID readOptionalUUID(JSONValue val) { 348 349 String id = readOptionalString(val); 350 if (null != id) { 351 try { 352 CmsUUID uuid = CmsUUID.valueOf(id); 353 return uuid; 354 } catch (@SuppressWarnings("unused") NumberFormatException e) { 355 // Do nothing, just return null 356 } 357 } 358 return null; 359 } 360 361 /** 362 * Read pattern information from the provided JSON object. 363 * @param patternJson the JSON object containing the pattern information. 364 */ 365 private void readPattern(JSONObject patternJson) { 366 367 setPatternType(readPatternType(patternJson.get(JsonKey.PATTERN_TYPE))); 368 setInterval(readOptionalInt(patternJson.get(JsonKey.PATTERN_INTERVAL))); 369 setWeekDays(readWeekDays(patternJson.get(JsonKey.PATTERN_WEEKDAYS))); 370 setDayOfMonth(readOptionalInt(patternJson.get(JsonKey.PATTERN_DAY_OF_MONTH))); 371 setEveryWorkingDay(readOptionalBoolean(patternJson.get(JsonKey.PATTERN_EVERYWORKINGDAY))); 372 setWeeksOfMonth(readWeeksOfMonth(patternJson.get(JsonKey.PATTERN_WEEKS_OF_MONTH))); 373 setIndividualDates(readDates(patternJson.get(JsonKey.PATTERN_DATES))); 374 setMonth(readOptionalMonth(patternJson.get(JsonKey.PATTERN_MONTH))); 375 376 } 377 378 /** 379 * Reads the pattern type from the provided JSON. Defaults to type NONE. 380 * @param val the JSON object containing the pattern type information. 381 * @return the pattern type. 382 */ 383 private PatternType readPatternType(JSONValue val) { 384 385 PatternType patterntype; 386 try { 387 String str = readOptionalString(val); 388 patterntype = PatternType.valueOf(str); 389 } catch (@SuppressWarnings("unused") IllegalArgumentException e) { 390 patterntype = PatternType.NONE; 391 } 392 return patterntype; 393 } 394 395 /** 396 * Read a single weekday from the provided JSON value. 397 * @param val the value to read the week day from. 398 * @return the week day read 399 * @throws IllegalArgumentException thrown if the provided JSON value is not the representation of a week day. 400 */ 401 private WeekDay readWeekDay(JSONValue val) throws IllegalArgumentException { 402 403 String str = readOptionalString(val); 404 if (null != str) { 405 return WeekDay.valueOf(str); 406 } 407 throw new IllegalArgumentException(); 408 } 409 410 /** 411 * Reads the weekday information. 412 * @param val the JSON object containing the weekday information. 413 * @return the weekdays extracted, defaults to the empty list, if no information is found. 414 */ 415 private SortedSet<WeekDay> readWeekDays(JSONValue val) { 416 417 JSONArray array = null == val ? null : val.isArray(); 418 if (null != array) { 419 SortedSet<WeekDay> result = new TreeSet<>(); 420 for (int i = 0; i < array.size(); i++) { 421 try { 422 result.add(readWeekDay(array.get(i))); 423 } catch (@SuppressWarnings("unused") IllegalArgumentException e) { 424 // Just skip 425 } 426 } 427 return result; 428 } 429 return new TreeSet<>(); 430 } 431 432 /** 433 * Read "weeks of month" information from JSON. 434 * @param json the JSON where information is read from. 435 * @return the list of weeks read, defaults to the empty list. 436 */ 437 private SortedSet<WeekOfMonth> readWeeksOfMonth(JSONValue json) { 438 439 JSONArray array = null == json ? null : json.isArray(); 440 if (null != array) { 441 SortedSet<WeekOfMonth> result = new TreeSet<>(); 442 for (int i = 0; i < array.size(); i++) { 443 String weekStr = readOptionalString(array.get(i)); 444 try { 445 WeekOfMonth week = WeekOfMonth.valueOf(weekStr); 446 result.add(week); 447 } catch (@SuppressWarnings("unused") IllegalArgumentException | NullPointerException e) { 448 // Just skip 449 } 450 } 451 return result; 452 } 453 return new TreeSet<>(); 454 } 455 456 /** 457 * Convert a list of objects to a JSON array with the string representations of that objects. 458 * @param list the list of objects. 459 * @return the JSON array with the string representations. 460 */ 461 private JSONValue toJsonStringList(Collection<? extends Object> list) { 462 463 if (null != list) { 464 JSONArray array = new JSONArray(); 465 for (Object o : list) { 466 array.set(array.size(), new JSONString(o.toString())); 467 } 468 return array; 469 } else { 470 return null; 471 } 472 } 473 474 /** 475 * Try to set the value from the provided Json string. 476 * @param value the value to set. 477 * @throws Exception thrown if parsing fails. 478 */ 479 private void tryToSetParsedValue(String value) throws Exception { 480 481 JSONObject json = JSONParser.parseStrict(value).isObject(); 482 JSONValue val = json.get(JsonKey.START); 483 setStart(readOptionalDate(val)); 484 val = json.get(JsonKey.END); 485 setEnd(readOptionalDate(val)); 486 setWholeDay(readOptionalBoolean(json.get(JsonKey.WHOLE_DAY))); 487 JSONObject patternJson = json.get(JsonKey.PATTERN).isObject(); 488 readPattern(patternJson); 489 setExceptions(readDates(json.get(JsonKey.EXCEPTIONS))); 490 setSeriesEndDate(readOptionalDate(json.get(JsonKey.SERIES_ENDDATE))); 491 setOccurrences(readOptionalInt(json.get(JsonKey.SERIES_OCCURRENCES))); 492 setDerivedEndType(); 493 setCurrentTillEnd(readOptionalBoolean(json.get(JsonKey.CURRENT_TILL_END))); 494 setParentSeriesId(readOptionalUUID(json.get(JsonKey.PARENT_SERIES))); 495 496 } 497}