001/* 002 * JGrapes Event Driven Framework 003 * Copyright (C) 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; 020 021import java.io.Serializable; 022import java.util.Map; 023import java.util.Optional; 024 025/** 026 * Combines a type and an id value to a key for an {@link Associator} 027 * or {@link Map}. This kind of key can be used if an instance of 028 * the type as key is too specific (or objects should not be kept alive 029 * only because they are used as keys) and the identifier alone cannot 030 * be guaranteed to be unique. 031 */ 032@SuppressWarnings({ "serial", "PMD.ShortVariable" }) 033public final class TypedIdKey<V> implements Serializable { 034 035 private final Class<V> type; 036 private final Serializable id; 037 038 private TypedIdKey(Class<V> type, Serializable id) { 039 this.type = type; 040 this.id = id; 041 } 042 043 /** 044 * Associates the given value's type and the id with the given value 045 * using the given associator. 046 * 047 * @param <V> the value type 048 * @param associator the associator 049 * @param id the id 050 * @param value the value 051 * @return the value for easy chaining 052 */ 053 @SuppressWarnings({ "unchecked" }) 054 public static <V> V associate(Associator associator, Serializable id, 055 V value) { 056 associator.setAssociated( 057 new TypedIdKey<>((Class<V>) value.getClass(), id), value); 058 return value; 059 } 060 061 /** 062 * Associates the given value's type and the id with the given value 063 * using the given map. 064 * 065 * @param <V> the value type 066 * @param map the map 067 * @param id the id 068 * @param value the value 069 * @return the value for easy chaining 070 */ 071 @SuppressWarnings({ "unchecked" }) 072 public static <V> V put(Map<? super TypedIdKey<V>, ? super V> map, 073 Serializable id, V value) { 074 map.put(new TypedIdKey<>((Class<V>) value.getClass(), id), value); 075 return value; 076 } 077 078 /** 079 * Retrieves a value with the given type and id from the given associator. 080 * 081 * @param <V> the value type 082 * @param associator the associator 083 * @param type the type 084 * @param id the id 085 * @return the associated value, if any 086 */ 087 public static <V> Optional<V> associated(Associator associator, 088 Class<V> type, Serializable id) { 089 return associator.associated(new TypedIdKey<>(type, id), type); 090 } 091 092 /** 093 * Retrieves a value with the given type and id from the given map. 094 * 095 * @param <V> the value type 096 * @param map the map 097 * @param type the type 098 * @param id the id 099 * @return the associated value, if any 100 */ 101 @SuppressWarnings({ "unchecked" }) 102 public static <V> Optional<V> get(Map<?, ?> map, Class<V> type, 103 Serializable id) { 104 return Optional 105 .ofNullable((V) map.get(new TypedIdKey<>(type, id))); 106 } 107 108 @Override 109 public int hashCode() { 110 final int prime = 31; 111 int result = 1; 112 result = prime * result + ((id == null) ? 0 : id.hashCode()); 113 result = prime * result + ((type == null) ? 0 : type.hashCode()); 114 return result; 115 } 116 117 @Override 118 public boolean equals(Object obj) { 119 if (this == obj) { 120 return true; 121 } 122 if (obj == null) { 123 return false; 124 } 125 if (getClass() != obj.getClass()) { 126 return false; 127 } 128 TypedIdKey<?> other = (TypedIdKey<?>) obj; 129 if (id == null) { 130 if (other.id != null) { 131 return false; 132 } 133 } else if (!id.equals(other.id)) { 134 return false; 135 } 136 if (type == null) { 137 if (other.type != null) { 138 return false; 139 } 140 } else if (!type.equals(other.type)) { 141 return false; 142 } 143 return true; 144 } 145 146 @Override 147 public String toString() { 148 return "TypedIdKey [" + type + ":" + id + "]"; 149 } 150}