001/** 002 * Copyright (c) 2011 - 2015, Lunifera GmbH (Gross Enzersdorf), Loetz GmbH&Co.KG (Heidelberg) 003 * All rights reserved. This program and the accompanying materials 004 * are made available under the terms of the Eclipse Public License v1.0 005 * which accompanies this distribution, and is available at 006 * http://www.eclipse.org/legal/epl-v10.html 007 * 008 * Contributors: 009 * Florian Pirchner - Initial implementation 010 */ 011package org.eclipse.osbp.runtime.web.ecview.presentation.vaadin.internal; 012 013import java.text.DateFormat; 014import java.text.SimpleDateFormat; 015import java.util.Arrays; 016import java.util.Date; 017import java.util.List; 018import java.util.Locale; 019 020import org.eclipse.core.databinding.Binding; 021import org.eclipse.core.databinding.observable.IObservable; 022import org.eclipse.core.databinding.observable.value.IObservableValue; 023import org.eclipse.emf.databinding.EMFObservables; 024import org.eclipse.osbp.ecview.core.common.editpart.IElementEditpart; 025import org.eclipse.osbp.ecview.core.common.model.core.YEmbeddableBindingEndpoint; 026import org.eclipse.osbp.ecview.core.common.model.core.YEmbeddableValueEndpoint; 027import org.eclipse.osbp.ecview.core.common.model.datatypes.YDatatype; 028import org.eclipse.osbp.ecview.core.emf.api.IDateFormatProvider; 029import org.eclipse.osbp.ecview.core.emf.api.IDateFormatProvider.Info; 030import org.eclipse.osbp.ecview.core.extension.model.datatypes.YDateTimeDatatype; 031import org.eclipse.osbp.ecview.core.extension.model.datatypes.YDateTimeFormat; 032import org.eclipse.osbp.ecview.core.extension.model.datatypes.YDateTimeResolution; 033import org.eclipse.osbp.ecview.core.extension.model.extension.ExtensionModelPackage; 034import org.eclipse.osbp.ecview.core.extension.model.extension.YDateTime; 035import org.eclipse.osbp.ecview.core.ui.core.editparts.extension.IDateTimeEditpart; 036import org.eclipse.osbp.runtime.web.ecview.presentation.vaadin.common.AbstractFieldWidgetPresenter; 037import org.eclipse.osbp.runtime.web.ecview.presentation.vaadin.internal.util.Util; 038 039import com.vaadin.data.util.ObjectProperty; 040import com.vaadin.server.ErrorMessage; 041import com.vaadin.shared.ui.datefield.Resolution; 042import com.vaadin.ui.Component; 043import com.vaadin.ui.ComponentContainer; 044import com.vaadin.ui.DateField; 045import com.vaadin.ui.Field; 046import com.vaadin.ui.UI; 047 048// TODO: Auto-generated Javadoc 049/** 050 * This presenter is responsible to render a text area on the given layout. 051 */ 052public class DateTimePresentation extends 053 AbstractFieldWidgetPresenter<Component> { 054 055 /** The model access. */ 056 private final ModelAccess modelAccess; 057 058 /** The date field. */ 059 private CustomField dateField; 060 061 /** The binding_value to ui. */ 062 private Binding binding_valueToUI; 063 064 /** The property. */ 065 private ObjectProperty<Date> property; 066 067 /** The format info. */ 068 private Info formatInfo; 069 070 /** 071 * Constructor. 072 * 073 * @param editpart 074 * The editpart of that presenter 075 */ 076 public DateTimePresentation(IElementEditpart editpart) { 077 super((IDateTimeEditpart) editpart); 078 this.modelAccess = new ModelAccess((YDateTime) editpart.getModel()); 079 } 080 081 /** 082 * {@inheritDoc} 083 */ 084 @Override 085 public Component doCreateWidget(Object parent) { 086 if (dateField == null) { 087 088 dateField = new CustomField(); 089 dateField.addStyleName(CSS_CLASS_CONTROL); 090 dateField.setImmediate(true); 091 setupComponent(dateField, getCastedModel()); 092 093 associateWidget(dateField, modelAccess.yField); 094 095 if (modelAccess.isCssIdValid()) { 096 dateField.setId(modelAccess.getCssID()); 097 } else { 098 dateField.setId(getEditpart().getId()); 099 } 100 101 property = new ObjectProperty<Date>(null, Date.class); 102 dateField.setPropertyDataSource(property); 103 104 // creates the binding for the field 105 createBindings(modelAccess.yField, dateField); 106 107 if (modelAccess.isCssClassValid()) { 108 dateField.addStyleName(modelAccess.getCssClass()); 109 } 110 111 doApplyDatatype(modelAccess.yField.getDatatype()); 112 113 applyCaptions(); 114 115 initializeField(dateField); 116 } 117 return dateField; 118 } 119 120 /** 121 * Applies the datatype options to the field. 122 * 123 * @param yDt 124 * the y dt 125 */ 126 protected void doApplyDatatype(YDatatype yDt) { 127 if (dateField == null) { 128 return; 129 } 130 131 IDateFormatProvider service = getViewContext().getService( 132 IDateFormatProvider.class.getName()); 133 YDateTimeDatatype yDatatype = (YDateTimeDatatype) yDt; 134 if (service != null) { 135 formatInfo = service 136 .getInfo(yDatatype, UI.getCurrent().getLocale()); 137 } else { 138 formatInfo = new DefaultDateFormatProvider().getInfo(yDatatype, UI 139 .getCurrent().getLocale()); 140 } 141 142 dateField.setDateFormat(formatInfo.getDateFormat()); 143 dateField.setResolution(mapToVaadin(formatInfo.getResolution())); 144 } 145 146 /** 147 * Map to vaadin. 148 * 149 * @param resolution 150 * the resolution 151 * @return the resolution 152 */ 153 private Resolution mapToVaadin(YDateTimeResolution resolution) { 154 switch (resolution) { 155 case YEAR: 156 return Resolution.YEAR; 157 case MONTH: 158 return Resolution.MONTH; 159 case DAY: 160 return Resolution.DAY; 161 case HOUR: 162 return Resolution.HOUR; 163 case MINUTE: 164 return Resolution.MINUTE; 165 case SECOND: 166 return Resolution.SECOND; 167 case UNDEFINED: 168 return Resolution.DAY; 169 } 170 171 return Resolution.DAY; 172 } 173 174 /* (non-Javadoc) 175 * @see org.eclipse.osbp.runtime.web.ecview.presentation.vaadin.common.AbstractVaadinWidgetPresenter#doUpdateLocale(java.util.Locale) 176 */ 177 @Override 178 protected void doUpdateLocale(Locale locale) { 179 // need to refresh the locale datetime pattern 180 doApplyDatatype(modelAccess.yField.getDatatype()); 181 // update the captions 182 applyCaptions(); 183 } 184 185 /** 186 * Applies the labels to the widgets. 187 */ 188 protected void applyCaptions() { 189 Util.applyCaptions(getI18nService(), modelAccess.getLabel(), 190 modelAccess.getLabelI18nKey(), getLocale(), dateField); 191 } 192 193 /* (non-Javadoc) 194 * @see org.eclipse.osbp.runtime.web.ecview.presentation.vaadin.common.AbstractFieldWidgetPresenter#doGetField() 195 */ 196 @Override 197 protected Field<?> doGetField() { 198 return dateField; 199 } 200 201 /* (non-Javadoc) 202 * @see org.eclipse.osbp.runtime.web.ecview.presentation.vaadin.common.AbstractVaadinWidgetPresenter#internalGetObservableEndpoint(org.eclipse.osbp.ecview.core.common.model.core.YEmbeddableBindingEndpoint) 203 */ 204 @Override 205 protected IObservable internalGetObservableEndpoint( 206 YEmbeddableBindingEndpoint bindableValue) { 207 if (bindableValue == null) { 208 throw new IllegalArgumentException( 209 "BindableValue must not be null!"); 210 } 211 212 if (bindableValue instanceof YEmbeddableValueEndpoint) { 213 return internalGetValueEndpoint(); 214 } 215 throw new IllegalArgumentException("Not a valid input: " 216 + bindableValue); 217 } 218 219 /** 220 * Returns the observable to observe value. 221 * 222 * @return the i observable value 223 */ 224 protected IObservableValue internalGetValueEndpoint() { 225 // return the observable value for text 226 return EMFObservables.observeValue(castEObject(getModel()), 227 ExtensionModelPackage.Literals.YDATE_TIME__VALUE); 228 } 229 230 /** 231 * Creates the bindings for the given values. 232 * 233 * @param yField 234 * the y field 235 * @param field 236 * the field 237 */ 238 protected void createBindings(YDateTime yField, DateField field) { 239 // create the model binding from widget to ECView-model 240 binding_valueToUI = createBindingsValue(castEObject(getModel()), 241 ExtensionModelPackage.Literals.YDATE_TIME__VALUE, field, null, 242 null); 243 registerBinding(binding_valueToUI); 244 245 super.createBindings(yField, field, null); 246 } 247 248 /* (non-Javadoc) 249 * @see org.eclipse.osbp.ecview.core.common.presentation.IWidgetPresentation#getWidget() 250 */ 251 @Override 252 public Component getWidget() { 253 return dateField; 254 } 255 256 /* (non-Javadoc) 257 * @see org.eclipse.osbp.ecview.core.common.presentation.IWidgetPresentation#isRendered() 258 */ 259 @Override 260 public boolean isRendered() { 261 return dateField != null; 262 } 263 264 /** 265 * {@inheritDoc} 266 */ 267 @Override 268 public void doUnrender() { 269 if (dateField != null) { 270 271 // unbind all active bindings 272 unbind(); 273 274 Component parent = ((Component) dateField.getParent()); 275 if (parent != null && parent instanceof ComponentContainer) { 276 ((ComponentContainer) parent).removeComponent(dateField); 277 } 278 279 // remove assocations 280 unassociateWidget(dateField); 281 282 dateField = null; 283 } 284 } 285 286 /** 287 * {@inheritDoc} 288 */ 289 @Override 290 protected void internalDispose() { 291 try { 292 unrender(); 293 } finally { 294 super.internalDispose(); 295 } 296 297 if (binding_valueToUI != null) { 298 binding_valueToUI.dispose(); 299 binding_valueToUI = null; 300 } 301 } 302 303 /** 304 * A helper class. 305 */ 306 private static class ModelAccess { 307 308 /** The y field. */ 309 private final YDateTime yField; 310 311 /** 312 * Instantiates a new model access. 313 * 314 * @param yField 315 * the y field 316 */ 317 public ModelAccess(YDateTime yField) { 318 super(); 319 this.yField = yField; 320 } 321 322 /** 323 * Gets the css class. 324 * 325 * @return the css class 326 * @see org.eclipse.osbp.ecview.core.ui.core.model.core.YCssAble#getCssClass() 327 */ 328 public String getCssClass() { 329 return yField.getCssClass(); 330 } 331 332 /** 333 * Returns true, if the css class is not null and not empty. 334 * 335 * @return true, if is css class valid 336 */ 337 public boolean isCssClassValid() { 338 return getCssClass() != null && !getCssClass().equals(""); 339 } 340 341 /** 342 * Gets the css id. 343 * 344 * @return the css id 345 * @see org.eclipse.osbp.ecview.core.ui.core.model.core.YCssAble#getCssID() 346 */ 347 public String getCssID() { 348 return yField.getCssID(); 349 } 350 351 /** 352 * Returns true, if the css id is not null and not empty. 353 * 354 * @return true, if is css id valid 355 */ 356 public boolean isCssIdValid() { 357 return getCssID() != null && !getCssID().equals(""); 358 } 359 360 /** 361 * Returns the label. 362 * 363 * @return the label 364 */ 365 public String getLabel() { 366 return yField.getDatadescription() != null ? yField 367 .getDatadescription().getLabel() : null; 368 } 369 370 /** 371 * Returns the label. 372 * 373 * @return the label i18n key 374 */ 375 public String getLabelI18nKey() { 376 return yField.getDatadescription() != null ? yField 377 .getDatadescription().getLabelI18nKey() : null; 378 } 379 380 /** 381 * Returns true, if the date format is valid. 382 * 383 * @return true, if is dateformat valid 384 */ 385 @SuppressWarnings("unused") 386 public boolean isDateformatValid() { 387 return yField.getDatadescription() != null 388 && yField.getDatatype().getFormat() != null; 389 } 390 } 391 392 /** 393 * The Class CustomField. 394 */ 395 @SuppressWarnings("serial") 396 private class CustomField extends DateField { 397 398 /* (non-Javadoc) 399 * @see com.vaadin.ui.AbstractField#getErrorMessage() 400 */ 401 @Override 402 public ErrorMessage getErrorMessage() { 403 if (isDisposed()) { 404 // after disposal, Vaadin will call this method once. 405 return null; 406 } 407 408 ErrorMessage message = super.getErrorMessage(); 409 reportValidationError(message); 410 return message; 411 } 412 } 413 414 /** 415 * The Class DefaultDateFormatProvider. 416 */ 417 private static class DefaultDateFormatProvider implements 418 IDateFormatProvider { 419 420 /* (non-Javadoc) 421 * @see org.eclipse.osbp.ecview.core.emf.api.IDateFormatProvider#getInfo(org.eclipse.osbp.ecview.core.extension.model.datatypes.YDateTimeDatatype, java.util.Locale) 422 */ 423 @Override 424 public Info getInfo(YDateTimeDatatype yDt, Locale locale) { 425 426 if (yDt == null) { 427 DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.SHORT, locale); 428 return new IDateFormatProvider.Info(((SimpleDateFormat)formatter).toPattern(), 429 YDateTimeResolution.MINUTE); 430 } 431 432 DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, locale); 433 String pattern = ((SimpleDateFormat)formatter).toPattern(); 434 435 String dateFormat = null; 436 YDateTimeResolution resolution = calcResolution(yDt); 437 YDateTimeFormat yFormat = yDt.getFormat(); 438 if (yFormat != null) { 439 switch (yFormat) { 440 case DATE: 441 switch (resolution) { 442 case YEAR: 443 dateFormat = filterFormat(pattern, "yyyy"); 444 break; 445 case MONTH: 446 dateFormat = filterFormat(pattern, "yyyy.MM"); 447 break; 448 case DAY: 449 dateFormat = filterFormat(pattern, "yyyy.MM.dd"); 450 break; 451 default: 452 throw new IllegalArgumentException(resolution 453 + " is not a valid resolution for " + yFormat); 454 } 455 break; 456 case DATE_TIME: 457 switch (resolution) { 458 case YEAR: 459 dateFormat = filterFormat(pattern, "yyyy"); 460 break; 461 case MONTH: 462 dateFormat = filterFormat(pattern, "yyyy.MM"); 463 break; 464 case DAY: 465 dateFormat = filterFormat(pattern, "yyyy.MM.dd"); 466 break; 467 case HOUR: 468 dateFormat = filterFormat(pattern, "yyyy.MM.dd HH"); 469 break; 470 case MINUTE: 471 dateFormat = filterFormat(pattern, "yyyy.MM.dd HH:mm"); 472 break; 473 case SECOND: 474 dateFormat = filterFormat(pattern, "yyyy.MM.dd HH:mm:ss"); 475 break; 476 default: 477 throw new IllegalArgumentException(resolution 478 + " is not a valid resolution for " + yFormat); 479 } 480 break; 481 case TIME: 482 switch (resolution) { 483 case HOUR: 484 dateFormat = filterFormat(pattern, "HH"); 485 break; 486 case MINUTE: 487 dateFormat = filterFormat(pattern, "HH:mm"); 488 break; 489 case SECOND: 490 dateFormat = filterFormat(pattern, "HH:mm:ss"); 491 break; 492 default: 493 throw new IllegalArgumentException(resolution 494 + " is not a valid resolution for " + yFormat); 495 } 496 break; 497 } 498 } 499 500 return new IDateFormatProvider.Info(dateFormat, resolution); 501 } 502 503 /** 504 * filters from any localized date-time pattern the desired subset 505 * defined by filterPattern without destroying the original localized 506 * pattern . 507 * 508 * @param localizedPattern 509 * the localized full date-time pattern 510 * @param filterPattern 511 * the subset of desired date-time formatter patterns 512 * @return the string 513 */ 514 private String filterFormat(String localizedPattern, String filterPattern) { 515 // remove any multiple characters sequences and remove all separator signs from filterPattern 516 String filter = filterPattern.replaceAll("(.)\\1+", "$1").replaceAll("[^\\w\\s]", "")+","; 517 // create a replacement pattern to remove unnecessary blanks disturbing the recognition of orphaned separators 518 // rule: each blank must be surrounded by any filter-letter to be valid 519 String invalidBlanks = "(?!["+filter+"])( )(?!["+filter+"])"; 520 // create a replacement pattern to remove remaining separators without formatting function 521 // rule: each separator must be surrounded by any filter-letter or blank to be valid 522 String invalidSeparators = "(?!["+filter+" ])([.:])(?!["+filter+" ])"; 523 return localizedPattern.replaceAll("[^"+filter+",.: ]", "").replaceAll(invalidBlanks, "").replaceAll(invalidSeparators, ""); 524 } 525 526 /** 527 * Calc resolution. 528 * 529 * @param yDt 530 * the y dt 531 * @return the y date time resolution 532 */ 533 private YDateTimeResolution calcResolution(YDateTimeDatatype yDt) { 534 YDateTimeFormat yFormat = yDt.getFormat(); 535 YDateTimeResolution resolution = null; 536 if (yFormat != null) { 537 YDateTimeResolution yResolution = yDt.getResolution(); 538 switch (yFormat) { 539 case DATE: 540 if (yResolution == YDateTimeResolution.UNDEFINED 541 || yResolution == YDateTimeResolution.SECOND 542 || yResolution == YDateTimeResolution.MINUTE 543 || yResolution == YDateTimeResolution.HOUR) { 544 resolution = YDateTimeResolution.DAY; 545 } 546 break; 547 case DATE_TIME: 548 if (yResolution == YDateTimeResolution.UNDEFINED 549 || yResolution == YDateTimeResolution.DAY 550 || yResolution == YDateTimeResolution.MONTH 551 || yResolution == YDateTimeResolution.YEAR) { 552 resolution = YDateTimeResolution.MINUTE; 553 } 554 break; 555 case TIME: 556 if (yResolution == YDateTimeResolution.UNDEFINED 557 || yResolution == YDateTimeResolution.DAY 558 || yResolution == YDateTimeResolution.MONTH 559 || yResolution == YDateTimeResolution.YEAR) { 560 resolution = YDateTimeResolution.MINUTE; 561 } 562 break; 563 } 564 } 565 566 if (resolution == null) { 567 switch (yDt.getResolution()) { 568 case SECOND: 569 resolution = YDateTimeResolution.SECOND; 570 break; 571 case MINUTE: 572 resolution = YDateTimeResolution.MINUTE; 573 break; 574 case HOUR: 575 resolution = YDateTimeResolution.HOUR; 576 break; 577 case DAY: 578 resolution = YDateTimeResolution.DAY; 579 break; 580 case MONTH: 581 resolution = YDateTimeResolution.MONTH; 582 break; 583 case YEAR: 584 resolution = YDateTimeResolution.YEAR; 585 break; 586 case UNDEFINED: 587 resolution = YDateTimeResolution.MINUTE; 588 break; 589 default: 590 resolution = YDateTimeResolution.MINUTE; 591 } 592 } 593 594 return resolution; 595 } 596 597 } 598}