Coverage Report - org.openpermis.policy.bean.basic.BasicRoleHierarchy
 
Classes in this File Line Coverage Branch Coverage Complexity
BasicRoleHierarchy
78%
187/237
64%
86/134
2.261
BasicRoleHierarchy$AdjacentSet
62%
23/37
20%
10/50
2.261
BasicRoleHierarchy$Direction
100%
3/3
N/A
2.261
BasicRoleHierarchy$RoleSet
89%
25/28
57%
8/14
2.261
 
 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.net.URI;
 13  
 import java.util.Collection;
 14  
 import java.util.Collections;
 15  
 import java.util.Map;
 16  
 import java.util.Set;
 17  
 import java.util.TreeMap;
 18  
 import java.util.TreeSet;
 19  
 
 20  
 import org.openpermis.policy.PartProblemReporter;
 21  
 import org.openpermis.policy.PartProblemReporter.ProblemMessage;
 22  
 import org.openpermis.policy.bean.PartBean;
 23  
 import org.openpermis.policy.bean.RoleHierarchyBean;
 24  
 import org.openpermis.policy.bean.SerialNumber;
 25  
 
 26  
 /**
 27  
  * A DAG of roles.
 28  
  *
 29  
  * @since 0.1.0
 30  
  */
 31  1
 public class BasicRoleHierarchy extends BasicPartBean implements RoleHierarchyBean {
 32  
 
 33  
         // ---- Static
 34  
 
 35  
         /**
 36  
          * @since 0.1.0
 37  
          */
 38  
         private static final long serialVersionUID = -1728538401158979463L;
 39  
 
 40  
         /**
 41  
          * @since 0.3.0
 42  
          */
 43  3
         public enum Direction {
 44  
                 /***/
 45  1
                 parents,
 46  
                 /***/
 47  1
                 children
 48  
         }
 49  
 
 50  
         /**
 51  
          * Set for Roles using SerialNumber as primary key.
 52  
          * @since 0.3.0
 53  
          */
 54  142
         private class RoleSet {
 55  
 
 56  
                 private final Set<String> set;
 57  
 
 58  2193
                 protected RoleSet () {
 59  2193
                         this.set = new TreeSet<String>();
 60  2193
                 }
 61  
 
 62  
                 protected RoleSet (Collection<String> roles) {
 63  375
                         this();
 64  375
                         this.addAllRoles(roles);
 65  375
                 }
 66  
 
 67  
                 public void addRole (String role) {
 68  1650
                         assert !this.set.contains(role);
 69  1650
                         this.set.add(role);
 70  1650
                 }
 71  
 
 72  
                 public void addAllRoles (Collection<String> roleCollection) {
 73  884
                         for (String role : roleCollection) {
 74  517
                                 addRole(role);
 75  
                         }
 76  884
                 }
 77  
 
 78  
                 public void removeRole (String role) {
 79  19
                         assert this.set.contains(role);
 80  19
                         this.set.remove(role);
 81  19
                 }
 82  
 
 83  
                 public void renameRole (String role, String newName) {
 84  0
                         this.removeRole(role);
 85  0
                         this.addRole(newName);
 86  0
                 }
 87  
 
 88  
                 public boolean containsRole (String role) {
 89  5031
                         return this.set.contains(role);
 90  
                 }
 91  
 
 92  
                 /**
 93  
                  * Returns a unmodifiable set of all role.
 94  
                  * @return a unmodifiable set of all roles.
 95  
                  * @since 0.3.0
 96  
                  */
 97  
                 public Set<String> getRoles () {
 98  1458
                         return Collections.unmodifiableSet(this.set);
 99  
                 }
 100  
 
 101  
                 public boolean sharesElements (RoleSet otherSet) {
 102  19
                         for (String role : otherSet.getRoles()) {
 103  35
                                 if (this.containsRole(role)) {
 104  7
                                         return true;
 105  
                                 }
 106  
                         }
 107  12
                         return false;
 108  
                 }
 109  
 
 110  
                 public int size () {
 111  256
                         return this.set.size();
 112  
                 }
 113  
 
 114  
                 public void clear () {
 115  324
                         this.set.clear();
 116  324
                 }
 117  
 
 118  
         }
 119  
 
 120  
         /**
 121  
          * Set for Adjacent Roles using SerialNumber as primary key.
 122  
          * Offers a map role => set of adjacent roles.
 123  
          * @since 0.3.0
 124  
          */
 125  1
         private class AdjacentSet {
 126  
 
 127  
                 private final Map<String, RoleSet> set;
 128  
 
 129  668
                 protected AdjacentSet () {
 130  668
                         this.set = new TreeMap<String, RoleSet>();
 131  668
                 }
 132  
 
 133  
                 public void addRole (String role) {
 134  1190
                         assert ! this.set.containsKey(role);
 135  1190
                         this.set.put(role, new RoleSet());
 136  1190
                 }
 137  
 
 138  
                 public void addRole (String role, RoleSet adjacentList) {
 139  0
                         assert ! this.set.containsKey(role);
 140  0
                         this.set.put(role, adjacentList);
 141  0
                 }
 142  
 
 143  
                 public void removeRole (String role) {
 144  
 
 145  
                         // checks
 146  6
                         assert this.set.containsKey(role);
 147  
 
 148  
                         // remove all edges with role as target
 149  6
                         for (String sourceRole : this.set.keySet()) {
 150  12
                                 if (this.set.get(sourceRole).containsRole(role)) {
 151  2
                                         this.set.get(sourceRole).removeRole(role);
 152  
                                 }
 153  
                         }
 154  
 
 155  
                         // remove all edges with role as source
 156  6
                         this.set.remove(role);
 157  6
                 }
 158  
 
 159  
                 public void renameRole (String role, String newName) {
 160  
 
 161  
                         // checks
 162  0
                         assert this.set.containsKey(role);
 163  0
                         assert !this.set.containsKey(newName);
 164  
 
 165  
                         // rename all edges with role as target
 166  0
                         for (String sourceRole : this.set.keySet()) {
 167  0
                                 if (this.set.get(sourceRole).containsRole(role)) {
 168  0
                                         this.set.get(sourceRole).renameRole(role, newName);
 169  
                                 }
 170  
                         }
 171  
 
 172  
                         // rename role itself
 173  0
                         RoleSet adjacentRoles = this.getAdjacentRoles(role);
 174  0
                         this.removeRole(role);
 175  0
                         this.addRole(newName, adjacentRoles);
 176  0
                 }
 177  
 
 178  
                 public void addAdjacentRole (String role, String adjacentRole) {
 179  312
                         assert !containsAdjacentRole(role, adjacentRole);
 180  312
                         this.set.get(role).addRole(adjacentRole);
 181  312
                 }
 182  
 
 183  
                 public void removeAdjacentRole (String role, String adjacentRole) {
 184  2
                         assert containsAdjacentRole(role, adjacentRole);
 185  2
                         this.set.get(role).removeRole(adjacentRole);
 186  2
                 }
 187  
 
 188  
                 public boolean containsAdjacentRole (String role, String adjacentRole) {
 189  922
                         assert this.set.containsKey(role);
 190  922
                         return this.set.get(role).containsRole(adjacentRole);
 191  
                 }
 192  
 
 193  
                 /**
 194  
                  * Returns a unmodifiable roleset of all adjacent roles.
 195  
                  * @return a unmodifiable roleset of all adjacent roles.
 196  
                  * @since 0.3.0
 197  
                  */
 198  
                 public RoleSet getAdjacentRoles (String role) {
 199  344
                         assert this.set.containsKey(role);
 200  344
                         return new RoleSet(this.set.get(role).getRoles());
 201  
                 }
 202  
 
 203  
                 public boolean hasAdjacentRoles (String role) {
 204  0
                         assert this.set.containsKey(role);
 205  0
                         return this.set.get(role).size() > 0;
 206  
                 }
 207  
         }
 208  
 
 209  
 
 210  
         // ---- State
 211  
 
 212  
         /**
 213  
          * @since 0.3.0
 214  
          */
 215  
         private final RoleSet roles;
 216  
 
 217  
         /**
 218  
          * @since 0.3.0
 219  
          */
 220  
         private final AdjacentSet subroles;
 221  
 
 222  
         /**
 223  
          * @since 0.3.0
 224  
          */
 225  
         private final AdjacentSet superroles;
 226  
 
 227  
 
 228  
         // ---- Constructors
 229  
 
 230  
         /**
 231  
          * Creates a role hierarchy.
 232  
          * @param serialNumber the serial number of this part.
 233  
          */
 234  
         protected BasicRoleHierarchy (SerialNumber serialNumber, URI identity) {
 235  334
                 super(RoleHierarchyBean.class, serialNumber);
 236  334
                 setIdentity(identity);
 237  334
                 this.roles = new RoleSet();
 238  334
                 this.subroles = new AdjacentSet();
 239  334
                 this.superroles = new AdjacentSet();
 240  334
         }
 241  
 
 242  
 
 243  
         // ---- Methods
 244  
 
 245  
         /**
 246  
          * @since 0.3.0
 247  
          */
 248  
         private void fireRoleHierarchyEvent () {
 249  753
                 firePropertyChange("roleHierarchies", this, null);
 250  753
         }
 251  
 
 252  
         /**
 253  
          * Checks for equal roles.
 254  
          * @param role1 the first role
 255  
          * @param role2 the second role
 256  
          * @return if roles are equal
 257  
          * @since 0.3.0
 258  
          */
 259  
         private boolean areRolesEqual (String role1, String role2) {
 260  214
                 return role1.equals(role2);
 261  
         }
 262  
 
 263  
         /**
 264  
          * Returns true if childRole is a child-role of role.
 265  
          * @param parentRole the role to start search from..
 266  
          * @param childRole the role to find.
 267  
          * @return {@code true} if found.
 268  
          * @since 0.3.0
 269  
          */
 270  
         private boolean edgeExists (String parentRole, String childRole) {
 271  
 
 272  
                 // checks
 273  451
                 if (!roleExists(parentRole) || !roleExists(childRole)) {
 274  0
                         return false;
 275  
                 }
 276  
 
 277  
                 // search
 278  451
                 boolean subExists = this.subroles.containsAdjacentRole(parentRole, childRole);
 279  
 
 280  
                 // assertions
 281  451
                 boolean superExists = this.superroles.containsAdjacentRole(childRole, parentRole);
 282  451
                 assert subExists == superExists;
 283  
 
 284  
                 // return
 285  451
                 return subExists;
 286  
         }
 287  
 
 288  
         /**
 289  
          * Checks for null-argument.
 290  
          * @param obj the object to check.
 291  
          * @since 0.3.0
 292  
          */
 293  
         private void forceArgumentNotNull (Object obj) {
 294  1524
                 if (obj == null) {
 295  1
                         throw new IllegalArgumentException("argument must not be null.");
 296  
                 }
 297  1523
         }
 298  
 
 299  
         /**
 300  
          * Checks for existence.
 301  
          * @param parentRole the role to start search from..
 302  
          * @param childRole the role to find.
 303  
          * @since 0.3.0
 304  
          */
 305  
         private void forceEdgeExists (String parentRole, String childRole) {
 306  1
                 forceRoleExists(parentRole);
 307  1
                 forceRoleExists(childRole);
 308  1
                 if (!edgeExists(parentRole, childRole)) {
 309  0
                         throw new IllegalArgumentException("edge does not exist in hierarchy.");
 310  
                 }
 311  1
         }
 312  
 
 313  
         /**
 314  
          * Checks for existence.
 315  
          * @param parentRole the role to start search from..
 316  
          * @param childRole the role to find.
 317  
          * @since 0.3.0
 318  
          */
 319  
         private void forceEdgeNotExists (String parentRole, String childRole) {
 320  158
                 forceRoleExists(parentRole);
 321  158
                 forceRoleExists(childRole);
 322  158
                 if (edgeExists(parentRole, childRole)) {
 323  0
                         throw new IllegalArgumentException("edge does exist in hierarchy.");
 324  
                 }
 325  158
         }
 326  
 
 327  
         /**
 328  
          * Checks for existence.
 329  
          * @param role the role to check
 330  
          * @since 0.3.0
 331  
          */
 332  
         private void forceRoleExists (String role) {
 333  927
                 forceArgumentNotNull(role);
 334  927
                 if (!roleExists(role)) {
 335  0
                         throw new IllegalArgumentException("role does not exist in hierarchy.");
 336  
                 }
 337  927
         }
 338  
 
 339  
         /**
 340  
          * Checks for existence.
 341  
          * @param checkRoles the roles to check
 342  
          * @since 0.3.0
 343  
          */
 344  
         private void forceRolesExist (Collection<String> checkRoles) {
 345  2
                 for (String role : checkRoles) {
 346  4
                         forceRoleExists(role);
 347  
                 }
 348  2
         }
 349  
 
 350  
         /**
 351  
          * Checks for existence.
 352  
          * @param role the role to check
 353  
          * @since 0.3.0
 354  
          */
 355  
         private void forceRoleNotExists (String role) {
 356  597
                 forceArgumentNotNull(role);
 357  596
                 if (roleExists(role)) {
 358  1
                         throw new IllegalArgumentException("role does exist in hierarchy.");
 359  
                 }
 360  595
         }
 361  
 
 362  
         /**
 363  
          * Adds an edge. precondition: edge not yet exists.
 364  
          * precondition: edge not exists.
 365  
          * @param role the parent role.
 366  
          * @param subrole the child role.
 367  
          * @since 0.3.0
 368  
          */
 369  
         private void addEdge (String role, String subrole) {
 370  
 
 371  
                 // checks
 372  158
                 forceRoleExists(role);
 373  158
                 forceRoleExists(subrole);
 374  158
                 forceEdgeNotExists(role, subrole);
 375  
 
 376  
                 // check different
 377  158
                 if (areRolesEqual(role, subrole) || edgeExists(subrole, role)) {
 378  2
                         throw new IllegalArgumentException("no cyclic edges allowed.");
 379  
                 }
 380  
 
 381  
                 // insert edge
 382  156
                 this.subroles.addAdjacentRole(role, subrole);
 383  156
                 this.superroles.addAdjacentRole(subrole, role);
 384  156
         }
 385  
 
 386  
         /**
 387  
          * Removes an edge. precondition: edge exists.
 388  
          * precondition: edge exists.
 389  
          * @param role the parent role.
 390  
          * @param subrole the child role.
 391  
          * @since 0.3.0
 392  
          */
 393  
         private void removeEdge (String role, String subrole) {
 394  
 
 395  
                 // checks
 396  1
                 forceRoleExists(role);
 397  1
                 forceRoleExists(subrole);
 398  1
                 forceEdgeExists(role, subrole);
 399  
 
 400  
                 // remove edge
 401  1
                 this.subroles.removeAdjacentRole(role, subrole);
 402  1
                 this.superroles.removeAdjacentRole(subrole, role);
 403  1
         }
 404  
 
 405  
         /**
 406  
          * Try to find any node in given subsetRoles
 407  
          * with no successors within given subsetRoles.
 408  
          * Please note: this code is simple, but not optimized. the determination of successors could
 409  
          * easily be done in O(n) holding an additional map <node,size> with adjacency-size
 410  
          * @param subsetRoles the subset to search in.
 411  
          * @since 0.1.0
 412  
          */
 413  
         private String findNodeWithNoSubNodes (Direction direction, RoleSet subsetRoles) {
 414  
 
 415  
                 // search
 416  13
                 for (String currRole : subsetRoles.getRoles()) {
 417  19
                         if (!getAdjacentRoles(currRole, direction).sharesElements(subsetRoles)) {
 418  12
                                 return currRole;
 419  
                         }
 420  
                 }
 421  
 
 422  
                 // no node with no successors found
 423  1
                 return null;
 424  
         }
 425  
 
 426  
         /**
 427  
          * Check if role tree is cycle-free in the given direction.
 428  
          * @since 0.3.0
 429  
          */
 430  
         private boolean treeTopologicallySortable (Direction direction) {
 431  
                 String currRole;
 432  
                 RoleSet subsetRoles;
 433  
 
 434  19
                 subsetRoles = new RoleSet(this.getRoles());
 435  19
                 int numNode = subsetRoles.size();
 436  31
                 while (numNode > 0) {
 437  13
                         currRole = findNodeWithNoSubNodes(direction, subsetRoles);
 438  13
                         if (currRole == null) {
 439  1
                                 return false; // cycle found
 440  
                         }
 441  12
                         subsetRoles.removeRole(currRole);
 442  12
                         numNode--;
 443  
                 }
 444  18
                 return true; // cycle-free
 445  
         }
 446  
 
 447  
         /**
 448  
          * Check if role tree is cycle-free.
 449  
          * @since 0.1.0
 450  
          */
 451  
         private boolean treeTopologicallySortable () {
 452  10
                 return treeTopologicallySortable(Direction.children)
 453  
                         && treeTopologicallySortable(Direction.parents);
 454  
         }
 455  
 
 456  
         /**
 457  
          * Check if role tree is closed.
 458  
          * @since 0.1.0
 459  
          */
 460  
         private boolean treeClosed () {
 461  
                 RoleSet checkRoles;
 462  
 
 463  
                 // get roles
 464  10
                 checkRoles = new RoleSet(this.getRoles());
 465  
 
 466  
                 // check
 467  10
                 for (String currRole : checkRoles.getRoles()) {
 468  
 
 469  9
                         assert currRole != null;
 470  
 
 471  
                         // check children
 472  9
                         for (String subRole : getChildRoles(currRole)) {
 473  6
                                 if (!checkRoles.containsRole(subRole)) {
 474  0
                                         return false;
 475  
                                 }
 476  
                         }
 477  
 
 478  
                         // check parents
 479  9
                         for (String superRole : getParentRoles(currRole)) {
 480  6
                                 if (!checkRoles.containsRole(superRole)) {
 481  0
                                         return false;
 482  
                                 }
 483  
                         }
 484  
                 }
 485  
 
 486  10
                 return true;
 487  
         }
 488  
 
 489  
         /**
 490  
          * Returns a unmodifiable list of all adjacent role.
 491  
          * @param role role to search for
 492  
          * @param direction {DIRECTION_CHILDREN, DIRECTION_PARENTS}
 493  
          * @return list of adjacent roles.
 494  
          * @since 0.3.0
 495  
          */
 496  
         private RoleSet getAdjacentRoles (String role, Direction direction) {
 497  238
                 if (direction == Direction.children) {
 498  144
                         return this.subroles.getAdjacentRoles(role);
 499  94
                 } else if (direction == Direction.parents) {
 500  94
                         return this.superroles.getAdjacentRoles(role);
 501  
                 } else {
 502  0
                         throw new IllegalArgumentException("illegal direction.");
 503  
                 }
 504  
         }
 505  
 
 506  
         /**
 507  
          * @since 0.3.0
 508  
          */
 509  
         private RoleSet getTransitiveClosure (String role, Direction direction) {
 510  89
                 forceRoleExists(role);
 511  
                 RoleSet adjacentRoles;
 512  89
                 RoleSet allLevels = new RoleSet();
 513  89
                 RoleSet currentLevel =  new RoleSet();
 514  89
                 RoleSet nextLevel =  new RoleSet();
 515  89
                 allLevels.addRole(role);
 516  89
                 currentLevel.addRole(role);
 517  
                 do {
 518  162
                         for (String role1 : currentLevel.getRoles()) {
 519  
                                 // get adjacent roles
 520  185
                                 adjacentRoles = getAdjacentRoles(role1, direction);
 521  
 
 522  
                                 // check them for cycles
 523  185
                                 for (String role2 : adjacentRoles.getRoles()) {
 524  96
                                         if (allLevels.containsRole(role2)) {
 525  0
                                                 throw new IllegalArgumentException("cyclic role hierarchy.");
 526  
                                         }
 527  
                                 }
 528  
 
 529  
                                 // add them to the next level roles
 530  185
                                 nextLevel.addAllRoles(adjacentRoles.getRoles());
 531  
                         }
 532  
 
 533  
                         // go to next level
 534  162
                         currentLevel.clear();
 535  162
                         currentLevel.addAllRoles(nextLevel.getRoles());
 536  162
                         nextLevel.clear();
 537  162
                         allLevels.addAllRoles(currentLevel.getRoles());
 538  162
                 } while (currentLevel.size() > 0);
 539  
 
 540  
                 // return
 541  89
                 return allLevels;
 542  
         }
 543  
 
 544  
         /**
 545  
          * Returns true if there's a way from "role" to "findrole" using given direction. precondition:
 546  
          * role exists and graph is cycle-free
 547  
          * @param role the role to start search.
 548  
          * @param findRole the role to find
 549  
          * @return {@code true} if the findRole could be found.
 550  
          * @since 0.3.0
 551  
          */
 552  
         private boolean inTransitiveClosure (String role, String findRole, Direction direction) {
 553  
                 // checks
 554  56
                 forceRoleExists(role);
 555  56
                 forceRoleExists(findRole);
 556  
 
 557  
                 // ask current role
 558  56
                 if (areRolesEqual(role, findRole)) {
 559  22
                         return true;
 560  
                 }
 561  
 
 562  
                 // loop over children
 563  34
                 for (String subRole : getAdjacentRoles(role, direction).getRoles()) {
 564  34
                         if (inTransitiveClosure(subRole, findRole, direction)) {
 565  28
                                 return true;
 566  
                         }
 567  
                 }
 568  
 
 569  
                 // not found
 570  6
                 return false;
 571  
         }
 572  
 
 573  
         // ---- RoleHierarchy
 574  
 
 575  
         /**
 576  
          * Returns all superior roles of 'role' including 'role' itself.
 577  
          * @return all superior roles of 'role' including 'role' itself.
 578  
          * @since 0.3.0
 579  
          */
 580  
         public Set<String> getSuperRoles (String role) {
 581  38
                 return getTransitiveClosure(role, Direction.parents).set;
 582  
         }
 583  
 
 584  
         /**
 585  
          * Returns all sub roles of 'role' including 'role' itself.
 586  
          * @return all sub roles of 'role' including 'role' itself.
 587  
          * @since 0.3.0
 588  
          */
 589  
         public Set<String> getSubRoles (String role) {
 590  51
                 return getTransitiveClosure(role, Direction.children).set;
 591  
         }
 592  
 
 593  
 
 594  
         // ---- RoleHierarchyBean
 595  
 
 596  
         /**
 597  
          * Checks if a role exists in this role hierarchy.
 598  
          * @param role the role to search.
 599  
          * @return if found * @since 0.3.0
 600  
          */
 601  
         public boolean roleExists (String role) {
 602  3954
                 return role != null && this.roles.containsRole(role);
 603  
         }
 604  
 
 605  
         /**
 606  
          * Returns a unmodifiable list of all roles in this hierarchy.
 607  
          * @return list of roles in this hierarchy.
 608  
          * @since 0.1.0
 609  
          */
 610  
         public Set<String> getRoles () {
 611  151
                 return this.roles.getRoles();
 612  
         }
 613  
 
 614  
 
 615  
         /**
 616  
          * Returns a list of all roles with no parents.
 617  
          * @return list of all roles with no parents.
 618  
          * @since 0.3.0
 619  
          */
 620  
         public Set<String> getRootRoles () {
 621  11
                 RoleSet selectedRoles = new RoleSet();
 622  11
                 for (String role : this.roles.getRoles()) {
 623  35
                         if (this.superroles.getAdjacentRoles(role).size() == 0) {
 624  20
                                 selectedRoles.addRole(role);
 625  
                         }
 626  
                 }
 627  11
                 return selectedRoles.set;
 628  
         }
 629  
 
 630  
         /**
 631  
          * Returns a list of all roles with no children.
 632  
          * precondition: role exists.
 633  
          * @return list of all roles with no children.
 634  
          * @since 0.3.0
 635  
          */
 636  
         public Set<String> getLeafRoles () {
 637  12
                 RoleSet selectedRoles = new RoleSet();
 638  12
                 for (String role : this.roles.getRoles()) {
 639  40
                         if (this.subroles.getAdjacentRoles(role).size() == 0) {
 640  24
                                 selectedRoles.addRole(role);
 641  
                         }
 642  
                 }
 643  12
                 return selectedRoles.set;
 644  
         }
 645  
 
 646  
         /**
 647  
          * Returns a unmodifiable list of all children.
 648  
          * precondition: role exists.
 649  
          * @return list of children roles.
 650  
          * @since 0.3.0
 651  
          */
 652  
         public Set<String> getChildRoles (String role) {
 653  20
                 forceRoleExists(role);
 654  20
                 return this.subroles.getAdjacentRoles(role).set;
 655  
         }
 656  
 
 657  
         /**
 658  
          * Returns a unmodifiable list of all parents.
 659  
          * precondition: role exists.
 660  
          * @return list of parent roles.
 661  
          * @since 0.3.0
 662  
          */
 663  
         public Set<String> getParentRoles (String role) {
 664  9
                 forceRoleExists(role);
 665  9
                 return this.superroles.getAdjacentRoles(role).set;
 666  
         }
 667  
 
 668  
         /**
 669  
          * Returns true if role is root, i.e. has no parents.
 670  
          * precondition: role exists.
 671  
          * @param role the role to check.
 672  
          * @return {@code true} if found.
 673  
          * @since 0.3.0
 674  
          */
 675  
         public boolean isRootRole (String role) {
 676  0
                 forceRoleExists(role);
 677  0
                 return !this.superroles.hasAdjacentRoles(role);
 678  
         }
 679  
 
 680  
         /**
 681  
          * Returns true if role is leaf, i.e. has no children.
 682  
          * precondition: role exists.
 683  
          * @param role the role to check.
 684  
          * @return {@code true} if found.
 685  
          * @since 0.3.0
 686  
          */
 687  
         public boolean isLeafRole (String role) {
 688  0
                 forceRoleExists(role);
 689  0
                 return !this.subroles.hasAdjacentRoles(role);
 690  
         }
 691  
 
 692  
         /**
 693  
          * Returns true if subRole is a sub-role of role.
 694  
          * precondition: both roles exist.
 695  
          * @param role the role to start search from.
 696  
          * @param subRole the role to find.
 697  
          * @return {@code true} if found.
 698  
          * @since 0.3.0
 699  
          */
 700  
         public boolean isSubRole (String role, String subRole) {
 701  11
                 return inTransitiveClosure(role, subRole, Direction.children);
 702  
         }
 703  
 
 704  
         /**
 705  
          * Returns true if superRole is a super-role of role.
 706  
          * precondition: both roles exist.
 707  
          * @param role the role to start search from..
 708  
          * @param superRole the role to find.
 709  
          * @return {@code true} if found.
 710  
          * @since 0.3.0
 711  
          */
 712  
         public boolean isSuperRole (String role, String superRole) {
 713  11
                 return inTransitiveClosure(role, superRole, Direction.parents);
 714  
         }
 715  
 
 716  
         /**
 717  
          * Returns true if childRole is a child-role of role.
 718  
          * precondition: both roles exist.
 719  
          * @param role the role to start search from..
 720  
          * @param childRole the role to find.
 721  
          * @return {@code true} if found.
 722  
          * @since 0.3.0
 723  
          */
 724  
         public boolean isChildRole (String role, String childRole) {
 725  
                 // checks
 726  8
                 forceRoleExists(role);
 727  8
                 forceRoleExists(childRole);
 728  
 
 729  
                 // search
 730  8
                 return this.subroles.containsAdjacentRole(role, childRole);
 731  
         }
 732  
 
 733  
         /**
 734  
          * Returns true if parentRole is a parent-role of role.
 735  
          * precondition: both roles exist.
 736  
          * @param role the role to start search from..
 737  
          * @param parentRole the role to find.
 738  
          * @return {@code true} if found.
 739  
          * @since 0.3.0
 740  
          */
 741  
         public boolean isParentRole (String role, String parentRole) {
 742  
                 // checks
 743  8
                 forceRoleExists(role);
 744  8
                 forceRoleExists(parentRole);
 745  
 
 746  
                 // search
 747  8
                 return this.superroles.containsAdjacentRole(role, parentRole);
 748  
         }
 749  
 
 750  
         /**
 751  
          * Set new children roles of a role.
 752  
          * precondition: all roles exist.
 753  
          * @param role role to set children for.
 754  
          * @param childrenRoles children roles to set
 755  
          * @since 0.3.0
 756  
          */
 757  
         public void setChildRoles (String role, Set<String> childrenRoles) {
 758  
 
 759  
                 // checks
 760  1
                 forceRoleExists(role);
 761  1
                 forceRolesExist(childrenRoles);
 762  
 
 763  
                 // convert to roleset
 764  1
                 RoleSet childrenRolesSet = new RoleSet(childrenRoles);
 765  
 
 766  
                 // calculate difference
 767  1
                 RoleSet addChildren = new RoleSet();
 768  1
                 for (String childrenRole : childrenRolesSet.getRoles()) {
 769  2
                         if (!this.subroles.containsAdjacentRole(role, childrenRole)) {
 770  2
                                 addChildren.addRole(childrenRole);
 771  
                         }
 772  
                 }
 773  
 
 774  
                 // calculate difference
 775  1
                 RoleSet removeChildren = new RoleSet();
 776  1
                 for (String childrenRole : this.subroles.getAdjacentRoles(role).getRoles()) {
 777  0
                         if (!childrenRolesSet.containsRole(childrenRole)) {
 778  0
                                 removeChildren.addRole(childrenRole);
 779  
                         }
 780  
                 }
 781  
 
 782  
                 // remove old edges
 783  1
                 for (String childrenRole : removeChildren.getRoles()) {
 784  0
                         removeEdge(role, childrenRole);
 785  
                 }
 786  
 
 787  
                 // add new edges
 788  1
                 for (String childrenRole : addChildren.getRoles()) {
 789  2
                         addEdge(role, childrenRole);
 790  
                 }
 791  
 
 792  
                 // fire property change
 793  1
                 fireRoleHierarchyEvent();
 794  1
         }
 795  
 
 796  
         /**
 797  
          * Set new parent roles of a role.
 798  
          * precondition: all roles exist.
 799  
          * @param role role to set parents for.
 800  
          * @param parentRoles parent roles to set
 801  
          * @since 0.3.0
 802  
          */
 803  
         public void setParentRoles (String role, Set<String> parentRoles) {
 804  
 
 805  
                 // checks
 806  1
                 forceRoleExists(role);
 807  1
                 forceRolesExist(parentRoles);
 808  
 
 809  
                 // convert to roleset
 810  1
                 RoleSet parentRolesSet = new RoleSet(parentRoles);
 811  
 
 812  
                 // calculate difference
 813  1
                 RoleSet addParents = new RoleSet();
 814  1
                 for (String parentRole : parentRolesSet.getRoles()) {
 815  2
                         if (!this.superroles.containsAdjacentRole(role, parentRole)) {
 816  2
                                 addParents.addRole(parentRole);
 817  
                         }
 818  
                 }
 819  
 
 820  
                 // calculate difference
 821  1
                 RoleSet removeParents = new RoleSet();
 822  1
                 for (String parentRole : this.superroles.getAdjacentRoles(role).getRoles()) {
 823  0
                         if (!parentRolesSet.containsRole(parentRole)) {
 824  0
                                 removeParents.addRole(parentRole);
 825  
                         }
 826  
                 }
 827  
 
 828  
                 // remove old edges
 829  1
                 for (String parentRole : removeParents.getRoles()) {
 830  0
                         removeEdge(parentRole, role);
 831  
                 }
 832  
 
 833  
                 // add new edges
 834  1
                 for (String parentRole : addParents.getRoles()) {
 835  2
                         addEdge(parentRole, role);
 836  
                 }
 837  
 
 838  
                 // fire property change
 839  1
                 fireRoleHierarchyEvent();
 840  1
         }
 841  
 
 842  
         /**
 843  
          * Convenience method:
 844  
          * add an edge between two roles.
 845  
          * Nothing is done if either role is null oder edge already exists.
 846  
          * @param parentRole parent role.
 847  
          * @param childRole child role
 848  
          * @return {@code true} if edge was added
 849  
          * @since 0.3.0
 850  
          */
 851  
         public boolean connectRoles (String parentRole, String childRole) {
 852  134
                 if (!roleExists(parentRole) ||
 853  
                         !roleExists(childRole) ||
 854  
                         edgeExists(parentRole, childRole)) {
 855  0
                         return false;
 856  
                 }
 857  134
                 addEdge(parentRole, childRole);
 858  132
                 fireRoleHierarchyEvent();
 859  132
                 return true;
 860  
         }
 861  
 
 862  
         /**
 863  
          * Convenience method:
 864  
          * remove an edge between two roles.
 865  
          * Nothing is done if either role is null oder edge not exists.
 866  
          * @param parentRole parent role.
 867  
          * @param childRole child role
 868  
          * @return {@code true} if edge was removed
 869  
          * @since 0.3.0
 870  
          */
 871  
         public boolean disconnectRoles (String parentRole, String childRole) {
 872  1
                 if (!roleExists(parentRole) ||
 873  
                         !roleExists(childRole) ||
 874  
                         !edgeExists(parentRole, childRole)) {
 875  0
                         return false;
 876  
                 }
 877  1
                 removeEdge(parentRole, childRole);
 878  1
                 fireRoleHierarchyEvent();
 879  1
                 return true;
 880  
         }
 881  
 
 882  
         /**
 883  
          * Adds a new role in this role hierarchy, if not yet exists.
 884  
          * precondition: role not yet exists.
 885  
          * @param role the new role to be added.
 886  
          * @since 0.3.0
 887  
          */
 888  
         public void addRole (String role) {
 889  597
                 forceRoleNotExists(role);
 890  
 
 891  595
                 this.roles.addRole(role);
 892  595
                 this.subroles.addRole(role);
 893  595
                 this.superroles.addRole(role);
 894  
 
 895  
                 // fire property change
 896  595
                 fireRoleHierarchyEvent();
 897  595
         }
 898  
 
 899  
         /**
 900  
          * Adds a new role in this role hierarchy.
 901  
          * precondition: role not yet exists, parentRole exists.
 902  
          * @param role the new role to be added.
 903  
          * @param parentRole the parent where the role is appended.
 904  
          * @since 0.3.0
 905  
          */
 906  
         public void addRole (String role, String parentRole) {
 907  20
                 forceRoleExists(parentRole);
 908  
 
 909  20
                 this.addRole(role);
 910  20
                 this.addEdge(parentRole, role);
 911  
 
 912  
                 // fire property change
 913  20
                 fireRoleHierarchyEvent();
 914  20
         }
 915  
 
 916  
         /**
 917  
          * Adds a new role in this role hierarchy.
 918  
          * precondition: role not yet exists, parentRoles exist.
 919  
          * @param role the new role to be added.
 920  
          * @param parentRoles the parents where the role is appended.
 921  
          * @since 0.3.0
 922  
          */
 923  
         public void addRole (String role, Set<String> parentRoles) {
 924  
                 // checks
 925  0
                 for (String parentRole : parentRoles) {
 926  0
                         forceRoleExists(parentRole);
 927  
                 }
 928  
 
 929  
                 // add
 930  0
                 this.addRole(role);
 931  0
                 for (String parentRole : parentRoles) {
 932  0
                         this.addEdge(parentRole, role);
 933  
                 }
 934  
 
 935  
                 // fire property change
 936  0
                 fireRoleHierarchyEvent();
 937  0
         }
 938  
 
 939  
         /**
 940  
          * Removes a role from role hierarchy.
 941  
          *          * precondition: role exists.
 942  
          * @param role the role to be removed.
 943  
          * @since 0.3.0
 944  
          */
 945  
         public void removeRole (String role) {
 946  
 
 947  
                 // checks
 948  3
                 forceRoleExists(role);
 949  
 
 950  
                 // remove role itself
 951  3
                 this.subroles.removeRole(role);
 952  3
                 this.superroles.removeRole(role);
 953  3
                 this.roles.removeRole(role);
 954  
 
 955  
                 // fire property change
 956  3
                 fireRoleHierarchyEvent();
 957  3
         }
 958  
 
 959  
         /**
 960  
          * @since 0.3.0
 961  
          */
 962  
         public boolean renameRole (String role, String newName) {
 963  
 
 964  
                 // checks
 965  0
                 forceRoleExists(role);
 966  0
                 if (roleExists(newName)) {
 967  0
                         return false;
 968  
                 }
 969  
 
 970  
                 // rename
 971  0
                 this.subroles.renameRole(role, newName);
 972  0
                 this.superroles.renameRole(role, newName);
 973  0
                 this.roles.renameRole(role, newName);
 974  
 
 975  
                 // fire property change
 976  0
                 fireRoleHierarchyEvent();
 977  
 
 978  0
                 return true;
 979  
         }
 980  
 
 981  
         // ---- BasicPartBean
 982  
 
 983  
         /**
 984  
          * @since 0.1.0
 985  
          */
 986  
         @Override
 987  
         public PartBean findBySerialNumber (SerialNumber partSerialNumber) {
 988  0
                 return super.findBySerialNumber(partSerialNumber);
 989  
         }
 990  
 
 991  
         // ---- BasicPart
 992  
 
 993  
         /**
 994  
          * @since 0.1.0
 995  
          */
 996  
         @Override
 997  
         public boolean isPartValid (PartProblemReporter reporter) {
 998  
                 boolean valid;
 999  
 
 1000  
                 // init
 1001  10
                 valid = true;
 1002  
 
 1003  
                 // check if tree is closed
 1004  10
                 if (!treeClosed()) {
 1005  0
                         valid = false;
 1006  0
                         reportProblem(reporter, ProblemMessage.roleTreeNotClosed);
 1007  
                 }
 1008  
 
 1009  
                 // check DAG using topological sort
 1010  10
                 if (!treeTopologicallySortable()) {
 1011  1
                         valid = false;
 1012  1
                         reportProblem(reporter, ProblemMessage.roleTreeCyclic);
 1013  
                 }
 1014  
 
 1015  
                 // return
 1016  10
                 return valid;
 1017  
         }
 1018  
 
 1019  
         /**
 1020  
          * @since 0.1.0
 1021  
          */
 1022  
         @Override
 1023  
         protected boolean comparablePart (BasicPart part) {
 1024  348
                 return part instanceof RoleHierarchyBean;
 1025  
         }
 1026  
 
 1027  
         /**
 1028  
          * @since 0.1.0
 1029  
          */
 1030  
         @Override
 1031  
         protected String getSimpleClassName () {
 1032  0
                 return RoleHierarchyBean.class.getSimpleName();
 1033  
         }
 1034  
 
 1035  
         /**
 1036  
          * @since 0.3.0
 1037  
          */
 1038  
         private void appendRolePartDetails (StringBuilder sb) {
 1039  0
                 sb.append("{");
 1040  0
                 boolean separate = false;
 1041  0
                 for (String role : getRoles()) {
 1042  0
                         if (separate) {
 1043  0
                                 sb.append(", ");
 1044  
                         } else {
 1045  0
                                 separate = true;
 1046  
                         }
 1047  0
                         sb.append("[").append(role).append("]");
 1048  
                 }
 1049  0
                 sb.append("}");
 1050  0
         }
 1051  
 
 1052  
         /**
 1053  
          * @since 0.1.0
 1054  
          */
 1055  
         @Override
 1056  
         protected void appendPartDetails (StringBuilder sb) {
 1057  0
                 appendRolePartDetails(sb);
 1058  0
         }
 1059  
 
 1060  
 }