View Javadoc
1   /*
2    * Copyright (C) 2012-2024 RRiBbit.org
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.rribbit.creation;
17  
18  import java.lang.reflect.Method;
19  import java.util.ArrayList;
20  import java.util.Arrays;
21  import java.util.Collection;
22  import java.util.concurrent.CopyOnWriteArrayList;
23  
24  import org.rribbit.Listener;
25  import org.rribbit.ListenerObject;
26  import org.rribbit.creation.notification.ListenerObjectCreationObserver;
27  import org.slf4j.Logger;
28  import org.slf4j.LoggerFactory;
29  
30  /**
31   * This {@link ListenerObjectCreator} creates {@link ListenerObject}s from objects. Users can pass in {@link Object}s and this class will scan the {@link Object}'s
32   * class and create {@link ListenerObject}s for the public methods that are annotated with {@link Listener}. This includes all public methods that are declared in superclasses.
33   * <p />
34   * Please note that in Java, method annotations are NOT inherited. This means that, if you override/implement a method in a subclass or subinterface, and the overriding/implementing method
35   * does not have the annotation, then that method will not inherit it. If a class or interface just inherits a method, without overriding it, then the annotation WILL exist.
36   * <p />
37   * Subclasses of this class are required to call {@link #notifyObserversOnClassAdded(Class)} whenever a class is scanned and its listeners are created.
38   *
39   * @author G.J. Schouten
40   *
41   */
42  public class ObjectBasedListenerObjectCreator implements ListenerObjectCreator {
43  
44  	private static final Logger log = LoggerFactory.getLogger(ObjectBasedListenerObjectCreator.class);
45  
46  	/**
47  	 * Subclasses are recommended to use this {@link Collection} to store the {@link ListenerObject}s in.
48  	 */
49  	protected Collection<ListenerObject> listenerObjects;
50  
51  	protected Collection<ListenerObjectCreationObserver> observers;
52  
53  	/**
54  	 * Initializes the {@link Collection} of {@link ListenerObject}s and calls {@link #addObject(Object)} for each given {@link Object}.
55  	 *
56  	 * @param objects
57  	 */
58  	public ObjectBasedListenerObjectCreator(Object... objects) {
59  
60  		listenerObjects = new CopyOnWriteArrayList<>();
61  		observers = new CopyOnWriteArrayList<>();
62  
63  		for(Object object : objects) {
64  			this.addObject(object);
65  		}
66  	}
67  
68  	/**
69  	 * Scans all public methods of the dynamic runtime {@link Class} of the given {@link Object} (the {@link Class} returned by {@link #getClass()}) and creates {@link ListenerObject}s
70  	 * for them if they have a {@link Listener} annotation, then initializes those {@link ListenerObject}s with the given {@link Object} as execution target.
71  	 *
72  	 * @param object
73  	 */
74  	public void addObject(Object object) {
75  
76  		log.debug("Processing Object");
77  		Collection<ListenerObject> incompleteListenerObjects = this.getIncompleteListenerObjectsFromClass(object.getClass());
78  		for(ListenerObject listenerObject : incompleteListenerObjects) {
79  			listenerObject.setTarget(object);
80  		}
81  		listenerObjects.addAll(incompleteListenerObjects);
82  		this.notifyObserversOnClassAdded(object.getClass());
83  	}
84  
85  	/**
86  	 * Scans all public methods in the given {@link Class}, including those inherited from superclasses / superinterfaces and creates {@link ListenerObject}s for them if they have a
87  	 * {@link Listener} annotation. These {@link ListenerObject} have their target {@link Object} NOT YET SET! This is the responsibility of the caller of this method.
88  	 *
89  	 * @param clazz	the class to scan
90  	 * @return a {@link Collection} of incomplete {@link ListenerObject}s that have their target {@link Object} not yet set.
91  	 */
92  	protected Collection<ListenerObject> getIncompleteListenerObjectsFromClass(Class<?> clazz) {
93  
94  		Collection<ListenerObject> incompleteListenerObjects = new ArrayList<>();
95  		for(Method method : clazz.getMethods()) {
96  			Listener listener = method.getAnnotation(Listener.class);
97  			if(listener != null) {
98  				log.debug("Listener annotation found for method '{}', instantiating ListenerObject", method);
99  				ListenerObject listenerObject = new ListenerObject();
100 				listenerObject.setHints(Arrays.asList(listener.hint()));
101 				listenerObject.setMethod(method);
102 				listenerObject.setReturnType(method.getReturnType());
103 				incompleteListenerObjects.add(listenerObject);
104 			}
105 		}
106 		return incompleteListenerObjects;
107 	}
108 
109 	/**
110 	 * Notifies all registered {@link ListenerObjectCreationObserver}s that a class is scanned and its listeners created.
111 	 *
112 	 * @param addedClass
113 	 */
114 	protected void notifyObserversOnClassAdded(Class<?> addedClass) {
115 
116 		for(ListenerObjectCreationObserver listenerObjectCreationObserver : observers) {
117 			listenerObjectCreationObserver.onClassAdded(addedClass);
118 		}
119 	}
120 
121 	@Override
122 	public Collection<ListenerObject> getListenerObjects() {
123 		return listenerObjects;
124 	}
125 
126 	@Override
127 	public void registerObserver(ListenerObjectCreationObserver listenerObjectCreationObserver) {
128 		observers.add(listenerObjectCreationObserver);
129 	}
130 }