Added support for MultiStatus, migrated DateField to new concept

The FormField in Scout JS now works with a 'multi-status' in a similar
way the MultiStatus works in Scout Classic. Instead of having a single
error status at one time, the Status has children with different types:
ParsingFailedStatus, ValidationFailedStatus and other Status. These
status can co-exist, so when a programmer adds a functional error, this
error will not replace an existing parse or validate error.

In Scout JS the functions addErrorStatus and removeErrorStatus have
been added. These are required for functional errors, it is the
responsibility of the programmer, to add/remove this type of error.

The synchronisation between Scout JS and Classic has been improved.
The UI server now can flag certain Status objects with 'deletable=false'
which means Scout JS is not allowed to remove these Status objects.

Adjusted specs where required. All specs and selenium tests are green.

Change-Id: I31e41656fa62a9ef15f6cb4e032a8509ccf8a886
diff --git a/eclipse-scout-core/src/form/fields/FormField.js b/eclipse-scout-core/src/form/fields/FormField.js
index 05541a8..991ac72 100644
--- a/eclipse-scout-core/src/form/fields/FormField.js
+++ b/eclipse-scout-core/src/form/fields/FormField.js
@@ -66,6 +66,7 @@
     this.tooltipText = null;
     this.tooltipAnchor = FormField.TooltipAnchor.DEFAULT;
     this.onFieldTooltipOptionsCreator = null;
+    this.suppressStatus = null;
 
     this.$label = null;
     /**
@@ -106,6 +107,21 @@
     ALTERNATIVE: 'alternative'
   };
 
+  static SuppressStatus = {
+    /**
+     * Suppress status on icon and field (CSS class).
+     */
+    ALL: 'all',
+    /**
+     * Suppress status on icon, but still show status on field (CSS class).
+     */
+    ICON: 'icon',
+    /**
+     * Suppress status on field (CSS class), but still show status as icon.
+     */
+    FIELD: 'field'
+  };
+
   /** Global variable to make it easier to adjust the default field style for all fields */
   static DEFAULT_FIELD_STYLE = FormField.FieldStyle.ALTERNATIVE;
 
@@ -301,6 +317,75 @@
     this._setProperty('errorStatus', errorStatus);
   }
 
+  /**
+   * Adds the given (functional) error status to the list of error status. Prefer this function over #setErrorStatus
+   * when you don't want to mess with the internal error states of the field (parsing, validation).
+   *
+   * @param errorStatus
+   */
+  addErrorStatus(errorStatus) {
+    if (!(errorStatus instanceof Status)) {
+      throw new Error('errorStatus is not a Status');
+    }
+    var status = this._errorStatus();
+    if (status) {
+      status = status.ensureChildren(); // new instance is required for property change
+    } else {
+      status = Status.ok('Root');
+    }
+    status.addStatus(errorStatus);
+    this.setErrorStatus(status);
+  }
+
+  setSuppressStatus(suppressStatus) {
+    this.setProperty('suppressStatus', suppressStatus);
+  }
+
+  _renderSuppressStatus() {
+    this._renderErrorStatus();
+  }
+
+  /**
+   * @returns {boolean} Whether or not error status icon is suppressed
+   */
+  _isSuppressStatusIcon() {
+    return scout.isOneOf(this.suppressStatus, FormField.SuppressStatus.ALL, FormField.SuppressStatus.ICON);
+  }
+
+  /**
+   * @returns {boolean} Whether or not error status CSS class is suppressed on field
+   */
+  _isSuppressStatusField() {
+    return scout.isOneOf(this.suppressStatus, FormField.SuppressStatus.ALL, FormField.SuppressStatus.FIELD);
+  }
+
+  /**
+   * Removes all status (incl. children) with the given type.
+   * @param {object} statusType
+   */
+  removeErrorStatus(statusType) {
+    this.removeErrorStatusPredicate(function(status) {
+      return status instanceof statusType;
+    });
+  }
+
+  removeErrorStatusPredicate(predicate) {
+    var status = this._errorStatus();
+    if (!status) {
+      return;
+    }
+    if (status.containsStatusPredicate(predicate)) {
+      var newStatus = status.clone();
+      newStatus.removeAllStatusPredicate(predicate);
+      // If no other status remains -> clear error status
+      if (newStatus.hasChildren()) {
+        this.setErrorStatus(newStatus);
+      } else {
+        this.clearErrorStatus();
+      }
+    }
+  }
+
   clearErrorStatus() {
     this.setErrorStatus(null);
   }
@@ -308,19 +393,24 @@
   _renderErrorStatus() {
     var status = this._errorStatus(),
       hasStatus = !!status,
-      statusClass = hasStatus ? 'has-' + status.cssClass() : '';
+      statusClass = (hasStatus && !this._isSuppressStatusField()) ? 'has-' + status.cssClass() : '';
 
     this._updateErrorStatusClasses(statusClass, hasStatus);
     this._updateFieldStatus();
   }
 
   _updateErrorStatusClasses(statusClass, hasStatus) {
-    this.$container.removeClass(FormField.SEVERITY_CSS_CLASSES);
-    this.$container.addClass(statusClass, hasStatus);
-    if (this.$field) {
-      this.$field.removeClass(FormField.SEVERITY_CSS_CLASSES);
-      this.$field.addClass(statusClass, hasStatus);
+    this._updateErrorStatusClassesOnElement(this.$container, statusClass, hasStatus);
+    this._updateErrorStatusClassesOnElement(this.$field, statusClass, hasStatus);
+  }
+
+  _updateErrorStatusClassesOnElement($element, statusClass, hasStatus) {
+    if (!$element) {
+      return;
     }
+    $element
+      .removeClass(FormField.SEVERITY_CSS_CLASSES)
+      .addClass(statusClass, hasStatus);
   }
 
   setTooltipText(tooltipText) {
@@ -548,7 +638,7 @@
       hasStatus = !!status,
       hasTooltip = this.hasStatusTooltip();
 
-    return !this.suppressStatus && this.visible && (statusVisible || hasStatus || hasTooltip || (this._hasMenus() && this.menusVisible));
+    return !this._isSuppressStatusIcon() && this.visible && (statusVisible || hasStatus || hasTooltip || (this._hasMenus() && this.menusVisible));
   }
 
   _renderChildVisible($child, visible) {
diff --git a/eclipse-scout-core/src/form/fields/FormFieldAdapter.js b/eclipse-scout-core/src/form/fields/FormFieldAdapter.js
index 5c3a81a..51648f2 100644
--- a/eclipse-scout-core/src/form/fields/FormFieldAdapter.js
+++ b/eclipse-scout-core/src/form/fields/FormFieldAdapter.js
@@ -20,7 +20,6 @@
      * By default the field will be disabled.
      */
     this.enabledWhenOffline = false;
-
   }
 
   _goOffline() {
@@ -37,4 +36,5 @@
     }
     this.widget.setEnabled(this._enabledBeforeOffline);
   }
+
 }
diff --git a/eclipse-scout-core/src/form/fields/ValueField.js b/eclipse-scout-core/src/form/fields/ValueField.js
index aa440eb..5a281a0 100644
--- a/eclipse-scout-core/src/form/fields/ValueField.js
+++ b/eclipse-scout-core/src/form/fields/ValueField.js
@@ -8,7 +8,7 @@
  * Contributors:
  *     BSI Business Systems Integration AG - initial API and implementation
  */
-import {arrays, Event, focusUtils, FormField, menus as menus_1, objects, scout, Status, strings} from '../../index';
+import {arrays, Event, focusUtils, FormField, menus as menus_1, objects, scout, Status, strings, ParsingFailedStatus, ValidationFailedStatus} from '../../index';
 import * as $ from 'jquery';
 
 /**
@@ -135,7 +135,7 @@
   }
 
   parseAndSetValue(displayText) {
-    this.clearErrorStatus();
+    this.removeErrorStatus(ParsingFailedStatus);
     try {
       var event = new Event({
         displayText: displayText
@@ -158,13 +158,17 @@
     });
     this.trigger('parseError', event);
     if (!event.defaultPrevented) {
-      var status = this._createParsingFailedStatus(displayText, error);
-      this.setErrorStatus(status);
+      this._addParsingFailedErrorStatus(displayText, error);
     }
   }
 
+  _addParsingFailedErrorStatus(displayText, error) {
+    var status = this._createParsingFailedStatus(displayText, error);
+    this.addErrorStatus(status);
+  }
+
   _createParsingFailedStatus(displayText, error) {
-    return this._createInvalidValueStatus(displayText, error);
+    return this._createInvalidValueStatus('ParsingFailedStatus', displayText, error);
   }
 
   /**
@@ -345,8 +349,14 @@
   }
 
   _setValue(value) {
+    // When widget is initialized with a given errorStatus and a value -> don't remove the error
+    // status. This is a typical case for Scout Classic: field has a ParsingFailedError and user
+    // hits reload.
+    if (this.initialized) {
+      this.removeErrorStatus(ParsingFailedStatus);
+      this.removeErrorStatus(ValidationFailedStatus);
+    }
     var oldValue = this.value;
-    this._updateErrorStatus(null);
     var typedValue = null;
     try {
       typedValue = this._ensureValue(value);
@@ -460,38 +470,40 @@
   _validationFailed(value, error) {
     $.log.isDebugEnabled() && $.log.debug('Validation failed for field with id ' + this.id, error);
     var status = this._createValidationFailedStatus(value, error);
-    this._updateErrorStatus(status);
+    this.addErrorStatus(status);
     this._updateDisplayText(value);
   }
 
   _createValidationFailedStatus(value, error) {
-    return this._createInvalidValueStatus(value, error);
+    return this._createInvalidValueStatus('ValidationFailedStatus', value, error);
   }
 
-  _createInvalidValueStatus(value, error) {
-    if (error instanceof Status) {
+  /**
+   * @param {string} statusType
+   * @returns {Status}
+   */
+  _createInvalidValueStatus(statusType, value, error) {
+    var statusFunc = Status.classForName(statusType);
+    // type of status is correct
+    if (error instanceof statusFunc) {
       return error;
     }
-    if (typeof error === 'string') {
-      return Status.error({
-        message: error
-      });
-    }
-    return Status.error({
-      message: this.session.text(this.invalidValueMessageKey, value)
-    });
-  }
-
-  _updateErrorStatus(status) {
-    if (!this.initialized && this.errorStatus) {
-      // Don't override the error status specified by the init model
-      return;
-    }
-    if (!status) {
-      this.clearErrorStatus();
+    var message, severity = Status.Severity.ERROR;
+    if (error instanceof Status) {
+      // its a Status, but it has the wrong specific type
+      message = error.message;
+      severity = error.severity;
+    } else if (typeof error === 'string') {
+      // convert string to status
+      message = error;
     } else {
-      this.setErrorStatus(status);
+      // create status with default message
+      message = this.session.text(this.invalidValueMessageKey, value);
     }
+    return scout.create(statusType, {
+      message: message,
+      severity: severity
+    });
   }
 
   _updateDisplayText(value) {
diff --git a/eclipse-scout-core/src/form/fields/ValueFieldAdapter.js b/eclipse-scout-core/src/form/fields/ValueFieldAdapter.js
index cea3383..2d92ec4 100644
--- a/eclipse-scout-core/src/form/fields/ValueFieldAdapter.js
+++ b/eclipse-scout-core/src/form/fields/ValueFieldAdapter.js
@@ -66,4 +66,5 @@
       return comparators.TEXT.compare(a, b); // both are not in list
     };
   }
+
 }
diff --git a/eclipse-scout-core/src/form/fields/datefield/DateField.js b/eclipse-scout-core/src/form/fields/datefield/DateField.js
index c50c150..60e8c9c 100644
--- a/eclipse-scout-core/src/form/fields/datefield/DateField.js
+++ b/eclipse-scout-core/src/form/fields/datefield/DateField.js
@@ -9,8 +9,10 @@
  *     BSI Business Systems Integration AG - initial API and implementation
  */
 import {
+  arrays,
   DateFormat,
   DatePickerPopup,
+  DatePredictionFailedStatus,
   dates,
   DateTimeCompositeLayout,
   Device,
@@ -21,6 +23,7 @@
   InputFieldKeyStrokeContext,
   keys,
   objects,
+  ParsingFailedStatus,
   scout,
   Status,
   strings,
@@ -76,6 +79,10 @@
     PARSE_ERROR: -1
   };
 
+  static PARSE_ERROR_PREDICATE = function(status) {
+    return status.code === DateField.ErrorCode.PARSE_ERROR;
+  };
+
   /**
    * @override Widget.js
    */
@@ -499,36 +506,27 @@
       statusClass = this._errorStatusClass();
 
     if (this.$dateField) {
-      this.$dateField.removeClass(FormField.SEVERITY_CSS_CLASSES);
-      this.$dateField.toggleClass(statusClass, hasStatus);
+      this._updateErrorStatusClassesOnElement(this.$dateField, statusClass, hasStatus);
 
       // Because the error color of field icons depends on the error status of sibling <input> elements.
       // The prediction fields are clones of the input fields, so the 'has-error' class has to be
       // removed from them as well to make the icon "valid".
-      if (this._$predictDateField) {
-        this._$predictDateField.removeClass(FormField.SEVERITY_CSS_CLASSES);
-        this._$predictDateField.toggleClass(statusClass, hasStatus);
-      }
+      this._updateErrorStatusClassesOnElement(this._$predictDateField, statusClass, hasStatus);
     }
 
     // Do the same for the time field
     if (this.$timeField) {
-      this.$timeField.removeClass(FormField.SEVERITY_CSS_CLASSES);
-      this.$timeField.toggleClass(statusClass, hasStatus);
-      if (this._$predictTimeField) {
-        this._$predictTimeField.removeClass(FormField.SEVERITY_CSS_CLASSES);
-        this._$predictTimeField.toggleClass(statusClass, hasStatus);
-      }
+      this._updateErrorStatusClassesOnElement(this.$timeField, statusClass, hasStatus);
+      this._updateErrorStatusClassesOnElement(this._$predictTimeField, statusClass, hasStatus);
     }
 
     if (this.popup) {
-      this.popup.$container.removeClass(FormField.SEVERITY_CSS_CLASSES);
-      this.popup.$container.toggleClass(statusClass, hasStatus);
+      this._updateErrorStatusClassesOnElement(this.popup.$container, statusClass, hasStatus);
     }
   }
 
   _errorStatusClass() {
-    return this.errorStatus ? 'has-' + this.errorStatus.cssClass() : '';
+    return (this.errorStatus && !this._isSuppressStatusField()) ? 'has-' + this.errorStatus.cssClass() : '';
   }
 
   /**
@@ -969,6 +967,7 @@
     }
 
     // Predict date
+    this._removePredictErrorStatus();
     var datePrediction = this._predictDate(displayText); // this also updates the errorStatus
     if (datePrediction) {
       fields.valOrText(this._$predictDateField, datePrediction.text);
@@ -1014,7 +1013,7 @@
    * Clears the time field if date field is empty before accepting the input
    */
   acceptDate() {
-    if (this.hasTime && !this.errorStatus && strings.empty(this.$dateField.val())) {
+    if (this.hasTime && strings.empty(this.$dateField.val())) {
       this.$timeField.val('');
     }
     this.acceptInput();
@@ -1024,7 +1023,7 @@
    * Clears the date field if time field is empty before accepting the input
    */
   acceptTime() {
-    if (this.hasDate && !this.errorStatus && strings.empty(this.$timeField.val())) {
+    if (this.hasDate && strings.empty(this.$timeField.val())) {
       this.$dateField.val('');
     }
     this.acceptInput();
@@ -1219,6 +1218,7 @@
   }
 
   _createPredictionField($inputField) {
+    this.setSuppressStatus(FormField.SuppressStatus.ALL);
     var $predictionField = $inputField.clone()
       .addClass('predict')
       .attr('tabIndex', '-1')
@@ -1230,6 +1230,7 @@
   }
 
   _removePredictionFields() {
+    this.setSuppressStatus(null);
     if (this._$predictDateField) {
       this._$predictDateField.remove();
       this._$predictDateField = null;
@@ -1448,6 +1449,7 @@
     var timePrediction = {};
     var success = true;
 
+    this._removePredictErrorStatus();
     if (this.hasDate) {
       datePrediction = this._predictDate(dateText); // this also updates the errorStatus
       if (!datePrediction) {
@@ -1464,9 +1466,10 @@
       this._setTimeDisplayText(timeText);
     }
 
-    // Error status was already set by _predict functions, just throw it so that setValue is not called
+    // Error status was already set by _predict functions, throw this typed Status so DateField knows it must
+    // not set the error status again when the parse error is catched
     if (!success) {
-      throw this.errorStatus;
+      throw new DatePredictionFailedStatus();
     }
 
     // parse success -> return new value
@@ -1477,6 +1480,17 @@
   }
 
   /**
+   * Don't add error if it is a DatePredictionFailedStatus because the predict function
+   * has already added a parsing error.
+   */
+  _addParsingFailedErrorStatus(displayText, error) {
+    if (error instanceof DatePredictionFailedStatus) {
+      return;
+    }
+    super._addParsingFailedErrorStatus(displayText, error);
+  }
+
+  /**
    * @returns null if input is invalid, otherwise an object with properties 'date' and 'text'
    */
   _predictDate(inputText) {
@@ -1583,34 +1597,54 @@
    * This method updates the parts (date, time) of the error status.
    */
   _setErrorStatusPart(property, valid) {
+
+    // check if date/time error exists
+    var status = null;
+    var storedStatus = null;
+    if (this.errorStatus) {
+      storedStatus = arrays.find(this.errorStatus.asFlatList(), DateField.PARSE_ERROR_PREDICATE);
+    }
+
+    if (storedStatus) {
+      status = storedStatus;
+    } else {
+      status = new ParsingFailedStatus({
+        message: this.session.text('ui.InvalidDate'),
+        severity: Status.Severity.ERROR,
+        code: DateField.ErrorCode.PARSE_ERROR
+      });
+    }
+
     if (valid) {
-      this.setErrorStatus(null);
-      return;
+      delete status[property];
+    } else {
+      status[property] = true;
     }
-    var errorStatus = this.errorStatus;
-    if (!errorStatus) {
-      errorStatus = this._createErrorStatus();
+
+    if (!status.hasOwnProperty('invalidDate') && !status.hasOwnProperty('invalidTime')) {
+      status = null;
     }
-    errorStatus[property] = true;
-    this.setErrorStatus(errorStatus);
+
+    if (status && !storedStatus) {
+      this.addErrorStatus(status);
+    } else if (!status && storedStatus) {
+      this._removePredictErrorStatus();
+    } // else: just update existing error
   }
 
-  _createErrorStatus() {
-    return new Status({
-      message: this.session.text('ui.InvalidDate'),
-      severity: Status.Severity.ERROR,
-      code: DateField.ErrorCode.PARSE_ERROR
-    });
+  _removePredictErrorStatus() {
+    this.removeErrorStatusPredicate(DateField.PARSE_ERROR_PREDICATE);
   }
 
   /**
    * @override
    */
-  _createInvalidValueStatus(value, error) {
-    var errorStatus = super._createInvalidValueStatus(value, error);
+  _createInvalidValueStatus(statusType, value, error) {
+    var errorStatus = super._createInvalidValueStatus(statusType, value, error);
     // Set date and time to invalid, otherwise isDateValid and isTimeValid return false even though there is a validation error
     errorStatus.invalidDate = true;
     errorStatus.invalidTime = true;
+    errorStatus.code = DateField.ErrorCode.PARSE_ERROR;
     return errorStatus;
   }
 
@@ -1623,10 +1657,14 @@
   }
 
   _isErrorStatusPartValid(property) {
-    if (this.errorStatus && this.errorStatus[property]) {
-      return false;
+    if (!this.errorStatus) {
+      return true;
     }
-    return true;
+
+    // return false if one of the status has the invalid* property
+    return !this.errorStatus.asFlatList().some(function(status) {
+      return !!status[property];
+    });
   }
 
   _isDateValid() {
diff --git a/eclipse-scout-core/src/form/fields/datefield/DateFieldAdapter.js b/eclipse-scout-core/src/form/fields/datefield/DateFieldAdapter.js
index 9f57664..15bf81e 100644
--- a/eclipse-scout-core/src/form/fields/datefield/DateFieldAdapter.js
+++ b/eclipse-scout-core/src/form/fields/datefield/DateFieldAdapter.js
@@ -8,14 +8,12 @@
  * Contributors:
  *     BSI Business Systems Integration AG - initial API and implementation
  */
-import {arrays, DateField, dates, Status, ValueFieldAdapter} from '../../../index';
+import {arrays, dates, FormFieldAdapter, ParsingFailedStatus, ValueFieldAdapter} from '../../../index';
 
 export default class DateFieldAdapter extends ValueFieldAdapter {
 
   constructor() {
     super();
-    this._errorStatus = null;
-    this._errorStatusDisplayText = null;
   }
 
   static PROPERTIES_ORDER = ['hasTime', 'hasDate'];
@@ -25,35 +23,27 @@
    */
   _initProperties(model) {
     super._initProperties(model);
-    this._updateErrorStatus(model.errorStatus, model.displayText);
-  }
-
-  /**
-   * @override
-   */
-  _attachWidget() {
-    super._attachWidget();
-    this.widget.setValidator(function(value, defaultValidator) {
-      // If the server reported an error for current display text,
-      // make sure it will be shown in the UI if the user enters that display text again or selects the same date using the picker
-      var displayText = this.formatValue(value);
-      if (this.modelAdapter._errorStatus && displayText === this.modelAdapter._errorStatusDisplayText) {
-        throw this.modelAdapter._errorStatus;
-      }
-      return defaultValidator(value);
-    }.bind(this.widget), false);
   }
 
   /**
    * @override
    */
   _onWidgetAcceptInput(event) {
+    var parsingFailedError = null;
+    var errorStatus = this.widget.errorStatus;
+    // Only send Parsing errors to the server
+    if (errorStatus && errorStatus.hasChildren()) {
+      parsingFailedError = arrays.find(errorStatus.asFlatList(), function(childStatus) {
+        return childStatus instanceof ParsingFailedStatus;
+      });
+    }
+
     var data = {
       displayText: this.widget.displayText,
-      errorStatus: this.widget.errorStatus
+      errorStatus: parsingFailedError
     };
     // In case of an error, the value is still the old, valid value -> don't send it
-    if (!this.widget.errorStatus) {
+    if (!parsingFailedError) {
       data.value = dates.toJsonDate(this.widget.value);
     }
     this._send('acceptInput', data, {
@@ -62,17 +52,6 @@
         return this.target === previous.target && this.type === previous.type;
       }
     });
-    this._errorStatus = null;
-    this._errorStatusDisplayText = null;
-  }
-
-  /**
-   * @override
-   */
-  _syncDisplayText(displayText) {
-    // No need to call parseAndSetValue, the value will come separately
-    this.widget.setDisplayText(displayText);
-    this._updateErrorStatus(this._errorStatus, displayText);
   }
 
   /**
@@ -85,40 +64,4 @@
     return Object.keys(newProperties).sort(this._createPropertySortFunc(DateFieldAdapter.PROPERTIES_ORDER));
   }
 
-  _syncErrorStatus(errorStatus) {
-    if (errorStatus) {
-      if (this.widget.errorStatus) {
-        // Don't loose information which part was invalid
-        errorStatus.invalidDate = this.widget.errorStatus.invalidDate;
-        errorStatus.invalidTime = this.widget.errorStatus.invalidTime;
-      } else {
-        // If the error happened only on server side, we do not know which part was invalid.
-        // Set both to true so that DateField._isDateValid / isTimeValid does not return true
-        errorStatus.invalidDate = true;
-        errorStatus.invalidTime = true;
-      }
-    }
-
-    this._updateErrorStatus(errorStatus, this.widget.displayText);
-    this.widget.setErrorStatus(errorStatus);
-  }
-
-  _updateErrorStatus(errorStatus, displayText) {
-    // Find the first model error status. If server sends a UI error status (=PARSE_ERROR) then don't remember it
-    errorStatus = Status.ensure(errorStatus);
-    var modelErrorStatus = null;
-    if (errorStatus) {
-      modelErrorStatus = arrays.find(errorStatus.asFlatList(), function(status) {
-        return status.code !== DateField.ErrorCode.PARSE_ERROR;
-      });
-    }
-    // Remember errorStatus from model
-    if (modelErrorStatus) {
-      this._errorStatus = modelErrorStatus;
-      this._errorStatusDisplayText = displayText;
-    } else {
-      this._errorStatus = null;
-      this._errorStatusDisplayText = null;
-    }
-  }
 }
diff --git a/eclipse-scout-core/src/form/fields/datefield/DatePredictionFailedStatus.js b/eclipse-scout-core/src/form/fields/datefield/DatePredictionFailedStatus.js
new file mode 100644
index 0000000..d8ae7fd
--- /dev/null
+++ b/eclipse-scout-core/src/form/fields/datefield/DatePredictionFailedStatus.js
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2010-2020 BSI Business Systems Integration AG.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     BSI Business Systems Integration AG - initial API and implementation
+ */
+import {Status} from '../../../index';
+
+/**
+ * This status is used as a marker class to distinct between regular errors and errors thrown by the predict* functions of the DateField.
+ */
+export default class DatePredictionFailedStatus extends Status {
+
+  constructor() {
+    super();
+  }
+}
diff --git a/eclipse-scout-core/src/form/fields/sequencebox/SequenceBox.js b/eclipse-scout-core/src/form/fields/sequencebox/SequenceBox.js
index 9c12fdb..4542d92 100644
--- a/eclipse-scout-core/src/form/fields/sequencebox/SequenceBox.js
+++ b/eclipse-scout-core/src/form/fields/sequencebox/SequenceBox.js
@@ -8,7 +8,7 @@
  * Contributors:
  *     BSI Business Systems Integration AG - initial API and implementation
  */
-import {CheckBoxField, CompositeField, DateField, dates, HtmlComponent, LogicalGridData, LogicalGridLayoutConfig, scout, SequenceBoxGridConfig, SequenceBoxLayout} from '../../../index';
+import {CheckBoxField, CompositeField, DateField, dates, FormField, HtmlComponent, LogicalGridData, LogicalGridLayoutConfig, scout, SequenceBoxGridConfig, SequenceBoxLayout} from '../../../index';
 
 export default class SequenceBox extends CompositeField {
 
@@ -137,7 +137,7 @@
   _handleStatus(visibilityChanged) {
     if (visibilityChanged && this._lastVisibleField) {
       // if there is a new last visible field, make sure the status is shown on the previously last one
-      this._lastVisibleField.suppressStatus = false;
+      this._lastVisibleField.setSuppressStatus(null);
       if (this._lastVisibleField.rendered) {
         this._lastVisibleField._renderErrorStatus();
         this._lastVisibleField._renderTooltipText();
@@ -181,8 +181,8 @@
     }
     this._isOverwritingStatusFromField = false;
 
-    // Make sure the last field won't display a status
-    this._lastVisibleField.suppressStatus = true;
+    // Make sure the last field won't display a status (but shows status CSS class)
+    this._lastVisibleField.setSuppressStatus(FormField.SuppressStatus.ICON);
     if (visibilityChanged) {
       // If the last field got invisible, make sure the new last field does not display a status anymore (now done by the seq box)
       if (this._lastVisibleField.rendered) {
diff --git a/eclipse-scout-core/src/form/fields/smartfield/SmartFieldAdapter.js b/eclipse-scout-core/src/form/fields/smartfield/SmartFieldAdapter.js
index 44605d3..1f557bd 100644
--- a/eclipse-scout-core/src/form/fields/smartfield/SmartFieldAdapter.js
+++ b/eclipse-scout-core/src/form/fields/smartfield/SmartFieldAdapter.js
@@ -22,6 +22,8 @@
    * Property lookup-row must be handled before value, since the smart-field has either a lookup-row
    * or a value but never both (when we only have a value, the smart-field must perform a lookup by key
    * in order to resolve the display name for that value).
+   * <br>
+   * Intentionally don't re-use properties from super-classes.
    */
   static PROPERTIES_ORDER = ['lookupRow', 'value', 'errorStatus', 'displayText'];
 
diff --git a/eclipse-scout-core/src/form/fields/smartfield/SmartFieldMultiline.js b/eclipse-scout-core/src/form/fields/smartfield/SmartFieldMultiline.js
index f44cae3..04722b6 100644
--- a/eclipse-scout-core/src/form/fields/smartfield/SmartFieldMultiline.js
+++ b/eclipse-scout-core/src/form/fields/smartfield/SmartFieldMultiline.js
@@ -93,7 +93,6 @@
 
   _updateErrorStatusClasses(statusClass, hasStatus) {
     super._updateErrorStatusClasses(statusClass, hasStatus);
-    this._$multilineLines.removeClass(FormField.SEVERITY_CSS_CLASSES);
-    this._$multilineLines.addClass(statusClass, hasStatus);
+    this._updateErrorStatusClassesOnElement(this._$multilineLines, statusClass, hasStatus);
   }
 }
diff --git a/eclipse-scout-core/src/index.js b/eclipse-scout-core/src/index.js
index c244004..7d86986 100644
--- a/eclipse-scout-core/src/index.js
+++ b/eclipse-scout-core/src/index.js
@@ -42,7 +42,10 @@
 export {default as PromiseCreator} from './util/PromiseCreator';
 export {default as promises} from './util/promises';
 export {default as Range} from './util/Range';
-export {default as Status} from './util/Status';
+export {default as Status} from './status/Status';
+export {default as DefaultStatus} from './status/DefaultStatus';
+export {default as ParsingFailedStatus} from './status/ParsingFailedStatus';
+export {default as ValidationFailedStatus} from './status/ValidationFailedStatus';
 
 export {default as CachedElement} from './encoder/CachedElement';
 export {default as PlainTextEncoder} from './encoder/PlainTextEncoder';
@@ -426,6 +429,7 @@
 export {default as ClipboardFieldAdapter} from './form/fields/clipboardfield/ClipboardFieldAdapter';
 export {default as ColorField} from './form/fields/colorfield/ColorField';
 export {default as ColorFieldAdapter} from './form/fields/colorfield/ColorFieldAdapter';
+export {default as DatePredictionFailedStatus} from './form/fields/datefield/DatePredictionFailedStatus';
 export {default as DateField} from './form/fields/datefield/DateField';
 export {default as DateFieldAdapter} from './form/fields/datefield/DateFieldAdapter';
 export {default as DateTimeCompositeLayout} from './form/fields/datefield/DateTimeCompositeLayout';
diff --git a/eclipse-scout-core/src/status/DefaultStatus.js b/eclipse-scout-core/src/status/DefaultStatus.js
new file mode 100644
index 0000000..0f3791e
--- /dev/null
+++ b/eclipse-scout-core/src/status/DefaultStatus.js
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2010-2020 BSI Business Systems Integration AG.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     BSI Business Systems Integration AG - initial API and implementation
+ */
+import {Status} from '../index';
+
+/**
+ * The DefaultStatus class is used add programmatic Status triggered by business logic
+ * in cases where you don't want or don't have to implement your own Status sub-class.
+ */
+export default class DefaultStatus extends Status {
+
+  constructor(model) {
+    super(model);
+  }
+
+  /**
+   * @returns {DefaultStatus} a DefaultStatus object with severity ERROR.
+   */
+  static error(model) {
+    return new DefaultStatus(Status.ensureModel(model, Status.Severity.ERROR));
+  }
+
+  /**
+   * @returns {DefaultStatus} a DefaultStatus object with severity WARNING.
+   */
+  static warning(model) {
+    return new DefaultStatus(Status.ensureModel(model, Status.Severity.WARNING));
+  }
+
+  /**
+   * @returns {DefaultStatus} a DefaultStatus object with severity INFO.
+   */
+  static info(model) {
+    return new DefaultStatus(Status.ensureModel(model, Status.Severity.INFO));
+  }
+
+}
diff --git a/eclipse-scout-core/src/status/ParsingFailedStatus.js b/eclipse-scout-core/src/status/ParsingFailedStatus.js
new file mode 100644
index 0000000..49b3091
--- /dev/null
+++ b/eclipse-scout-core/src/status/ParsingFailedStatus.js
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2010-2020 BSI Business Systems Integration AG.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     BSI Business Systems Integration AG - initial API and implementation
+ */
+import {Status} from '../index';
+
+export default class ParsingFailedStatus extends Status {
+
+  constructor(model) {
+    super(model);
+  }
+
+  /**
+   * @returns {Status} a ParsingFailedStatus object with severity ERROR.
+   */
+  static error(model) {
+    return new ParsingFailedStatus(Status.ensureModel(model, Status.Severity.ERROR));
+  }
+
+}
diff --git a/eclipse-scout-core/src/status/Status.js b/eclipse-scout-core/src/status/Status.js
new file mode 100644
index 0000000..aaa6a6c
--- /dev/null
+++ b/eclipse-scout-core/src/status/Status.js
@@ -0,0 +1,358 @@
+/*
+ * Copyright (c) 2014-2018 BSI Business Systems Integration AG.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     BSI Business Systems Integration AG - initial API and implementation
+ */
+import {arrays, objects, strings, DefaultStatus, ParsingFailedStatus, ValidationFailedStatus, ObjectFactory} from '../index';
+import * as $ from 'jquery';
+
+export default class Status {
+
+  constructor(model) {
+    this.message = null;
+    this.severity = Status.Severity.ERROR;
+    this.code = 0;
+    this.children = null;
+    this.deletable = true;
+    $.extend(this, model);
+
+    // severity may be a string (e.g. if set in a model json file) -> convert to real severity
+    if (typeof this.severity === 'string') {
+      this.severity = Status.Severity[this.severity.toUpperCase()];
+    }
+    // children
+    if (model && model.children && Array.isArray(model.children)) {
+      this.children = model.children.map(function(child) {
+        return Status.ensure(child);
+      });
+    }
+  }
+
+  static Severity = {
+    OK: 0x01,
+    INFO: 0x100,
+    WARNING: 0x10000,
+    ERROR: 0x1000000
+  };
+
+  static SEVERITY_CSS_CLASSES = 'error warning info ok';
+
+  cssClass() {
+    return Status.cssClassForSeverity(this.severity);
+  }
+
+  /**
+   * @returns {boolean} true if severity is OK or INFO, false if severity is WARNING or ERROR.
+   */
+  isValid() {
+    return this.severity === Status.Severity.OK ||
+      this.severity === Status.Severity.INFO;
+  }
+
+  isError() {
+    return this.severity === Status.Severity.ERROR;
+  }
+
+  isWarning() {
+    return this.severity === Status.Severity.WARNING;
+  }
+
+  isInfo() {
+    return this.severity === Status.Severity.INFO;
+  }
+
+  isOk() {
+    return this.severity === Status.Severity.OK;
+  }
+
+  /**
+   * @returns {Status[]} status including children as flat list.
+   */
+  asFlatList() {
+    return Status.asFlatList(this);
+  }
+
+  /**
+   * @return {Status} a clone of this Status instance.
+   */
+  clone() {
+    var modelClone = $.extend({}, this);
+    return new Status(modelClone);
+  }
+
+  equals(o) {
+    if (!(o instanceof Status)) {
+      return false;
+    }
+    if (!objects.equalsRecursive(this.children, o.children)) {
+      return false;
+    }
+    return objects.propertiesEquals(this, o, ['severity', 'message', 'invalidDate', 'invalidTime']);
+  }
+
+  /**
+   * @param {object} statusType
+   * @return {boolean} whether or not this status contains a child with the give type
+   */
+  containsStatus(statusType) {
+    return this.containsStatusPredicate(function(status) {
+      return status instanceof statusType;
+    });
+  }
+
+  containsStatusPredicate(predicate) {
+    return this.asFlatList().some(predicate);
+  }
+
+  addStatus(status) {
+    if (this.hasChildren()) {
+      this.children.push(status);
+    } else {
+      this.children = [status];
+    }
+    this._updateProperties();
+  }
+
+  /**
+   * Removes all children of the given type from this status. The type is checked by inheritance.
+   *
+   * @param {object} statusType
+   */
+  removeAllStatus(statusType) {
+    this.removeAllStatusPredicate(function(status) {
+      return status instanceof statusType;
+    });
+  }
+
+  removeAllStatusPredicate(predicate) {
+    if (this.hasChildren()) {
+      this.children.forEach(function(status) {
+        status.removeAllStatusPredicate(predicate);
+      });
+      var newChildren = this.children.filter(function(status) {
+        // when status is not deletable we must add it as child again, thus --> true
+        if (!status.deletable) {
+          return true;
+        }
+        return !predicate(status); // negate predicate
+      });
+      this.children = newChildren;
+      this._updateProperties();
+    }
+  }
+
+  _updateProperties() {
+    if (!this.hasChildren()) {
+      this.message = null;
+      this.severity = Status.Severity.OK;
+      this.code = 0;
+      return;
+    }
+
+    var firstStatus = this.asFlatList().sort(function(a, b) {
+      return calcPriority(b) - calcPriority(a);
+
+      function calcPriority(status) {
+        var multiplier = 1;
+        if (status instanceof ParsingFailedStatus) {
+          multiplier = 4;
+        } else if (status instanceof ValidationFailedStatus) {
+          multiplier = 2;
+        }
+        return multiplier * status.severity;
+      }
+    })[0];
+    this.message = firstStatus.message;
+    this.severity = firstStatus.severity;
+    this.code = firstStatus.code;
+  }
+
+  /**
+   * @return {boolean} whether this status has children (= multi status)
+   */
+  hasChildren() {
+    return !!(this.children && this.children.length > 0);
+  }
+
+  /**
+   * In some cases we need to transform an error status without children to a multi-status with children.
+   * If the instance already has children, this function returns a clone of the instance.
+   * If the instance is not yet a multi-status, we return a new instance with the current instance as first child.
+   *
+   * @returns {Status}
+   */
+  ensureChildren() {
+    if (objects.isArray(this.children)) {
+      return this.clone();
+    }
+    var childStatus = this;
+    var newStatus = this.clone();
+    newStatus.children = [childStatus];
+    newStatus._updateProperties();
+    return newStatus;
+  }
+
+  /* --- STATIC HELPERS ------------------------------------------------------------- */
+
+  /**
+   * Null-safe static clone method.
+   */
+  static clone(original) {
+    return original ? original.clone() : null;
+  }
+
+  /**
+   * @param {number} severity
+   * @returns {string}
+   * @static
+   */
+  static cssClassForSeverity(severity) {
+    var cssSeverity,
+      Severity = Status.Severity;
+
+    switch (severity) {
+      case Severity.OK:
+        cssSeverity = 'ok';
+        break;
+      case Severity.INFO:
+        cssSeverity = 'info';
+        break;
+      case Severity.WARNING:
+        cssSeverity = 'warning';
+        break;
+      case Severity.ERROR:
+        cssSeverity = 'error';
+        break;
+    }
+    return cssSeverity;
+  }
+
+  static animateStatusMessage($status, message) {
+    if (strings.endsWith(message, '...')) {
+      var $ellipsis = $status.makeSpan('ellipsis');
+      for (var i = 0; i < 3; i++) {
+        $ellipsis.append($status.makeSpan('animate-dot delay-' + i, '.'));
+      }
+      message = message.substring(0, message.length - 3);
+      $status.empty().text(message).append($ellipsis);
+    } else {
+      $status.text(message);
+    }
+  }
+
+  static ensure(status) {
+    if (!status) {
+      return status;
+    }
+    if (status instanceof Status) {
+      return status;
+    }
+    // May return a specialized sub-class of Status
+    if (!status.objectType) {
+      status.objectType = 'Status';
+    }
+    return ObjectFactory.get().create(status);
+  }
+
+  /**
+   * @returns {Status} a Status object with severity OK.
+   */
+  static ok(model) {
+    return new Status(Status.ensureModel(model, Status.Severity.OK));
+  }
+
+  /**
+   * @returns {Status} a Status object with severity INFO.
+   */
+  static info(model) {
+    return new Status(Status.ensureModel(model, Status.Severity.INFO));
+  }
+
+  /**
+   * @returns {Status} a Status object with severity WARNING.
+   * @deprecated do not use this legacy function, use Status.warning() instead!
+   */
+  static _warnDeprecationLogged = false;
+
+  static warn(model) {
+    if (!Status._warnDeprecationLogged && window.console && (window.console.warn || window.console.log)) {
+      (window.console.warn || window.console.log)('scout.Status.warn() is deprecated and will be removed in a future release. Please use Status.warning() instead.');
+      Status._warnDeprecationLogged = true; // only warn once
+    }
+    return Status.warning(model);
+  }
+
+  /**
+   * @returns {Status} a Status object with severity WARNING.
+   */
+  static warning(model) {
+    return new Status(Status.ensureModel(model, Status.Severity.WARNING));
+  }
+
+  /**
+   * @returns {Status} a Status object with severity ERROR.
+   */
+  static error(model) {
+    return new Status(Status.ensureModel(model, Status.Severity.ERROR));
+  }
+
+  /**
+   * @returns {object}
+   */
+  static ensureModel(model, severity) {
+    if (typeof model === 'string') {
+      model = {
+        message: model
+      };
+    } else {
+      model = model || {};
+    }
+    return $.extend({}, model, {
+      severity: severity
+    });
+  }
+
+  /**
+   * @returns {Status[]} all Status objects as flat list (goes through the status hierarchy)
+   */
+  static asFlatList(status) {
+    if (!status) {
+      return [];
+    }
+    var list = [];
+    if (status.hasChildren()) {
+      status.children.forEach(function(childStatus) {
+        arrays.pushAll(list, Status.asFlatList(childStatus));
+      });
+    } else {
+      list.push(status);
+    }
+    return list;
+  }
+
+  /**
+   * Returns a constructor function for the given class-name.
+   * <p>
+   * The key of this map is a string which is equals to the objectType string, the value is a reference to the constructor function.
+   * This map is required because in JavaScript we don't have the class-name at runtime.
+   * <p>
+   * Note: we cannot initialize this map as static variable, because webpack dependencies are not resolved in the moment the variable
+   * is initialized.
+   *
+   * @param {string} className
+   * @returns {function} Status constructor
+   */
+  static classForName(className) {
+    return {
+      Status: Status,
+      DefaultStatus: DefaultStatus,
+      ParsingFailedStatus: ParsingFailedStatus,
+      ValidationFailedStatus: ValidationFailedStatus
+    }[className];
+  }
+}
diff --git a/eclipse-scout-core/src/status/ValidationFailedStatus.js b/eclipse-scout-core/src/status/ValidationFailedStatus.js
new file mode 100644
index 0000000..99bfc19
--- /dev/null
+++ b/eclipse-scout-core/src/status/ValidationFailedStatus.js
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2010-2020 BSI Business Systems Integration AG.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     BSI Business Systems Integration AG - initial API and implementation
+ */
+import {Status} from '../index';
+
+export default class ValidationFailedStatus extends Status {
+
+  constructor(model) {
+    super(model);
+  }
+
+}
diff --git a/eclipse-scout-core/src/util/Status.js b/eclipse-scout-core/src/util/Status.js
deleted file mode 100644
index 562893d..0000000
--- a/eclipse-scout-core/src/util/Status.js
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright (c) 2014-2018 BSI Business Systems Integration AG.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- *     BSI Business Systems Integration AG - initial API and implementation
- */
-import {arrays, objects, strings} from '../index';
-import * as $ from 'jquery';
-
-export default class Status {
-
-  constructor(model) {
-    this.message = null;
-    this.severity = Status.Severity.ERROR;
-    this.code = 0;
-    $.extend(this, model);
-
-    // severity may be a string (e.g. if set in a model json file) -> convert to real severity
-    if (typeof this.severity === 'string') {
-      this.severity = Status.Severity[this.severity.toUpperCase()];
-    }
-    // children
-    if (model && model.children && Array.isArray(model.children)) {
-      this.children = model.children.map(function(child) {
-        return Status.ensure(child);
-      });
-    }
-  }
-
-  static Severity = {
-    OK: 0x01,
-    INFO: 0x100,
-    WARNING: 0x10000,
-    ERROR: 0x1000000
-  };
-
-  static SEVERITY_CSS_CLASSES = 'error warning info ok';
-
-  cssClass() {
-    return Status.cssClassForSeverity(this.severity);
-  }
-
-  /**
-   * @returns {boolean} true if severity is OK or INFO, false if severity is WARNING or ERROR.
-   */
-  isValid() {
-    return this.severity === Status.Severity.OK ||
-      this.severity === Status.Severity.INFO;
-  }
-
-  isError() {
-    return this.severity === Status.Severity.ERROR;
-  }
-
-  isWarning() {
-    return this.severity === Status.Severity.WARNING;
-  }
-
-  isInfo() {
-    return this.severity === Status.Severity.INFO;
-  }
-
-  isOk() {
-    return this.severity === Status.Severity.OK;
-  }
-
-  /**
-   * @returns {Status[]} status including children as flat list.
-   */
-  asFlatList() {
-    return Status.asFlatList(this);
-  }
-
-  /**
-   * @return {Status} a clone of this Status instance.
-   */
-  clone() {
-    var modelClone = $.extend({}, this);
-    return new Status(modelClone);
-  }
-
-  equals(o) {
-    if (!(o instanceof Status)) {
-      return false;
-    }
-    return objects.propertiesEquals(this, o, ['severity', 'message', 'invalidDate', 'invalidTime']);
-  }
-
-  /* --- STATIC HELPERS ------------------------------------------------------------- */
-
-  /**
-   * Null-safe static clone method.
-   */
-  static clone(original) {
-    return original ? original.clone() : null;
-  }
-
-  /**
-   * @param {number} severity
-   * @returns {string}
-   * @static
-   */
-  static cssClassForSeverity(severity) {
-    var cssSeverity,
-      Severity = Status.Severity;
-
-    switch (severity) {
-      case Severity.OK:
-        cssSeverity = 'ok';
-        break;
-      case Severity.INFO:
-        cssSeverity = 'info';
-        break;
-      case Severity.WARNING:
-        cssSeverity = 'warning';
-        break;
-      case Severity.ERROR:
-        cssSeverity = 'error';
-        break;
-    }
-    return cssSeverity;
-  }
-
-  static animateStatusMessage($status, message) {
-    if (strings.endsWith(message, '...')) {
-      var $ellipsis = $status.makeSpan('ellipsis');
-      for (var i = 0; i < 3; i++) {
-        $ellipsis.append($status.makeSpan('animate-dot delay-' + i, '.'));
-      }
-      message = message.substring(0, message.length - 3);
-      $status.empty().text(message).append($ellipsis);
-    } else {
-      $status.text(message);
-    }
-  }
-
-  static ensure(status) {
-    if (!status) {
-      return status;
-    }
-    if (status instanceof Status) {
-      return status;
-    }
-    return new Status(status);
-  }
-
-  /**
-   * @returns {Status} a Status object with severity OK.
-   */
-  static ok(model) {
-    return Status._create(model, Status.Severity.OK);
-  }
-
-  /**
-   * @returns {Status} a Status object with severity INFO.
-   */
-  static info(model) {
-    return Status._create(model, Status.Severity.INFO);
-  }
-
-  /**
-   * @returns {Status} a Status object with severity WARNING.
-   * @deprecated do not use this legacy function, use Status.warning() instead!
-   */
-  static _warnDeprecationLogged = false;
-
-  static warn(model) {
-    if (!Status._warnDeprecationLogged && window.console && (window.console.warn || window.console.log)) {
-      (window.console.warn || window.console.log)('scout.Status.warn() is deprecated and will be removed in a future release. Please use Status.warning() instead.');
-      Status._warnDeprecationLogged = true; // only warn once
-    }
-    return Status.warning(model);
-  }
-
-  /**
-   * @returns {Status} a Status object with severity WARNING.
-   */
-  static warning(model) {
-    return Status._create(model, Status.Severity.WARNING);
-  }
-
-  /**
-   * @returns {Status} a Status object with severity ERROR.
-   */
-  static error(model) {
-    return Status._create(model, Status.Severity.ERROR);
-  }
-
-  static _create(model, severity) {
-    if (typeof model === 'string') {
-      model = {
-        message: model
-      };
-    } else {
-      model = model || {};
-    }
-    model = $.extend({}, model, {
-      severity: severity
-    });
-    return new Status(model);
-  }
-
-  /**
-   * @returns {Status[]} all Status objects as flat list (goes through the status hierarchy)
-   */
-  static asFlatList(status) {
-    if (!status) {
-      return [];
-    }
-    var list = [];
-    if (status.children) {
-      status.children.forEach(function(childStatus) {
-        arrays.pushAll(list, Status.asFlatList(childStatus));
-      });
-    }
-    list.push(status);
-    return list;
-  }
-}
diff --git a/eclipse-scout-core/test/form/fields/FormFieldAdapterSpec.js b/eclipse-scout-core/test/form/fields/FormFieldAdapterSpec.js
index 68f295d..a4fc282 100644
--- a/eclipse-scout-core/test/form/fields/FormFieldAdapterSpec.js
+++ b/eclipse-scout-core/test/form/fields/FormFieldAdapterSpec.js
@@ -46,7 +46,7 @@
       // required
       formField._$statusLabel = $('<div></div>');
       adapter.onModelPropertyChange(event);
-      expect(formField.errorStatus).toEqual(new Status({message: 'foo'}));
+      expect(formField.errorStatus.equals(Status.ensure({message: 'foo'}))).toBe(true);
       // never apply id, type, properties on model
       expect(formField.id).toBe(model.id);
       expect(formField.hasOwnProperty('type')).toBe(false);
diff --git a/eclipse-scout-core/test/form/fields/FormFieldSpec.js b/eclipse-scout-core/test/form/fields/FormFieldSpec.js
index 0498791..712c9cf 100644
--- a/eclipse-scout-core/test/form/fields/FormFieldSpec.js
+++ b/eclipse-scout-core/test/form/fields/FormFieldSpec.js
@@ -374,6 +374,25 @@
 
   });
 
+  it('property suppressStatus', function() {
+    var formField = createFormField(helper.createFieldModel());
+    expect(formField.suppressStatus).toEqual(null); // default value
+    expect(formField._isSuppressStatusField()).toBe(false);
+    expect(formField._isSuppressStatusIcon()).toBe(false);
+
+    formField.setSuppressStatus(FormField.SuppressStatus.ALL);
+    expect(formField._isSuppressStatusField()).toBe(true);
+    expect(formField._isSuppressStatusIcon()).toBe(true);
+
+    formField.setSuppressStatus(FormField.SuppressStatus.ICON);
+    expect(formField._isSuppressStatusField()).toBe(false);
+    expect(formField._isSuppressStatusIcon()).toBe(true);
+
+    formField.setSuppressStatus(FormField.SuppressStatus.FIELD);
+    expect(formField._isSuppressStatusField()).toBe(true);
+    expect(formField._isSuppressStatusIcon()).toBe(false);
+  });
+
   describe('property visible', function() {
     var formField, model;
 
diff --git a/eclipse-scout-core/test/form/fields/ValueFieldSpec.js b/eclipse-scout-core/test/form/fields/ValueFieldSpec.js
index ba283c8..61f0980 100644
--- a/eclipse-scout-core/test/form/fields/ValueFieldSpec.js
+++ b/eclipse-scout-core/test/form/fields/ValueFieldSpec.js
@@ -8,7 +8,7 @@
  * Contributors:
  *     BSI Business Systems Integration AG - initial API and implementation
  */
-import {scout, Status, StringField, ValueField} from '../../../src/index';
+import {ParsingFailedStatus, arrays, scout, Status, StringField, ValueField} from '../../../src/index';
 import {FormSpecHelper, MenuSpecHelper} from '@eclipse-scout/testing';
 
 /* global removePopups */
@@ -131,20 +131,32 @@
         parent: session.desktop,
         value: 'Foo',
         errorStatus: {
-          message: 'initial error status'
+          children: [{
+            message: 'initial error status'
+          }]
         }
       });
-      expect(field.errorStatus.message).toBe('initial error status');
+      expect(field.errorStatus.message).toBe('Validation failed');
+      expect(field.errorStatus.children.length).toEqual(2);
+      expect(findInitialError(field).message).toEqual('initial error status');
 
-      // If setValue is called after initialization, error status will be replaced
+      // Same thing should happen when setValue is called
       field.setValue('ABC');
       expect(field.errorStatus.message).toBe('Validation failed');
+      expect(field.errorStatus.children.length).toEqual(2);
+      expect(findInitialError(field).message).toEqual('initial error status');
 
-      // If calling setErrorStatus error status may be set explicitly independent of the value
+      // calling setErrorStatus error status may be set explicitly independent of the value
       field.setErrorStatus(Status.error({
         message: 'another error'
       }));
       expect(field.errorStatus.message).toBe('another error');
+
+      function findInitialError(field) {
+        return arrays.find(field.errorStatus.children, function(status) {
+          return !(status instanceof ParsingFailedStatus);
+        });
+      }
     });
 
     it('calls validate and format when value is set initially', function() {
@@ -273,10 +285,10 @@
       expect(field.errorStatus.message).toBe('[undefined text: InvalidValueMessageX]');
     });
 
-    it('may throw a Status if value is invalid', function() {
+    it('may throw a ParsingFailedStatus if value is invalid', function() {
       var field = helper.createField('StringField');
       field.setValidator(function(value) {
-        throw Status.error({
+        throw ParsingFailedStatus.error({
           message: 'Custom message'
         });
       });
diff --git a/eclipse-scout-core/test/form/fields/datefield/DateFieldAdapterSpec.js b/eclipse-scout-core/test/form/fields/datefield/DateFieldAdapterSpec.js
index 784897e..79c4287 100644
--- a/eclipse-scout-core/test/form/fields/datefield/DateFieldAdapterSpec.js
+++ b/eclipse-scout-core/test/form/fields/datefield/DateFieldAdapterSpec.js
@@ -68,40 +68,44 @@
     it('sets the server errorStatus if the displayText was reverted to the one provoking the error', function() {
       var field = createWithAdapter({
         hasTime: true,
-        value: dates.create('2017-05-23 12:30:00.000')
-      });
-      field.modelAdapter._syncErrorStatus({
-        message: 'error status from server'
+        value: dates.create('2017-05-23 12:30:00.000'),
+        errorStatus: {
+          children: [{message: 'error status from server'}]
+        }
       });
       field.render();
       field.$dateField.focus();
       expect(field.$dateField.val()).toBe('23.05.2017');
-      expect(field.errorStatus.message).toBe('error status from server');
+      expect(field.errorStatus.children.length).toBe(1);
+      expect(field.errorStatus.children[0].message).toBe('error status from server');
 
       // Enter another date, but don't press enter
       field.$dateField.val('23.05.201');
       field._onDateFieldInput();
       expect(field.value.toISOString()).toBe(dates.create('2017-05-23 12:30:00.000').toISOString());
-      expect(field.errorStatus).toBe(null);
+      expect(field.errorStatus.children.length).toBe(1);
+      expect(field.errorStatus.children[0].message).toBe('error status from server');
 
       // Revert to the old date and press enter -> send the event so that server may validate again
       field.$dateField.val('23.05.2017');
       field._onDateFieldInput();
       field.acceptInput();
-      expect(field.errorStatus.message).toBe('error status from server');
+      expect(field.errorStatus.children.length).toBe(1);
+      expect(field.errorStatus.children[0].message).toBe('error status from server');
     });
 
     it('sets the server errorStatus if the displayText was reverted to the one provoking the error using key down/up', function() {
       var field = createWithAdapter({
-        value: dates.create('2017-05-23')
-      });
-      field.modelAdapter._syncErrorStatus({
-        message: 'error status from server'
+        value: dates.create('2017-05-23'),
+        errorStatus: {
+          children: [{message: 'error status from server'}]
+        }
       });
       field.render();
       field.$dateField.focus();
       expect(field.$dateField.val()).toBe('23.05.2017');
-      expect(field.errorStatus.message).toBe('error status from server');
+      expect(field.errorStatus.children.length).toBe(1);
+      expect(field.errorStatus.children[0].message).toBe('error status from server');
 
       // Enter another date, but don't press enter
       field.$dateField.triggerKeyDown(keys.DOWN);
@@ -111,38 +115,42 @@
       field.$dateField.triggerKeyDown(keys.UP);
       expect(field.displayText).toBe('23.05.2017');
       field.acceptInput();
-      expect(field.errorStatus.message).toBe('error status from server');
+      expect(field.errorStatus.children.length).toBe(1);
+      expect(field.errorStatus.children[0].message).toBe('error status from server');
     });
 
     it('sets the server errorStatus if the displayText was reverted to the one provoking the error using picker', function() {
       var field = createWithAdapter({
-        value: dates.create('2017-05-23')
-      });
-      field.modelAdapter._syncErrorStatus({
-        message: 'error status from server'
+        value: dates.create('2017-05-23'),
+        errorStatus: {
+          children: [{message: 'error status from server'}]
+        }
       });
       field.render();
       field.$dateField.focus();
       expect(field.$dateField.val()).toBe('23.05.2017');
-      expect(field.errorStatus.message).toBe('error status from server');
+      expect(field.errorStatus.children.length).toBe(1);
+      expect(field.errorStatus.children[0].message).toBe('error status from server');
 
       // Open picker and select invalid date again -> error status must not vanish
       openDatePicker(field);
       find$Day(field.getDatePicker(), new Date(2017, 4, 23)).triggerClick();
       expect(field.$dateField.val()).toBe('23.05.2017');
-      expect(field.errorStatus.message).toBe('error status from server');
+      expect(field.errorStatus.children.length).toBe(1);
+      expect(field.errorStatus.children[0].message).toBe('error status from server');
     });
 
-    it('does not accidentially remove the model error status on acceptInput', function() {
+    it('does not accidentally remove the model error status on acceptInput', function() {
       var field = createWithAdapter({
-        value: dates.create('2017-05-23')
-      });
-      field.modelAdapter._syncErrorStatus({
-        message: 'error status from server'
+        value: dates.create('2017-05-23'),
+        errorStatus: {
+          children: [{message: 'error status from server'}]
+        }
       });
       field.render();
       field.acceptInput();
-      expect(field.errorStatus.message).toBe('error status from server');
+      expect(field.errorStatus.children.length).toBe(1);
+      expect(field.errorStatus.children[0].message).toBe('error status from server');
     });
 
   });
diff --git a/eclipse-scout-core/test/form/fields/datefield/DateFieldSpec.js b/eclipse-scout-core/test/form/fields/datefield/DateFieldSpec.js
index a6b3609..4345b00 100644
--- a/eclipse-scout-core/test/form/fields/datefield/DateFieldSpec.js
+++ b/eclipse-scout-core/test/form/fields/datefield/DateFieldSpec.js
@@ -8,7 +8,7 @@
  * Contributors:
  *     BSI Business Systems Integration AG - initial API and implementation
  */
-import {DateFormat, DatePickerTouchPopup, dates, keys, RemoteEvent, scout, Status, TimePickerTouchPopup} from '../../../../src/index';
+import {DateFormat, DatePickerTouchPopup, dates, keys, RemoteEvent, scout, Status, TimePickerTouchPopup, ValidationFailedStatus} from '../../../../src/index';
 import {FormSpecHelper} from '@eclipse-scout/testing';
 
 describe('DateField', function() {
@@ -269,7 +269,8 @@
       // Enter invalid date
       field.setValue(dates.create('2017-05-23 12:30:00.000'));
       expect(field.value).toBe(null);
-      expect(field.errorStatus instanceof Status).toBe(true);
+      expect(field.errorStatus.children.length).toBe(1);
+      expect(field.errorStatus.children[0] instanceof ValidationFailedStatus).toBe(true);
 
       // Enter another date, but don't press enter
       field.$dateField.val('23.05.201');
@@ -282,7 +283,8 @@
       field._onDateFieldInput();
       field.acceptInput();
       expect(field.value).toBe(null);
-      expect(field.errorStatus instanceof Status).toBe(true);
+      expect(field.errorStatus.children.length).toBe(1);
+      expect(field.errorStatus.children[0] instanceof ValidationFailedStatus).toBe(true);
     });
 
   });
@@ -313,31 +315,6 @@
       expect(dateField.errorStatus instanceof Status).toBe(false);
     });
 
-    it('does not remove time if date was deleted and time has an error', function() {
-      var dateField = scout.create('DateField', {
-        parent: session.desktop,
-        displayText: '01.10.2014\nasdf',
-        hasTime: true
-      });
-      dateField.render();
-      focusDate(dateField);
-      openDatePicker(dateField);
-      dateField.acceptInput();
-      expect(dateField.$dateField.val()).toBe('01.10.2014');
-      expect(dateField.$timeField.val()).toBe('asdf');
-      expect(dateField.errorStatus instanceof Status).toBe(true);
-
-      dateField.$dateField.val('');
-      expect(dateField.$dateField.val()).toBe('');
-      expect(dateField.$timeField.val()).toBe('asdf');
-
-      dateField.acceptDate();
-      expect(dateField.$dateField.val()).toBe('');
-      expect(dateField.$timeField.val()).toBe('asdf');
-      expect(dateField.value).toBe(null);
-      expect(dateField.errorStatus instanceof Status).toBe(true);
-    });
-
   });
 
   describe('acceptTime', function() {
@@ -366,31 +343,6 @@
       expect(dateField.errorStatus instanceof Status).toBe(false);
     });
 
-    it('does not remove date if time was deleted and date has an error', function() {
-      var dateField = scout.create('DateField', {
-        parent: session.desktop,
-        displayText: 'asdf\n05:00',
-        hasTime: true
-      });
-      dateField.render();
-      focusDate(dateField);
-      openDatePicker(dateField);
-      dateField.acceptInput();
-      expect(dateField.$dateField.val()).toBe('asdf');
-      expect(dateField.$timeField.val()).toBe('05:00');
-      expect(dateField.errorStatus instanceof Status).toBe(true);
-
-      dateField.$timeField.val('');
-      expect(dateField.$dateField.val()).toBe('asdf');
-      expect(dateField.$timeField.val()).toBe('');
-
-      dateField.acceptTime();
-      expect(dateField.$dateField.val()).toBe('asdf');
-      expect(dateField.$timeField.val()).toBe('');
-      expect(dateField.value).toBe(null);
-      expect(dateField.errorStatus instanceof Status).toBe(true);
-    });
-
   });
 
   describe('click', function() {
diff --git a/eclipse-scout-core/test/form/fields/integerfield/IntegerFieldSpec.js b/eclipse-scout-core/test/form/fields/integerfield/IntegerFieldSpec.js
index ea4b81a..8435778 100644
--- a/eclipse-scout-core/test/form/fields/integerfield/IntegerFieldSpec.js
+++ b/eclipse-scout-core/test/form/fields/integerfield/IntegerFieldSpec.js
@@ -10,7 +10,7 @@
  */
 
 import {FormSpecHelper} from '@eclipse-scout/testing';
-import {IntegerField, scout, Status} from '../../../../src/index';
+import {IntegerField, scout, Status, ParsingFailedStatus, ValidationFailedStatus} from '../../../../src/index';
 
 describe('IntegerField', () => {
 
@@ -80,4 +80,49 @@
     }
   }
 
+  describe('errorStatus', function() {
+    it('parse, validate and custom-error', function() {
+      var field = scout.create('IntegerField', {
+        parent: session.desktop
+      });
+
+      // invalid number
+      field.parseAndSetValue('foo');
+      expect(field.errorStatus.containsStatus(ParsingFailedStatus)).toBe(true);
+      expect(field.errorStatus.children.length).toEqual(1);
+      field.setErrorStatus(null);
+
+      // add a validator
+      var validator = function() {
+        throw 'Never valid';
+      };
+      field.addValidator(validator);
+      field.parseAndSetValue('123');
+      expect(field.errorStatus.containsStatus(ValidationFailedStatus)).toBe(true);
+      expect(field.errorStatus.children.length).toEqual(1);
+
+      // now set a functional error (typically added by business-logic)
+      // we use "set" here intentionally in place of "add"
+      field.setErrorStatus(Status.error('functional'));
+      expect(field.errorStatus.containsStatus(Status)).toBe(true);
+      expect(field.errorStatus.children).toBe(null);
+      field.removeValidator(validator);
+
+      // now make a parse error
+      // the existing (non-multi) error status should be transformed into a multi-status, so we have two status at the same time
+      field.parseAndSetValue('foo');
+      expect(field.errorStatus.containsStatus(ParsingFailedStatus)).toBe(true);
+      expect(field.errorStatus.children.length).toBe(2);
+      expect(field.errorStatus.children.some(function(status) {
+        return status.message === 'functional';
+      })).toBe(true);
+
+      // when the parse error is resolved, the multi status will remain
+      field.parseAndSetValue('123');
+      expect(field.errorStatus.containsStatus(ParsingFailedStatus)).toBe(false);
+      expect(field.errorStatus.children.length).toBe(1);
+      expect(field.errorStatus.message).toEqual('functional');
+    });
+  });
+
 });
diff --git a/eclipse-scout-core/test/util/StatusSpec.js b/eclipse-scout-core/test/util/StatusSpec.js
index 16d2c67..b72ab28 100644
--- a/eclipse-scout-core/test/util/StatusSpec.js
+++ b/eclipse-scout-core/test/util/StatusSpec.js
@@ -8,7 +8,7 @@
  * Contributors:
  *     BSI Business Systems Integration AG - initial API and implementation
  */
-import {Status} from '../../src/index';
+import {Status, DefaultStatus, ParsingFailedStatus} from '../../src/index';
 
 describe('scout.Status', function() {
 
@@ -78,4 +78,105 @@
 
   });
 
+  it('addStatus / hasChildren', function() {
+    var status = Status.error('root');
+    expect(status.hasChildren()).toBe(false);
+    status.addStatus(Status.info('foo'));
+    expect(status.hasChildren()).toBe(true);
+    status.removeAllStatus(Status);
+    expect(status.hasChildren()).toBe(false);
+  });
+
+  it('removeAllStatus', function() {
+    var status = Status.error('root');
+    status.addStatus(ParsingFailedStatus.error('foo'));
+    status.addStatus(Status.error('bar'));
+    expect(status.children.length).toEqual(2);
+    status.removeAllStatus(Status);
+    expect(status.hasChildren()).toBe(false); // because Status is the base-class of all status
+
+    // only remove status with type DefaultStatus
+    status.addStatus(ParsingFailedStatus.error('foo'));
+    status.addStatus(DefaultStatus.error('bar'));
+    status.removeAllStatus(DefaultStatus);
+    expect(status.children.length).toEqual(1);
+    expect(status.children[0].message).toEqual('foo');
+  });
+
+  it('containsStatus', function() {
+    var status = Status.error('root');
+
+    expect(status.containsStatus(ParsingFailedStatus)).toBe(false);
+    status.addStatus(ParsingFailedStatus.error('foo'));
+    expect(status.containsStatus(ParsingFailedStatus)).toBe(true);
+
+    expect(status.containsStatus(DefaultStatus)).toBe(false);
+    status.addStatus(DefaultStatus.error('bar'));
+    expect(status.containsStatus(DefaultStatus)).toBe(true);
+  });
+
+  it('updateProperties', function() {
+    var status = Status.ok('root');
+    status.addStatus(ParsingFailedStatus.error('foo'));
+    status.addStatus(DefaultStatus.warning('bar'));
+
+    expect(status.message).toEqual('foo');
+    expect(status.severity).toEqual(Status.Severity.ERROR);
+
+    // use properties from last remaining status (DefaultStatus)
+    status.removeAllStatus(ParsingFailedStatus);
+    expect(status.message).toEqual('bar');
+    expect(status.severity).toEqual(Status.Severity.WARNING);
+
+    // ParsingFailed should have the higher priority than DefaultStatus
+    status = Status.ok('root');
+    status.addStatus(DefaultStatus.error('baz'));
+    status.addStatus(ParsingFailedStatus.error('foo'));
+    expect(status.message).toEqual('foo');
+    expect(status.severity).toEqual(Status.Severity.ERROR);
+
+    // when the last child is removed
+    status.removeAllStatus(Status);
+    expect(status.message).toEqual(null);
+    expect(status.severity).toEqual(Status.Severity.OK);
+  });
+
+  it('equals', function() {
+    var a = Status.ok('root');
+    var b = Status.ok('root');
+    expect(a.equals(b)).toBe(true);
+    expect(b.equals(a)).toBe(true);
+
+    // make sure property 'children' is checked
+    a.addStatus(Status.error('foo'));
+    expect(a.equals(b)).toBe(false);
+    expect(b.equals(a)).toBe(false);
+
+    b.addStatus(Status.error('foo'));
+    expect(a.equals(b)).toBe(true);
+    expect(b.equals(a)).toBe(true);
+  });
+
+  describe('ensureChildren', function() {
+
+    it('status with no children should be transformed in a status with children', function() {
+      var status = Status.ok('foo');
+      expect(status.children).toBe(null);
+      var newStatus = status.ensureChildren();
+      expect(newStatus.children.length).toEqual(1);
+      expect(status).toBe(newStatus.children[0]);
+    });
+
+    it('status with children should return a clone', function() {
+      var status = Status.ensure({
+        children: [{message: 'foo'}]
+      });
+      expect(status.children.length).toEqual(1);
+      var newStatus = status.ensureChildren();
+      expect(newStatus.children.length).toEqual(1);
+      expect(status).not.toBe(newStatus);
+    });
+
+  });
+
 });
diff --git a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/ParsingFailedStatus.java b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/ParsingFailedStatus.java
index 2b040c6..646ef49 100644
--- a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/ParsingFailedStatus.java
+++ b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/ParsingFailedStatus.java
@@ -17,7 +17,7 @@
 /**
  * Internal marker status for parsing errors.
  *
- * @see AbstractValueField#parseValue(String)
+ * @see AbstractValueField#parseValueInternal(String)
  */
 @Order(10)
 public final class ParsingFailedStatus extends ScoutFieldStatus {
diff --git a/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/status/MultiStatus.java b/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/status/MultiStatus.java
index a8cd1e2..7f0b3d3 100644
--- a/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/status/MultiStatus.java
+++ b/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/status/MultiStatus.java
@@ -24,7 +24,7 @@
 /**
  * Implementation of a {@link IMultiStatus}: A status with child statuses.
  * <p>
- * The severity given by maximum severity of the children or {@link #IStatus#OK}, if no children available.
+ * The severity given by maximum severity of the children or {@link IStatus#OK}, if no children available.
  * </p>
  */
 public class MultiStatus extends Status implements IMultiStatus {
diff --git a/org.eclipse.scout.rt.ui.html/src/main/java/org/eclipse/scout/rt/ui/html/JsonObjectFactory.java b/org.eclipse.scout.rt.ui.html/src/main/java/org/eclipse/scout/rt/ui/html/JsonObjectFactory.java
index 8fc66f9..830481e 100644
--- a/org.eclipse.scout.rt.ui.html/src/main/java/org/eclipse/scout/rt/ui/html/JsonObjectFactory.java
+++ b/org.eclipse.scout.rt.ui.html/src/main/java/org/eclipse/scout/rt/ui/html/JsonObjectFactory.java
@@ -56,6 +56,8 @@
 import org.eclipse.scout.rt.client.ui.form.IFormMenu;
 import org.eclipse.scout.rt.client.ui.form.ITileOverviewForm;
 import org.eclipse.scout.rt.client.ui.form.fields.IStatusMenuMapping;
+import org.eclipse.scout.rt.client.ui.form.fields.ParsingFailedStatus;
+import org.eclipse.scout.rt.client.ui.form.fields.ValidationFailedStatus;
 import org.eclipse.scout.rt.client.ui.form.fields.accordionfield.IAccordionField;
 import org.eclipse.scout.rt.client.ui.form.fields.beanfield.IBeanField;
 import org.eclipse.scout.rt.client.ui.form.fields.booleanfield.IBooleanField;
@@ -109,6 +111,7 @@
 import org.eclipse.scout.rt.client.ui.tile.IWidgetTile;
 import org.eclipse.scout.rt.platform.Bean;
 import org.eclipse.scout.rt.platform.Order;
+import org.eclipse.scout.rt.platform.status.IStatus;
 import org.eclipse.scout.rt.ui.html.json.AbstractJsonObjectFactory;
 import org.eclipse.scout.rt.ui.html.json.IJsonAdapter;
 import org.eclipse.scout.rt.ui.html.json.IJsonObject;
@@ -116,6 +119,9 @@
 import org.eclipse.scout.rt.ui.html.json.JsonClientSession;
 import org.eclipse.scout.rt.ui.html.json.JsonDate;
 import org.eclipse.scout.rt.ui.html.json.JsonDecimalFormat;
+import org.eclipse.scout.rt.ui.html.json.JsonParsingFailedStatus;
+import org.eclipse.scout.rt.ui.html.json.JsonStatus;
+import org.eclipse.scout.rt.ui.html.json.JsonValidationFailedStatus;
 import org.eclipse.scout.rt.ui.html.json.accordion.JsonAccordion;
 import org.eclipse.scout.rt.ui.html.json.action.keystroke.JsonKeyStroke;
 import org.eclipse.scout.rt.ui.html.json.basic.filechooser.JsonFileChooser;
@@ -483,9 +489,18 @@
     if (object instanceof Date) {
       return new JsonDate((Date) object);
     }
-    else if (object instanceof byte[]) {
+    if (object instanceof byte[]) {
       return new JsonByteArray((byte[]) object);
     }
+    if (object instanceof ParsingFailedStatus) {
+      return new JsonParsingFailedStatus((ParsingFailedStatus) object);
+    }
+    if (object instanceof ValidationFailedStatus) {
+      return new JsonValidationFailedStatus((ValidationFailedStatus) object);
+    }
+    if (object instanceof IStatus) {
+      return new JsonStatus((IStatus) object);
+    }
     if (object instanceof INumberColumn<?>) {
       return new JsonNumberColumn((INumberColumn<?>) object);
     }
diff --git a/org.eclipse.scout.rt.ui.html/src/main/java/org/eclipse/scout/rt/ui/html/json/JsonParsingFailedStatus.java b/org.eclipse.scout.rt.ui.html/src/main/java/org/eclipse/scout/rt/ui/html/json/JsonParsingFailedStatus.java
new file mode 100644
index 0000000..9b87350
--- /dev/null
+++ b/org.eclipse.scout.rt.ui.html/src/main/java/org/eclipse/scout/rt/ui/html/json/JsonParsingFailedStatus.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2010-2020 BSI Business Systems Integration AG.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     BSI Business Systems Integration AG - initial API and implementation
+ */
+package org.eclipse.scout.rt.ui.html.json;
+
+import org.eclipse.scout.rt.client.ui.form.fields.ParsingFailedStatus;
+
+public class JsonParsingFailedStatus extends JsonStatus {
+
+  public JsonParsingFailedStatus(ParsingFailedStatus status) {
+    super(status);
+  }
+
+  @Override
+  public String getObjectType() {
+    return "ParsingFailedStatus";
+  }
+
+}
diff --git a/org.eclipse.scout.rt.ui.html/src/main/java/org/eclipse/scout/rt/ui/html/json/JsonStatus.java b/org.eclipse.scout.rt.ui.html/src/main/java/org/eclipse/scout/rt/ui/html/json/JsonStatus.java
index ff6b6d8..77a0960 100644
--- a/org.eclipse.scout.rt.ui.html/src/main/java/org/eclipse/scout/rt/ui/html/json/JsonStatus.java
+++ b/org.eclipse.scout.rt.ui.html/src/main/java/org/eclipse/scout/rt/ui/html/json/JsonStatus.java
@@ -23,6 +23,10 @@
     m_status = status;
   }
 
+  public String getObjectType() {
+    return "Status";
+  }
+
   public IStatus getStatus() {
     return m_status;
   }
@@ -30,13 +34,14 @@
   @Override
   public JSONObject toJson() {
     JSONObject json = new JSONObject();
+    json.put("objectType", getObjectType());
     json.put("message", m_status.getMessage());
     json.put("severity", m_status.getSeverity());
     json.put("iconId", m_status.getIconId());
     json.put("code", m_status.getCode());
     if (m_status.isMultiStatus()) {
       for (IStatus cs : ((IMultiStatus) m_status).getChildren()) {
-        json.append("children", new JsonStatus(cs).toJson());
+        json.append("children", MainJsonObjectFactory.get().createJsonObject(cs).toJson());
       }
     }
     return json;
diff --git a/org.eclipse.scout.rt.ui.html/src/main/java/org/eclipse/scout/rt/ui/html/json/JsonValidationFailedStatus.java b/org.eclipse.scout.rt.ui.html/src/main/java/org/eclipse/scout/rt/ui/html/json/JsonValidationFailedStatus.java
new file mode 100644
index 0000000..55320a4
--- /dev/null
+++ b/org.eclipse.scout.rt.ui.html/src/main/java/org/eclipse/scout/rt/ui/html/json/JsonValidationFailedStatus.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2010-2020 BSI Business Systems Integration AG.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     BSI Business Systems Integration AG - initial API and implementation
+ */
+package org.eclipse.scout.rt.ui.html.json;
+
+import org.eclipse.scout.rt.client.ui.form.fields.ValidationFailedStatus;
+import org.json.JSONObject;
+
+public class JsonValidationFailedStatus extends JsonStatus {
+
+  public JsonValidationFailedStatus(ValidationFailedStatus status) {
+    super(status);
+  }
+
+  @Override
+  public String getObjectType() {
+    return "ValidationFailedStatus";
+  }
+
+  /**
+   * Set the 'deletable' flag to false. The UI is not allowed to remove validation errors from the server.
+   *
+   * @return
+   */
+  @Override
+  public JSONObject toJson() {
+    JSONObject json = super.toJson();
+    json.put("deletable", false);
+    return json;
+  }
+}
diff --git a/org.eclipse.scout.rt.ui.html/src/main/java/org/eclipse/scout/rt/ui/html/json/form/fields/JsonFormField.java b/org.eclipse.scout.rt.ui.html/src/main/java/org/eclipse/scout/rt/ui/html/json/form/fields/JsonFormField.java
index 3fa6c7b..458878e 100644
--- a/org.eclipse.scout.rt.ui.html/src/main/java/org/eclipse/scout/rt/ui/html/json/form/fields/JsonFormField.java
+++ b/org.eclipse.scout.rt.ui.html/src/main/java/org/eclipse/scout/rt/ui/html/json/form/fields/JsonFormField.java
@@ -24,7 +24,7 @@
 import org.eclipse.scout.rt.ui.html.json.JsonGridData;
 import org.eclipse.scout.rt.ui.html.json.JsonProperty;
 import org.eclipse.scout.rt.ui.html.json.JsonResponse;
-import org.eclipse.scout.rt.ui.html.json.JsonStatus;
+import org.eclipse.scout.rt.ui.html.json.MainJsonObjectFactory;
 import org.eclipse.scout.rt.ui.html.json.action.DisplayableActionFilter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -172,7 +172,7 @@
 
       @Override
       public Object prepareValueForToJson(Object value) {
-        return JsonStatus.toJson((IStatus) value);
+        return MainJsonObjectFactory.get().createJsonObject(value).toJson();
       }
     });
     putJsonProperty(new JsonProperty<FORM_FIELD>("gridData", model) {