001/*
002 * JGrapes Event Driven Framework
003 * Copyright (C) 2016-2026 Michael N. Lipp
004 * 
005 * This program is free software; you can redistribute it and/or modify it 
006 * under the terms of the GNU Affero General Public License as published by 
007 * the Free Software Foundation; either version 3 of the License, or 
008 * (at your option) any later version.
009 * 
010 * This program is distributed in the hope that it will be useful, but 
011 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
012 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
013 * License for more details.
014 * 
015 * You should have received a copy of the GNU Affero General Public License
016 * along with this program; if not, see <http://www.gnu.org/licenses/>.
017 */
018
019package org.jgrapes.core.events;
020
021import java.lang.reflect.InvocationTargetException;
022import org.jgrapes.core.Channel;
023import org.jgrapes.core.Components;
024import org.jgrapes.core.Event;
025
026/**
027 * This event signals that an error occurred while processing an event.
028 */
029public class Error extends Event<Void> {
030
031    private final Event<?> event;
032    private final String message;
033    private Throwable throwable;
034
035    /**
036     * Creates a new event as a copy of an existing event. Useful
037     * for forwarding an event. Constructors "`<T extend Error> T(T event)`"
038     * must be implemented by all derived classes.
039     *
040     * @param event the event to copy
041     */
042    public Error(Error event) {
043        this.event = event.event;
044        this.message = event.message;
045        this.throwable = event.throwable;
046    }
047
048    /**
049     * Duplicate the event. Returns a new event with the same class
050     * and the same properties of the given event. Relies on the
051     * proper implementation of constructors "`<T extend Error> T(T event)`"
052     * for derived classes.
053     * 
054     * Creating a duplicate id useful for forwarding a derived `Error` while
055     * handling a base class.
056     *
057     * @param <T> the generic type
058     * @param event the event
059     * @return the t
060     */
061    @SuppressWarnings({ "unchecked" })
062    public static <T extends Error> T duplicate(T event) {
063        try {
064            return (T) event.getClass().getConstructor(event.getClass())
065                .newInstance(event);
066        } catch (InstantiationException | IllegalAccessException
067                | IllegalArgumentException | InvocationTargetException
068                | NoSuchMethodException | SecurityException e) {
069            throw new IllegalArgumentException(e);
070        }
071    }
072
073    /**
074     * Creates a new event.
075     * 
076     * @param event the event being processed when the problem occurred
077     * @param message the message
078     */
079    public Error(Event<?> event, String message) {
080        this.event = event;
081        this.message = message;
082    }
083
084    /**
085     * Creates a new event caused by the given throwable.
086     * 
087     * @param event the event being processed when the problem occurred
088     * @param message the message
089     * @param throwable the throwable
090     */
091    public Error(Event<?> event, String message, Throwable throwable) {
092        this.event = event;
093        this.message = message;
094        this.throwable = throwable;
095    }
096
097    /**
098     * Creates a new event caused by the given throwable. The message
099     * is initialized from the throwable.
100     * 
101     * @param event the event being processed when the problem occurred
102     * @param throwable the throwable
103     */
104    public Error(Event<?> event, Throwable throwable) {
105        this.event = event;
106        this.message = throwable.getMessage() == null
107            ? throwable.getClass().getName()
108            : throwable.getMessage();
109        this.throwable = throwable;
110    }
111
112    /**
113     * Returns the event that was handled when the problem occurred.
114     * 
115     * @return the event
116     */
117    public Event<?> event() {
118        return event;
119    }
120
121    /**
122     * Returns the message passed to the constructor.
123     * 
124     * @return the message
125     */
126    public String message() {
127        return message;
128    }
129
130    /**
131     * Returns the throwable that caused the problem.
132     * 
133     * @return the throwable or {@code null} if the problem wasn't caused
134     * by a throwable.
135     */
136    public Throwable throwable() {
137        return throwable;
138    }
139
140    /*
141     * (non-Javadoc)
142     * 
143     * @see java.lang.Object#toString()
144     */
145    @Override
146    public String toString() {
147        StringBuilder builder = new StringBuilder(50);
148        builder.append(Components.objectName(this))
149            .append(" [");
150        if (channels().length > 0) {
151            builder.append("channels=").append(Channel.toString(channels()));
152        }
153        if (message != null) {
154            builder.append(", message=\"").append(message).append('"');
155        }
156        if (event != null) {
157            builder.append(", caused by: ").append(event.toString());
158        }
159        builder.append(']');
160        return builder.toString();
161    }
162}