
com.diffplug.common.base.TreeDef Maven / Gradle / Ivy
Show all versions of durian Show documentation
/*
* Copyright 2015 DiffPlug
*
* 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.diffplug.common.base;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* A function which defines a tree structure.
* @see TreeStream
* @see TreeQuery
* @see TreeComparison
* @see TreeIterable
* @see TreeNode
*/
@FunctionalInterface
public interface TreeDef {
/** Returns all the children of the given node. */
List childrenOf(T node);
/** Creates a new TreeDef which whose {@code childrenOf} method is filtered by the given predicate. */
default TreeDef filter(Predicate predicate) {
return TreeDef.of(node -> filteredList(childrenOf(node), predicate));
}
/** Creates a TreeDef which is implemented by the given function. */
public static TreeDef of(Function> childFunc) {
return new TreeDef() {
@Override
public List childrenOf(T node) {
return childFunc.apply(node);
}
};
}
/**
* A pair of functions which define a doubly-linked tree, where nodes know about both their parent and their children.
*
* It is critical that the {@code TreeDef.Parented} is consistent - if Vader claims that Luke
* and Leia are his children, then both Luke and Leia must say that Vader is their parent.
*
* If Luke or Leia don't agree that Vader is their father, then the algorithms that use
* this {@code TreeDef.Parented} are likely to fail in unexpected ways.
*/
public interface Parented extends TreeDef {
/** Returns the parent of the given node. */
T parentOf(T node);
/** Creates a new {@code TreeDef.Parented} whose {@code childrenOf} and {@code parentOf} methods are filtered by {@code predicate}. */
@Override
default Parented filter(Predicate predicate) {
return of(node -> filteredList(childrenOf(node), predicate), node -> {
if (predicate.test(node)) {
return parentOf(node);
} else {
return null;
}
});
}
/** Creates a new {@code TreeDef.Parented} which is implemented by the two given functions. */
public static TreeDef.Parented of(Function> childFunc, Function parentFunc) {
return new TreeDef.Parented() {
@Override
public List childrenOf(T node) {
return childFunc.apply(node);
}
@Override
public T parentOf(T node) {
return parentFunc.apply(node);
}
};
}
}
/** Returns a filtered version of the given list. */
static List filteredList(List unfiltered, Predicate filter) {
return unfiltered.stream().filter(filter).collect(Collectors.toList());
}
/** An instance of {@code TreeDef.Parented} for {@link File}. */
public static TreeDef.Parented forFile(Consumer errorPolicy) {
Errors.Handling errors = Errors.createHandling(errorPolicy);
return TreeDef.Parented.of(
file -> errors.> getWithDefault(() -> {
if (file.isDirectory()) {
return Arrays.asList(file.listFiles());
} else {
return Collections.emptyList();
}
}, Collections.emptyList()),
file -> errors. getWithDefault(() -> {
return file.getParentFile();
}, null));
}
/** An instance of {@code TreeDef.Parented} for {@link Path}. */
public static TreeDef.Parented forPath(Consumer errorPolicy) {
Errors.Handling errors = Errors.createHandling(errorPolicy);
return TreeDef.Parented.of(
path -> errors.> getWithDefault(() -> {
if (Files.isDirectory(path)) {
return Files.list(path).collect(Collectors.toList());
} else {
return Collections.emptyList();
}
}, Collections.emptyList()),
path -> errors. getWithDefault(() -> {
return path.getParent();
}, null));
}
}