1/* 2 * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25package com.sun.beans.introspect; 26 27import java.lang.reflect.Method; 28import java.lang.reflect.Modifier; 29import java.util.Collections; 30import java.util.EventListener; 31import java.util.Iterator; 32import java.util.List; 33import java.util.Map; 34import java.util.TooManyListenersException; 35import java.util.TreeMap; 36 37public final class EventSetInfo { 38 private MethodInfo add; 39 private MethodInfo remove; 40 private MethodInfo get; 41 42 private EventSetInfo() { 43 } 44 45 private boolean initialize() { 46 if ((this.add == null) || (this.remove == null) || (this.remove.type != this.add.type)) { 47 return false; 48 } 49 if ((this.get != null) && (this.get.type != this.add.type)) { 50 this.get = null; 51 } 52 return true; 53 } 54 55 public Class<?> getListenerType() { 56 return this.add.type; 57 } 58 59 public Method getAddMethod() { 60 return this.add.method; 61 } 62 63 public Method getRemoveMethod() { 64 return this.remove.method; 65 } 66 67 public Method getGetMethod() { 68 return (this.get == null) ? null : this.get.method; 69 } 70 71 public boolean isUnicast() { 72 // if the adder method throws the TooManyListenersException 73 // then it is an Unicast event source 74 return this.add.isThrow(TooManyListenersException.class); 75 } 76 77 private static MethodInfo getInfo(MethodInfo info, Method method, int prefix, int postfix) { 78 Class<?> type = (postfix > 0) 79 ? MethodInfo.resolve(method, method.getGenericReturnType()).getComponentType() 80 : MethodInfo.resolve(method, method.getGenericParameterTypes()[0]); 81 82 if ((type != null) && EventListener.class.isAssignableFrom(type)) { 83 String name = method.getName(); 84 if (prefix + postfix < name.length()) { 85 if (type.getName().endsWith(name.substring(prefix, name.length() - postfix))) { 86 if ((info == null) || info.type.isAssignableFrom(type)) { 87 return new MethodInfo(method, type); 88 } 89 } 90 } 91 } 92 return info; 93 } 94 95 private static EventSetInfo getInfo(Map<String,EventSetInfo> map, String key) { 96 EventSetInfo info = map.get(key); 97 if (info == null) { 98 info = new EventSetInfo(); 99 map.put(key, info); 100 } 101 return info; 102 } 103 104 public static Map<String,EventSetInfo> get(Class<?> type) { 105 List<Method> methods = ClassInfo.get(type).getMethods(); 106 if (methods.isEmpty()) { 107 return Collections.emptyMap(); 108 } 109 Map<String,EventSetInfo> map = new TreeMap<>(); 110 for (Method method : ClassInfo.get(type).getMethods()) { 111 if (!Modifier.isStatic(method.getModifiers())) { 112 Class<?> returnType = method.getReturnType(); 113 String name = method.getName(); 114 switch (method.getParameterCount()) { 115 case 1: 116 if ((returnType == void.class) && name.endsWith("Listener")) { 117 if (name.startsWith("add")) { 118 EventSetInfo info = getInfo(map, name.substring(3, name.length() - 8)); 119 info.add = getInfo(info.add, method, 3, 0); 120 } else if (name.startsWith("remove")) { 121 EventSetInfo info = getInfo(map, name.substring(6, name.length() - 8)); 122 info.remove = getInfo(info.remove, method, 6, 0); 123 } 124 } 125 break; 126 case 0: 127 if (returnType.isArray() && name.startsWith("get") && name.endsWith("Listeners")) { 128 EventSetInfo info = getInfo(map, name.substring(3, name.length() - 9)); 129 info.get = getInfo(info.get, method, 3, 1); 130 } 131 break; 132 } 133 } 134 } 135 Iterator<EventSetInfo> iterator = map.values().iterator(); 136 while (iterator.hasNext()) { 137 if (!iterator.next().initialize()) { 138 iterator.remove(); 139 } 140 } 141 return !map.isEmpty() 142 ? Collections.unmodifiableMap(map) 143 : Collections.emptyMap(); 144 } 145} 146