All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.fge.jsonschema.report.Message Maven / Gradle / Ivy

/*
 * Copyright (c) 2012, Francis Galiegue 
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the Lesser GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * Lesser GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */

package com.github.fge.jsonschema.report;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.github.fge.jsonschema.old.keyword.KeywordValidator;
import com.github.fge.jsonschema.old.syntax.SyntaxChecker;
import com.github.fge.jsonschema.util.jackson.JacksonUtils;
import com.google.common.base.Joiner;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Ordering;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * One validation message
 *
 * 

A validation message consists of three mandatory fields and optional * information. The three mandatory fields are:

* *
    *
  • the validation domain (see {@link Domain});
  • *
  • the associated keyword;
  • *
  • the validation message.
  • *
* *

Supplementary information can be added via the different {@code * .addInfo()} methods.

* *

Example of the JSON representation of one message:

* *
 *     {
 *         "domain": "validation",
 *         "keyword": "maxItems",
 *         "message": "too many elements in array",
 *         "maxItems": 4,
 *         "found": 5
 *     }
 * 
* *

You cannot instantiate this class directly: use {@link * Domain#newMessage()} for that.

* *

This class is immutable.

* * @see Builder * @see ValidationReport */ public final class Message implements Comparable { private static final JsonNodeFactory FACTORY = JacksonUtils.nodeFactory(); private static final Joiner JOINER = Joiner.on("; "); private final Domain domain; private final String keyword; private final String message; private final boolean fatal; private final Map info; private Message(final Builder builder) { domain = builder.domain; keyword = builder.keyword; message = builder.message; fatal = builder.fatal; info = ImmutableMap.copyOf(JacksonUtils.asMap(builder.info)); } public Domain getDomain() { return domain; } public String getKeyword() { return keyword; } public String getMessage() { return message; } public boolean isFatal() { return fatal; } public JsonNode toJsonNode() { final ObjectNode ret = FACTORY.objectNode() .put("domain", domain.toString()).put("keyword", keyword) .put("message", message); if (fatal) ret.put("fatal", true); ret.putAll(info); return ret; } /** * Implementation of {@link Comparable} for this class * *

The compared fields are the three mandatory parameters. Note that * whether the message is fatal has no influence here, since if a message is * fatal it will be the only reported message.

* * @param o the message to compare to * @return the result of the comparison */ @Override public int compareTo(final Message o) { return ComparisonChain.start() .compare(domain, o.domain) .compare(keyword, o.keyword) .compare(message, o.message) .result(); } @Override public int hashCode() { return Objects.hashCode(domain, keyword, message, fatal, info); } @Override public boolean equals(final Object obj) { if (obj == null) return false; if (this == obj) return true; if (getClass() != obj.getClass()) return false; final Message other = (Message) obj; return domain == other.domain && keyword.equals(other.keyword) && message.equals(other.message) && fatal == other.fatal && info.equals(other.info); } @Override public String toString() { final List list = new ArrayList(); list.add("domain: " + domain.toString()); list.add("keyword: " + keyword); list.add("message: " + message); for (final String key: Ordering.natural().sortedCopy(info.keySet())) list.add(key + ": " + info.get(key)); return (fatal ? "FATAL ERROR: " : "") + JOINER.join(list); } /** * Builder class for a {@link Message} * *

To build a validation message, you instantiate this class, fill the * necessary information and finally call {@link #build()} to obtain the * message.

* *

In most cases, you won't need to instantiate one directly:

* *
    *
  • when implementing a {@link SyntaxChecker}, an instance of this * class will be passed as an argument, already filled with the * correct domain and keyword;
  • *
  • when implementing a {@link KeywordValidator}, you can obtain an * instance also filled with the correct domain and keyword.
  • *
* */ public static final class Builder { /** * Reserved set of keywords which will be removed off {@link #info} * before the message is built */ private static final Set RESERVED = ImmutableSet.of("domain", "keyword", "message", "fatal"); /** * Validation domain */ private final Domain domain; /** * Keyword associated with the message * *

In some events, it may be set to {@code N/A}.

*/ private String keyword; /** * Error message */ private String message; /** * Further information associated with the error message */ private final ObjectNode info = FACTORY.objectNode(); /** * Is this error message fatal? */ private boolean fatal = false; Builder(final Domain domain) { Preconditions.checkNotNull(domain, "domain is null"); this.domain = domain; } /** * Set the keyword associated with this message * * @param keyword the keyword * @return the builder */ public Builder setKeyword(final String keyword) { this.keyword = keyword; return this; } /** * Set the error message * * @param message the error message * @return the builder */ public Builder setMessage(final String message) { this.message = message; return this; } /** * Should this error message be marked as fatal? * * @param fatal true if this error message is fatal (false by default) * @return the builder */ public Builder setFatal(final boolean fatal) { this.fatal = fatal; return this; } /** * Add further information to the message as a {@link JsonNode} * * @param key the key * @param value the value * @return the builder */ public Builder addInfo(final String key, final JsonNode value) { info.put(key, value); return this; } /** * Add further information to the message for an arbitrary type * *

This will call {@link Object#toString()} on the passed value. It * is therefore important that objects passed as arguments implement it * correctly.

* * @param key the key * @param the type of the value * @param value the value * @return the builder */ public Builder addInfo(final String key, final T value) { info.put(key, value.toString()); return this; } /** * Add further information to the message as a {@link Collection} of * objects of an arbitrary type * *

This will call {@link Object#toString()} on each element of the * collection.It is therefore important that objects passed as arguments * implement it correctly.

* * @see #addInfo(String, Object) * * @param key the key * @param the type of values in the collections * @param values the collection * @return the builder */ public Builder addInfo(final String key, final Collection values) { final ArrayNode node = FACTORY.arrayNode(); for (final T value: values) node.add(value.toString()); info.put(key, node); return this; } /** * Add further information to the message as an integer * * @param key the key * @param value the value * @return the builder */ public Builder addInfo(final String key, final int value) { info.put(key, value); return this; } /** * Clear all supplementary information * *

This will not reset the domain, keyword or message.

* * @return the builder */ public Builder clearInfo() { info.removeAll(); return this; } /** * Build the actual message * * @return a {@link Message} * @throws NullPointerException the keyword or message are null */ public Message build() { Preconditions.checkNotNull(keyword, "keyword is null"); Preconditions.checkNotNull(message, "message is null"); info.remove(RESERVED); return new Message(this); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy