summaryrefslogtreecommitdiff
path: root/src/main/java/org/apache/commons/math3/exception/util/ExceptionContext.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/apache/commons/math3/exception/util/ExceptionContext.java')
-rw-r--r--src/main/java/org/apache/commons/math3/exception/util/ExceptionContext.java334
1 files changed, 334 insertions, 0 deletions
diff --git a/src/main/java/org/apache/commons/math3/exception/util/ExceptionContext.java b/src/main/java/org/apache/commons/math3/exception/util/ExceptionContext.java
new file mode 100644
index 0000000..1a78bd3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/util/ExceptionContext.java
@@ -0,0 +1,334 @@
+/*
+ * 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.commons.math3.exception.util;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.Map;
+import java.io.IOException;
+import java.io.Serializable;
+import java.io.ObjectOutputStream;
+import java.io.ObjectInputStream;
+import java.util.HashMap;
+import java.text.MessageFormat;
+import java.util.Locale;
+
+/**
+ * Class that contains the actual implementation of the functionality mandated
+ * by the {@link ExceptionContext} interface.
+ * All Commons Math exceptions delegate the interface's methods to this class.
+ *
+ * @since 3.0
+ */
+public class ExceptionContext implements Serializable {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -6024911025449780478L;
+ /**
+ * The throwable to which this context refers to.
+ */
+ private Throwable throwable;
+ /**
+ * Various informations that enrich the informative message.
+ */
+ private List<Localizable> msgPatterns;
+ /**
+ * Various informations that enrich the informative message.
+ * The arguments will replace the corresponding place-holders in
+ * {@link #msgPatterns}.
+ */
+ private List<Object[]> msgArguments;
+ /**
+ * Arbitrary context information.
+ */
+ private Map<String, Object> context;
+
+ /** Simple constructor.
+ * @param throwable the exception this context refers too
+ */
+ public ExceptionContext(final Throwable throwable) {
+ this.throwable = throwable;
+ msgPatterns = new ArrayList<Localizable>();
+ msgArguments = new ArrayList<Object[]>();
+ context = new HashMap<String, Object>();
+ }
+
+ /** Get a reference to the exception to which the context relates.
+ * @return a reference to the exception to which the context relates
+ */
+ public Throwable getThrowable() {
+ return throwable;
+ }
+
+ /**
+ * Adds a message.
+ *
+ * @param pattern Message pattern.
+ * @param arguments Values for replacing the placeholders in the message
+ * pattern.
+ */
+ public void addMessage(Localizable pattern,
+ Object ... arguments) {
+ msgPatterns.add(pattern);
+ msgArguments.add(ArgUtils.flatten(arguments));
+ }
+
+ /**
+ * Sets the context (key, value) pair.
+ * Keys are assumed to be unique within an instance. If the same key is
+ * assigned a new value, the previous one will be lost.
+ *
+ * @param key Context key (not null).
+ * @param value Context value.
+ */
+ public void setValue(String key, Object value) {
+ context.put(key, value);
+ }
+
+ /**
+ * Gets the value associated to the given context key.
+ *
+ * @param key Context key.
+ * @return the context value or {@code null} if the key does not exist.
+ */
+ public Object getValue(String key) {
+ return context.get(key);
+ }
+
+ /**
+ * Gets all the keys stored in the exception
+ *
+ * @return the set of keys.
+ */
+ public Set<String> getKeys() {
+ return context.keySet();
+ }
+
+ /**
+ * Gets the default message.
+ *
+ * @return the message.
+ */
+ public String getMessage() {
+ return getMessage(Locale.US);
+ }
+
+ /**
+ * Gets the message in the default locale.
+ *
+ * @return the localized message.
+ */
+ public String getLocalizedMessage() {
+ return getMessage(Locale.getDefault());
+ }
+
+ /**
+ * Gets the message in a specified locale.
+ *
+ * @param locale Locale in which the message should be translated.
+ * @return the localized message.
+ */
+ public String getMessage(final Locale locale) {
+ return buildMessage(locale, ": ");
+ }
+
+ /**
+ * Gets the message in a specified locale.
+ *
+ * @param locale Locale in which the message should be translated.
+ * @param separator Separator inserted between the message parts.
+ * @return the localized message.
+ */
+ public String getMessage(final Locale locale,
+ final String separator) {
+ return buildMessage(locale, separator);
+ }
+
+ /**
+ * Builds a message string.
+ *
+ * @param locale Locale in which the message should be translated.
+ * @param separator Message separator.
+ * @return a localized message string.
+ */
+ private String buildMessage(Locale locale,
+ String separator) {
+ final StringBuilder sb = new StringBuilder();
+ int count = 0;
+ final int len = msgPatterns.size();
+ for (int i = 0; i < len; i++) {
+ final Localizable pat = msgPatterns.get(i);
+ final Object[] args = msgArguments.get(i);
+ final MessageFormat fmt = new MessageFormat(pat.getLocalizedString(locale),
+ locale);
+ sb.append(fmt.format(args));
+ if (++count < len) {
+ // Add a separator if there are other messages.
+ sb.append(separator);
+ }
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Serialize this object to the given stream.
+ *
+ * @param out Stream.
+ * @throws IOException This should never happen.
+ */
+ private void writeObject(ObjectOutputStream out)
+ throws IOException {
+ out.writeObject(throwable);
+ serializeMessages(out);
+ serializeContext(out);
+ }
+ /**
+ * Deserialize this object from the given stream.
+ *
+ * @param in Stream.
+ * @throws IOException This should never happen.
+ * @throws ClassNotFoundException This should never happen.
+ */
+ private void readObject(ObjectInputStream in)
+ throws IOException,
+ ClassNotFoundException {
+ throwable = (Throwable) in.readObject();
+ deSerializeMessages(in);
+ deSerializeContext(in);
+ }
+
+ /**
+ * Serialize {@link #msgPatterns} and {@link #msgArguments}.
+ *
+ * @param out Stream.
+ * @throws IOException This should never happen.
+ */
+ private void serializeMessages(ObjectOutputStream out)
+ throws IOException {
+ // Step 1.
+ final int len = msgPatterns.size();
+ out.writeInt(len);
+ // Step 2.
+ for (int i = 0; i < len; i++) {
+ final Localizable pat = msgPatterns.get(i);
+ // Step 3.
+ out.writeObject(pat);
+ final Object[] args = msgArguments.get(i);
+ final int aLen = args.length;
+ // Step 4.
+ out.writeInt(aLen);
+ for (int j = 0; j < aLen; j++) {
+ if (args[j] instanceof Serializable) {
+ // Step 5a.
+ out.writeObject(args[j]);
+ } else {
+ // Step 5b.
+ out.writeObject(nonSerializableReplacement(args[j]));
+ }
+ }
+ }
+ }
+
+ /**
+ * Deserialize {@link #msgPatterns} and {@link #msgArguments}.
+ *
+ * @param in Stream.
+ * @throws IOException This should never happen.
+ * @throws ClassNotFoundException This should never happen.
+ */
+ private void deSerializeMessages(ObjectInputStream in)
+ throws IOException,
+ ClassNotFoundException {
+ // Step 1.
+ final int len = in.readInt();
+ msgPatterns = new ArrayList<Localizable>(len);
+ msgArguments = new ArrayList<Object[]>(len);
+ // Step 2.
+ for (int i = 0; i < len; i++) {
+ // Step 3.
+ final Localizable pat = (Localizable) in.readObject();
+ msgPatterns.add(pat);
+ // Step 4.
+ final int aLen = in.readInt();
+ final Object[] args = new Object[aLen];
+ for (int j = 0; j < aLen; j++) {
+ // Step 5.
+ args[j] = in.readObject();
+ }
+ msgArguments.add(args);
+ }
+ }
+
+ /**
+ * Serialize {@link #context}.
+ *
+ * @param out Stream.
+ * @throws IOException This should never happen.
+ */
+ private void serializeContext(ObjectOutputStream out)
+ throws IOException {
+ // Step 1.
+ final int len = context.size();
+ out.writeInt(len);
+ for (Map.Entry<String, Object> entry : context.entrySet()) {
+ // Step 2.
+ out.writeObject(entry.getKey());
+ final Object value = entry.getValue();
+ if (value instanceof Serializable) {
+ // Step 3a.
+ out.writeObject(value);
+ } else {
+ // Step 3b.
+ out.writeObject(nonSerializableReplacement(value));
+ }
+ }
+ }
+
+ /**
+ * Deserialize {@link #context}.
+ *
+ * @param in Stream.
+ * @throws IOException This should never happen.
+ * @throws ClassNotFoundException This should never happen.
+ */
+ private void deSerializeContext(ObjectInputStream in)
+ throws IOException,
+ ClassNotFoundException {
+ // Step 1.
+ final int len = in.readInt();
+ context = new HashMap<String, Object>();
+ for (int i = 0; i < len; i++) {
+ // Step 2.
+ final String key = (String) in.readObject();
+ // Step 3.
+ final Object value = in.readObject();
+ context.put(key, value);
+ }
+ }
+
+ /**
+ * Replaces a non-serializable object with an error message string.
+ *
+ * @param obj Object that does not implement the {@code Serializable}
+ * interface.
+ * @return a string that mentions which class could not be serialized.
+ */
+ private String nonSerializableReplacement(Object obj) {
+ return "[Object could not be serialized: " + obj.getClass().getName() + "]";
+ }
+}