| /* |
| * 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.math4.legacy.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<>(); |
| msgArguments = new ArrayList<>(); |
| context = new HashMap<>(); |
| } |
| |
| /** 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<>(len); |
| msgArguments = new ArrayList<>(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<>(); |
| 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 static String nonSerializableReplacement(Object obj) { |
| return "[Object could not be serialized: " + obj.getClass().getName() + "]"; |
| } |
| } |