Repository: logging-log4j2 Updated Branches: refs/heads/GenericMapMessageSimple [created] fb7e4eaae
Branch for 2nd GenericMapMessage experiment as discussed on the ML with Ralph. Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/fb7e4eaa Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/fb7e4eaa Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/fb7e4eaa Branch: refs/heads/GenericMapMessageSimple Commit: fb7e4eaaeab9c73c058617b0eb88d7fc5de771db Parents: 5411ea1 Author: Gary Gregory <[email protected]> Authored: Sat Jun 3 12:04:20 2017 -0700 Committer: Gary Gregory <[email protected]> Committed: Sat Jun 3 12:04:20 2017 -0700 ---------------------------------------------------------------------- .../log4j/message/GenericMapMessage.java | 460 ------------------- .../logging/log4j/message/MapMessage.java | 430 ++++++++++++++++- 2 files changed, 424 insertions(+), 466 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/fb7e4eaa/log4j-api/src/main/java/org/apache/logging/log4j/message/GenericMapMessage.java ---------------------------------------------------------------------- diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/GenericMapMessage.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/GenericMapMessage.java deleted file mode 100644 index 0cbf0ff..0000000 --- a/log4j-api/src/main/java/org/apache/logging/log4j/message/GenericMapMessage.java +++ /dev/null @@ -1,460 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ -package org.apache.logging.log4j.message; - -import java.util.Collections; -import java.util.Map; -import java.util.TreeMap; - -import org.apache.logging.log4j.util.BiConsumer; -import org.apache.logging.log4j.util.EnglishEnums; -import org.apache.logging.log4j.util.IndexedReadOnlyStringMap; -import org.apache.logging.log4j.util.IndexedStringMap; -import org.apache.logging.log4j.util.PerformanceSensitive; -import org.apache.logging.log4j.util.ReadOnlyStringMap; -import org.apache.logging.log4j.util.SortedArrayStringMap; -import org.apache.logging.log4j.util.StringBuilderFormattable; -import org.apache.logging.log4j.util.StringBuilders; -import org.apache.logging.log4j.util.Strings; -import org.apache.logging.log4j.util.TriConsumer; - -/** - * Represents a Message that consists of a Map. - * <p> - * Thread-safety note: the contents of this message can be modified after construction. - * When using asynchronous loggers and appenders it is not recommended to modify this message after the message is - * logged, because it is undefined whether the logged message string will contain the old values or the modified - * values. - * </p> - */ -@PerformanceSensitive("allocation") -@AsynchronouslyFormattable -public class MapMessage implements MultiformatMessage, StringBuilderFormattable { - - /** - * When set as the format specifier causes the Map to be formatted as XML. - */ - public enum MapFormat { - - /** The map should be formatted as XML. */ - XML, - - /** The map should be formatted as JSON. */ - JSON, - - /** The map should be formatted the same as documented by java.util.AbstractMap.toString(). */ - JAVA; - - /** - * Maps a format name to an {@link MapFormat} while ignoring case. - * - * @param format a MapFormat name - * @return a MapFormat - */ - public static MapFormat lookupIgnoreCase(final String format) { - return XML.name().equalsIgnoreCase(format) ? XML // - : JSON.name().equalsIgnoreCase(format) ? JSON // - : JAVA.name().equalsIgnoreCase(format) ? JAVA // - : null; - } - - /** - * All {@code MapFormat} names. - * - * @return All {@code MapFormat} names. - */ - public static String[] names() { - return new String[] {XML.name(), JSON.name(), JAVA.name()}; - } - } - - private static final long serialVersionUID = -5031471831131487120L; - - private final IndexedStringMap data; - - /** - * Constructs a new instance. - */ - public MapMessage() { - data = new SortedArrayStringMap(); - } - - /** - * Constructs a new instance based on an existing Map. - * @param map The Map. - */ - public MapMessage(final Map<String, String> map) { - this.data = new SortedArrayStringMap(map); - } - - @Override - public String[] getFormats() { - return MapFormat.names(); - } - - /** - * Returns the data elements as if they were parameters on the logging event. - * @return the data elements. - */ - @Override - public Object[] getParameters() { - final Object[] result = new Object[data.size()]; - for (int i = 0; i < data.size(); i++) { - result[i] = data.getValueAt(i); - } - return result; - } - - /** - * Returns the message. - * @return the message. - */ - @Override - public String getFormat() { - return Strings.EMPTY; - } - - /** - * Returns the message data as an unmodifiable Map. - * @return the message data as an unmodifiable map. - */ - public Map<String, String> getData() { - final TreeMap<String, String> result = new TreeMap<>(); // returned map must be sorted - for (int i = 0; i < data.size(); i++) { - result.put(data.getKeyAt(i), (String) data.getValueAt(i)); - } - return Collections.unmodifiableMap(result); - } - - /** - * Returns a read-only view of the message data. - * @return the read-only message data. - */ - public IndexedReadOnlyStringMap getIndexedReadOnlyStringMap() { - return data; - } - - /** - * Clear the data. - */ - public void clear() { - data.clear(); - } - - /** - * Returns {@code true} if this data structure contains the specified key, {@code false} otherwise. - * - * @param key the key whose presence to check. May be {@code null}. - * @return {@code true} if this data structure contains the specified key, {@code false} otherwise - * @since 2.9 - */ - public boolean containsKey(final String key) { - return data.containsKey(key); - } - - /** - * Adds an item to the data Map in fluent style. - * @param key The name of the data item. - * @param value The value of the data item. - * @return {@code this} - */ - public MapMessage with(final String key, final String value) { - put(key, value); - return this; - } - - /** - * Adds an item to the data Map. - * @param key The name of the data item. - * @param value The value of the data item. - */ - public void put(final String key, final String value) { - if (value == null) { - throw new IllegalArgumentException("No value provided for key " + key); - } - validate(key, value); - data.putValue(key, value); - } - - protected void validate(final String key, final String value) { - - } - - /** - * Adds all the elements from the specified Map. - * @param map The Map to add. - */ - public void putAll(final Map<String, String> map) { - for (final Map.Entry<String, ?> entry : map.entrySet()) { - data.putValue(entry.getKey(), entry.getValue()); - } - } - - /** - * Retrieves the value of the element with the specified key or null if the key is not present. - * @param key The name of the element. - * @return The value of the element or null if the key is not present. - */ - public String get(final String key) { - return data.getValue(key); - } - - /** - * Removes the element with the specified name. - * @param key The name of the element. - * @return The previous value of the element. - */ - public String remove(final String key) { - final String result = data.getValue(key); - data.remove(key); - return result; - } - - /** - * Formats the Structured data as described in <a href="https://tools.ietf.org/html/rfc5424">RFC 5424</a>. - * - * @return The formatted String. - */ - public String asString() { - return format((MapFormat) null, new StringBuilder()).toString(); - } - - /** - * Formats the Structured data as described in <a href="https://tools.ietf.org/html/rfc5424">RFC 5424</a>. - * - * @param format The format identifier. - * @return The formatted String. - */ - public String asString(final String format) { - try { - return format(EnglishEnums.valueOf(MapFormat.class, format), new StringBuilder()).toString(); - } catch (final IllegalArgumentException ex) { - return asString(); - } - } - - /** - * Performs the given action for each key-value pair in this data structure - * until all entries have been processed or the action throws an exception. - * <p> - * Some implementations may not support structural modifications (adding new elements or removing elements) while - * iterating over the contents. In such implementations, attempts to add or remove elements from the - * {@code BiConsumer}'s {@link BiConsumer#accept(Object, Object)} accept} method may cause a - * {@code ConcurrentModificationException} to be thrown. - * </p> - * - * @param action The action to be performed for each key-value pair in this collection - * @param <V> type of the value - * @throws java.util.ConcurrentModificationException some implementations may not support structural modifications - * to this data structure while iterating over the contents with {@link #forEach(BiConsumer)} or - * {@link #forEach(TriConsumer, Object)}. - * @see ReadOnlyStringMap#forEach(BiConsumer) - * @since 2.9 - */ - public <V> void forEach(final BiConsumer<String, ? super V> action) { - data.forEach(action); - } - - /** - * Performs the given action for each key-value pair in this data structure - * until all entries have been processed or the action throws an exception. - * <p> - * The third parameter lets callers pass in a stateful object to be modified with the key-value pairs, - * so the TriConsumer implementation itself can be stateless and potentially reusable. - * </p> - * <p> - * Some implementations may not support structural modifications (adding new elements or removing elements) while - * iterating over the contents. In such implementations, attempts to add or remove elements from the - * {@code TriConsumer}'s {@link TriConsumer#accept(Object, Object, Object) accept} method may cause a - * {@code ConcurrentModificationException} to be thrown. - * </p> - * - * @param action The action to be performed for each key-value pair in this collection - * @param state the object to be passed as the third parameter to each invocation on the specified - * triconsumer - * @param <V> type of the value - * @param <S> type of the third parameter - * @throws java.util.ConcurrentModificationException some implementations may not support structural modifications - * to this data structure while iterating over the contents with {@link #forEach(BiConsumer)} or - * {@link #forEach(TriConsumer, Object)}. - * @see ReadOnlyStringMap#forEach(TriConsumer, Object) - * @since 2.9 - */ - public <V, S> void forEach(final TriConsumer<String, ? super V, S> action, final S state) { - data.forEach(action, state); - } - - /** - * Formats the Structured data as described in <a href="https://tools.ietf.org/html/rfc5424">RFC 5424</a>. - * - * @param format The format identifier. - * @return The formatted String. - */ - private StringBuilder format(final MapFormat format, final StringBuilder sb) { - if (format == null) { - appendMap(sb); - } else { - switch (format) { - case XML : { - asXml(sb); - break; - } - case JSON : { - asJson(sb); - break; - } - case JAVA : { - asJava(sb); - break; - } - default : { - appendMap(sb); - } - } - } - return sb; - } - - /** - * Formats this message as an XML fragment String into the given builder. - * - * @param sb format into this builder. - */ - public void asXml(final StringBuilder sb) { - sb.append("<Map>\n"); - for (int i = 0; i < data.size(); i++) { - sb.append(" <Entry key=\"").append(data.getKeyAt(i)).append("\">").append((String)data.getValueAt(i)) - .append("</Entry>\n"); - } - sb.append("</Map>"); - } - - /** - * Formats the message and return it. - * @return the formatted message. - */ - @Override - public String getFormattedMessage() { - return asString(); - } - - /** - * - * @param formats - * An array of Strings that provide extra information about how to format the message. MapMessage uses - * the first format specifier it recognizes. The supported formats are XML, JSON, and JAVA. The default - * format is key1="value1" key2="value2" as required by <a href="https://tools.ietf.org/html/rfc5424">RFC - * 5424</a> messages. - * - * @return The formatted message. - */ - @Override - public String getFormattedMessage(final String[] formats) { - if (formats == null || formats.length == 0) { - return asString(); - } - for (int i = 0; i < formats.length; i++) { - final MapFormat mapFormat = MapFormat.lookupIgnoreCase(formats[i]); - if (mapFormat != null) { - return format(mapFormat, new StringBuilder()).toString(); - } - } - return asString(); - - } - - protected void appendMap(final StringBuilder sb) { - for (int i = 0; i < data.size(); i++) { - if (i > 0) { - sb.append(' '); - } - StringBuilders.appendKeyDqValue(sb, data.getKeyAt(i), data.getValueAt(i)); - } - } - - protected void asJson(final StringBuilder sb) { - sb.append('{'); - for (int i = 0; i < data.size(); i++) { - if (i > 0) { - sb.append(", "); - } - StringBuilders.appendDqValue(sb, data.getKeyAt(i)).append(':'); - StringBuilders.appendDqValue(sb, data.getValueAt(i)); - } - sb.append('}'); - } - - - protected void asJava(final StringBuilder sb) { - sb.append('{'); - for (int i = 0; i < data.size(); i++) { - if (i > 0) { - sb.append(", "); - } - StringBuilders.appendKeyDqValue(sb, data.getKeyAt(i), data.getValueAt(i)); - } - sb.append('}'); - } - - /** - * Constructs a new instance based on an existing Map. - * @param map The Map. - * @return A new MapMessage - */ - public MapMessage newInstance(final Map<String, String> map) { - return new MapMessage(map); - } - - @Override - public String toString() { - return asString(); - } - - @Override - public void formatTo(final StringBuilder buffer) { - format((MapFormat) null, buffer); - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || this.getClass() != o.getClass()) { - return false; - } - - final MapMessage that = (MapMessage) o; - - return this.data.equals(that.data); - } - - @Override - public int hashCode() { - return data.hashCode(); - } - - /** - * Always returns null. - * - * @return null - */ - @Override - public Throwable getThrowable() { - return null; - } -} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/fb7e4eaa/log4j-api/src/main/java/org/apache/logging/log4j/message/MapMessage.java ---------------------------------------------------------------------- diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/MapMessage.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/MapMessage.java index ab5c522..0cbf0ff 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/message/MapMessage.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/MapMessage.java @@ -14,21 +14,400 @@ * See the license for the specific language governing permissions and * limitations under the license. */ - package org.apache.logging.log4j.message; +import java.util.Collections; import java.util.Map; +import java.util.TreeMap; + +import org.apache.logging.log4j.util.BiConsumer; +import org.apache.logging.log4j.util.EnglishEnums; +import org.apache.logging.log4j.util.IndexedReadOnlyStringMap; +import org.apache.logging.log4j.util.IndexedStringMap; +import org.apache.logging.log4j.util.PerformanceSensitive; +import org.apache.logging.log4j.util.ReadOnlyStringMap; +import org.apache.logging.log4j.util.SortedArrayStringMap; +import org.apache.logging.log4j.util.StringBuilderFormattable; +import org.apache.logging.log4j.util.StringBuilders; +import org.apache.logging.log4j.util.Strings; +import org.apache.logging.log4j.util.TriConsumer; + +/** + * Represents a Message that consists of a Map. + * <p> + * Thread-safety note: the contents of this message can be modified after construction. + * When using asynchronous loggers and appenders it is not recommended to modify this message after the message is + * logged, because it is undefined whether the logged message string will contain the old values or the modified + * values. + * </p> + */ +@PerformanceSensitive("allocation") +@AsynchronouslyFormattable +public class MapMessage implements MultiformatMessage, StringBuilderFormattable { -public class MapMessage extends GenericMapMessage<MapMessage, String> { + /** + * When set as the format specifier causes the Map to be formatted as XML. + */ + public enum MapFormat { + + /** The map should be formatted as XML. */ + XML, + + /** The map should be formatted as JSON. */ + JSON, + + /** The map should be formatted the same as documented by java.util.AbstractMap.toString(). */ + JAVA; - private static final long serialVersionUID = 1L; + /** + * Maps a format name to an {@link MapFormat} while ignoring case. + * + * @param format a MapFormat name + * @return a MapFormat + */ + public static MapFormat lookupIgnoreCase(final String format) { + return XML.name().equalsIgnoreCase(format) ? XML // + : JSON.name().equalsIgnoreCase(format) ? JSON // + : JAVA.name().equalsIgnoreCase(format) ? JAVA // + : null; + } + + /** + * All {@code MapFormat} names. + * + * @return All {@code MapFormat} names. + */ + public static String[] names() { + return new String[] {XML.name(), JSON.name(), JAVA.name()}; + } + } + private static final long serialVersionUID = -5031471831131487120L; + + private final IndexedStringMap data; + + /** + * Constructs a new instance. + */ public MapMessage() { - super(); + data = new SortedArrayStringMap(); + } + + /** + * Constructs a new instance based on an existing Map. + * @param map The Map. + */ + public MapMessage(final Map<String, String> map) { + this.data = new SortedArrayStringMap(map); + } + + @Override + public String[] getFormats() { + return MapFormat.names(); } - public MapMessage(Map<String, String> map) { - super(map); + /** + * Returns the data elements as if they were parameters on the logging event. + * @return the data elements. + */ + @Override + public Object[] getParameters() { + final Object[] result = new Object[data.size()]; + for (int i = 0; i < data.size(); i++) { + result[i] = data.getValueAt(i); + } + return result; + } + + /** + * Returns the message. + * @return the message. + */ + @Override + public String getFormat() { + return Strings.EMPTY; + } + + /** + * Returns the message data as an unmodifiable Map. + * @return the message data as an unmodifiable map. + */ + public Map<String, String> getData() { + final TreeMap<String, String> result = new TreeMap<>(); // returned map must be sorted + for (int i = 0; i < data.size(); i++) { + result.put(data.getKeyAt(i), (String) data.getValueAt(i)); + } + return Collections.unmodifiableMap(result); + } + + /** + * Returns a read-only view of the message data. + * @return the read-only message data. + */ + public IndexedReadOnlyStringMap getIndexedReadOnlyStringMap() { + return data; + } + + /** + * Clear the data. + */ + public void clear() { + data.clear(); + } + + /** + * Returns {@code true} if this data structure contains the specified key, {@code false} otherwise. + * + * @param key the key whose presence to check. May be {@code null}. + * @return {@code true} if this data structure contains the specified key, {@code false} otherwise + * @since 2.9 + */ + public boolean containsKey(final String key) { + return data.containsKey(key); + } + + /** + * Adds an item to the data Map in fluent style. + * @param key The name of the data item. + * @param value The value of the data item. + * @return {@code this} + */ + public MapMessage with(final String key, final String value) { + put(key, value); + return this; + } + + /** + * Adds an item to the data Map. + * @param key The name of the data item. + * @param value The value of the data item. + */ + public void put(final String key, final String value) { + if (value == null) { + throw new IllegalArgumentException("No value provided for key " + key); + } + validate(key, value); + data.putValue(key, value); + } + + protected void validate(final String key, final String value) { + + } + + /** + * Adds all the elements from the specified Map. + * @param map The Map to add. + */ + public void putAll(final Map<String, String> map) { + for (final Map.Entry<String, ?> entry : map.entrySet()) { + data.putValue(entry.getKey(), entry.getValue()); + } + } + + /** + * Retrieves the value of the element with the specified key or null if the key is not present. + * @param key The name of the element. + * @return The value of the element or null if the key is not present. + */ + public String get(final String key) { + return data.getValue(key); + } + + /** + * Removes the element with the specified name. + * @param key The name of the element. + * @return The previous value of the element. + */ + public String remove(final String key) { + final String result = data.getValue(key); + data.remove(key); + return result; + } + + /** + * Formats the Structured data as described in <a href="https://tools.ietf.org/html/rfc5424">RFC 5424</a>. + * + * @return The formatted String. + */ + public String asString() { + return format((MapFormat) null, new StringBuilder()).toString(); + } + + /** + * Formats the Structured data as described in <a href="https://tools.ietf.org/html/rfc5424">RFC 5424</a>. + * + * @param format The format identifier. + * @return The formatted String. + */ + public String asString(final String format) { + try { + return format(EnglishEnums.valueOf(MapFormat.class, format), new StringBuilder()).toString(); + } catch (final IllegalArgumentException ex) { + return asString(); + } + } + + /** + * Performs the given action for each key-value pair in this data structure + * until all entries have been processed or the action throws an exception. + * <p> + * Some implementations may not support structural modifications (adding new elements or removing elements) while + * iterating over the contents. In such implementations, attempts to add or remove elements from the + * {@code BiConsumer}'s {@link BiConsumer#accept(Object, Object)} accept} method may cause a + * {@code ConcurrentModificationException} to be thrown. + * </p> + * + * @param action The action to be performed for each key-value pair in this collection + * @param <V> type of the value + * @throws java.util.ConcurrentModificationException some implementations may not support structural modifications + * to this data structure while iterating over the contents with {@link #forEach(BiConsumer)} or + * {@link #forEach(TriConsumer, Object)}. + * @see ReadOnlyStringMap#forEach(BiConsumer) + * @since 2.9 + */ + public <V> void forEach(final BiConsumer<String, ? super V> action) { + data.forEach(action); + } + + /** + * Performs the given action for each key-value pair in this data structure + * until all entries have been processed or the action throws an exception. + * <p> + * The third parameter lets callers pass in a stateful object to be modified with the key-value pairs, + * so the TriConsumer implementation itself can be stateless and potentially reusable. + * </p> + * <p> + * Some implementations may not support structural modifications (adding new elements or removing elements) while + * iterating over the contents. In such implementations, attempts to add or remove elements from the + * {@code TriConsumer}'s {@link TriConsumer#accept(Object, Object, Object) accept} method may cause a + * {@code ConcurrentModificationException} to be thrown. + * </p> + * + * @param action The action to be performed for each key-value pair in this collection + * @param state the object to be passed as the third parameter to each invocation on the specified + * triconsumer + * @param <V> type of the value + * @param <S> type of the third parameter + * @throws java.util.ConcurrentModificationException some implementations may not support structural modifications + * to this data structure while iterating over the contents with {@link #forEach(BiConsumer)} or + * {@link #forEach(TriConsumer, Object)}. + * @see ReadOnlyStringMap#forEach(TriConsumer, Object) + * @since 2.9 + */ + public <V, S> void forEach(final TriConsumer<String, ? super V, S> action, final S state) { + data.forEach(action, state); + } + + /** + * Formats the Structured data as described in <a href="https://tools.ietf.org/html/rfc5424">RFC 5424</a>. + * + * @param format The format identifier. + * @return The formatted String. + */ + private StringBuilder format(final MapFormat format, final StringBuilder sb) { + if (format == null) { + appendMap(sb); + } else { + switch (format) { + case XML : { + asXml(sb); + break; + } + case JSON : { + asJson(sb); + break; + } + case JAVA : { + asJava(sb); + break; + } + default : { + appendMap(sb); + } + } + } + return sb; + } + + /** + * Formats this message as an XML fragment String into the given builder. + * + * @param sb format into this builder. + */ + public void asXml(final StringBuilder sb) { + sb.append("<Map>\n"); + for (int i = 0; i < data.size(); i++) { + sb.append(" <Entry key=\"").append(data.getKeyAt(i)).append("\">").append((String)data.getValueAt(i)) + .append("</Entry>\n"); + } + sb.append("</Map>"); + } + + /** + * Formats the message and return it. + * @return the formatted message. + */ + @Override + public String getFormattedMessage() { + return asString(); + } + + /** + * + * @param formats + * An array of Strings that provide extra information about how to format the message. MapMessage uses + * the first format specifier it recognizes. The supported formats are XML, JSON, and JAVA. The default + * format is key1="value1" key2="value2" as required by <a href="https://tools.ietf.org/html/rfc5424">RFC + * 5424</a> messages. + * + * @return The formatted message. + */ + @Override + public String getFormattedMessage(final String[] formats) { + if (formats == null || formats.length == 0) { + return asString(); + } + for (int i = 0; i < formats.length; i++) { + final MapFormat mapFormat = MapFormat.lookupIgnoreCase(formats[i]); + if (mapFormat != null) { + return format(mapFormat, new StringBuilder()).toString(); + } + } + return asString(); + + } + + protected void appendMap(final StringBuilder sb) { + for (int i = 0; i < data.size(); i++) { + if (i > 0) { + sb.append(' '); + } + StringBuilders.appendKeyDqValue(sb, data.getKeyAt(i), data.getValueAt(i)); + } + } + + protected void asJson(final StringBuilder sb) { + sb.append('{'); + for (int i = 0; i < data.size(); i++) { + if (i > 0) { + sb.append(", "); + } + StringBuilders.appendDqValue(sb, data.getKeyAt(i)).append(':'); + StringBuilders.appendDqValue(sb, data.getValueAt(i)); + } + sb.append('}'); + } + + + protected void asJava(final StringBuilder sb) { + sb.append('{'); + for (int i = 0; i < data.size(); i++) { + if (i > 0) { + sb.append(", "); + } + StringBuilders.appendKeyDqValue(sb, data.getKeyAt(i), data.getValueAt(i)); + } + sb.append('}'); } /** @@ -39,4 +418,43 @@ public class MapMessage extends GenericMapMessage<MapMessage, String> { public MapMessage newInstance(final Map<String, String> map) { return new MapMessage(map); } + + @Override + public String toString() { + return asString(); + } + + @Override + public void formatTo(final StringBuilder buffer) { + format((MapFormat) null, buffer); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || this.getClass() != o.getClass()) { + return false; + } + + final MapMessage that = (MapMessage) o; + + return this.data.equals(that.data); + } + + @Override + public int hashCode() { + return data.hashCode(); + } + + /** + * Always returns null. + * + * @return null + */ + @Override + public Throwable getThrowable() { + return null; + } }
