Java tutorial
/* * Copyright (c) 2013, Francis Galiegue <fgaliegue@gmail.com> * * 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 <http://www.gnu.org/licenses/>. */ 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.NullNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.github.fge.jackson.JacksonUtils; import com.github.fge.jsonschema.exceptions.ExceptionProvider; import com.github.fge.jsonschema.exceptions.ProcessingException; import com.github.fge.jsonschema.exceptions.unchecked.ProcessingError; import com.github.fge.jsonschema.messages.MessageBundle; import com.github.fge.jsonschema.messages.MessageBundles; import com.github.fge.jsonschema.util.AsJson; import com.google.common.base.Preconditions; import com.google.common.collect.Maps; import javax.annotation.concurrent.NotThreadSafe; import java.util.Map; /** * One processing message * * <p>Internally, a processing message has a {@link Map} whose keys are strings * and values are {@link JsonNode}s. Note that all methods altering the message * contents accept {@code null}: in this case, the value for that key will be a * {@link NullNode}.</p> * * <p>You can alter the behaviour of a processing message in two ways: its log * level and its {@link ExceptionProvider} (used in {@link #asException()}.</p> * * <p>All mutation methods of a message return {@code this}.</p> */ @NotThreadSafe public final class ProcessingMessage implements AsJson { private static final MessageBundle BUNDLE = MessageBundles.PROCESSING; private static final JsonNodeFactory FACTORY = JacksonUtils.nodeFactory(); private final Map<String, JsonNode> map = Maps.newLinkedHashMap(); private ExceptionProvider exceptionProvider = SimpleExceptionProvider.getInstance(); private LogLevel level; /** * Constructor * * <p>By default, a message is generated with a log level of {@link * LogLevel#INFO}.</p> */ public ProcessingMessage() { setLogLevel(LogLevel.INFO); } /** * Set the exception provider for that particular message * * @param exceptionProvider the exception provider * @return this * @throws ProcessingError exception provider is null */ public ProcessingMessage setExceptionProvider(final ExceptionProvider exceptionProvider) { if (exceptionProvider == null) throw new ProcessingError(BUNDLE.getString("nullExceptionProvider")); this.exceptionProvider = exceptionProvider; return this; } /** * Set the log level for this message * * @param level the log level * @return this * @throws ProcessingError log level is null */ public ProcessingMessage setLogLevel(final LogLevel level) { if (level == null) throw new ProcessingError(BUNDLE.getString("nullLevel")); this.level = Preconditions.checkNotNull(level, "log level cannot be null"); return put("level", level); } /** * Set the main message * * @param message the message as a string * @return this */ public ProcessingMessage message(final String message) { return put("message", message); } /** * Set the main message * * <p>The value type can be anything. It is your responsibility to make sure * that {@link Object#toString()} is implemented correctly!</p> * * @param value the value * @param <T> the type of the value * @return this */ public <T> ProcessingMessage message(final T value) { return put("message", value); } /** * Get the main message * * @return the main message as a string */ public String getMessage() { return map.containsKey("message") ? map.get("message").asText() : "(no message)"; } /** * Add a key/value pair to this message * * <p>This is the main method. All other put methods call this one.</p> * * <p>Note that if the key is {@code null}, the content is <b>ignored</b>. * </p> * @param key the key * @param value the value as a {@link JsonNode} * @return this */ public ProcessingMessage put(final String key, final JsonNode value) { if (key == null) return this; if (value == null) return putNull(key); map.put(key, value.deepCopy()); return this; } /** * Add a key/value pair to this message * * @param key the key * @param asJson the value, which implements {@link AsJson} * @return this */ public ProcessingMessage put(final String key, final AsJson asJson) { return put(key, asJson.asJson()); } /** * Add a key/value pair to this message * * @param key the key * @param value the value * @return this */ public ProcessingMessage put(final String key, final String value) { return value == null ? putNull(key) : put(key, FACTORY.textNode(value)); } /** * Add a key/value pair to this message * * @param key the key * @param value the value as an integer * @return this */ public ProcessingMessage put(final String key, final int value) { return put(key, FACTORY.numberNode(value)); } /** * Add a key/value pair to this message * * <p>As for {@link #message(Object)}, ensure that {@code toString()} is * correctly implemented.</p> * * @param key the key * @param value the value * @param <T> the type of the value * @return this */ public <T> ProcessingMessage put(final String key, final T value) { return value == null ? putNull(key) : put(key, FACTORY.textNode(value.toString())); } /** * Add a key/value pair to this message, where the value is a collection of * items * * <p>This will put all values (again, using {@link #toString()} of the * collection into an array.</p> * * @param key the key * @param values the collection of values * @param <T> the element type of the collection * @return this */ public <T> ProcessingMessage put(final String key, final Iterable<T> values) { if (values == null) return putNull(key); final ArrayNode node = FACTORY.arrayNode(); for (final T value : values) node.add(value == null ? FACTORY.nullNode() : FACTORY.textNode(value.toString())); return put(key, node); } /** * Get the log level for this message * * @return the log level */ public LogLevel getLogLevel() { return level; } /** * Put a {@link NullNode} as a value for a key * * <p>Note: if {@code key} is null, the put will be <b>ignored</b>.</p> * * @param key the key * @return this */ private ProcessingMessage putNull(final String key) { if (key == null) return this; map.put(key, FACTORY.nullNode()); return this; } @Override public JsonNode asJson() { final ObjectNode ret = FACTORY.objectNode(); ret.putAll(map); return ret; } /** * Build an exception out of this message * * <p>This uses the {@link ExceptionProvider} built into the message and * invokes {@link ExceptionProvider#doException(ProcessingMessage)} with * {@code this} as an argument.</p> * * @return an exception * @see #setExceptionProvider(ExceptionProvider) */ public ProcessingException asException() { return exceptionProvider.doException(this); } @Override public String toString() { final Map<String, JsonNode> tmp = Maps.newLinkedHashMap(map); final String message = tmp.remove("message").textValue(); final StringBuilder sb = new StringBuilder().append(level).append(": "); sb.append(message == null ? "(no message)" : message); for (final Map.Entry<String, JsonNode> entry : tmp.entrySet()) sb.append("\n ").append(entry.getKey()).append(": ").append(entry.getValue()); return sb.append('\n').toString(); } }