// Copyright 2010 Google Inc. All Rights Reseved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.testing.testify.risk.frontend.client.riskprovider.impl;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.testing.testify.risk.frontend.model.Checkin;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;
/**
* Node in a tree representing a directory hierarchy as it applies to code checkins.
*
* @author chrsmith@google.com (Chris Smith)
*/
public class CheckinDirectoryTreeNode {
/** The name of this directory, e.g. "alpha" */
private final String directoryName;
/** Child directories. */
private final HashMap<String, CheckinDirectoryTreeNode> childDirectories =
new HashMap<String, CheckinDirectoryTreeNode>();
/**
* All checkins which have touched this directory or one of its children.
*
* NOTE: As you descend down the tree this will be strictly additive. For deep trees with many
* checkins it might be more efficent to store a parent node and walk up the tree rebuilding the
* set of checkins on each node.
*/
private final Set<Checkin> checkins = new HashSet<Checkin>();
/**
* Creates a new root directory tree node.
*/
public CheckinDirectoryTreeNode() {
directoryName = "";
}
/**
* Creates a new directory tree node with the given parent and directory name.
*/
public CheckinDirectoryTreeNode(CheckinDirectoryTreeNode parent, String directoryName) {
this.directoryName = directoryName;
}
public String getDirectoryName() {
return directoryName;
}
public ImmutableMap<String, CheckinDirectoryTreeNode> getChildNodes() {
ImmutableMap<String, CheckinDirectoryTreeNode> immutableMap =
ImmutableMap.copyOf(childDirectories);
return immutableMap;
}
public ImmutableSet<Checkin> getCheckins() {
ImmutableSet<Checkin> immutableSet = ImmutableSet.copyOf(checkins);
return immutableSet;
}
/**
* Returns the list of all checkins that happen in the directory under this node. For example,
* if the checkinDirectory was "alpha/beta" then it would return all bugs under:
* this.getChildNodes().get("alpha").getChildNodes().get("beta").getCheckins()
*/
public ImmutableSet<Checkin> getCheckinsUnder(String checkinDirectory) {
if (checkinDirectory.trim().isEmpty()) {
return getCheckins();
}
LinkedList<String> directoryNames = getDirectoryAsLinkedList(checkinDirectory);
CheckinDirectoryTreeNode node = this;
while (!directoryNames.isEmpty()) {
String directoryName = directoryNames.remove();
if (node.childDirectories.containsKey(directoryName)) {
node = node.childDirectories.get(directoryName);
} else {
return ImmutableSet.of();
}
}
return node.getCheckins();
}
/**
* @return the input string represented as a linked list, split by '/' or '\' characters.
*/
private LinkedList<String> getDirectoryAsLinkedList(String checkinDirectory) {
// Normalize folder path separators.
if (checkinDirectory.indexOf('|') != -1) {
throw new IllegalArgumentException("The checkin directory contains an invalid character '|'");
}
checkinDirectory = checkinDirectory.replace('/', '|');
checkinDirectory = checkinDirectory.replace('\\', '|');
// Convert an array of directory names into a linked list for more efficent processing.
String[] directories = checkinDirectory.split("|");
LinkedList<String> list = Lists.newLinkedList();
for (String s : directories) {
list.add(s);
}
return list;
}
/**
* Adds the given checkin to the directory tree, building child nodes as necessary.
*/
public void addCheckin(String checkinDirectory, Checkin checkin) {
LinkedList<String> directoryNames = getDirectoryAsLinkedList(checkinDirectory);
addCheckin(directoryNames, checkin);
}
/**
* Adds a checkin to the tree node under the given path of directory names.
*
* @param pathToCheckin List of directories left in the path. E.g. directory "foo\bar\ram" will
* become "foo" -> "bar -> "ram"
* @param checkin the checkin associated with the tree. Note that it will be attached to each
* node up to the root. (Since the checkin is 'under' the root.)
*/
private void addCheckin(LinkedList<String> pathToCheckin, Checkin checkin) {
checkins.add(checkin);
if (!pathToCheckin.isEmpty()) {
// Get the head element and chop it from the list.
String nextDirectory = pathToCheckin.remove();
if (!childDirectories.containsKey(nextDirectory)) {
CheckinDirectoryTreeNode newChild = new CheckinDirectoryTreeNode(this, nextDirectory);
childDirectories.put(nextDirectory, newChild);
}
CheckinDirectoryTreeNode child = childDirectories.get(nextDirectory);
child.addCheckin(pathToCheckin, checkin);
}
}
}