1 package org.djutils.event; 2 3 import java.io.Serializable; 4 import java.util.Objects; 5 6 import org.djutils.exceptions.Throw; 7 import org.djutils.metadata.MetaData; 8 9 /** 10 * Reference implementation of the EventType. The AbstractEventType is the description of a topic used for the subscription to 11 * asynchronous events. Event types are used by EventProducers to show which events they potentially fire. EventTypes are 12 * typically defined as static final fields. In order to prevent name clashes for the EventType, the full name of the class from 13 * which the EventType was defined (usually in the <clinit>) is added to the equals() and hashCode() methods of the 14 * EventType. In that way, EventTypes that are the same will be unique, but EventTypes with just the same name but defined in 15 * different classes will be different. This is the abstract class that can be tailored to any event type. Subclasses that 16 * extend the AbstractEventType are the EventType for regular events, and TimedEventType for timed events.<br> 17 * <br> 18 * Note: the reason why this is important is because <b>remote events</b> that use EventTypes can have <i>multiple versions</i> 19 * of the same public static final EventType: one the is defined in the client, and one that is defined via the network. These 20 * will have <i>different addresses in memory</i> but they share the same class and name info, so equals() will yield true. 21 * <p> 22 * Copyright (c) 2002-2023 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See 23 * for project information <a href="https://djutils.org" target="_blank"> https://djutils.org</a>. The DJUTILS project is 24 * distributed under a three-clause BSD-style license, which can be found at 25 * <a href="https://djutils.org/docs/license.html" target="_blank"> https://djutils.org/docs/license.html</a>. This class was 26 * originally part of the DSOL project, see <a href="https://simulation.tudelft.nl/dsol/manual" target="_blank"> 27 * https://simulation.tudelft.nl/dsol/manual</a>. 28 * </p> 29 * @author <a href="https://www.linkedin.com/in/peterhmjacobs">Peter Jacobs </a> 30 * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a> 31 */ 32 public class EventType implements Serializable 33 { 34 /** The default serial version UID for serializable classes. */ 35 private static final long serialVersionUID = 20140830L; 36 37 /** The name of the eventType. */ 38 private final String name; 39 40 /** Meta data (describes the payload). */ 41 private final MetaData metaData; 42 43 /** 44 * The class name from which the event type construction was called; together with the event name this should lead to a 45 * unique hash, even when the same name is used in different classes. 46 */ 47 private String definingClassName; 48 49 /** 50 * Construct a new EventType. Only events of the type Event, and no subclasses of Event, can be used to fire events of this 51 * type. This means that firing a TimedEvent of this type will result in an error. 52 * @param name String; the name of the new eventType. Two values are not appreciated: null and the empty string. 53 * @param metaData MetaData; describes the payload of events of the new EventType; 54 */ 55 public EventType(final String name, final MetaData metaData) 56 { 57 Throw.when(name == null || name.equals(""), IllegalArgumentException.class, 58 "EventType name == null || EventType name == \"\""); 59 Throw.whenNull(metaData, 60 "Meta data may not be null (but you could provide the NO_META_DATA value if the payload will be varying)"); 61 this.name = name; 62 StackTraceElement[] steArray = new Throwable().getStackTrace(); 63 for (StackTraceElement ste : steArray) 64 { 65 if (!(ste.getClassName().endsWith("EventType"))) 66 { 67 this.definingClassName = ste.getClassName(); 68 break; 69 } 70 } 71 Throw.whenNull(this.definingClassName, "no defining class name found that is not an EventType"); 72 this.metaData = metaData; 73 } 74 75 /** 76 * Construct a new EventType. The name of the metadata will function as the name of the event. Only events of the type 77 * Event, and no subclasses of Event, can be used to fire events of this type. This means that firing a TimedEvent of this 78 * type will result in an error. 79 * @param metaData MetaData; describes the payload of events of the new EventType; 80 */ 81 public EventType(final MetaData metaData) 82 { 83 this(metaData == null ? null : metaData.getName(), metaData); 84 } 85 86 /** 87 * Construct a new EventType with no meta data. Only events of the type Event, and no subclasses of Event, can be used to 88 * fire events of this type. This means that firing a TimedEvent of this type will result in an error. 89 * @param name String; the name of the new eventType. Two values are not appreciated: null and the empty string. 90 */ 91 @Deprecated 92 public EventType(final String name) 93 { 94 this(name, MetaData.NO_META_DATA); 95 } 96 97 /** 98 * Return the event type name. 99 * @return String; the event type name 100 */ 101 public String getName() 102 { 103 return this.name; 104 } 105 106 /** 107 * Retrieve the MetaData that describes the payload of events of this EventType. 108 * @return MetaData; describes the payload of events of this EventType 109 */ 110 public MetaData getMetaData() 111 { 112 return this.metaData; 113 } 114 115 /** {@inheritDoc} */ 116 @Override 117 public int hashCode() 118 { 119 return Objects.hash(this.definingClassName, this.name); 120 } 121 122 /** {@inheritDoc} */ 123 @Override 124 @SuppressWarnings("checkstyle:needbraces") 125 public boolean equals(final Object obj) 126 { 127 if (this == obj) 128 return true; 129 if (obj == null) 130 return false; 131 if (getClass() != obj.getClass()) 132 return false; 133 EventType other = (EventType) obj; 134 return Objects.equals(this.definingClassName, other.definingClassName) && Objects.equals(this.name, other.name); 135 } 136 137 /** {@inheritDoc} */ 138 @Override 139 public String toString() 140 { 141 return this.name; 142 } 143 144 }