Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
BasicPropertyForwarder |
|
| 5.8;5.8 |
1 | /* | |
2 | * Copyright (c) 2009, Swiss Federal Department of Defence Civil Protection and Sport | |
3 | * (http://www.vbs.admin.ch) | |
4 | * Copyright (c) 2009, Ergon Informatik AG (http://www.ergon.ch) | |
5 | * All rights reserved. | |
6 | * | |
7 | * Licensed under the Open Permis License which accompanies this distribution, | |
8 | * and is available at http://www.openpermis.org/BSDlicenceKent.txt | |
9 | */ | |
10 | package org.openpermis.policy.bean.basic; | |
11 | ||
12 | import java.beans.PropertyChangeEvent; | |
13 | import java.beans.PropertyChangeListener; | |
14 | import java.beans.PropertyChangeSupport; | |
15 | import java.util.Collection; | |
16 | import java.util.IdentityHashMap; | |
17 | ||
18 | import org.slf4j.Logger; | |
19 | import org.slf4j.LoggerFactory; | |
20 | ||
21 | import org.openpermis.policy.bean.PartBean; | |
22 | ||
23 | ||
24 | /** | |
25 | * Forwarder for bean part property changes. | |
26 | * <p>Once registered at a bean part it will observe changes and register itself as listener | |
27 | * for any bean part value changes. Any changes in contained bean parts will then be forwarded.</p> | |
28 | * @since 0.1.0 | |
29 | */ | |
30 | public class BasicPropertyForwarder | |
31 | implements PropertyChangeListener | |
32 | { | |
33 | ||
34 | //---- Static | |
35 | ||
36 | /** | |
37 | * The logger object of this class. | |
38 | * @since 0.1.0 | |
39 | */ | |
40 | 1 | private static final Logger LOGGER = |
41 | LoggerFactory.getLogger(BasicPropertyForwarder.class); | |
42 | ||
43 | /** | |
44 | * Enable to see verbose log output message forwarding. | |
45 | * @since 0.1.0 | |
46 | */ | |
47 | private static final boolean TRACE = false; | |
48 | ||
49 | //---- State | |
50 | ||
51 | /** | |
52 | * The part bean for which to forward changes in contained part beans. | |
53 | * @since 0.1.0 | |
54 | */ | |
55 | private final PartBean partBean; | |
56 | ||
57 | /** | |
58 | * The property change support to use for forwarding. | |
59 | * @since 0.1.0 | |
60 | */ | |
61 | private final PropertyChangeSupport propertyChangeSupport; | |
62 | ||
63 | /** | |
64 | * Maps the contained part beans this forwarder is attached to their property names. | |
65 | * @note We have to use an identity hash map here since the beans are mutable and will | |
66 | * thus change their hash code. Using the identity will allow us to track them anyway. | |
67 | * We could also have used the serial number of the part bean, but then we would have | |
68 | * to make sure that the serial number lies in the same context and we could not support | |
69 | * undefined serial numbers. | |
70 | * @since 0.1.0 | |
71 | */ | |
72 | private final IdentityHashMap<PartBean, String> propertyMap; | |
73 | ||
74 | //---- Constructors | |
75 | ||
76 | /** | |
77 | * Creates and registers new basic property forwarder for the specified part bean. | |
78 | * @param partBean the part bean for which to forward contained part bean property changes. | |
79 | * @param propertyChangeSupport the property change support to use for forwarding. | |
80 | * @since 0.1.0 | |
81 | */ | |
82 | public BasicPropertyForwarder ( | |
83 | PartBean partBean, PropertyChangeSupport propertyChangeSupport | |
84 | 9680 | ) { |
85 | 9680 | this.partBean = partBean; |
86 | 9680 | this.propertyChangeSupport = propertyChangeSupport; |
87 | 9680 | this.propertyMap = new IdentityHashMap<PartBean, String>(); |
88 | 9680 | this.partBean.addPropertyChangeListener(this); |
89 | 9680 | } |
90 | ||
91 | //---- Methods | |
92 | ||
93 | /** | |
94 | * Detaches this forwarder as a change listener from the specified object. | |
95 | * <p>If the object is a {@link PartBean} it simply detaches itself.</p> | |
96 | * <p>If the object is a {@link Collection} it will detach itself deeply.</p> | |
97 | * @param propertyName the name of the property that caused the detach. | |
98 | * @param obj the object to detach from, may be {@code null}. | |
99 | * @since 0.1.0 | |
100 | */ | |
101 | protected void detach (String propertyName, Object obj) { | |
102 | 6851 | if (obj == null) { |
103 | 3967 | return; |
104 | } | |
105 | 2884 | if (obj instanceof BasicAbstractCollection<?>) { |
106 | 1681 | for (Object candidate : ((Iterable<?>) obj)) { |
107 | 2 | detach(propertyName, candidate); |
108 | } | |
109 | 1203 | } else if (obj instanceof PartBean) { |
110 | 253 | final PartBean bean = (PartBean) obj; |
111 | 253 | this.propertyMap.remove(bean); |
112 | 253 | bean.removePropertyChangeListener(this); |
113 | if (TRACE) { | |
114 | LOGGER.debug( | |
115 | "[{}] detaching [{}] from [{}].", | |
116 | new Object[] { | |
117 | this.partBean.toShortString(), | |
118 | propertyName, | |
119 | bean.toShortString() | |
120 | } | |
121 | ); | |
122 | } | |
123 | 253 | } else if (obj instanceof Collection<?>) { |
124 | 0 | for (Object candidate : ((Collection<?>) obj)) { |
125 | 0 | detach(propertyName, candidate); |
126 | } | |
127 | } | |
128 | 2884 | } |
129 | ||
130 | /** | |
131 | * Attaches this forwarder as a change listener to the specified object. | |
132 | * <p>If the object is a {@link PartBean} it simply attaches itself.</p> | |
133 | * <p>If the object is a {@link Collection} it will attach itself deeply.</p> | |
134 | * @param propertyName the name of the property that caused the attach. | |
135 | * @param obj the object to attach to, may be {@code null}. | |
136 | * @since 0.1.0 | |
137 | */ | |
138 | protected void attach (String propertyName, Object obj) { | |
139 | 8920 | if (obj == null) { |
140 | 299 | return; |
141 | } | |
142 | 8621 | if (obj instanceof BasicAbstractCollection<?>) { |
143 | 1681 | for (Object candidate : ((Iterable<?>) obj)) { |
144 | 2071 | attach(propertyName, candidate); |
145 | } | |
146 | 6940 | } else if (obj instanceof PartBean) { |
147 | 3203 | final PartBean bean = (PartBean) obj; |
148 | 3203 | bean.addPropertyChangeListener(this); |
149 | 3203 | this.propertyMap.put(bean, propertyName); |
150 | if (TRACE) { | |
151 | LOGGER.debug( | |
152 | "[{}] attaching [{}] to [{}].", | |
153 | new Object[] { | |
154 | this.partBean.toShortString(), | |
155 | propertyName, | |
156 | bean.toShortString() | |
157 | } | |
158 | ); | |
159 | } | |
160 | } | |
161 | 8621 | } |
162 | ||
163 | /** | |
164 | * Forwards this event to listeners of the owner part bean. | |
165 | * <p>The event forwarded will have the same propagation ID as the event passed in.</p> | |
166 | * @param event the event to forward, source must not be {@code null}. | |
167 | * @since 0.1.0 | |
168 | */ | |
169 | protected void forward (PropertyChangeEvent event) { | |
170 | 4 | final Object source = event.getSource(); |
171 | 4 | if (!(source instanceof PartBean)) { |
172 | 0 | return; |
173 | } | |
174 | 4 | final PartBean bean = (PartBean) source; |
175 | 4 | final String propertyName = this.propertyMap.get(bean); |
176 | 4 | if (propertyName == null) { |
177 | 0 | return; |
178 | } | |
179 | 4 | final PropertyChangeEvent forwardEvent = |
180 | new PropertyChangeEvent(this.partBean, propertyName, null, null); | |
181 | 4 | forwardEvent.setPropagationId(event.getPropagationId()); |
182 | if (TRACE) { | |
183 | LOGGER.debug( | |
184 | "[{}] forwarding change of [{}] caused by a change of [{}] in [{}].", | |
185 | new Object[] { | |
186 | this.partBean.toShortString(), | |
187 | propertyName, | |
188 | event.getPropertyName(), | |
189 | bean.toShortString() | |
190 | } | |
191 | ); | |
192 | } | |
193 | 4 | this.propertyChangeSupport.firePropertyChange(forwardEvent); |
194 | 4 | } |
195 | ||
196 | //---- PropertyChangeListener | |
197 | ||
198 | /** | |
199 | * @since 0.1.0 | |
200 | */ | |
201 | public void propertyChange (PropertyChangeEvent event) { | |
202 | 6853 | final String propertyName = event.getPropertyName(); |
203 | 6853 | if (propertyName == null || propertyName.length() == 0) { |
204 | 0 | return; |
205 | } | |
206 | 6853 | if (event.getSource() == this.partBean) { |
207 | if (TRACE) { | |
208 | LOGGER.debug( | |
209 | "[{}] reports a change in [{}] (old = [{}], new = [{}]).", | |
210 | new Object[] { | |
211 | this.partBean.toShortString(), | |
212 | propertyName, | |
213 | event.getOldValue(), | |
214 | event.getNewValue() | |
215 | } | |
216 | ); | |
217 | } | |
218 | 6849 | detach(propertyName, event.getOldValue()); |
219 | 6849 | attach(propertyName, event.getNewValue()); |
220 | } else { | |
221 | 4 | forward(event); |
222 | } | |
223 | 6853 | } |
224 | ||
225 | } |