Add Javadoc for Groups and GroupsUpdate

Change-Id: I95d2e23957bbabd2d8a848f713bf13681b30b5f1
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/Groups.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/Groups.java
index 578e4d1..9f840ee 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/Groups.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/Groups.java
@@ -31,19 +31,56 @@
 import java.util.Optional;
 import java.util.stream.Stream;
 
+/**
+ * A database accessor for read calls related to groups.
+ *
+ * <p>All calls which read group related details from the database (either ReviewDb or NoteDb) are
+ * gathered here. Other classes should always use this class instead of accessing the database
+ * directly. There are a few exceptions though: schema classes, wrapper classes, and classes
+ * executed during init. The latter ones should use {@code GroupsOnInit} instead.
+ *
+ * <p>If not explicitly stated, all methods of this class refer to <em>internal</em> groups.
+ */
 @Singleton
 public class Groups {
 
+  /**
+   * Returns the {@code AccountGroup} for the specified UUID.
+   *
+   * @param db the {@code ReviewDb} instance to use for lookups
+   * @param groupUuid the UUID of the group
+   * @return the {@code AccountGroup} which has the specified UUID
+   * @throws OrmDuplicateKeyException if multiple groups are found for the specified UUID
+   * @throws OrmException if the group couldn't be retrieved from ReviewDb
+   * @throws NoSuchGroupException if a group with such a UUID doesn't exist
+   */
   public AccountGroup getExistingGroup(ReviewDb db, AccountGroup.UUID groupUuid)
       throws OrmException, NoSuchGroupException {
     Optional<AccountGroup> group = getGroup(db, groupUuid);
     return group.orElseThrow(() -> new NoSuchGroupException(groupUuid));
   }
 
+  /**
+   * Returns the {@code AccountGroup} for the specified ID if it exists.
+   *
+   * @param db the {@code ReviewDb} instance to use for lookups
+   * @param groupId the ID of the group
+   * @return the found {@code AccountGroup} if it exists, or else an empty {@code Optional}
+   * @throws OrmException if the group couldn't be retrieved from ReviewDb
+   */
   public Optional<AccountGroup> getGroup(ReviewDb db, AccountGroup.Id groupId) throws OrmException {
     return Optional.ofNullable(db.accountGroups().get(groupId));
   }
 
+  /**
+   * Returns the {@code AccountGroup} for the specified UUID if it exists.
+   *
+   * @param db the {@code ReviewDb} instance to use for lookups
+   * @param groupUuid the UUID of the group
+   * @return the found {@code AccountGroup} if it exists, or else an empty {@code Optional}
+   * @throws OrmDuplicateKeyException if multiple groups are found for the specified UUID
+   * @throws OrmException if the group couldn't be retrieved from ReviewDb
+   */
   public Optional<AccountGroup> getGroup(ReviewDb db, AccountGroup.UUID groupUuid)
       throws OrmException {
     List<AccountGroup> accountGroups = db.accountGroups().byUUID(groupUuid).toList();
@@ -56,6 +93,14 @@
     }
   }
 
+  /**
+   * Returns the {@code AccountGroup} for the specified name if it exists.
+   *
+   * @param db the {@code ReviewDb} instance to use for lookups
+   * @param groupName the name of the group
+   * @return the found {@code AccountGroup} if it exists, or else an empty {@code Optional}
+   * @throws OrmException if the group couldn't be retrieved from ReviewDb
+   */
   public Optional<AccountGroup> getGroup(ReviewDb db, AccountGroup.NameKey groupName)
       throws OrmException {
     AccountGroupName accountGroupName = db.accountGroupNames().get(groupName);
@@ -71,6 +116,18 @@
     return Streams.stream(db.accountGroups().all());
   }
 
+  /**
+   * Indicates whether the specified account is a member of the specified group.
+   *
+   * <p><strong>Note</strong>: This method doesn't check whether the account exists!
+   *
+   * @param db the {@code ReviewDb} instance to use for lookups
+   * @param groupUuid the UUID of the group
+   * @param accountId the ID of the account
+   * @return {@code true} if the account is a member of the group, or else {@code false}
+   * @throws OrmException if an error occurs while reading from ReviewDb
+   * @throws NoSuchGroupException if the specified group doesn't exist
+   */
   public boolean isMember(ReviewDb db, AccountGroup.UUID groupUuid, Account.Id accountId)
       throws OrmException, NoSuchGroupException {
     AccountGroup group = getExistingGroup(db, groupUuid);
@@ -78,6 +135,21 @@
     return db.accountGroupMembers().get(key) != null;
   }
 
+  /**
+   * Indicates whether the specified group is a subgroup of the specified parent group.
+   *
+   * <p>The parent group must be an internal group whereas the subgroup may either be an internal or
+   * an external group.
+   *
+   * <p><strong>Note</strong>: This method doesn't check whether the subgroup exists!
+   *
+   * @param db the {@code ReviewDb} instance to use for lookups
+   * @param parentGroupUuid the UUID of the parent group
+   * @param includedGroupUuid the UUID of the subgroup
+   * @return {@code true} if the group is a subgroup of the other group, or else {@code false}
+   * @throws OrmException if an error occurs while reading from ReviewDb
+   * @throws NoSuchGroupException if the specified parent group doesn't exist
+   */
   public boolean isIncluded(
       ReviewDb db, AccountGroup.UUID parentGroupUuid, AccountGroup.UUID includedGroupUuid)
       throws OrmException, NoSuchGroupException {
@@ -86,6 +158,17 @@
     return db.accountGroupById().get(key) != null;
   }
 
+  /**
+   * Returns the members (accounts) of a group.
+   *
+   * <p><strong>Note</strong>: This method doesn't check whether the accounts exist!
+   *
+   * @param db the {@code ReviewDb} instance to use for lookups
+   * @param groupUuid the UUID of the group
+   * @return a stream of the IDs of the members
+   * @throws OrmException if an error occurs while reading from ReviewDb
+   * @throws NoSuchGroupException if the specified group doesn't exist
+   */
   public Stream<Account.Id> getMembers(ReviewDb db, AccountGroup.UUID groupUuid)
       throws OrmException, NoSuchGroupException {
     AccountGroup group = getExistingGroup(db, groupUuid);
@@ -94,6 +177,20 @@
     return Streams.stream(accountGroupMembers).map(AccountGroupMember::getAccountId);
   }
 
+  /**
+   * Returns the subgroups of a group.
+   *
+   * <p>This parent group must be an internal group whereas the subgroups can either be internal or
+   * external groups.
+   *
+   * <p><strong>Note</strong>: This method doesn't check whether the subgroups exist!
+   *
+   * @param db the {@code ReviewDb} instance to use for lookups
+   * @param groupUuid the UUID of the parent group
+   * @return a stream of the UUIDs of the subgroups
+   * @throws OrmException if an error occurs while reading from ReviewDb
+   * @throws NoSuchGroupException if the specified parent group doesn't exist
+   */
   public Stream<AccountGroup.UUID> getIncludes(ReviewDb db, AccountGroup.UUID groupUuid)
       throws OrmException, NoSuchGroupException {
     AccountGroup group = getExistingGroup(db, groupUuid);
@@ -101,6 +198,17 @@
     return Streams.stream(accountGroupByIds).map(AccountGroupById::getIncludeUUID).distinct();
   }
 
+  /**
+   * Returns the groups of which the specified account is a member.
+   *
+   * <p><strong>Note</strong>: This method returns an empty stream if the account doesn't exist.
+   * This method doesn't check whether the groups exist.
+   *
+   * @param db the {@code ReviewDb} instance to use for lookups
+   * @param accountId the ID of the account
+   * @return a stream of the IDs of the groups of which the account is a member
+   * @throws OrmException if an error occurs while reading from ReviewDb
+   */
   public Stream<AccountGroup.Id> getGroupsWithMember(ReviewDb db, Account.Id accountId)
       throws OrmException {
     ResultSet<AccountGroupMember> accountGroupMembers =
@@ -108,6 +216,20 @@
     return Streams.stream(accountGroupMembers).map(AccountGroupMember::getAccountGroupId);
   }
 
+  /**
+   * Returns the parent groups of the specified (sub)group.
+   *
+   * <p>The subgroup may either be an internal or an external group whereas the returned parent
+   * groups represent only internal groups.
+   *
+   * <p><strong>Note</strong>: This method returns an empty stream if the specified group doesn't
+   * exist. This method doesn't check whether the parent groups exist.
+   *
+   * @param db the {@code ReviewDb} instance to use for lookups
+   * @param includedGroupUuid the UUID of the subgroup
+   * @return a stream of the IDs of the parent groups
+   * @throws OrmException if an error occurs while reading from ReviewDb
+   */
   public Stream<AccountGroup.Id> getParentGroups(ReviewDb db, AccountGroup.UUID includedGroupUuid)
       throws OrmException {
     ResultSet<AccountGroupById> accountGroupByIds =
@@ -115,6 +237,14 @@
     return Streams.stream(accountGroupByIds).map(AccountGroupById::getGroupId);
   }
 
+  /**
+   * Returns all known external groups. External groups are 'known' when they are specified as a
+   * subgroup of an internal group.
+   *
+   * @param db the {@code ReviewDb} instance to use for lookups
+   * @return a stream of the UUIDs of the known external groups
+   * @throws OrmException if an error occurs while reading from ReviewDb
+   */
   public Stream<AccountGroup.UUID> getExternalGroups(ReviewDb db) throws OrmException {
     return Streams.stream(db.accountGroupById().all())
         .map(AccountGroupById::getIncludeUUID)
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupsUpdate.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupsUpdate.java
index 0aea001..bbf4eca 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupsUpdate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupsUpdate.java
@@ -44,6 +44,16 @@
 import java.util.function.Consumer;
 import org.eclipse.jgit.lib.PersonIdent;
 
+/**
+ * A database accessor for write calls related to groups.
+ *
+ * <p>All calls which write group related details to the database (either ReviewDb or NoteDb) are
+ * gathered here. Other classes should always use this class instead of accessing the database
+ * directly. There are a few exceptions though: schema classes, wrapper classes, and classes
+ * executed during init. The latter ones should use {@code GroupsOnInit} instead.
+ *
+ * <p>If not explicitly stated, all methods of this class refer to <em>internal</em> groups.
+ */
 public class GroupsUpdate {
   public interface Factory {
     GroupsUpdate create(@Nullable IdentifiedUser currentUser);
@@ -109,10 +119,24 @@
     return user.newCommitterIdent(ident.getWhen(), ident.getTimeZone());
   }
 
+  /**
+   * Adds/Creates the specified group.
+   *
+   * @param db the {@code ReviewDb} instance to update
+   * @param group the group to add
+   * @throws OrmException if an error occurs while reading/writing from/to ReviewDb
+   */
   public void addGroup(ReviewDb db, AccountGroup group) throws OrmException {
     addNewGroup(db, group);
   }
 
+  /**
+   * Adds the specified group.
+   *
+   * @param db the {@code ReviewDb} instance to update
+   * @param group the group to add
+   * @throws OrmException if an error occurs while reading/writing from/to ReviewDb
+   */
   public static void addNewGroup(ReviewDb db, AccountGroup group) throws OrmException {
     AccountGroupName gn = new AccountGroupName(group);
     // first insert the group name to validate that the group name hasn't
@@ -121,6 +145,16 @@
     db.accountGroups().insert(ImmutableList.of(group));
   }
 
+  /**
+   * Updates the specified group.
+   *
+   * @param db the {@code ReviewDb} instance to update
+   * @param groupUuid the UUID of the group to update
+   * @param groupConsumer a {@code Consumer} which performs the desired updates on the group
+   * @throws OrmException if an error occurs while reading/writing from/to ReviewDb
+   * @throws IOException if the cache entry for the group couldn't be invalidated
+   * @throws NoSuchGroupException if the specified group doesn't exist
+   */
   public void updateGroup(
       ReviewDb db, AccountGroup.UUID groupUuid, Consumer<AccountGroup> groupConsumer)
       throws OrmException, IOException, NoSuchGroupException {
@@ -131,13 +165,24 @@
   @VisibleForTesting
   public AccountGroup updateGroupInDb(
       ReviewDb db, AccountGroup.UUID groupUuid, Consumer<AccountGroup> groupConsumer)
-      throws OrmException, IOException, NoSuchGroupException {
+      throws OrmException, NoSuchGroupException {
     AccountGroup group = groups.getExistingGroup(db, groupUuid);
     groupConsumer.accept(group);
     db.accountGroups().update(ImmutableList.of(group));
     return group;
   }
 
+  /**
+   * Renames the specified group.
+   *
+   * @param db the {@code ReviewDb} instance to update
+   * @param groupUuid the UUID of the group to rename
+   * @param newName the new name of the group
+   * @throws OrmException if an error occurs while reading/writing from/to ReviewDb
+   * @throws IOException if the cache entry for the group couldn't be invalidated
+   * @throws NoSuchGroupException if the specified group doesn't exist
+   * @throws NameAlreadyUsedException if another group has the name {@code newName}
+   */
   public void renameGroup(ReviewDb db, AccountGroup.UUID groupUuid, AccountGroup.NameKey newName)
       throws OrmException, IOException, NameAlreadyUsedException, NoSuchGroupException {
     AccountGroup group = groups.getExistingGroup(db, groupUuid);
@@ -175,11 +220,37 @@
             .start(0, TimeUnit.MILLISECONDS);
   }
 
+  /**
+   * Adds an account as member to a group. The account is only added as a new member if it isn't
+   * already a member of the group.
+   *
+   * <p><strong>Note</strong>: This method doesn't check whether the account exists!
+   *
+   * @param db the {@code ReviewDb} instance to update
+   * @param groupUuid the UUID of the group
+   * @param accountId the ID of the account to add
+   * @throws OrmException if an error occurs while reading/writing from/to ReviewDb
+   * @throws IOException if the cache entry of the new member couldn't be invalidated
+   * @throws NoSuchGroupException if the specified group doesn't exist
+   */
   public void addGroupMember(ReviewDb db, AccountGroup.UUID groupUuid, Account.Id accountId)
       throws OrmException, IOException, NoSuchGroupException {
     addGroupMembers(db, groupUuid, ImmutableSet.of(accountId));
   }
 
+  /**
+   * Adds several accounts as members to a group. Only accounts which currently aren't members of
+   * the group are added.
+   *
+   * <p><strong>Note</strong>: This method doesn't check whether the accounts exist!
+   *
+   * @param db the {@code ReviewDb} instance to update
+   * @param groupUuid the UUID of the group
+   * @param accountIds a set of IDs of accounts to add
+   * @throws OrmException if an error occurs while reading/writing from/to ReviewDb
+   * @throws IOException if the cache entry of one of the new members couldn't be invalidated
+   * @throws NoSuchGroupException if the specified group doesn't exist
+   */
   public void addGroupMembers(ReviewDb db, AccountGroup.UUID groupUuid, Set<Account.Id> accountIds)
       throws OrmException, IOException, NoSuchGroupException {
     AccountGroup group = groups.getExistingGroup(db, groupUuid);
@@ -206,6 +277,17 @@
     }
   }
 
+  /**
+   * Removes several members (accounts) from a group. Only accounts which currently are members of
+   * the group are removed.
+   *
+   * @param db the {@code ReviewDb} instance to update
+   * @param groupUuid the UUID of the group
+   * @param accountIds a set of IDs of accounts to remove
+   * @throws OrmException if an error occurs while reading/writing from/to ReviewDb
+   * @throws IOException if the cache entry of one of the removed members couldn't be invalidated
+   * @throws NoSuchGroupException if the specified group doesn't exist
+   */
   public void removeGroupMembers(
       ReviewDb db, AccountGroup.UUID groupUuid, Set<Account.Id> accountIds)
       throws OrmException, IOException, NoSuchGroupException {
@@ -233,6 +315,21 @@
     }
   }
 
+  /**
+   * Adds several groups as subgroups to a group. Only groups which currently aren't subgroups of
+   * the group are added.
+   *
+   * <p>The parent group must be an internal group whereas the subgroups can either be internal or
+   * external groups.
+   *
+   * <p><strong>Note</strong>: This method doesn't check whether the subgroups exist!
+   *
+   * @param db the {@code ReviewDb} instance to update
+   * @param parentGroupUuid the UUID of the parent group
+   * @param includedGroupUuids a set of IDs of the groups to add as subgroups
+   * @throws OrmException if an error occurs while reading/writing from/to ReviewDb
+   * @throws NoSuchGroupException if the specified parent group doesn't exist
+   */
   public void addIncludedGroups(
       ReviewDb db, AccountGroup.UUID parentGroupUuid, Set<AccountGroup.UUID> includedGroupUuids)
       throws OrmException, NoSuchGroupException {
@@ -261,6 +358,19 @@
     groupIncludeCache.evictSubgroupsOf(parentGroupUuid);
   }
 
+  /**
+   * Removes several subgroups from a parent group. Only groups which currently are subgroups of the
+   * group are removed.
+   *
+   * <p>The parent group must be an internal group whereas the subgroups can either be internal or
+   * external groups.
+   *
+   * @param db the {@code ReviewDb} instance to update
+   * @param parentGroupUuid the UUID of the parent group
+   * @param includedGroupUuids a set of IDs of the subgroups to remove from the parent group
+   * @throws OrmException if an error occurs while reading/writing from/to ReviewDb
+   * @throws NoSuchGroupException if the specified parent group doesn't exist
+   */
   public void deleteIncludedGroups(
       ReviewDb db, AccountGroup.UUID parentGroupUuid, Set<AccountGroup.UUID> includedGroupUuids)
       throws OrmException, NoSuchGroupException {