/* * Copyright (C) 2021 Katherine Murphy. All rights reserved. Permission is * hereby granted to students registered for University of Washington * CSE 331 for use solely during Summer Quarter 2021 for purposes of * the course. No other use, copying, distribution, or modification * is permitted without prior written consent. Copyrights for * third-party components of this work must be honored. Instructors * interested in reusing these course materials should contact the * author. */ package todo.sec09; import java.util.*; /** * Genealogy is a family tree with a collection of family members and branches, * i.e. Genealogy g = {Members, Branches} where * Members is the set of family members who belong in the family tree and * Branches is the set of parent-child connections between the family members. * The members are uniquely identified by names, and * the branches are uniquely identified by parent and child members. */ public class Genealogy { private static final boolean DEBUG = false; // AF(this) = {Members, Branches} where // M = adjList.key and B = union of adjList.value // For each entry in adjList, entry.key is a family member in this Genealogy, // entry.value is the set of outgoing branches from entry.key. // Representation Invariant: // adjList != null && adjList.keySet doesn't contains null // && adjList.value doesn't contains null // for any branch b = (Parent, Child) in adjList.value // adjList contains Parent, Child as keys private final Map> adjList; /** * @spec.effects Constructs a new Genealogy, {}. */ public Genealogy() { adjList = new HashMap<>(); } /** * Adds a family member to this Genealogy. * @param name the Member to be added. * * @spec.requires name != null && there is no family member with label name in this.Members * @spec.modifies this * @spec.effects this = {Members_post, Branches} where Members_post = Members_pre + { name } */ public void addMember(String name) { checkRep(); if(name == null) { throw new IllegalArgumentException("name is required to not be null"); } if (!contains(name)) { adjList.put(name, new HashSet<>()); } checkRep(); } /** * Adds a Branch to this Genealogy. * @param parent the start Member of Branch * @param child the end Member of Branch * * @spec.requires parent, child != null && this.Members contains parent and child, * && this.Branches doesn't contain a branch b = (parent, child) * @spec.modifies this * @spec.effects a branch b = (parent, child) is added to this.Branches */ public void addBranch(String parent, String child) { checkRep(); if(parent == null || child == null) { throw new IllegalArgumentException("input is required to not be null"); } Branch b = new Branch(parent, child); if (this.contains(parent) && this.contains(child) && !this.adjList.get(parent).contains(b)) { this.adjList.get(parent).add(b); } checkRep(); } /** * Returns if this contains the specified Member * @param name the Member to be checked if it is in this Genealogy * @return true if contains the specified member, false otherwise. * * @spec.requires name != null */ public boolean contains(String name) { if(name == null) { throw new IllegalArgumentException("name is required to not be null"); } checkRep(); return adjList.containsKey(name); } /** * Returns all outgoing branches from the Member * @param name the Member to be checked in this Genealogy * @return a Set of all branches that are from member name and currently in the family tree. * If the member is not in the family tree, return null. If the member is not starting member * for any branches in the family tree, return an empty set. * * @spec.requires name != null */ public Set getOutgoingBranches(String name){ checkRep(); if(name == null) { throw new IllegalArgumentException("name is required to not be null"); } if(!contains(name)) { return null; } try { return Collections.unmodifiableSet(adjList.get(name)); } finally { checkRep(); } } /** * Returns all members in the genealogy * @return all members in the genealogy as an unmodifiable set. */ public Set getAllMembers(){ checkRep(); try { return Collections.unmodifiableSet(adjList.keySet()); } finally { checkRep(); } } /** * Throws an exception if the representation invariant is violated. */ private void checkRep() { assert(adjList != null); assert(!adjList.containsKey(null)); if (DEBUG) { assert(!adjList.values().contains(null)); for (Map.Entry> entry : adjList.entrySet()) { String start = entry.getKey(); assert(start != null): "a member is null"; for (Branch b : entry.getValue()) { assert(b != null) : "a branch is null"; assert(contains(b.child)) : "child of some branch doesn't exist in family tree"; } } } } /** * Branch is an immutable one-way line connecting two family members in a Genealogy. * Each Branch has a parent Member and a child Member: b = (parent, child) indicates `child` is * directly reachable from `parent`. Two branches with the same `parent` and `child` will be * considered equal. */ public static class Branch { /** * The member the Branch starts from. */ public final String parent; /** * The member the Branch ends at. */ public final String child; /** * @param parent the starting Member of this Branch * @param child the ending Member of this Branch * @spec.requires parent,child != null * @spec.effects constructs a new Branch with given parent and child */ public Branch(String parent, String child) { if (parent == null || child == null) { throw new IllegalArgumentException("input is required to not be null"); } this.parent = parent; this.child = child; } /** Standard equality operation. * @param o the object to be compared for equality * @return true if and only if 'o' is an instance of Edge * and 'this' and 'o' represent the same edge, that is they * have the same starting Member and ending Member. */ @Override public boolean equals(Object o) { if(!(o instanceof Genealogy.Branch)) { return false; } Genealogy.Branch b = (Genealogy.Branch) o; return parent.equals(b.parent) && child.equals(b.child); } /** Standard hashCode function. * @return an int that all objects equal to this will also return. */ @Override public int hashCode() { return parent.hashCode() + 17 * child.hashCode(); } } }