001/* 002 * JGrapes Event Driven Framework 003 * Copyright (C) 2016-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.internal; 020 021import java.lang.reflect.Field; 022import java.lang.reflect.InvocationTargetException; 023import org.jgrapes.core.Channel; 024import org.jgrapes.core.ComponentType; 025import org.jgrapes.core.Manager; 026import org.jgrapes.core.NamedChannel; 027import org.jgrapes.core.annotation.ComponentManager; 028import org.jgrapes.core.annotation.Handler; 029 030/** 031 * The ComponentProxy is a special ComponentVertex that references the 032 * object implementing the Component interface (instead of being 033 * its base class). 034 */ 035public final class ComponentProxy extends ComponentVertex { 036 037 /** The reference to the actual component. */ 038 private final ComponentType component; 039 /** The referenced component's channel. */ 040 private Channel componentChannel; 041 042 private static Field getManagerField(Class<?> clazz) { 043 try { 044 while (true) { 045 for (Field field : clazz.getDeclaredFields()) { 046 if (Manager.class.isAssignableFrom(field.getType()) 047 && field 048 .getAnnotation(ComponentManager.class) != null) { 049 return field; 050 } 051 } 052 clazz = clazz.getSuperclass(); 053 if (clazz == null) { 054 throw new IllegalArgumentException( 055 "Components must have a manager attribute"); 056 } 057 } 058 } catch (SecurityException e) { 059 throw (RuntimeException) (new IllegalArgumentException( 060 "Cannot access component's manager attribute")).initCause(e); 061 } 062 } 063 064 private static Channel getComponentChannel(Field field) { 065 ComponentManager cma = field.getAnnotation(ComponentManager.class); 066 if (cma.channel() != Handler.NoChannel.class) { 067 if (cma.channel() != Channel.BROADCAST.defaultCriterion()) { 068 try { 069 return cma.channel().getConstructor().newInstance(); 070 } catch (InstantiationException // NOPMD 071 | IllegalAccessException | IllegalArgumentException 072 | InvocationTargetException | NoSuchMethodException 073 | SecurityException e) { 074 // Ignored 075 } 076 } 077 return Channel.BROADCAST; 078 } 079 if (!cma.namedChannel().isEmpty()) { 080 return new NamedChannel(cma.namedChannel()); 081 } 082 return Channel.SELF; 083 } 084 085 /** 086 * Create a new component proxy for the component and assign it to 087 * the specified field which must be of type {@link Manager}. 088 * 089 * @param field the field that gets the proxy assigned 090 * @param componentChannel the componen't channel 091 * @param component the component 092 */ 093 private ComponentProxy( 094 Field field, ComponentType component, Channel componentChannel) { 095 this.component = component; 096 try { 097 field.set(component, this); 098 if (componentChannel == null) { 099 componentChannel = getComponentChannel(field); 100 } 101 if (componentChannel.equals(Channel.SELF)) { 102 componentChannel = this; 103 } 104 this.componentChannel = componentChannel; 105 initComponentsHandlers(null); 106 } catch (SecurityException | IllegalAccessException e) { 107 throw (RuntimeException) (new IllegalArgumentException( 108 "Cannot access component's manager attribute")).initCause(e); 109 } 110 } 111 112 /** 113 * Return the component node for a component that is represented 114 * by a proxy in the tree. 115 * 116 * @param component the component 117 * @param componentChannel the component's channel 118 * @return the node representing the component in the tree 119 */ 120 @SuppressWarnings({ "PMD.DataflowAnomalyAnalysis", 121 "PMD.AvoidAccessibilityAlteration", "PMD.ConfusingTernary" }) 122 /* default */ static ComponentVertex getComponentProxy( 123 ComponentType component, Channel componentChannel) { 124 ComponentProxy componentProxy; 125 try { 126 Field field = getManagerField(component.getClass()); 127 synchronized (component) { 128 if (!field.canAccess(component)) { 129 field.setAccessible(true); 130 componentProxy = (ComponentProxy) field.get(component); 131 if (componentProxy == null) { 132 componentProxy = new ComponentProxy( 133 field, component, componentChannel); 134 } 135 field.setAccessible(false); 136 } else { 137 componentProxy = (ComponentProxy) field.get(component); 138 if (componentProxy == null) { 139 componentProxy = new ComponentProxy( 140 field, component, componentChannel); 141 } 142 } 143 } 144 } catch (SecurityException | IllegalAccessException e) { 145 throw (RuntimeException) (new IllegalArgumentException( 146 "Cannot access component's manager attribute")).initCause(e); 147 } 148 return componentProxy; 149 } 150 151 public ComponentType component() { 152 return component; 153 } 154 155 /* 156 * (non-Javadoc) 157 * 158 * @see org.jgrapes.core.Manager#getChannel() 159 */ 160 @Override 161 public Channel channel() { 162 return componentChannel; 163 } 164 165 /** 166 * Return the object itself as value. 167 */ 168 @Override 169 public Object defaultCriterion() { 170 return this; 171 } 172 173 /** 174 * Matches the object itself (using identity comparison) or the 175 * {@link Channel} class. 176 * 177 * @see Channel#isEligibleFor(Object) 178 */ 179 @Override 180 public boolean isEligibleFor(Object value) { 181 return value.equals(Channel.class) 182 || value == defaultCriterion(); 183 } 184}