Coverage Report - org.openpermis.policy.bean.basic.BasicPartBean
 
Classes in this File Line Coverage Branch Coverage Complexity
BasicPartBean
85%
24/28
100%
8/8
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  
 }