001/* 002 * JGrapes Event Driven Framework 003 * Copyright (C) 2018 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 License 013 * for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License along 016 * 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<V>((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 @SuppressWarnings("PMD.DataflowAnomalyAnalysis") 110 public int hashCode() { 111 @SuppressWarnings("PMD.AvoidFinalLocalVariable") 112 final int prime = 31; 113 int result = 1; 114 result = prime * result + ((id == null) ? 0 : id.hashCode()); 115 result = prime * result + ((type == null) ? 0 : type.hashCode()); 116 return result; 117 } 118 119 @Override 120 public boolean equals(Object obj) { 121 if (this == obj) { 122 return true; 123 } 124 if (obj == null) { 125 return false; 126 } 127 if (getClass() != obj.getClass()) { 128 return false; 129 } 130 TypedIdKey<?> other = (TypedIdKey<?>) obj; 131 if (id == null) { 132 if (other.id != null) { 133 return false; 134 } 135 } else if (!id.equals(other.id)) { 136 return false; 137 } 138 if (type == null) { 139 if (other.type != null) { 140 return false; 141 } 142 } else if (!type.equals(other.type)) { 143 return false; 144 } 145 return true; 146 } 147 148 @Override 149 public String toString() { 150 return "TypedIdKey [" + type + ":" + id + "]"; 151 } 152}