@Override
 public void jsonWrite(ObjectWriter writer) throws IOException, JsonException {
   writer.writeEntry("loggingType", LOGGING_TYPE_CODES.getCode(loggingType));
   writer.writeEntry(
       "intervalLoggingPeriodType", Common.TIME_PERIOD_CODES.getCode(intervalLoggingPeriodType));
   writer.writeEntry(
       "intervalLoggingType", INTERVAL_LOGGING_TYPE_CODES.getCode(intervalLoggingType));
   writer.writeEntry("purgeType", Common.TIME_PERIOD_CODES.getCode(purgeType));
   writer.writeEntry("pointLocator", pointLocator);
   writer.writeEntry("eventDetectors", eventDetectors);
   writer.writeEntry("engineeringUnits", ENGINEERING_UNITS_CODES.getCode(engineeringUnits));
   writer.writeEntry("plotType", PLOT_TYPE_CODES.getCode(plotType));
 }
  @Override
  public void jsonRead(JsonReader reader, JsonObject jsonObject) throws JsonException {
    String text = jsonObject.getString("loggingType");
    if (text != null) {
      loggingType = LOGGING_TYPE_CODES.getId(text);
      if (loggingType == -1)
        throw new TranslatableJsonException(
            "emport.error.invalid", "loggingType", text, LOGGING_TYPE_CODES.getCodeList());
    }

    text = jsonObject.getString("intervalLoggingPeriodType");
    if (text != null) {
      intervalLoggingPeriodType = Common.TIME_PERIOD_CODES.getId(text);
      if (intervalLoggingPeriodType == -1)
        throw new TranslatableJsonException(
            "emport.error.invalid",
            "intervalLoggingPeriodType",
            text,
            Common.TIME_PERIOD_CODES.getCodeList());
    }

    text = jsonObject.getString("intervalLoggingType");
    if (text != null) {
      intervalLoggingType = INTERVAL_LOGGING_TYPE_CODES.getId(text);
      if (intervalLoggingType == -1)
        throw new TranslatableJsonException(
            "emport.error.invalid",
            "intervalLoggingType",
            text,
            INTERVAL_LOGGING_TYPE_CODES.getCodeList());
    }

    text = jsonObject.getString("purgeType");
    if (text != null) {
      purgeType = Common.TIME_PERIOD_CODES.getId(text);
      if (purgeType == -1)
        throw new TranslatableJsonException(
            "emport.error.invalid", "purgeType", text, Common.TIME_PERIOD_CODES.getCodeList());
    }

    JsonObject locatorJson = jsonObject.getJsonObject("pointLocator");
    if (locatorJson != null) reader.readInto(pointLocator, locatorJson);

    JsonArray pedArray = jsonObject.getJsonArray("eventDetectors");
    if (pedArray != null) {
      for (JsonValue jv : pedArray) {
        JsonObject pedObject = jv.toJsonObject();

        String pedXid = pedObject.getString("xid");
        if (StringUtils.isBlank(pedXid))
          throw new TranslatableJsonException("emport.error.ped.missingAttr", "xid");

        // Use the ped xid to lookup an existing ped.
        PointEventDetectorVO ped = null;
        for (PointEventDetectorVO existing : eventDetectors) {
          if (StringUtils.equals(pedXid, existing.getXid())) {
            ped = existing;
            break;
          }
        }

        if (ped == null) {
          // Create a new one
          ped = new PointEventDetectorVO();
          ped.setId(Common.NEW_ID);
          ped.setXid(pedXid);
          ped.njbSetDataPoint(this);
          eventDetectors.add(ped);
        }

        reader.readInto(ped, pedObject);
      }
    }

    text = jsonObject.getString("engineeringUnits");
    if (text != null) {
      engineeringUnits = ENGINEERING_UNITS_CODES.getId(text);
      if (engineeringUnits == -1) engineeringUnits = ENGINEERING_UNITS_DEFAULT;
    }

    text = jsonObject.getString("plotType");
    if (text != null) {
      plotType = PLOT_TYPE_CODES.getId(text);
      if (plotType == -1)
        throw new TranslatableJsonException(
            "emport.error.invalid", "plotType", text, PLOT_TYPE_CODES.getCodeList());
    }
  }
  public void validate(ProcessResult response) {
    if (StringUtils.isBlank(xid)) response.addContextualMessage("xid", "validate.required");
    else if (StringValidation.isLengthGreaterThan(xid, 50))
      response.addMessage("xid", new TranslatableMessage("validate.notLongerThan", 50));
    else if (!new DataPointDao().isXidUnique(xid, id))
      response.addContextualMessage("xid", "validate.xidUsed");

    if (StringUtils.isBlank(name)) response.addContextualMessage("name", "validate.required");

    if (!LOGGING_TYPE_CODES.isValidId(loggingType))
      response.addContextualMessage("loggingType", "validate.invalidValue");
    if (loggingType == DataPointVO.LoggingTypes.ON_CHANGE
        && pointLocator.getDataTypeId() == DataTypes.NUMERIC) {
      if (tolerance < 0) response.addContextualMessage("tolerance", "validate.cannotBeNegative");
    }

    if (!Common.TIME_PERIOD_CODES.isValidId(intervalLoggingPeriodType))
      response.addContextualMessage("intervalLoggingPeriodType", "validate.invalidValue");
    if (intervalLoggingPeriod <= 0)
      response.addContextualMessage("intervalLoggingPeriod", "validate.greaterThanZero");
    if (!INTERVAL_LOGGING_TYPE_CODES.isValidId(intervalLoggingType))
      response.addContextualMessage("intervalLoggingType", "validate.invalidValue");

    if (purgeOverride) {
      if (!Common.TIME_PERIOD_CODES.isValidId(purgeType))
        response.addContextualMessage("purgeType", "validate.invalidValue");
      if (purgePeriod <= 0)
        response.addContextualMessage("purgePeriod", "validate.greaterThanZero");
    }

    if (textRenderer == null) response.addContextualMessage("textRenderer", "validate.required");

    if (defaultCacheSize < 0)
      response.addContextualMessage("defaultCacheSize", "validate.cannotBeNegative");

    if (discardExtremeValues && discardHighLimit <= discardLowLimit)
      response.addContextualMessage("discardHighLimit", "validate.greaterThanDiscardLow");

    if (!StringUtils.isBlank(chartColour)) {
      try {
        ColorUtils.toColor(chartColour);
      } catch (InvalidArgumentException e) {
        response.addContextualMessage("chartColour", "validate.invalidValue");
      }
    }

    pointLocator.validate(response, this);

    // Check text renderer type
    if (textRenderer != null && !textRenderer.getDef().supports(pointLocator.getDataTypeId()))
      response.addGenericMessage("validate.text.incompatible");

    // Check chart renderer type
    if (chartRenderer != null && !chartRenderer.getDef().supports(pointLocator.getDataTypeId()))
      response.addGenericMessage("validate.chart.incompatible");

    // Check the plot type
    if (!PLOT_TYPE_CODES.isValidId(plotType))
      response.addContextualMessage("plotType", "validate.invalidValue");
    if (plotType != PlotTypes.STEP && pointLocator.getDataTypeId() != DataTypes.NUMERIC)
      response.addContextualMessage("plotType", "validate.invalidValue");
  }