Coverage Report - org.openpermis.policy.bean.basic.BasicPart
 
Classes in this File Line Coverage Branch Coverage Complexity
BasicPart
80%
129/161
74%
103/138
3.206
 
 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 static org.openpermis.policy.bean.basic.BasicUtilities.equalObjects;
 13  
 import static org.openpermis.policy.bean.basic.BasicUtilities.multiHashCode;
 14  
 
 15  
 import java.net.URI;
 16  
 import java.util.HashMap;
 17  
 import java.util.Map;
 18  
 
 19  
 import org.openpermis.policy.Identifiable;
 20  
 import org.openpermis.policy.Nameable;
 21  
 import org.openpermis.policy.Part;
 22  
 import org.openpermis.policy.PartProblemReporter;
 23  
 import org.openpermis.policy.PartProblemReporter.ProblemMessage;
 24  
 
 25  
 /**
 26  
  * Abstract base class for policy parts.
 27  
  * <p><strong>Mix-in Interfaces</strong></p>
 28  
  * <p>This base class provides support for the mix-in interfaces:</p>
 29  
  * <ul>
 30  
  * <li>{@link Nameable}</li>
 31  
  * <li>{@link Identifiable}</li>
 32  
  * </ul>
 33  
  * <p>To create an implementation that features one of the mix-in interfaces listed above simply
 34  
  * implement the interface in your {@code AbstractPart} extension, the base class will take care
 35  
  * of the mix-in interface implementation details.</p>
 36  
  * <p><strong>State of Extensions</strong></p>
 37  
  * <p>If your part implementation features additional state make sure to provide implementations
 38  
  * for the following methods:</p>
 39  
  * <ul>
 40  
  * <li>{@link #isPartValid(PartProblemReporter)}</li>
 41  
  * <li>{@link #equalPart(BasicPart)}</li>
 42  
  * <li>{@link #comparablePart(BasicPart)}</li>
 43  
  * <li>{@link #partHashCode()}</li>
 44  
  * <li>{@link #appendPartDetails(StringBuilder)}</li>
 45  
  * </ul>
 46  
  * <p>(If your identity part does not have further state there is no need to provide
 47  
  * implementations of the methods listed above. The default implementations will guarantee
 48  
  * that your identity part implementation works as expected.)</p>
 49  
  * <p><strong>Note:</strong> If your implementation contains lists or sets of part beans make
 50  
  * sure they are compared using {@link BasicUtilities#equalLists(java.util.List, java.util.List)}
 51  
  * and the hash code is calculated using {@link BasicUtilities#listHashCode(java.util.List)}.
 52  
  * These implementations disregard the list item order.</p>
 53  
  * <p><strong>Getters/Setters</strong></p>
 54  
  * <p>Follow the JavaBean property specification when implementing getters and setters, e.g. if
 55  
  * your part features a value called {@code myValue} then provide {@code MyType getMyValue()} and
 56  
  * {@code setMyValue(Type myValue)}.</p>
 57  
  * <p>Make sure collection classes are cloned before you assign them to your internal state,
 58  
  * otherwise the caller can modify the collection from outside again.</p>
 59  
  * @since 0.1.0
 60  
  */
 61  
 public abstract class BasicPart
 62  
         implements Part
 63  
 {
 64  
 
 65  
         //---- Static
 66  
 
 67  
         /**
 68  
          * @since 0.1.0
 69  
          */
 70  
         private static final long serialVersionUID = 5635383950293028314L;
 71  
 
 72  
         /**
 73  
          * The URI consisting of an empty string.
 74  
          * @see #isIdentityValid(PartProblemReporter)
 75  
          * @since 0.1.0
 76  
          */
 77  1
         private static final URI EMPTY_URI = URI.create("");
 78  
 
 79  
         //---- State
 80  
 
 81  
         /**
 82  
          * The identity of this part.
 83  
          * <p>Only used if the mix-in interface {@link Identifiable} is implemented.</p>
 84  
          * @see #hasPartIdentity()
 85  
          * @since 0.1.0
 86  
          */
 87  
         private URI identity;
 88  
 
 89  
         /**
 90  
          * The name of this part.
 91  
          * <p>Only used if the mix-in interface {@link Nameable} is implemented.</p>
 92  
          * @see #hasPartName()
 93  
          * @since 0.1.0
 94  
          */
 95  
         private String name;
 96  
 
 97  
         //---- Constructors
 98  
 
 99  
         /**
 100  
          * Creates a new, uninitialized basic part.
 101  
          * @since 0.1.0
 102  
          */
 103  
         protected BasicPart () {
 104  9714
                 super();
 105  9714
         }
 106  
 
 107  
         //---- Methods
 108  
 
 109  
         /**
 110  
          * Convenience method to reports a problem using the specified reporter.
 111  
          * <p>This method guarantees that the reporter is only called if it is not {@code null}.</p>
 112  
          * @param reporter the problem reporter to use, may be {@code null}.
 113  
          * @param message the problem to report.
 114  
          * @param param additional details about the type of violation.
 115  
          * @since 0.1.0
 116  
          */
 117  
         protected final void reportProblem (
 118  
                 PartProblemReporter reporter, ProblemMessage message, Object... param
 119  
         ) {
 120  51
                 if (reporter != null) {
 121  0
                         reporter.reportProblem(this, message, param);
 122  
                 }
 123  51
         }
 124  
 
 125  
         /**
 126  
          * Convenience method to append the details of an extended part to a string builder.
 127  
          * <p>The convenience method takes care of the correct formatting.</p>
 128  
          * @param sb the string builder to append the details to.
 129  
          * @param type the type of details to add.
 130  
          * @param details the details to add.
 131  
          * @return the string builder specified to allow call chaining.
 132  
          * @since 0.1.0
 133  
          */
 134  
         protected final StringBuilder appendDetails (StringBuilder sb, String type, Object details) {
 135  0
                 return sb.append(", ").append(type).append("=").append(details);
 136  
         }
 137  
 
 138  
         /**
 139  
          * Compares another part to this part.
 140  
          * @note The part supplied is guaranteed to be {@link #comparablePart(BasicPart) comparable}
 141  
          * to this part (i.e. it is safe to cast the object without any further checks if the
 142  
          * implementation of {@link #comparablePart(BasicPart)} makes the instanceof check).
 143  
          * @param part the part to compare to this part.
 144  
          * @return {@code true} if the part supplied is considered equivalent to this part.
 145  
          * @see BasicUtilities#equalObjects(Object, Object)
 146  
          * @see BasicPart
 147  
          * @since 0.1.0
 148  
          */
 149  
         protected boolean equalPart (BasicPart part) {
 150  325
                 return true;
 151  
         }
 152  
 
 153  
         /**
 154  
          * Check if the part specified is comparable to this part.
 155  
          * @note Extensions should perform an {@code instanceof} check with the implementation class.
 156  
          * This check is performed in {@link #equals(Object)} before calling
 157  
          * {@link #equalPart(BasicPart)} to guarantee symmetry and transitivity on both objects.
 158  
          * @param part the part to check.
 159  
          * @return {@code true} if the part supplied is comparable to this part.
 160  
          * @see BasicPart
 161  
          * @since 0.1.0
 162  
          */
 163  
         protected abstract boolean comparablePart (BasicPart part);
 164  
 
 165  
         /**
 166  
          * Returns the hash code of the extended part state.
 167  
          * @note Extensions should calculate a combined hash code for all their extended state,
 168  
          * use {@link BasicUtilities#multiHashCode(int...)} to get a suitable hash code distribution.
 169  
          * @return the hash code of the extended part state.
 170  
          * @see BasicPart
 171  
          * @see BasicUtilities#multiHashCode(int...)
 172  
          * @since 0.1.0
 173  
          */
 174  
         protected int partHashCode () {
 175  1119
                 return 0;
 176  
         }
 177  
 
 178  
         /**
 179  
          * Checks if a child part is not null.
 180  
          * @param part child part to check.
 181  
          * @return if all conditions are ok
 182  
          * @since 0.1.0
 183  
          */
 184  
         protected boolean isChildNotNull (
 185  
                 PartProblemReporter reporter,
 186  
                 Part part)
 187  
         {
 188  
                 boolean valid;
 189  
 
 190  
                 // check if part is null
 191  2
                 valid = part != null;
 192  2
                 if (!valid) {
 193  1
                         reportProblem(reporter, ProblemMessage.nullChild);
 194  
                 }
 195  
 
 196  
                 // return
 197  2
                 return valid;
 198  
         }
 199  
 
 200  
         /**
 201  
          * Checks if a child part is null or valid.
 202  
          * @param part child part to check.
 203  
          * @return if all conditions are ok
 204  
          * @since 0.1.0
 205  
          */
 206  
         protected boolean isChildNullOrValid (
 207  
                 PartProblemReporter reporter,
 208  
                 Part part)
 209  
         {
 210  2
                 return part == null || isChildValid(reporter, part);
 211  
         }
 212  
 
 213  
 
 214  
         /**
 215  
          * Checks if a child part is set and valid.
 216  
          * @param part child part to check.
 217  
          * @return if all conditions are ok
 218  
          * @since 0.1.0
 219  
          */
 220  
         protected boolean isChildValid (
 221  
                 PartProblemReporter reporter,
 222  
                 Part part)
 223  
         {
 224  
                 boolean valid;
 225  
 
 226  
                 // init
 227  32
                 valid = true;
 228  
 
 229  
                 // check if part is not null
 230  32
                 if (part == null) {
 231  4
                         valid = false;
 232  4
                         reportProblem(reporter, ProblemMessage.nullChild);
 233  
                 }
 234  
 
 235  
                 // check if all part is valid
 236  32
                 if (part != null) {
 237  28
                         if (!part.isValid(reporter)) {
 238  0
                                 valid = false;
 239  0
                                 reportProblem(reporter, ProblemMessage.invalidChild);
 240  
                         }
 241  
                 }
 242  
 
 243  
                 // return
 244  32
                 return valid;
 245  
         }
 246  
 
 247  
         /**
 248  
          * Checks if a collection is set, elements are set and valid and not empty.
 249  
          * @param reporter reporter to use.
 250  
          * @param collection the collection to check.
 251  
          * @param checkNotEmpty check if collection is empty.
 252  
          * @param checkValid check if collection's elements are valid.
 253  
          * @param checkNameUniqueness check if collection's elements are name-unique.
 254  
          * @param checkObjectUniqueness check if collection's elements are object-unique.
 255  
          * @param checkIdentityUniqueness check if collectrion's elements are identity-unique.
 256  
          * @return <code>true</code> if all conditions are ok.
 257  
          * @since 0.3.0
 258  
          */
 259  
         protected boolean isChildCollectionValid (
 260  
                 PartProblemReporter reporter,
 261  
                 Iterable<? extends Part> collection,
 262  
                 boolean checkNotEmpty,
 263  
                 boolean checkValid,
 264  
                 boolean checkNameUniqueness,
 265  
                 boolean checkObjectUniqueness,
 266  
                 boolean checkIdentityUniqueness)
 267  
         {
 268  
                 boolean valid;
 269  
 
 270  
                 // init
 271  45
                 valid = true;
 272  
 
 273  
                 // check if collection is not null
 274  45
                 if (collection == null) {
 275  0
                         valid = false;
 276  0
                         reportProblem(reporter, ProblemMessage.nullChildCollection);
 277  
                 }
 278  
 
 279  
                 // check if all elements are set
 280  45
                 if (collection !=  null) {
 281  45
                         for (Part part : collection) {
 282  36
                                 if (part == null) {
 283  0
                                         valid = false;
 284  0
                                         reportProblem(reporter, ProblemMessage.collectionWithNullElements);
 285  0
                                         break;
 286  
                                 }
 287  
                         }
 288  
                 }
 289  
 
 290  
                 // check if all elements are valid
 291  45
                 if (checkValid) {
 292  41
                         if (collection !=  null) {
 293  41
                                 for (Part part : collection) {
 294  34
                                         if (part != null && !part.isValid(reporter)) {
 295  2
                                                 valid = false;
 296  2
                                                 reportProblem(reporter, ProblemMessage.invalidChildCollection);
 297  2
                                                 break;
 298  
                                         }
 299  
                                 }
 300  
                         }
 301  
                 }
 302  
 
 303  
                 // checkNotEmpty
 304  45
                 if (checkNotEmpty) {
 305  35
                         if (collection != null && !collection.iterator().hasNext()) {
 306  9
                                 valid = false;
 307  9
                                 reportProblem(reporter, ProblemMessage.emptyChildCollection);
 308  
                         }
 309  
                 }
 310  
                 
 311  
                 // checkNameUniqueness
 312  45
                 if (checkNameUniqueness) {
 313  10
                         if (collection !=  null) {
 314  10
                                 Map<String, Part> map = new HashMap<String, Part>();
 315  10
                                 for (Part part : collection) {
 316  8
                                         if (part != null && part instanceof Nameable) {
 317  8
                                                 String partName = ((Nameable) part).getName();
 318  8
                                                 if (map.get(partName) != null) {
 319  1
                                                         valid = false;
 320  1
                                                         reportProblem(
 321  
                                                                 reporter, ProblemMessage.collectionWithDuplicateName);
 322  1
                                                         break;
 323  
                                                 }
 324  7
                                                 map.put(partName, part);
 325  7
                                         }
 326  
                                 }
 327  
                         }
 328  
                 }
 329  
                 
 330  
                 // checkIdentityUniqueness
 331  45
                 if (checkIdentityUniqueness) {
 332  4
                         if (collection !=  null) {
 333  4
                                 Map<URI, Part> map = new HashMap<URI, Part>();
 334  4
                                 for (Part part : collection) {
 335  4
                                         if (part != null && part instanceof Identifiable) {
 336  4
                                                 URI partIdentity = ((Identifiable) part).getIdentity();
 337  4
                                                 if (map.get(partIdentity) != null) {
 338  2
                                                         valid = false;
 339  2
                                                         reportProblem(
 340  
                                                                 reporter, ProblemMessage.collectionWithDuplicateIdentity);
 341  2
                                                         break;
 342  
                                                 }
 343  2
                                                 map.put(partIdentity, part);
 344  2
                                         }
 345  
                                 }
 346  
                         }
 347  
                 }
 348  
 
 349  
                 // checkObjectUniqueness
 350  45
                 if (checkObjectUniqueness) {
 351  32
                         if (collection !=  null) {
 352  32
                                 Map<Object, Part> map = new HashMap<Object, Part>();
 353  32
                                 for (Part part : collection) {
 354  21
                                         if (map.containsKey(part)) {
 355  2
                                                 valid = false;
 356  2
                                                 reportProblem(
 357  
                                                         reporter, ProblemMessage.collectionWithDuplicateObject);
 358  2
                                                 break;
 359  
                                         }
 360  19
                                         map.put(part, part);
 361  
                                 }
 362  
                         }
 363  
                 }
 364  
 
 365  
                 // return
 366  45
                 return valid;
 367  
         }
 368  
 
 369  
         /**
 370  
          * Checks if the extended state of this part is valid.
 371  
          * @note Extensions should check their extended part state.
 372  
          * Use {@link #reportProblem} to avoid having to deal with {@code null} reporters.
 373  
          * @param reporter the problem reporter to use, may be {@code null}.
 374  
          * @see BasicPart
 375  
          * @see Part#isValid(PartProblemReporter)
 376  
          * @see #reportProblem
 377  
          * @since 0.1.0
 378  
          */
 379  
         public boolean isPartValid (PartProblemReporter reporter) {
 380  48
                 return true;
 381  
         }
 382  
 
 383  
         /**
 384  
          * Appends the details of this extended part for the string representation of this part.
 385  
          * @note Extensions can add their details using the convenience method
 386  
          * {@link #appendDetails(StringBuilder, String, Object)} to guarantee proper formatting.
 387  
          * @param sb the string builder to add the details to.
 388  
          * @see BasicPart
 389  
          * @see #appendDetails(StringBuilder, String, Object)
 390  
          * @since 0.1.0
 391  
          */
 392  
         protected void appendPartDetails (StringBuilder sb) {
 393  
                 // The default implementation does not add further part details.
 394  0
         }
 395  
 
 396  
         /**
 397  
          * Returns the string representation of this parts serial number if it has any.
 398  
          * <p>The default implementation simply returns {@code null}.</p>
 399  
          * @return the string representation of this parts serial number if it has any.
 400  
          * @since 0.1.0
 401  
          */
 402  
         protected String getSerialNumberString () {
 403  0
                 return null;
 404  
         }
 405  
 
 406  
         /**
 407  
          * Notifies a change of the identity property.
 408  
          * <p>The default implementation does nothing.</p>
 409  
          * @param oldIdentity the old identity.
 410  
          * @param newIdentity the new identity.
 411  
          * @since 0.1.0
 412  
          */
 413  
         protected void notifyIdentityChange (URI oldIdentity, URI newIdentity) {
 414  
                 // Nop.
 415  34
         }
 416  
 
 417  
         /**
 418  
          * Notifies a change of the name property.
 419  
          * <p>The default implementation does nothing.</p>
 420  
          * @param oldName the old name.
 421  
          * @param newName the new name.
 422  
          * @since 0.1.0
 423  
          */
 424  
         protected void notifyNameChange (String oldName, String newName) {
 425  
                 // Nop.
 426  0
         }
 427  
 
 428  
         /**
 429  
          * Returns the simple implementation class name of this class.
 430  
          * <p>If your implementation represents an interface implementation return the interface
 431  
          * simple class name here. In short: return the instance you are checking against in
 432  
          * {@link #comparablePart(BasicPart)}.</p>
 433  
          * @return the simple implementation class name of this class.
 434  
          * @since 0.1.0
 435  
          */
 436  
         protected String getSimpleClassName () {
 437  0
                 return getClass().getSimpleName();
 438  
         }
 439  
 
 440  
         //---- PartIdentity
 441  
 
 442  
         /**
 443  
          * Check if the implementation of this part supports a {@link Identifiable}.
 444  
          * @return {@code true} if this part is a {@link Identifiable}.
 445  
          * @since 0.1.0
 446  
          */
 447  
         protected final boolean hasPartIdentity () {
 448  12933
                 return this instanceof Identifiable;
 449  
         }
 450  
 
 451  
         /**
 452  
          * Check if the identity of this part and the part specified are equivalent.
 453  
          * <p>Makes sure that both objects implement the {@link Identifiable} mix-in interface
 454  
          * and then compares the identities. Also returns {@code true} if both objects do not
 455  
          * support the {@link Identifiable} interface.</p>
 456  
          * @param part the part to compare this identity to, must not be {@code null}.
 457  
          * @since 0.1.0
 458  
          */
 459  
         private final boolean equalIdentity (BasicPart part) {
 460  5080
                 if (hasPartIdentity()) {
 461  338
                         return part.hasPartIdentity() && equalObjects(getIdentity(), part.getIdentity());
 462  
                 }
 463  4742
                 return !part.hasPartIdentity();
 464  
         }
 465  
 
 466  
         /**
 467  
          * Returns the identity hash code of this part.
 468  
          * <p>If this part {@link #hasPartIdentity()} then the hash code of the identity is returned.
 469  
          * if it does not support the {@link Identifiable} mix-in interface {@code 0} is returned.</p>
 470  
          * @return the identity hash code of this part or {@code 0}.
 471  
          * @since 0.1.0
 472  
          */
 473  
         private final int identityHashCode () {
 474  1456
                 if (hasPartIdentity()) {
 475  1116
                         return getIdentity() == null ? 0 : getIdentity().hashCode();
 476  
                 }
 477  340
                 return 0;
 478  
         }
 479  
 
 480  
         /**
 481  
          * Checks if the identity portion of this part is valid.
 482  
          * <p>The identity portion is only checked if this part {@link #hasPartIdentity()}.</p>
 483  
          * @return {@code true} if the identity portion of this part is valid or this part
 484  
          * does not implement the mix-in interface {@link Identifiable}.
 485  
          * @since 0.1.0
 486  
          */
 487  
         public final boolean isIdentityValid (PartProblemReporter reporter) {
 488  84
                 if (hasPartIdentity()) {
 489  36
                         if (getIdentity() == null) {
 490  3
                                 reportProblem(reporter, ProblemMessage.nullIdentity);
 491  3
                                 return false;
 492  
                         }
 493  33
                         if (EMPTY_URI.equals(getIdentity())) {
 494  0
                                 reportProblem(reporter, ProblemMessage.emptyIdentity);
 495  0
                                 return false;
 496  
                         }
 497  
                 }
 498  81
                 return true;
 499  
         }
 500  
 
 501  
         /**
 502  
          * Appends the identity details of this part to the string builder specified.
 503  
          * <p>The identity details are only added if this part {@link #hasPartIdentity()}.</p>
 504  
          * @param sb the string builder to append to.
 505  
          * @since 0.1.0
 506  
          */
 507  
         private void appendIdentityDetails (StringBuilder sb) {
 508  0
                 if (hasPartIdentity()) {
 509  0
                         appendDetails(sb, "identity", getIdentity());
 510  
                 }
 511  0
         }
 512  
 
 513  
         /**
 514  
          * Returns the URI that serves as identity of this part.
 515  
          * @return the identity URI of this part.
 516  
          * @since 0.1.0
 517  
          */
 518  
         public final URI getIdentity () {
 519  3310
                 return this.identity;
 520  
         }
 521  
 
 522  
         /**
 523  
          * Sets the URI that serves as identity of this part.
 524  
          * @param identity the new identity URI of this part to set.
 525  
          * @since 0.1.0
 526  
          */
 527  
         public final void setIdentity (URI identity) {
 528  1233
                 final URI oldIdentity = this.identity;
 529  1233
                 this.identity = identity;
 530  1233
                 if (hasPartIdentity()) {
 531  1233
                         notifyIdentityChange(oldIdentity, identity);
 532  
                 }
 533  1233
         }
 534  
 
 535  
         //---- PartName
 536  
 
 537  
         /**
 538  
          * Check if the implementation of this part supports a {@link Nameable}.
 539  
          * @return {@code true} if this part is a {@link Nameable}.
 540  
          * @since 0.1.0
 541  
          */
 542  
         protected final boolean hasPartName () {
 543  12312
                 return this instanceof Nameable;
 544  
         }
 545  
 
 546  
         /**
 547  
          * Check if the name of this part and the part specified are equivalent.
 548  
          * <p>Makes sure that both objects implement the {@link Nameable} mix-in interface
 549  
          * and then compares the name. Also returns {@code true} if both objects do not
 550  
          * support the {@link Nameable} interface.</p>
 551  
          * @param part the part to compare this name to, must not be {@code null}.
 552  
          * @since 0.1.0
 553  
          */
 554  
         private final boolean equalName (BasicPart part) {
 555  5067
                 if (hasPartName()) {
 556  176
                         return part.hasPartName() && equalObjects(getName(), part.getName());
 557  
                 }
 558  4891
                 return !part.hasPartName();
 559  
         }
 560  
 
 561  
         /**
 562  
          * Returns the name hash code of this part.
 563  
          * <p>If this part {@link #hasPartName()} then the hash code of the name is returned.
 564  
          * if it does not support the {@link Nameable} mix-in interface {@code 0} is returned.</p>
 565  
          * @return the name hash code of this part or {@code 0}.
 566  
          * @since 0.1.0
 567  
          */
 568  
         private final int nameHashCode () {
 569  1456
                 if (hasPartName()) {
 570  106
                         return getName() == null ? 0 : getName().hashCode();
 571  
                 }
 572  1350
                 return 0;
 573  
         }
 574  
 
 575  
         /**
 576  
          * Checks if the name portion of this part is valid.
 577  
          * <p>The name portion is only checked if this part {@link #hasPartName()}.</p>
 578  
          * @return {@code true} if the name portion of this part is valid or this part
 579  
          * does not implement the mix-in interface {@link Nameable}.
 580  
          * @since 0.1.0
 581  
          */
 582  
         public final boolean isNameValid (PartProblemReporter reporter) {
 583  84
                 if (hasPartName()) {
 584  13
                         if (getName() == null) {
 585  1
                                 reportProblem(reporter, ProblemMessage.nameMissing);
 586  1
                                 return false;
 587  
                         }
 588  12
                         if (getName().length() == 0) {
 589  1
                                 reportProblem(reporter, ProblemMessage.nameEmpty);
 590  1
                                 return false;
 591  
                         }
 592  
                 }
 593  82
                 return true;
 594  
         }
 595  
 
 596  
         /**
 597  
          * Appends the name details of this part to the string builder specified.
 598  
          * <p>The name details are only added if this part {@link #hasPartName()}.</p>
 599  
          * @param sb the string builder to append to.
 600  
          * @since 0.1.0
 601  
          */
 602  
         private void appendNameDetails (StringBuilder sb) {
 603  0
                 if (hasPartName()) {
 604  0
                         appendDetails(sb, "name", getName());
 605  
                 }
 606  0
         }
 607  
 
 608  
         /**
 609  
          * Returns the name of this part.
 610  
          * @return the name of this part.
 611  
          * @since 0.1.0
 612  
          */
 613  
         public final String getName () {
 614  1111
                 return this.name;
 615  
         }
 616  
 
 617  
         /**
 618  
          * Sets the name of this part.
 619  
          * @param name the new name of this part.
 620  
          * @since 0.1.0
 621  
          */
 622  
         public final void setName (String name) {
 623  638
                 final String oldName = this.name;
 624  638
                 this.name = name;
 625  638
                 if (hasPartName()) {
 626  638
                         notifyNameChange(oldName, name);
 627  
                 }
 628  638
         }
 629  
 
 630  
         //---- Part
 631  
 
 632  
         /**
 633  
          * @since 0.1.0
 634  
          */
 635  
         public final boolean isValid (PartProblemReporter reporter) {
 636  
                 boolean valid;
 637  
 
 638  84
                 valid = true;
 639  
 
 640  84
                 if (!isIdentityValid(reporter)) {
 641  3
                         valid = false;
 642  
                 }
 643  
 
 644  84
                 if (!isNameValid(reporter)) {
 645  2
                         valid = false;
 646  
                 }
 647  
 
 648  84
                 if (!isPartValid(reporter)) {
 649  17
                         valid = false;
 650  
                 }
 651  
 
 652  84
                 return valid;
 653  
         }
 654  
 
 655  
         //---- Object
 656  
 
 657  
         /**
 658  
          * @since 0.1.0
 659  
          */
 660  
         @Override
 661  
         public final boolean equals (Object obj) {
 662  5167
                 if (obj == null) {
 663  0
                         return false;
 664  
                 }
 665  5167
                 if (obj == this) {
 666  87
                         return true;
 667  
                 }
 668  5080
                 if (obj instanceof BasicPart) {
 669  5080
                         final BasicPart part = (BasicPart) obj;
 670  
                         // Check equality of mix-in interfaces.
 671  5080
                         if (!equalIdentity(part) || !equalName(part)) {
 672  25
                                 return false;
 673  
                         }
 674  
                         // Check symmetry and transitivity before calling equalsPart.
 675  5055
                         if (comparablePart(part) && part.comparablePart(this)) {
 676  5055
                                 return equalPart(part);
 677  
                         }
 678  
                 }
 679  0
                 return false;
 680  
         }
 681  
 
 682  
         /**
 683  
          * @since 0.1.0
 684  
          */
 685  
         @Override
 686  
         public final int hashCode () {
 687  1456
                 return multiHashCode(identityHashCode(), nameHashCode(), partHashCode());
 688  
         }
 689  
 
 690  
         /**
 691  
          * @since 0.1.0
 692  
          */
 693  
         @Override
 694  
         public final String toString () {
 695  0
                 final StringBuilder sb = new StringBuilder(getSimpleClassName());
 696  0
                 final String serial = getSerialNumberString();
 697  0
                 if (serial != null) {
 698  0
                         sb.append(" [serial=").append(serial);
 699  
                 } else {
 700  0
                         sb.append(" [");
 701  
                 }
 702  0
                 appendIdentityDetails(sb);
 703  0
                 appendNameDetails(sb);
 704  0
                 appendPartDetails(sb);
 705  0
                 return sb.append("]").toString();
 706  
         }
 707  
 
 708  
 }