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 final 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 super(null); 096 this.component = component; 097 try { 098 field.set(component, this); 099 if (componentChannel == null) { 100 componentChannel = getComponentChannel(field); 101 } 102 if (componentChannel.equals(Channel.SELF)) { 103 componentChannel = this; 104 } 105 this.componentChannel = componentChannel; 106 initComponentsHandlers(); 107 } catch (SecurityException | IllegalAccessException e) { 108 throw (RuntimeException) new IllegalArgumentException( 109 "Cannot access component's manager attribute").initCause(e); 110 } 111 } 112 113 /** 114 * Return the component node for a component that is represented 115 * by a proxy in the tree. 116 * 117 * @param component the component 118 * @param componentChannel the component's channel 119 * @return the node representing the component in the tree 120 */ 121 @SuppressWarnings({ "PMD.DataflowAnomalyAnalysis", 122 "PMD.AvoidAccessibilityAlteration", "PMD.ConfusingTernary" }) 123 /* default */ static ComponentVertex getComponentProxy( 124 ComponentType component, Channel componentChannel) { 125 ComponentProxy componentProxy; 126 try { 127 Field field = getManagerField(component.getClass()); 128 synchronized (component) { 129 if (!field.canAccess(component)) { 130 field.setAccessible(true); 131 componentProxy = (ComponentProxy) field.get(component); 132 if (componentProxy == null) { 133 componentProxy = new ComponentProxy( 134 field, component, componentChannel); 135 } 136 field.setAccessible(false); 137 } else { 138 componentProxy = (ComponentProxy) field.get(component); 139 if (componentProxy == null) { 140 componentProxy = new ComponentProxy( 141 field, component, componentChannel); 142 } 143 } 144 } 145 } catch (SecurityException | IllegalAccessException e) { 146 throw (RuntimeException) new IllegalArgumentException( 147 "Cannot access component's manager attribute").initCause(e); 148 } 149 return componentProxy; 150 } 151 152 public ComponentType component() { 153 return component; 154 } 155 156 /* 157 * (non-Javadoc) 158 * 159 * @see org.jgrapes.core.Manager#getChannel() 160 */ 161 @Override 162 public Channel channel() { 163 return componentChannel; 164 } 165 166 /** 167 * Return the object itself as value. 168 */ 169 @Override 170 public Object defaultCriterion() { 171 return this; 172 } 173 174 /** 175 * Matches the object itself (using identity comparison) or the 176 * {@link Channel} class. 177 * 178 * @see Channel#isEligibleFor(Object) 179 */ 180 @Override 181 @SuppressWarnings("PMD.CompareObjectsWithEquals") 182 public boolean isEligibleFor(Object value) { 183 return value.equals(Channel.class) 184 || value == defaultCriterion(); 185 } 186}