001/*
002 * (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl-2.1.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Nicolas Chapurlat <nchapurlat@nuxeo.com>
016 */
017
018package org.nuxeo.ecm.core.io.marshallers.json.validation;
019
020import static org.nuxeo.ecm.core.io.registry.reflect.Instantiations.SINGLETON;
021import static org.nuxeo.ecm.core.io.registry.reflect.Priorities.REFERENCE;
022
023import java.io.IOException;
024
025import org.codehaus.jackson.JsonGenerator;
026import org.nuxeo.ecm.core.api.validation.ConstraintViolation;
027import org.nuxeo.ecm.core.api.validation.ConstraintViolation.PathNode;
028import org.nuxeo.ecm.core.api.validation.DocumentValidationReport;
029import org.nuxeo.ecm.core.io.marshallers.json.ExtensibleEntityJsonWriter;
030import org.nuxeo.ecm.core.io.marshallers.json.enrichers.AbstractJsonEnricher;
031import org.nuxeo.ecm.core.io.registry.reflect.Setup;
032import org.nuxeo.ecm.core.schema.types.constraints.Constraint;
033
034import com.thoughtworks.xstream.io.json.JsonWriter;
035
036/**
037 * Convert {@link DocumentValidationReport} to Json.
038 * <p>
039 * This marshaller is enrichable: register class implementing {@link AbstractJsonEnricher} and managing
040 * {@link DocumentValidationReport}.
041 * </p>
042 * <p>
043 * This marshaller is also extensible: extend it and simply override
044 * {@link ExtensibleEntityJsonWriter#extend(DocumentValidationReport, JsonWriter)}.
045 * </p>
046 * <p>
047 * Format is:
048 *
049 * <pre>
050 * {@code
051 * {
052 *   "entity-type":"validation_report",
053 *   "has_error": true|false,
054 *   "number": 123, <- number of errors present in this report
055 *   "violations": [
056 *     {
057 *       "message": "TheErrorMessage",
058 *       "invalid_value": null|"THE_INVALID_VALUE_AS_STRING",
059 *       "constraint": {
060 *         see {@link ConstraintJsonWriter} format
061 *       }
062 *     },
063 *     ...
064 *   ]
065 *             <-- contextParameters if there are enrichers activated
066 *             <-- additional property provided by extend() method
067 * }
068 * </pre>
069 *
070 * </p>
071 *
072 * @since 7.2
073 */
074@Setup(mode = SINGLETON, priority = REFERENCE)
075public class DocumentValidationReportJsonWriter extends ExtensibleEntityJsonWriter<DocumentValidationReport> {
076
077    public static final String ENTITY_TYPE = "validation_report";
078
079    public DocumentValidationReportJsonWriter() {
080        super(ENTITY_TYPE, DocumentValidationReport.class);
081    }
082
083    @Override
084    protected void writeEntityBody(DocumentValidationReport report, JsonGenerator jg) throws IOException {
085        jg.writeBooleanField("has_error", report.hasError());
086        jg.writeNumberField("number", report.numberOfErrors());
087        // constraint violations
088        jg.writeArrayFieldStart("violations");
089        for (ConstraintViolation violation : report.asList()) {
090            jg.writeStartObject();
091            // violation message
092            String message = violation.getMessage(ctx.getLocale());
093            jg.writeStringField("message", message);
094            // invalid value
095            Object invalidValue = violation.getInvalidValue();
096            if (invalidValue == null) {
097                jg.writeNullField("invalid_value");
098            } else {
099                jg.writeStringField("invalid_value", invalidValue.toString());
100            }
101            // violated constraint
102            Constraint constraint = violation.getConstraint();
103            writeEntityField("constraint", constraint, jg);
104            // violation place
105            jg.writeArrayFieldStart("path");
106            for (PathNode node : violation.getPath()) {
107                jg.writeStartObject();
108                jg.writeStringField("field_name", node.getField().getName().getPrefixedName());
109                jg.writeBooleanField("is_list_item", node.isListItem());
110                if (node.isListItem()) {
111                    jg.writeNumberField("index", node.getIndex());
112                }
113                jg.writeEndObject();
114            }
115            jg.writeEndArray();
116            jg.writeEndObject();
117        }
118        jg.writeEndArray();
119    }
120
121}