Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
BasicPartBean |
|
| 1.5;1.5 |
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.net.URI; | |
16 | ||
17 | import org.openpermis.policy.bean.PartBean; | |
18 | import org.openpermis.policy.bean.SerialNumber; | |
19 | ||
20 | ||
21 | /** | |
22 | * Abstract part bean implementation that features JavaBean event and serial number support. | |
23 | * <p><strong>JavaBean support</strong></p> | |
24 | * <p>Extending classes are expected to fire property change events using | |
25 | * {@link #firePropertyChange(String, Object, Object)} for all properties changes.</p> | |
26 | * <p>The general implementation of a change should look as follows:</p> | |
27 | * <pre> public void setSomeValue (SomeValue someValue) { | |
28 | * final SomeValue oldSomeValue = getSomeValue(); // Use getter here! | |
29 | * this.someValue = someValue; // Create a clone of someValue if necessary. | |
30 | * firePropertyChange("someValue", oldSomeValue, getSomeValue()); // Use getter again! | |
31 | * }</pre> | |
32 | * <p>Use of getters is mandatory since otherwise you might pass out mutable state in the | |
33 | * property change event!</p> | |
34 | * <p>Also make sure that you clone any collections received to make sure that the caller | |
35 | * cannot change internal state after the call.</p> | |
36 | * <p><strong>JavaBean content change forwarding</strong></p> | |
37 | * <p>Bean events of contained part beans and collections containing part beans are forwarded | |
38 | * automatically using {@code null} as old and new value of the affected property. All you have | |
39 | * to do in your implementation is to make sure that you are firing property change events | |
40 | * properly, the rest will be handled by the abstract implementation.</p> | |
41 | * <p>Example: Consider the {@code Target} holds its {@code Actions} with the beans property | |
42 | * {@code actions}. If the actions held are changed then a property change event with the old | |
43 | * and new actions are fired. If the state of one of the actions contained changes then the | |
44 | * {@code Target} will fire an event for property {@code actions} but without old or new values. | |
45 | * Listeners are then prompted to refresh the complete state they hold about the actions of the | |
46 | * target.</p> | |
47 | * <p><strong>SerialNumber support</strong></p> | |
48 | * <p>Serial numbers are supported as immutable property and have to be supplied at creation | |
49 | * time. They allow to identity the bean, even if its contents is changed. In contrast to using | |
50 | * the {@link System#identityHashCode(Object)} the serial number is serializable and can be | |
51 | * passed on the other objects (e.g. if an implementation class needs to clone a part bean in | |
52 | * a setter).</p> | |
53 | * <p>Apart from supplying the correct serial number an implementation class need not be | |
54 | * concerned about the presence of serial numbers.</p> | |
55 | * <p><strong>SerialNumbers and factory methods</strong></p> | |
56 | * <p>Should you require to create new parts in your implementation (e.g. in factory methods) | |
57 | * use {@link #getSerialNumber()}.{@link SerialNumber#next() next()} to create a unique serial | |
58 | * number that lies in the same context as the serial number of this part.</p> | |
59 | * @since 0.1.0 | |
60 | */ | |
61 | public abstract class BasicPartBean | |
62 | extends BasicPart | |
63 | implements PartBean | |
64 | { | |
65 | ||
66 | //---- Static | |
67 | ||
68 | /** | |
69 | * @since 0.1.0 | |
70 | */ | |
71 | private static final long serialVersionUID = -2727930116159890528L; | |
72 | ||
73 | //---- State | |
74 | ||
75 | /** | |
76 | * The primary part bean interface class that this part implements. | |
77 | * @since 0.3.0 | |
78 | */ | |
79 | private final Class<? extends PartBean> partBeanType; | |
80 | ||
81 | /** | |
82 | * The serial number of this part bean. | |
83 | * @since 0.1.0 | |
84 | */ | |
85 | private final SerialNumber serialNumber; | |
86 | ||
87 | /** | |
88 | * The property change support of this part bean. | |
89 | * @since 0.1.0 | |
90 | */ | |
91 | private final PropertyChangeSupport propertyChangeSupport; | |
92 | ||
93 | //---- Constructors | |
94 | ||
95 | /** | |
96 | * Creates a new part bean with the specified serial number. | |
97 | * @param partBeanType the primary part bean interface class that this part implements. | |
98 | * @param serialNumber the serial number of this part. | |
99 | * @since 0.1.0 | |
100 | * @since 0.3.0 added partBeanType. | |
101 | */ | |
102 | 9680 | protected BasicPartBean (Class<? extends PartBean> partBeanType, SerialNumber serialNumber) { |
103 | 9680 | this.partBeanType = partBeanType; |
104 | 9680 | this.serialNumber = serialNumber; |
105 | 9680 | this.propertyChangeSupport = new PropertyChangeSupport(this); |
106 | // Attach a forwarder for contained part bean property events. | |
107 | 9680 | new BasicPropertyForwarder(this, this.propertyChangeSupport); |
108 | 9680 | } |
109 | ||
110 | //---- Methods | |
111 | ||
112 | /** | |
113 | * Report a bound property update to any registered listeners. | |
114 | * <p>No event is fired if old and new are equal and non-null.</p> | |
115 | * <p>The event will use the event hash code as propagation ID.</p> | |
116 | * @note Take care to <em>always</em> use getters when retrieving the old/new values | |
117 | * to fire to avoid passing out mutable objects! | |
118 | * @param propertyName the programmatic name of the property that was changed. | |
119 | * @param oldValue the old value of the property. | |
120 | * @param newValue the new value of the property. | |
121 | * @since 0.1.0 | |
122 | */ | |
123 | protected final void firePropertyChange ( | |
124 | String propertyName, Object oldValue, Object newValue | |
125 | ) { | |
126 | 8852 | if (oldValue != null && newValue != null && oldValue.equals(newValue)) { |
127 | 1505 | return; |
128 | } | |
129 | 7347 | final PropertyChangeEvent event = |
130 | new PropertyChangeEvent(this, propertyName, oldValue, newValue); | |
131 | 7347 | event.setPropagationId(Integer.valueOf(event.hashCode())); |
132 | 7347 | this.propertyChangeSupport.firePropertyChange(event); |
133 | 7347 | } |
134 | ||
135 | //---- PartBean | |
136 | ||
137 | /** | |
138 | * @since 0.3.0 | |
139 | */ | |
140 | public Class<? extends PartBean> getPartBeanType () { | |
141 | 0 | return this.partBeanType; |
142 | } | |
143 | ||
144 | /** | |
145 | * @since 0.1.0 | |
146 | */ | |
147 | public final SerialNumber getSerialNumber () { | |
148 | 2173 | return this.serialNumber; |
149 | } | |
150 | ||
151 | /** | |
152 | * @since 0.1.0 | |
153 | */ | |
154 | public boolean equalSerialNumber (PartBean part) { | |
155 | 0 | return getSerialNumber().equals(part.getSerialNumber()); |
156 | } | |
157 | ||
158 | /** | |
159 | * {@inheritDoc} | |
160 | * <p>The basic implementation returns this bean if its serial number matches | |
161 | * the one passed in or <code>null</code> if not. Subclasses that contain other | |
162 | * parts must traverse their children. | |
163 | * @since 0.1.0 | |
164 | */ | |
165 | public PartBean findBySerialNumber (SerialNumber partSerialNumber) { | |
166 | 21 | if (getSerialNumber().equals(partSerialNumber)) { |
167 | 9 | return this; |
168 | } | |
169 | 12 | return null; |
170 | } | |
171 | ||
172 | /** | |
173 | * @since 0.1.0 | |
174 | */ | |
175 | public final String toShortString () { | |
176 | 0 | return new StringBuilder(getClass().getSimpleName()). |
177 | append(" [serial=").append(getSerialNumber()).append("]").toString(); | |
178 | } | |
179 | ||
180 | /** | |
181 | * @since 0.1.0 | |
182 | */ | |
183 | public void addPropertyChangeListener (PropertyChangeListener listener) { | |
184 | 12885 | this.propertyChangeSupport.addPropertyChangeListener(listener); |
185 | 12885 | } |
186 | ||
187 | /** | |
188 | * @since 0.1.0 | |
189 | */ | |
190 | public void removePropertyChangeListener (PropertyChangeListener listener) { | |
191 | 253 | this.propertyChangeSupport.removePropertyChangeListener(listener); |
192 | 253 | } |
193 | ||
194 | //---- BasicPart | |
195 | ||
196 | /** | |
197 | * @since 0.1.0 | |
198 | */ | |
199 | @Override | |
200 | protected String getSerialNumberString () { | |
201 | 0 | return String.valueOf(getSerialNumber()); |
202 | } | |
203 | ||
204 | /** | |
205 | * @since 0.1.0 | |
206 | */ | |
207 | @Override | |
208 | protected final void notifyIdentityChange (URI oldIdentity, URI newIdentity) { | |
209 | 1199 | firePropertyChange("identity", oldIdentity, newIdentity); |
210 | 1199 | } |
211 | ||
212 | /** | |
213 | * @since 0.1.0 | |
214 | */ | |
215 | @Override | |
216 | protected final void notifyNameChange (String oldName, String newName) { | |
217 | 638 | firePropertyChange("name", oldName, newName); |
218 | 638 | } |
219 | ||
220 | } |