text:
package org.gradle.execution.plan;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.gradle.api.Action;
import org.gradle.api.BuildCancelledException;
import org.gradle.api.CircularReferenceException;
import org.gradle.api.GradleException;
import org.gradle.api.NonNullApi;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.Transformer;
import org.gradle.api.UncheckedIOException;
import org.gradle.api.file.FileCollection;
import org.gradle.api.internal.GradleInternal;
import org.gradle.api.internal.TaskInternal;
import org.gradle.api.internal.project.ProjectInternal;
import org.gradle.api.internal.tasks.execution.DefaultTaskProperties;
import org.gradle.api.internal.tasks.execution.TaskProperties;
import org.gradle.api.internal.tasks.properties.PropertyWalker;
import org.gradle.api.specs.Spec;
import org.gradle.api.specs.Specs;
import org.gradle.internal.Pair;
import org.gradle.internal.file.PathToFileResolver;
import org.gradle.internal.graph.CachingDirectedGraphWalker;
import org.gradle.internal.graph.DirectedGraph;
import org.gradle.internal.graph.DirectedGraphRenderer;
import org.gradle.internal.graph.GraphNodeRenderer;
import org.gradle.internal.logging.text.StyledTextOutput;
import org.gradle.internal.resources.ResourceDeadlockException;
import org.gradle.internal.resources.ResourceLock;
import org.gradle.internal.resources.ResourceLockState;
import org.gradle.internal.service.ServiceRegistry;
import org.gradle.internal.work.WorkerLeaseRegistry;
import org.gradle.internal.work.WorkerLeaseService;
import org.gradle.util.CollectionUtils;
import org.gradle.util.Path;

import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.AbstractCollection;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * A reusable implementation of ExecutionPlan. The {@link #addEntryTasks(java.util.Collection)} and {@link #clear()} methods are NOT threadsafe, and callers must synchronize access to these methods.
 */
@NonNullApi
public class DefaultExecutionPlan implements ExecutionPlan {
    private final Set<Node> nodesInUnknownState = Sets.newLinkedHashSet();
    private final Set<TaskNode> entryTasks = new LinkedHashSet<TaskNode>();
    private final NodeMapping nodeMapping = new NodeMapping();
    private final List<Node> executionQueue = Lists.newLinkedList();
    private final Map<Project, ResourceLock> projectLocks = Maps.newHashMap();
    private final FailureCollector failureCollector = new FailureCollector();
    private final TaskNodeFactory taskNodeFactory;
    private final TaskDependencyResolver dependencyResolver;
    private Spec<? super Task> filter = Specs.satisfyAll();

    private boolean continueOnFailure;

    private final Set<Node> runningNodes = Sets.newIdentityHashSet();
    private final Set<Node> filteredNodes = Sets.newIdentityHashSet();
    private final Map<Node, MutationInfo> mutations = Maps.newIdentityHashMap();
    private final Map<File, String> canonicalizedFileCache = Maps.newIdentityHashMap();
    private final Map<Pair<Node, Node>, Boolean> reachableCache = Maps.newHashMap();
    private final Set<Node> dependenciesCompleteCache = Sets.newHashSet();
    private final WorkerLeaseService workerLeaseService;
    private final GradleInternal gradle;

    private boolean buildCancelled;

    public DefaultExecutionPlan(WorkerLeaseService workerLeaseService, GradleInternal gradle, TaskNodeFactory taskNodeFactory, TaskDependencyResolver dependencyResolver) {
        this.workerLeaseService = workerLeaseService;
        this.gradle = gradle;
        this.taskNodeFactory = taskNodeFactory;
        this.dependencyResolver = dependencyResolver;
    }

    @Override
    public String getDisplayName() {
        Path path = gradle.findIdentityPath();
        if (path == null) {
            return "gradle";
        }
        return path.toString();
    }

    @VisibleForTesting
    TaskNode getNode(Task task) {
        return nodeMapping.get(task);
    }

    public void addEntryTasks(Collection<? extends Task> tasks) {
        final Deque<Node> queue = new ArrayDeque<Node>();

        List<Task> sortedTasks = new ArrayList<Task>(tasks);
        Collections.sort(sortedTasks);
        for (Task task : sortedTasks) {
            TaskNode node = taskNodeFactory.getOrCreateNode(task);
            if (node.isMustNotRun()) {
                requireWithDependencies(node);
            } else if (filter.isSatisfiedBy(task)) {
                node.require();
            }
            entryTasks.add(node);
            queue.add(node);
        }

        final Set<Node> visiting = Sets.newHashSet();

        while (!queue.isEmpty()) {
            Node node = queue.getFirst();
            if (node.getDependenciesProcessed()) {
                // Have already visited this node - skip it
                queue.removeFirst();
                continue;
            }

            boolean filtered = !nodeSatisfiesTaskFilter(node);
            if (filtered) {
                // Task is not required - skip it
                queue.removeFirst();
                node.dependenciesProcessed();
                node.doNotRequire();
                filteredNodes.add(node);
                continue;
            }

            if (visiting.add(node)) {
                // Have not seen this node before - add its dependencies to the head of the queue and leave this
                // node in the queue
                // Make sure it has been configured
                node.prepareForExecution();
                node.resolveDependencies(dependencyResolver, new Action<Node>() {
                    @Override
                    public void execute(Node targetNode) {
                        if (!visiting.contains(targetNode)) {
                            queue.addFirst(targetNode);
                        }
                    }
                });
                if (node.isRequired()) {
                    for (Node successor : node.getDependencySuccessors()) {
                        if (nodeSatisfiesTaskFilter(successor)) {
                            successor.require();
                        }
                    }
                } else {
                    nodesInUnknownState.add(node);
                }
            } else {
                // Have visited this node's dependencies - add it to the graph
                queue.removeFirst();
                visiting.remove(node);
                node.dependenciesProcessed();
            }
        }
        resolveNodesInUnknownState();
    }

    private boolean nodeSatisfiesTaskFilter(Node successor) {
        if (successor instanceof LocalTaskNode) {
            return filter.isSatisfiedBy(((LocalTaskNode) successor).getTask());
        }
        return true;
    }

    private void resolveNodesInUnknownState() {
        List<Node> queue = Lists.newArrayList(nodesInUnknownState);
        Set<Node> visiting = Sets.newHashSet();

        while (!queue.isEmpty()) {
            Node node = queue.get(0);
            if (node.isInKnownState()) {
                queue.remove(0);
                continue;
            }

            if (visiting.add(node)) {
                for (Node hardPredecessor : node.getDependencyPredecessors()) {
                    if (!visiting.contains(hardPredecessor)) {
                        queue.add(0, hardPredecessor);
                    }
                }
            } else {
                queue.remove(0);
                visiting.remove(node);
                node.mustNotRun();
                for (Node predecessor : node.getDependencyPredecessors()) {
                    assert predecessor.isRequired() || predecessor.isMustNotRun();
                    if (predecessor.isRequired()) {
                        node.require();
                        break;
                    }
                }
            }
        }
    }

    private void requireWithDependencies(Node node) {
        if (node.isMustNotRun() && nodeSatisfiesTaskFilter(node)) {
            node.require();
            for (Node dependency : node.getDependencySuccessors()) {
                requireWithDependencies(dependency);
            }
        }
    }

    public void determineExecutionPlan() {
        List<NodeInVisitingSegment> nodeQueue = Lists.newArrayList(Iterables.transform(entryTasks, new Function<TaskNode, NodeInVisitingSegment>() {
            private int index;

            @Override
            @SuppressWarnings("NullableProblems")
            public NodeInVisitingSegment apply(TaskNode taskNode) {
                return new NodeInVisitingSegment(taskNode, index++);
            }
        }));
        int visitingSegmentCounter = nodeQueue.size();

        HashMultimap<Node, Integer> visitingNodes = HashMultimap.create();
        Deque<GraphEdge> walkedShouldRunAfterEdges = new ArrayDeque<GraphEdge>();
        Deque<Node> path = new ArrayDeque<Node>();
        Map<Node, Integer> planBeforeVisiting = Maps.newHashMap();

        while (!nodeQueue.isEmpty()) {
            NodeInVisitingSegment nodeInVisitingSegment = nodeQueue.get(0);
            int currentSegment = nodeInVisitingSegment.visitingSegment;
            Node node = nodeInVisitingSegment.node;

            if (node.isIncludeInGraph() || nodeMapping.contains(node)) {
                nodeQueue.remove(0);
                visitingNodes.remove(node, currentSegment);
                maybeRemoveProcessedShouldRunAfterEdge(walkedShouldRunAfterEdges, node);
                continue;
            }

            boolean alreadyVisited = visitingNodes.containsKey(node);
            visitingNodes.put(node, currentSegment);

            if (!alreadyVisited) {
                // Have not seen this node before - add its dependencies to the head of the queue and leave this
                // node in the queue
                recordEdgeIfArrivedViaShouldRunAfter(walkedShouldRunAfterEdges, path, node);
                removeShouldRunAfterSuccessorsIfTheyImposeACycle(visitingNodes, nodeInVisitingSegment);
                takePlanSnapshotIfCanBeRestoredToCurrentTask(planBeforeVisiting, node);

                for (Node successor : node.getAllSuccessorsInReverseOrder()) {
                    if (visitingNodes.containsEntry(successor, currentSegment)) {
                        if (!walkedShouldRunAfterEdges.isEmpty()) {
                            //remove the last walked should run after edge and restore state from before walking it
                            GraphEdge toBeRemoved = walkedShouldRunAfterEdges.pop();
                            // Should run after edges only exist between tasks, so this cast is safe
                            TaskNode sourceTask = (TaskNode) toBeRemoved.from;
                            TaskNode targetTask = (TaskNode) toBeRemoved.to;
                            sourceTask.removeShouldSuccessor(targetTask);
                            restorePath(path, toBeRemoved);
                            restoreQueue(nodeQueue, visitingNodes, toBeRemoved);
                            restoreExecutionPlan(planBeforeVisiting, toBeRemoved);
                            break;
                        } else {
                            onOrderingCycle(successor, node);
                        }
                    }
                    nodeQueue.add(0, new NodeInVisitingSegment(successor, currentSegment));
                }
                path.push(node);
            } else {
                // Have visited this node's dependencies - add it to the end of the plan
                nodeQueue.remove(0);
                maybeRemoveProcessedShouldRunAfterEdge(walkedShouldRunAfterEdges, node);
                visitingNodes.remove(node, currentSegment);
                path.pop();
                nodeMapping.add(node);

                MutationInfo mutations = getOrCreateMutationsOf(node);
                for (Node dependency : node.getDependencySuccessors()) {
                    getOrCreateMutationsOf(dependency).consumingNodes.add(node);
                    mutations.producingNodes.add(dependency);
                }

                if (node instanceof LocalTaskNode) {
                    LocalTaskNode taskNode = (LocalTaskNode) node;
                    TaskInternal task = taskNode.getTask();
                    Project project = task.getProject();
                    projectLocks.put(project, getOrCreateProjectLock(project));

                    // Add any finalizers to the queue
                    for (Node finalizer : taskNode.getFinalizers()) {
                        if (!visitingNodes.containsKey(finalizer)) {
                            nodeQueue.add(finalizerTaskPosition(finalizer, nodeQueue), new NodeInVisitingSegment(finalizer, visitingSegmentCounter++));
                        }
                    }
                }
            }
        }
        executionQueue.clear();
        Iterables.addAll(executionQueue, nodeMapping);
    }

    @Override
    public Set<Task> getDependencies(Task task) {
        TaskNode node = nodeMapping.get(task);
        ImmutableSet.Builder<Task> builder = ImmutableSet.builder();
        for (Node dependencyNode : node.getDependencySuccessors()) {
            dependencyNode.collectTaskInto(builder);
        }
        return builder.build();
    }

    private MutationInfo getOrCreateMutationsOf(Node node) {
        MutationInfo mutations = this.mutations.get(node);
        if (mutations == null) {
            mutations = new MutationInfo(node);
            this.mutations.put(node, mutations);
        }
        return mutations;
    }

    private void maybeRemoveProcessedShouldRunAfterEdge(Deque<GraphEdge> walkedShouldRunAfterEdges, Node node) {
        GraphEdge edge = walkedShouldRunAfterEdges.peek();
        if (edge != null && edge.to.equals(node)) {
            walkedShouldRunAfterEdges.pop();
        }
    }

    private void restoreExecutionPlan(Map<Node, Integer> planBeforeVisiting, GraphEdge toBeRemoved) {
        int count = planBeforeVisiting.get(toBeRemoved.from);
        nodeMapping.retainFirst(count);
    }

    private void restoreQueue(List<NodeInVisitingSegment> nodeQueue, HashMultimap<Node, Integer> visitingNodes, GraphEdge toBeRemoved) {
        NodeInVisitingSegment nextInQueue = null;
        while (nextInQueue == null || !toBeRemoved.from.equals(nextInQueue.node)) {
            nextInQueue = nodeQueue.get(0);
            visitingNodes.remove(nextInQueue.node, nextInQueue.visitingSegment);
            if (!toBeRemoved.from.equals(nextInQueue.node)) {
                nodeQueue.remove(0);
            }
        }
    }

    private void restorePath(Deque<Node> path, GraphEdge toBeRemoved) {
        Node removedFromPath = null;
        while (!toBeRemoved.from.equals(removedFromPath)) {
            removedFromPath = path.pop();
        }
    }

    private void removeShouldRunAfterSuccessorsIfTheyImposeACycle(final HashMultimap<Node, Integer> visitingNodes, final NodeInVisitingSegment nodeWithVisitingSegment) {
        Node node = nodeWithVisitingSegment.node;
        if (!(node instanceof TaskNode)) {
            return;
        }
        Iterables.removeIf(((TaskNode) node).getShouldSuccessors(), new Predicate<Node>() {
            @Override
            @SuppressWarnings("NullableProblems")
            public boolean apply(Node input) {
                return visitingNodes.containsEntry(input, nodeWithVisitingSegment.visitingSegment);
            }
        });
    }

    private void takePlanSnapshotIfCanBeRestoredToCurrentTask(Map<Node, Integer> planBeforeVisiting, Node node) {
        if (node instanceof TaskNode && !((TaskNode) node).getShouldSuccessors().isEmpty()) {
            planBeforeVisiting.put(node, nodeMapping.size());
        }
    }

    private void recordEdgeIfArrivedViaShouldRunAfter(Deque<GraphEdge> walkedShouldRunAfterEdges, Deque<Node> path, Node node) {
        if (!(node instanceof TaskNode)) {
            return;
        }
        Node previous = path.peek();
        if (previous instanceof TaskNode && ((TaskNode) previous).getShouldSuccessors().contains(node)) {
            walkedShouldRunAfterEdges.push(new GraphEdge(previous, node));
        }
    }

    /**
     * Given a finalizer task, determine where in the current node queue that it should be inserted.
     * The finalizer should be inserted after any of it's preceding tasks.
     */
    private int finalizerTaskPosition(Node finalizer, final List<NodeInVisitingSegment> nodeQueue) {
        if (nodeQueue.size() == 0) {
            return 0;
        }

        Set<Node> precedingTasks = getAllPrecedingNodes(finalizer);
        Set<Integer> precedingTaskIndices = CollectionUtils.collect(precedingTasks, new Transformer<Integer, Node>() {
            @Override
            public Integer transform(final Node dependsOnTask) {
                return Iterables.indexOf(nodeQueue, new Predicate<NodeInVisitingSegment>() {
                    @Override
                    @SuppressWarnings("NullableProblems")
                    public boolean apply(NodeInVisitingSegment nodeInVisitingSegment) {
                        return nodeInVisitingSegment.node.equals(dependsOnTask);
                    }
                });
            }
        });
        return Collections.max(precedingTaskIndices) + 1;
    }

    private Set<Node> getAllPrecedingNodes(Node finalizer) {
        Set<Node> precedingNodes = Sets.newHashSet();
        Deque<Node> candidateNodes = new ArrayDeque<Node>();

        // Consider every node that must run before the finalizer
        Iterables.addAll(candidateNodes, finalizer.getAllSuccessors());

        // For each candidate node, add it to the preceding nodes.
        while (!candidateNodes.isEmpty()) {
            Node precedingNode = candidateNodes.pop();
            if (precedingNodes.add(precedingNode) && precedingNode instanceof TaskNode) {
                // Any node that the preceding task must run after is also a preceding node.
                candidateNodes.addAll(((TaskNode) precedingNode).getMustSuccessors());
                candidateNodes.addAll(((TaskNode) precedingNode).getFinalizingSuccessors());
            }
        }

        return precedingNodes;
    }

    private void onOrderingCycle(Node successor, Node node) {
        CachingDirectedGraphWalker<Node, Void> graphWalker = new CachingDirectedGraphWalker<Node, Void>(new DirectedGraph<Node, Void>() {
            @Override
            public void getNodeValues(Node node, Collection<? super Void> values, Collection<? super Node> connectedNodes) {
                connectedNodes.addAll(node.getDependencySuccessors());
                if (node instanceof TaskNode) {
                    TaskNode taskNode = (TaskNode) node;
                    connectedNodes.addAll(taskNode.getMustSuccessors());
                    connectedNodes.addAll(taskNode.getFinalizingSuccessors());
                }
            }
        });
        graphWalker.add(entryTasks);

        List<Set<Node>> cycles = graphWalker.findCycles();
        if (cycles.isEmpty()) {
            // TODO: This isn't correct. This means that we've detected a cycle while determining the execution plan, but the graph walker did not find one.
            // https://github.com/gradle/gradle/issues/2293
            throw new GradleException("Misdetected cycle between " + node + " and " + successor + ". Help us by reporting this to https://github.com/gradle/gradle/issues/2293");
        }
        final List<Node> firstCycle = new ArrayList<Node>(cycles.get(0));
        Collections.sort(firstCycle);

        DirectedGraphRenderer<Node> graphRenderer = new DirectedGraphRenderer<Node>(new GraphNodeRenderer<Node>() {
            @Override
            public void renderTo(Node node, StyledTextOutput output) {
                output.withStyle(StyledTextOutput.Style.Identifier).text(node);
            }
        }, new DirectedGraph<Node, Object>() {
            @Override
            public void getNodeValues(Node node, Collection<? super Object> values, Collection<? super Node> connectedNodes) {
                for (Node dependency : firstCycle) {
                    if (node.hasHardSuccessor(dependency)) {
                        connectedNodes.add(dependency);
                    }
                }
            }
        });
        StringWriter writer = new StringWriter();
        graphRenderer.renderTo(firstCycle.get(0), writer);
        throw new CircularReferenceException(String.format("Circular dependency between the following tasks:%n%s", writer.toString()));
    }

    public void clear() {
        taskNodeFactory.clear();
        dependencyResolver.clear();
        entryTasks.clear();
        nodeMapping.clear();
        executionQueue.clear();
        projectLocks.clear();
        failureCollector.clearFailures();
        mutations.clear();
        canonicalizedFileCache.clear();
        reachableCache.clear();
        dependenciesCompleteCache.clear();
        runningNodes.clear();
    }

    @Override
    public Set<Task> getTasks() {
        return nodeMapping.getTasks();
    }

    @Override
    public Set<Task> getFilteredTasks() {
        ImmutableSet.Builder<Task> builder = ImmutableSet.builder();
        for (Node filteredNode : filteredNodes) {
            if (filteredNode instanceof LocalTaskNode) {
                builder.add(((LocalTaskNode) filteredNode).getTask());
            }
        }
        return builder.build();
    }

    public void useFilter(Spec<? super Task> filter) {
        this.filter = filter;
    }

    public void setContinueOnFailure(boolean continueOnFailre) {
        this.continueOnFailure = continueOnFailre;
    }

    @Override
    @Nullable
    public Node selectNext(WorkerLeaseRegistry.WorkerLease workerLease, ResourceLockState resourceLockState) {
        if (allProjectsLocked()) {
            return null;
        }

        Iterator<Node> iterator = executionQueue.iterator();
        while (iterator.hasNext()) {
            Node node = iterator.next();
            if (node.isReady() && allDependenciesComplete(node)) {
                MutationInfo mutations = getResolvedMutationInfo(node);

                // TODO: convert output file checks to a resource lock
                if (!tryLockProjectFor(node)
                    || !workerLease.tryLock()
                    || !canRunWithCurrentlyExecutedNodes(node, mutations)) {
                    resourceLockState.releaseLocks();
                    continue;
                }

                if (node.allDependenciesSuccessful()) {
                    recordNodeStarted(node);
                    node.startExecution();
                } else {
                    node.skipExecution();
                }
                iterator.remove();

                return node;
            }
        }
        return null;
    }

    private boolean tryLockProjectFor(Node node) {
        if (node instanceof LocalTaskNode) {
            return getProjectLock((LocalTaskNode) node).tryLock();
        } else {
            return true;
        }
    }

    private void unlockProjectFor(Node node) {
        if (node instanceof LocalTaskNode) {
            getProjectLock((LocalTaskNode) node).unlock();
        }
    }

    private ResourceLock getProjectLock(LocalTaskNode taskNode) {
        return projectLocks.get(taskNode.getTask().getProject());
    }

    private MutationInfo getResolvedMutationInfo(Node node) {
        MutationInfo mutations = this.mutations.get(node);
        if (!mutations.resolved) {
            resolveMutations(mutations, node);
        }
        return mutations;
    }

    private void resolveMutations(MutationInfo mutations, Node node) {
        if (node instanceof LocalTaskNode) {
            LocalTaskNode taskNode = (LocalTaskNode) node;
            TaskInternal task = taskNode.getTask();
            ProjectInternal project = (ProjectInternal) task.getProject();
            ServiceRegistry serviceRegistry = project.getServices();
            PathToFileResolver resolver = serviceRegistry.get(PathToFileResolver.class);
            PropertyWalker propertyWalker = serviceRegistry.get(PropertyWalker.class);
            TaskProperties taskProperties = DefaultTaskProperties.resolve(propertyWalker, resolver, task);
            mutations.outputPaths.addAll(getOutputPaths(canonicalizedFileCache, taskNode, taskProperties.getOutputFiles(), taskProperties.getLocalStateFiles()));
            mutations.destroyablePaths.addAll(getDestroyablePaths(canonicalizedFileCache, taskNode, taskProperties.getDestroyableFiles()));
            mutations.hasFileInputs = !taskProperties.getInputFileProperties().isEmpty();
            mutations.hasOutputs = taskProperties.hasDeclaredOutputs();
            mutations.hasLocalState = !taskProperties.getLocalStateFiles().isEmpty();
            mutations.resolved = true;

            if (!mutations.destroyablePaths.isEmpty()) {
                if (mutations.hasOutputs) {
                    throw new IllegalStateException("Task " + taskNode + " has both outputs and destroyables defined.  A task can define either outputs or destroyables, but not both.");
                }
                if (mutations.hasFileInputs) {
                    throw new IllegalStateException("Task " + taskNode + " has both inputs and destroyables defined.  A task can define either inputs or destroyables, but not both.");
                }
                if (mutations.hasLocalState) {
                    throw new IllegalStateException("Task " + taskNode + " has both local state and destroyables defined.  A task can define either local state or destroyables, but not both.");
                }
            }
        }
    }

    private boolean allDependenciesComplete(Node node) {
        if (dependenciesCompleteCache.contains(node)) {
            return true;
        }

        boolean dependenciesComplete = node.allDependenciesComplete();
        if (dependenciesComplete) {
            dependenciesCompleteCache.add(node);
        }

        return dependenciesComplete;
    }

    private boolean allProjectsLocked() {
        for (ResourceLock lock : projectLocks.values()) {
            if (!lock.isLocked()) {
                return false;
            }
        }
        return true;
    }

    private ResourceLock getOrCreateProjectLock(Project project) {
        Path buildPath = ((ProjectInternal) project).getMutationState().getOwner().getIdentityPath();
        Path projectPath = ((ProjectInternal) project).getIdentityPath();
        return workerLeaseService.getProjectLock(buildPath, projectPath);
    }

    private boolean canRunWithCurrentlyExecutedNodes(Node node, MutationInfo mutations) {
        Set<String> candidateNodeDestroyables = mutations.destroyablePaths;

        if (!runningNodes.isEmpty()) {
            Set<String> candidateNodeOutputs = mutations.outputPaths;
            Set<String> candidateMutations = !candidateNodeOutputs.isEmpty() ? candidateNodeOutputs : candidateNodeDestroyables;
            if (hasNodeWithOverlappingMutations(candidateMutations)) {
                return false;
            }
        }

        return !doesDestroyNotYetConsumedOutputOfAnotherNode(node, candidateNodeDestroyables);
    }

    private static ImmutableSet<String> canonicalizedPaths(final Map<File, String> cache, Iterable<File> files) {
        ImmutableSet.Builder<String> builder = ImmutableSet.builder();
        for (File file : files) {
            String path;
            try {
                path = cache.get(file);
                if (path == null) {
                    path = file.getCanonicalPath();
                    cache.put(file, path);
                }
                builder.add(path);
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
        }
        return builder.build();
    }

    private boolean hasNodeWithOverlappingMutations(Set<String> candidateMutationPaths) {
        if (!candidateMutationPaths.isEmpty()) {
            for (Node runningNode : runningNodes) {
                MutationInfo runningMutations = mutations.get(runningNode);
                Iterable<String> runningMutationPaths = Iterables.concat(runningMutations.outputPaths, runningMutations.destroyablePaths);
                if (hasOverlap(candidateMutationPaths, runningMutationPaths)) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean doesDestroyNotYetConsumedOutputOfAnotherNode(Node destroyer, Set<String> destroyablePaths) {
        if (!destroyablePaths.isEmpty()) {
            for (MutationInfo producingNode : mutations.values()) {
                if (!producingNode.node.isComplete()) {
                    // We don't care about producing nodes that haven't finished yet
                    continue;
                }
                if (producingNode.consumingNodes.isEmpty()) {
                    // We don't care about nodes whose output is not consumed by anyone anymore
                    continue;
                }
                if (!hasOverlap(destroyablePaths, producingNode.outputPaths)) {
                    // No overlap no cry
                    continue;
                }
                for (Node consumer : producingNode.consumingNodes) {
                    if (doesConsumerDependOnDestroyer(consumer, destroyer)) {
                        // If there's an explicit dependency from consuming node to destroyer,
                        // then we accept that as the will of the user
                        continue;
                    }
                    return true;
                }
            }
        }
        return false;
    }

    private boolean doesConsumerDependOnDestroyer(Node consumer, Node destroyer) {
        if (consumer == destroyer) {
            return true;
        }
        Pair<Node, Node> nodePair = Pair.of(consumer, destroyer);
        if (reachableCache.get(nodePair) != null) {
            return reachableCache.get(nodePair);
        }

        boolean reachable = false;
        for (Node dependency : consumer.getAllSuccessors()) {
            if (!dependency.isComplete()) {
                if (doesConsumerDependOnDestroyer(dependency, destroyer)) {
                    reachable = true;
                }
            }
        }

        reachableCache.put(nodePair, reachable);
        return reachable;
    }

    private static boolean hasOverlap(Iterable<String> paths1, Iterable<String> paths2) {
        for (String path1 : paths1) {
            for (String path2 : paths2) {
                String overLappedPath = getOverLappedPath(path1, path2);
                if (overLappedPath != null) {
                    return true;
                }
            }
        }

        return false;
    }

    private static Set<String> getOutputPaths(Map<File, String> canonicalizedFileCache, TaskNode task, FileCollection outputFiles, FileCollection localStateFiles) {
        try {
            return canonicalizedPaths(canonicalizedFileCache, Iterables.concat(outputFiles, localStateFiles));
        } catch (ResourceDeadlockException e) {
            throw new IllegalStateException(deadlockMessage(task, "an output or local state", "outputs"), e);
        }
    }

    private static Set<String> getDestroyablePaths(Map<File, String> canonicalizedFileCache, TaskNode task, FileCollection destroyableFiles) {
        try {
            return canonicalizedPaths(canonicalizedFileCache, destroyableFiles);
        } catch (ResourceDeadlockException e) {
            throw new IllegalStateException(deadlockMessage(task, "a destroyable", "destroyables"), e);
        }
    }

    private static String deadlockMessage(TaskNode task, String singular, String plural) {
        return String.format("A deadlock was detected while resolving the %s for task '%s'. This can be caused, for instance, by %s property causing dependency resolution.", plural, task, singular);
    }

    @Nullable
    private static String getOverLappedPath(String firstPath, String secondPath) {
        if (firstPath.equals(secondPath)) {
            return firstPath;
        }
        if (firstPath.length() == secondPath.length()) {
            return null;
        }

        String shorter;
        String longer;
        if (firstPath.length() > secondPath.length()) {
            shorter = secondPath;
            longer = firstPath;
        } else {
            shorter = firstPath;
            longer = secondPath;
        }

        boolean isOverlapping = longer.startsWith(shorter) && longer.charAt(shorter.length()) == File.separatorChar;
        if (isOverlapping) {
            return shorter;
        } else {
            return null;
        }
    }

    private void recordNodeStarted(Node node) {
        runningNodes.add(node);
    }

    private void recordNodeCompleted(Node node) {
        runningNodes.remove(node);
        MutationInfo mutations = this.mutations.get(node);
        for (Node producer : mutations.producingNodes) {
            MutationInfo producerMutations = this.mutations.get(producer);
            if (producerMutations.consumingNodes.remove(node) && canRemoveMutation(producerMutations)) {
                this.mutations.remove(producer);
            }
        }

        if (canRemoveMutation(mutations)) {
            this.mutations.remove(node);
        }
    }

    private static boolean canRemoveMutation(@Nullable MutationInfo mutations) {
        return mutations != null && mutations.node.isComplete() && mutations.consumingNodes.isEmpty();
    }

    @Override
    public void nodeComplete(Node node) {
        try {
            if (!node.isComplete()) {
                enforceFinalizers(node);
                if (node.isFailed()) {
                    handleFailure(node);
                }

                node.finishExecution();
                recordNodeCompleted(node);
            }
        } finally {
            unlockProjectFor(node);
        }
    }

    private static void enforceFinalizers(Node node) {
        if (!(node instanceof TaskNode)) {
            return;
        }
        for (Node finalizerNode : ((TaskNode) node).getFinalizers()) {
            if (finalizerNode.isRequired() || finalizerNode.isMustNotRun()) {
                enforceWithDependencies(finalizerNode, Sets.<Node>newHashSet());
            }
        }
    }

    private static void enforceWithDependencies(Node nodeInfo, Set<Node> enforcedNodes) {
        Deque<Node> candidateNodes = new ArrayDeque<Node>();
        candidateNodes.add(nodeInfo);

        while (!candidateNodes.isEmpty()) {
            Node node = candidateNodes.pop();
            if (!enforcedNodes.contains(node)) {
                enforcedNodes.add(node);

                candidateNodes.addAll(node.getDependencySuccessors());

                if (node.isMustNotRun() || node.isRequired()) {
                    node.enforceRun();
                }
            }
        }
    }

    @Override
    public void abortAllAndFail(Throwable t) {
        abortExecution(true);
        this.failureCollector.addFailure(t);
    }

    private void handleFailure(Node node) {
        Throwable executionFailure = node.getExecutionFailure();
        if (executionFailure != null) {
            // Always abort execution for an execution failure (as opposed to a node failure)
            abortExecution();
            this.failureCollector.addFailure(executionFailure);
            return;
        }

        // Failure
        try {
            if (!continueOnFailure) {
                node.rethrowNodeFailure();
            }
            this.failureCollector.addFailure(node.getNodeFailure());
        } catch (Exception e) {
            // If the failure handler rethrows exception, then execution of other nodes is aborted. (--continue will collect failures)
            abortExecution();
            this.failureCollector.addFailure(e);
        }
    }

    private boolean abortExecution() {
        return abortExecution(false);
    }

    @Override
    public void cancelExecution() {
        buildCancelled = abortExecution() || buildCancelled;
    }

    private boolean abortExecution(boolean abortAll) {
        boolean aborted = false;
        for (Node node : nodeMapping) {
            // Allow currently executing and enforced tasks to complete, but skip everything else.
            if (node.isRequired()) {
                node.skipExecution();
                aborted = true;
            }

            // If abortAll is set, also stop enforced tasks.
            if (abortAll && node.isReady()) {
                node.abortExecution();
                aborted = true;
            }
        }
        return aborted;
    }

    @Override
    public void collectFailures(Collection<? super Throwable> failures) {
        if (buildCancelled) {
            failures.add(new BuildCancelledException());
        }
        failures.addAll(failureCollector.getFailures());
    }

    @Override
    public boolean allNodesComplete() {
        for (Node node : nodeMapping) {
            if (!node.isComplete()) {
                return false;
            }
        }
        // TODO:lptr why don't we check runningNodes here like we do in hasNodesRemaining()?
        return true;
    }

    @Override
    public boolean hasNodesRemaining() {
        for (Node node : executionQueue) {
            if (!node.isComplete()) {
                return true;
            }
        }
        return !runningNodes.isEmpty();
    }

    @Override
    public int size() {
        return nodeMapping.nodes.size();
    }

    private static class GraphEdge {
        private final Node from;
        private final Node to;

        private GraphEdge(Node from, Node to) {
            this.from = from;
            this.to = to;
        }
    }

    private static class NodeInVisitingSegment {
        private final Node node;
        private final int visitingSegment;

        private NodeInVisitingSegment(Node node, int visitingSegment) {
            this.node = node;
            this.visitingSegment = visitingSegment;
        }
    }

    private static class MutationInfo {
        final Node node;
        final Set<Node> consumingNodes = Sets.newHashSet();
        final Set<Node> producingNodes = Sets.newHashSet();
        final Set<String> outputPaths = Sets.newHashSet();
        final Set<String> destroyablePaths = Sets.newHashSet();
        boolean hasFileInputs;
        boolean hasOutputs;
        boolean hasLocalState;
        boolean resolved;

        MutationInfo(Node node) {
            this.node = node;
        }
    }

    private static class NodeMapping extends AbstractCollection<Node> {
        private final Map<Task, LocalTaskNode> taskMapping = Maps.newLinkedHashMap();
        private final Set<Node> nodes = Sets.newLinkedHashSet();

        @Override
        public boolean contains(Object o) {
            return nodes.contains(o);
        }

        @Override
        public boolean add(Node node) {
            if (!nodes.add(node)) {
                return false;
            }
            if (node instanceof LocalTaskNode) {
                LocalTaskNode taskNode = (LocalTaskNode) node;
                taskMapping.put(taskNode.getTask(), taskNode);
            }
            return true;
        }

        public TaskNode get(Task task) {
            TaskNode taskNode = taskMapping.get(task);
            if (taskNode == null) {
                throw new IllegalStateException("Task is not part of the execution plan, no dependency information is available.");
            }
            return taskNode;
        }

        public Set<Task> getTasks() {
            return taskMapping.keySet();
        }

        @Override
        public Iterator<Node> iterator() {
            return nodes.iterator();
        }

        @Override
        public void clear() {
            nodes.clear();
            taskMapping.clear();
        }

        @Override
        public int size() {
            return nodes.size();
        }

        public void retainFirst(int count) {
            Iterator<Node> executionPlanIterator = nodes.iterator();
            for (int i = 0; i < count; i++) {
                executionPlanIterator.next();
            }
            while (executionPlanIterator.hasNext()) {
                Node removedNode = executionPlanIterator.next();
                executionPlanIterator.remove();
                if (removedNode instanceof LocalTaskNode) {
                    taskMapping.remove(((LocalTaskNode) removedNode).getTask());
                }
            }
        }
    }
}


text:
from django.core.exceptions import ValidationError
import re
import string

VARIABLE_ALLOWED_CHARS = string.ascii_lowercase + string.digits + '_'
VARIABLE_REGEX = '^[%s]*$' % (VARIABLE_ALLOWED_CHARS,)
VARIABLE_REGEX_COMPILED = re.compile(VARIABLE_REGEX)

VARIABLE_AND_DASH_ALLOWED_CHARS = VARIABLE_ALLOWED_CHARS + '-'
VARIABLE_AND_DASH_REGEX = '^[%s]*$' % (VARIABLE_AND_DASH_ALLOWED_CHARS,)
VARIABLE_AND_DASH_REGEX_COMPILED = re.compile(VARIABLE_AND_DASH_REGEX)

PYPI_PACKAGE_NAME_ALLOWED_CHARS = VARIABLE_ALLOWED_CHARS + '-' + '.'
PYPI_PACKAGE_NAME_REGEX = r'^[\.%s]*$' % (VARIABLE_AND_DASH_ALLOWED_CHARS,)
PYPI_PACKAGE_NAME_REGEX_COMPILED = re.compile(PYPI_PACKAGE_NAME_REGEX)

def variable_name_validator(value):
    """
    validates that the value is a valid variable name

    * lower case, a-z, 0-9, _
    * no spaces, no dashes or any other special characters
    """
    if not VARIABLE_REGEX_COMPILED.search(value):
        raise ValidationError('allowed characters: %s' % VARIABLE_ALLOWED_CHARS)

def variable_name_and_dash_validator(value):
    """
    validates the same as variable_name_validator, but may also include "-"
    """
    if not VARIABLE_AND_DASH_REGEX_COMPILED.search(value):
        raise ValidationError('allowed characters: %s' % VARIABLE_AND_DASH_ALLOWED_CHARS)

def pypi_package_name_validator(value):
    """
    validates the same as variable_name_validator, but may also include "-" and "."
    """
    if not PYPI_PACKAGE_NAME_REGEX_COMPILED.search(value):
        raise ValidationError('allowed characters: %s' % PYPI_PACKAGE_NAME_ALLOWED_CHARS)

redpajama_set_name: RedPajamaGithub


text:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>at.fhj.swd13</groupId>
	<artifactId>pse</artifactId>
	<version>1.0.0-SNAPSHOT</version>
	<packaging>war</packaging>

	<name>PSE Portal</name>

	<description>PSE Portal. Enterprise 2.0</description>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

		<!-- JBoss dependency versions -->
		<version.jboss.maven.plugin>7.4.Final</version.jboss.maven.plugin>

		<version.wildfly.bom>8.2.0.Final</version.wildfly.bom>

		<!-- other plugin versions -->
		<version.surefire.plugin>2.18.1</version.surefire.plugin>
		<version.war.plugin>2.6</version.war.plugin>

		<version.selenium>2.48.2</version.selenium>

		<!-- maven-plugins -->
		<version.surefire.plugin>2.19</version.surefire.plugin>
		<version.failsafe.plugin>2.19</version.failsafe.plugin>
		<version.wildfly.maven.plugin>1.0.2.Final</version.wildfly.maven.plugin>
		<!-- maven-compiler-plugin -->
		<maven.compiler.target>1.8</maven.compiler.target>
		<maven.compiler.source>1.8</maven.compiler.source>

		<sonar.scm.provider>git</sonar.scm.provider>

		<!-- jacoco properties -->
		<jacoco.version>0.7.4.201502262128</jacoco.version>

		<jacoco.outputDir>${project.build.directory}</jacoco.outputDir>

		<jacoco.out.ut.file>jacoco-ut.exec</jacoco.out.ut.file>
		<sonar.jacoco.reportPath>${jacoco.outputDir}/${jacoco.out.ut.file}</sonar.jacoco.reportPath>

		<jacoco.out.it.file>jacoco-it.exec</jacoco.out.it.file>
		<sonar.jacoco.itReportPath>${jacoco.outputDir}/${jacoco.out.it.file}</sonar.jacoco.itReportPath>
	</properties>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.wildfly.bom</groupId>
				<artifactId>jboss-javaee-7.0-with-tools</artifactId>
				<version>${version.wildfly.bom}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
			<dependency>
				<groupId>org.wildfly.bom</groupId>
				<artifactId>jboss-javaee-7.0-with-hibernate</artifactId>
				<version>${version.wildfly.bom}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
			<dependency>
				<groupId>org.wildfly.bom</groupId>
				<artifactId>jboss-javaee-7.0-with-resteasy</artifactId>
				<version>${version.wildfly.bom}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<dependencies>
		<dependency>
			<groupId>javax.enterprise</groupId>
			<artifactId>cdi-api</artifactId>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.jboss.spec.javax.annotation</groupId>
			<artifactId>jboss-annotations-api_1.2_spec</artifactId>
		</dependency>
		<dependency>
			<groupId>org.jboss.spec.javax.ejb</groupId>
			<artifactId>jboss-ejb-api_3.2_spec</artifactId>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.jboss.spec.javax.faces</groupId>
			<artifactId>jboss-jsf-api_2.2_spec</artifactId>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.jboss.spec.javax.el</groupId>
			<artifactId>jboss-el-api_2.2_spec</artifactId>
			<version>1.0.5.Final</version>
		</dependency>

		<dependency>
			<groupId>org.jboss.spec.javax.json</groupId>
			<artifactId>jboss-json-api_1.0_spec</artifactId>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.36</version>
		</dependency>
		<dependency>
			<groupId>org.mindrot</groupId>
			<artifactId>jbcrypt</artifactId>
			<version>0.3m</version>
		</dependency>

		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-validator</artifactId>
		</dependency>
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-lang3</artifactId>
			<version>3.4</version>
		</dependency>
		<dependency>
			<groupId>joda-time</groupId>
			<artifactId>joda-time</artifactId>
			<version>2.7</version>
		</dependency>
		<dependency>
			<groupId>org.jadira.usertype</groupId>
			<artifactId>usertype.core</artifactId>
			<version>3.2.0.GA</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
		</dependency>
		<dependency>
			<groupId>org.apache.logging.log4j</groupId>
			<artifactId>log4j-core</artifactId>
			<version>2.2</version>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.1.0</version>
		</dependency>
		<dependency>
			<groupId>javax.ws.rs</groupId>
			<artifactId>javax.ws.rs-api</artifactId>
			<version>2.0.1</version>
		</dependency>
		<dependency>
			<groupId>org.primefaces</groupId>
			<artifactId>primefaces</artifactId>
			<version>5.2</version>
		</dependency>
		<dependency>
			<groupId>org.jsoup</groupId>
			<artifactId>jsoup</artifactId>
			<version>1.8.3</version>
		</dependency>
		<dependency>
			<groupId>javax.mail</groupId>
			<artifactId>javax.mail-api</artifactId>
			<version>1.5.4</version>
		</dependency>
		<dependency>
			<groupId>com.sun.mail</groupId>
			<artifactId>javax.mail</artifactId>
			<version>1.5.4</version>
		</dependency>
		<dependency>
			<groupId>xml-apis</groupId>
			<artifactId>xml-apis</artifactId>
			<version>1.4.01</version>
		</dependency>

		<!-- test dependencies -->
		<dependency>
			<groupId>org.jboss</groupId>
			<artifactId>jboss-ejb-client</artifactId>
			<version>2.0.1.Final</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.jboss</groupId>
			<artifactId>jboss-remote-naming</artifactId>
			<version>2.0.1.Final</version>
			<scope>test</scope>
		</dependency>
		<!-- dependency> <groupId>org.jboss.logging</groupId> <artifactId>jboss-logging</artifactId>
			<version>3.1.4.GA</version> <scope>test</scope> </dependency -->
		<dependency>
			<groupId>org.jboss.marshalling</groupId>
			<artifactId>jboss-marshalling</artifactId>
			<version>1.4.9.Final</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.jboss.marshalling</groupId>
			<artifactId>jboss-marshalling-river</artifactId>
			<version>1.4.9.Final</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.jboss.remoting</groupId>
			<artifactId>jboss-remoting</artifactId>
			<version>4.0.6.Final</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.jboss.sasl</groupId>
			<artifactId>jboss-sasl</artifactId>
			<version>1.0.4.Final</version>
			<scope>test</scope>
		</dependency>
		<!-- dependency> <groupId>org.jboss.spec.javax.transaction</groupId> <artifactId>jboss-transaction-api_1.2_spec</artifactId>
			<version>1.0.0.Final</version> <scope>test</scope> </dependency> <dependency>
			<groupId>org.jboss.spec.javax.ejb</groupId> <artifactId>jboss-ejb-api_3.2_spec</artifactId>
			<version>1.0.0.Final</version> <scope>test</scope> </dependency -->
		<dependency>
			<groupId>org.jboss.xnio</groupId>
			<artifactId>xnio-api</artifactId>
			<version>3.3.0.Final</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.jboss.xnio</groupId>
			<artifactId>xnio-nio</artifactId>
			<version>3.3.0.Final</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>com.icegreen</groupId>
			<artifactId>greenmail</artifactId>
			<version>1.3</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.easymock</groupId>
			<artifactId>easymock</artifactId>
			<version>3.4</version>
		</dependency>
		<dependency>
			<groupId>org.seleniumhq.selenium</groupId>
			<artifactId>selenium-api</artifactId>
			<version>${version.selenium}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.seleniumhq.selenium</groupId>
			<artifactId>selenium-support</artifactId>
			<version>${version.selenium}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.seleniumhq.selenium</groupId>
			<artifactId>selenium-java</artifactId>
			<version>${version.selenium}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.seleniumhq.selenium</groupId>
			<artifactId>selenium-firefox-driver</artifactId>
			<version>${version.selenium}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.seleniumhq.selenium</groupId>
			<artifactId>selenium-remote-driver</artifactId>
			<version>${version.selenium}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpclient</artifactId>
			<version>4.5.1</version>
			<scope>test</scope>
		</dependency>

	</dependencies>

	<build>
		<finalName>${project.artifactId}</finalName>
		<plugins>
			<plugin>
				<artifactId>maven-war-plugin</artifactId>
				<version>${version.war.plugin}</version>
				<configuration>
					<failOnMissingWebXml>false</failOnMissingWebXml>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>build-helper-maven-plugin</artifactId>
				<version>1.9.1</version>
				<executions>
					<execution>
						<id>add-source</id>
						<phase>generate-sources</phase>
						<goals>
							<goal>add-test-source</goal>
						</goals>
						<configuration>
							<sources>
								<source>src/test/selenium</source>
							</sources>
						</configuration>
					</execution>
				</executions>
			</plugin>
			<plugin>
				<groupId>org.jacoco</groupId>
				<artifactId>jacoco-maven-plugin</artifactId>
				<version>0.7.4.201502262128</version>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-surefire-plugin</artifactId>
				<version>${version.surefire.plugin}</version>
				<!-- <configuration> -->
				<!-- <excludes> -->
				<!-- <exclude>Db*</exclude> -->
				<!-- </excludes> -->
				<!-- </configuration> -->
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-failsafe-plugin</artifactId>
				<version>${version.failsafe.plugin}</version>
				<executions>
					<execution>
						<goals>
							<goal>integration-test</goal>
							<goal>verify</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>

	<profiles>
		<profile>
			<id>coverage-per-test</id>
			<build>
				<plugins>
					<plugin>
						<groupId>org.apache.maven.plugins</groupId>
						<artifactId>maven-surefire-plugin</artifactId>
						<version>${version.surefire.plugin}</version>
						<configuration>
							<argLine>${jacoco.agent.ut.arg}</argLine>
							<properties>
								<property>
									<name>listener</name>
									<value>org.sonar.java.jacoco.JUnitListener</value>
								</property>
							</properties>
						</configuration>
					</plugin>
					<plugin>
						<groupId>org.apache.maven.plugins</groupId>
						<artifactId>maven-failsafe-plugin</artifactId>
						<version>${version.failsafe.plugin}</version>
						<executions>
							<execution>
								<goals>
									<goal>integration-test</goal>
									<goal>verify</goal>
								</goals>
							</execution>
						</executions>
						<configuration>
							<argLine>-Xmx1024m -XX:MaxPermSize=256m ${jacoco.agent.it.arg}</argLine>
							<properties>
								<property>
									<name>listener</name>
									<value>org.sonar.java.jacoco.JUnitListener</value>
								</property>
							</properties>
							<reportsDirectory>${project.build.directory}/surefire-reports</reportsDirectory>
						</configuration>
					</plugin>
					<plugin>
						<groupId>org.jacoco</groupId>
						<artifactId>jacoco-maven-plugin</artifactId>
						<version>${jacoco.version}</version>
						<executions>
							<!-- Prepares a variable, jacoco.agent.ut.arg, that contains the info
								to be passed to the JVM hosting the code being tested. -->
							<execution>
								<id>prepare-ut-agent</id>
								<phase>process-test-classes</phase>
								<goals>
									<goal>prepare-agent</goal>
								</goals>
								<configuration>
									<destFile>${sonar.jacoco.reportPath}</destFile>
									<propertyName>jacoco.agent.ut.arg</propertyName>
									<append>true</append>
								</configuration>
							</execution>
							<!-- Prepares a variable, jacoco.agent.it.arg, that contains the info
								to be passed to the JVM hosting the code being tested. -->
							<execution>
								<id>prepare-it-agent</id>
								<phase>pre-integration-test</phase>
								<goals>
									<goal>prepare-agent</goal>
								</goals>
								<configuration>
									<destFile>${sonar.jacoco.itReportPath}</destFile>
									<propertyName>jacoco.agent.it.arg</propertyName>
									<append>true</append>
								</configuration>
							</execution>
						</executions>
					</plugin>
				</plugins>
			</build>

			<dependencies>
				<dependency>
					<groupId>org.codehaus.sonar-plugins.java</groupId>
					<artifactId>sonar-jacoco-listeners</artifactId>
					<version>3.2</version>
					<scope>test</scope>
				</dependency>
			</dependencies>
		</profile>
		<profile>
			<id>ci</id>
			<build>
				<plugins>
					<plugin>
						<groupId>org.wildfly.plugins</groupId>
						<artifactId>wildfly-maven-plugin</artifactId>
						<version>${version.wildfly.maven.plugin}</version>
						<executions>

							<!-- Run wildfly and deploy application for integration tests. -->
							<execution>
								<id>wildfly-run</id>
								<phase>pre-integration-test</phase>
								<goals>
									<goal>deploy</goal>
								</goals>
							</execution>

							<!-- Integration test teardown. -->
							<execution>
								<id>wildfly-shutdown</id>
								<phase>post-integration-test</phase>
								<goals>
									<goal>undeploy</goal>
								</goals>
							</execution>
						</executions>
					</plugin>
				</plugins>

			</build>

		</profile>
	</profiles>
</project>


text:
Kubernetes
==========

.. toctree::
   :maxdepth: 1
   :hidden:

   Helm <kubernetes-helm.rst>
   Native <kubernetes-native.rst>

Kubernetes_ is a popular system for deploying distributed applications on clusters,
particularly in the cloud.  You can use Kubernetes to launch Dask workers in the
following two ways:

1.  **Helm**:
    You can launch a Dask scheduler, several workers, and an optional Jupyter Notebook server
    on a Kubernetes easily using Helm_

    .. code-block:: bash

       helm repo add dask https://helm.dask.org/    # add the Dask Helm chart repository
       helm repo update                             # get latest Helm charts
       helm install dask/dask                     # deploy standard Dask chart

    This is a good choice if you want to do the following:

    1.  Run a managed Dask cluster for a long period of time
    2.  Also deploy a Jupyter server from which to run code
    3.  Share the same Dask cluster between many automated services
    4.  Try out Dask for the first time on a cloud-based system
        like Amazon, Google, or Microsoft Azure
        (see also our :doc:`Cloud documentation <cloud>`)

    .. note::

      For more information, see :doc:`Dask and Helm documentation <kubernetes-helm>`.

2.  **Native**:
    You can quickly deploy Dask workers on Kubernetes
    from within a Python script or interactive session using Dask-Kubernetes_

    .. code-block:: python

       from dask_kubernetes import KubeCluster
       cluster = KubeCluster.from_yaml('worker-template.yaml')
       cluster.scale(20)  # add 20 workers
       cluster.adapt()    # or create and destroy workers dynamically based on workload

       from dask.distributed import Client
       client = Client(cluster)

    This is a good choice if you want to do the following:

    1.  Dynamically create a personal and ephemeral deployment for interactive use
    2.  Allow many individuals the ability to launch their own custom dask deployments,
        rather than depend on a centralized system
    3.  Quickly adapt Dask cluster size to the current workload

    .. note::

      For more information, see Dask-Kubernetes_ documentation.

You may also want to see the documentation on using
:doc:`Dask with Docker containers <docker>`
to help you manage your software environments on Kubernetes.

.. _Kubernetes: https://kubernetes.io/
.. _Dask-Kubernetes: https://kubernetes.dask.org/
.. _Helm: https://helm.sh/


text:
A browserify-compatible wrapper for Google's [libphonenumber](https://github.com/googlei18n/libphonenumber), a library to parse, format, store and validate international phone numbers.
Zero dependencies, always up-to-date.

## Status

[![npm version][npm-image]][npm-url]
[![build status][travis-image]][travis-url]

## Installation

Install the package via `npm`:

```sh
npm install google-libphonenumber --save
```

## Usage

Here's a simple example on how to format a US-based number in the international phone number format:

```js
// Require `PhoneNumberFormat`.
var PNF = require('google-libphonenumber').PhoneNumberFormat;

// Get an instance of `PhoneNumberUtil`.
var phoneUtil = require('google-libphonenumber').PhoneNumberUtil.getInstance();

// Parse number with country code.
var phoneNumber = phoneUtil.parse('202-456-1414', 'US');

// Print number in the international format.
console.log(phoneUtil.format(phoneNumber, PNF.INTERNATIONAL));
// => +1 202-456-1414
```

#### Using the "As You Type" Formatter

```js
// Require `AsYouTypeFormatter`.
var AsYouTypeFormatter = require('google-libphonenumber').AsYouTypeFormatter;
var formatter = new AsYouTypeFormatter('US');

console.log(formatter.inputDigit('6')); // => 6
console.log(formatter.inputDigit('5')); // => 65
console.log(formatter.inputDigit('0')); // => 650
console.log(formatter.inputDigit('2')); // => 650-2
console.log(formatter.inputDigit('5')); // => 650-25
console.log(formatter.inputDigit('3')); // => 650-253
console.log(formatter.inputDigit('2')); // => 650-2532
console.log(formatter.inputDigit('2')); // => (650) 253-22

formatter.clear();
```

## Notes

### Differences from other forks

* All classes available from `libphonenumber` are exported as-is. No magic methods.
* Always based on the latest `google-closure` library version available from Google with performance and bug fixes.
* Relies on a simplified and [well-documented update process](https://github.com/ruimarinho/google-libphonenumber/blob/master/bin/update.sh) to keep the underlying `libphonenumber` library always up-to-date.

## Tests

A small subset of tests guarantees that the main library functions are working as expected and are correctly exported. The actual heavy lifting is done by `libphonenumber`'s extensive test suite.

```sh
npm test
```

## Release

```sh
npm version [<newversion> | major | minor | patch] -m "Release %s"
```

## Acknowledgments

The exceptional work on `libphonenumber` was made possible by these [committers and contributors](https://github.com/googlei18n/libphonenumber/graphs/contributors).

## Licenses

This package is licensed under MIT. The bundled [libphonenumber](https://github.com/googlei18n/libphonenumber/blob/master/LICENSE) library is licensed under Apache 2.0.

[npm-image]: https://img.shields.io/npm/v/google-libphonenumber.svg?style=flat-square
[npm-url]: https://npmjs.org/package/google-libphonenumber
[travis-image]: https://img.shields.io/travis/ruimarinho/google-libphonenumber.svg?style=flat-square
[travis-url]: https://travis-ci.org/ruimarinho/google-libphonenumber


text:
2.0.0 / 2016-09-26
==================

* [[`df860eb96b`](https://github.com/TooTallNate/pac-resolver/commit/df860eb96b)] - **BREAKING** remove "regenerator", drops Node.js < v1 support (Nathan Rajlich)

1.3.0 / 2016-09-26
==================

* [[`58a3f9e62e`](https://github.com/TooTallNate/pac-resolver/commit/58a3f9e62e)] - Implemented `timeRange` function (Utsav Kesharwani) (#15)
* [[`a93a815f0d`](https://github.com/TooTallNate/pac-resolver/commit/a93a815f0d)] - Implemented `weekdayRange` function (Utsav Kesharwani) (#14)
* [[`8af3e689eb`](https://github.com/TooTallNate/pac-resolver/commit/8af3e689eb)] - **test**: fix typo in test names (Nathan Rajlich)
* [[`e0ddf63765`](https://github.com/TooTallNate/pac-resolver/commit/e0ddf63765)] - **travis**: test Node.js v0.8, v1, v2, v3, v4, v5, and v6 (Nathan Rajlich)
* [[`e3c28eedec`](https://github.com/TooTallNate/pac-resolver/commit/e3c28eedec)] - **myIpAddress**: fallback to querying local system for IP (tehdeadone) (#10)
* [[`40de3f9a22`](https://github.com/TooTallNate/pac-resolver/commit/40de3f9a22)] - **package**: require "degenerator" at least v1.0.2 (Nathan Rajlich)
* [[`29009b3c38`](https://github.com/TooTallNate/pac-resolver/commit/29009b3c38)] - **package**: specify any "mocha" v2 (Nathan Rajlich)

1.2.6 / 2015-02-21
==================

  * package: update "regenerator" to v0.8.3 (#9, @RichardLitt)

1.2.5 / 2015-02-20
==================

  * travis: test node v0.12, don't test v0.8, v0.11
  * update regenerator to newest version (#7, @RichardLitt)
  * fix spelling errors (#4, @RichardLitt)
  * README: use svg for Travis-CI badge

1.2.4 / 2014-11-22
==================

  * package: downgrade "co" back down to v3.0.6

1.2.3 / 2014-11-22
==================

  * package: update "co" to v3.1.0
  * package: update "degenerator" to v1.0.0
  * test: add test case for #3

1.2.2 / 2014-05-20
==================

  * package: update "regenerator" to v0.4.7
  * package: update "thunkify" to v2.1.1
  * package: use ~ for "netmask" version
  * package: update "co" to v3.0.6
  * .travis: don't test node v0.9.x

1.2.1 / 2014-04-04
==================

  * package: update outdated dependencies

1.2.0 / 2014-01-28
==================

  * README: document the `opts` options argument
  * README: document how to add custom functions to the sandbox
  * refactor: only add `yield` calls to the real async functions

1.1.0 / 2014-01-25
==================

  * index: make the `filename` configurable
  * index: make the `sandbox` configurable
  * index: add an `opts` argument
  * README++

1.0.0 / 2014-01-08
==================

  * index: don't default to "DIRECT" if a falsey value is returned

0.0.2 / 2013-12-29
==================

  * package: update "netmask" to v1.0.4
  * test: fix test names
  * myIpAddress: add comment about Google Public DNS
  * myIpAddress: use a slightly better host/port combo
  * test: fix test names
  * test: add "official docs Example #5" tests
  * test: add "official docs Example #1b tests
  * test: add "official docs Example #1 tests
  * add `timeRange()` skeleton function
  * add `dateRange()` skeleton function
  * add `weekdayRange()` skeleton function
  * index: add `isResolvable()` to the sandbox
  * implement `isResolvable()`

0.0.1 / 2013-12-09
==================

  * .gitignore: ignore local dev files
  * shExpMatch: implement basic "shell expression" functionality
  * package: remove "minimatch"
  * test: add initial `shExpMatch()` function tests
  * myIpAddress: refactor to use a different "technique"
  * myIpAddress: trying to debug failing Travis-CI...
  * test: add `myIpAddress()` test
  * isInNet: properly implement the `isInNet()` function
  * dnsResolve: switch to `dns.lookup()`
  * dnsDomainLevels: fix typo
  * test: add `dnsDomainLevels()` tests
  * test: add `dnsDomainIs()` tests
  * test: add `dnsResolve()` tests
  * dnsResolve: return a "string", not an Array
  * dnsResolve: use `dns.resolve4()`
  * test: add isPlainHostName() tests
  * implement `localHostOrDomainIs()`
  * package: remove --harmony-generators flag from `npm test`
  * dnsDomainIs: add examples to jsdocs
  * shExpMatch: fix comment
  * add .travis.yml file
  * implement `dnsDomainLevels()`
  * implement `myIpAddress()`
  * add README.md
  * add initial `isInNet()` implementation
  * isPlainHostName: fix comment
  * implement `dnsResolve()`
  * test: initial test cases
  * implement `isPlainHostName()`
  * index: expose `shExpMatch` to the sandbox
  * index: pass the `url` and `host` variables into the resolver
  * shExpMatch: turn into a thunk
  * index: cache the regenerator `wrapGenerator` function
  * dnsDomainIs: turn into a thunk
  * use `degenerator` to compile the PAC proxy file
  * package: update "description"
  * package: using "mocha" for `npm test`
  * index: default the return value to "DIRECT"
  * initial commit


text:
{-# OPTIONS -fglasgow-exts #-}

module LogicExample where

import Language.GroteTrap
import Data.Generics hiding (Prefix)

import Data.Set hiding (map)

-- Logic data structure.

data Logic
  = Var String
  | Or [Logic]
  | And [Logic]
  | Impl Logic Logic
  | Not Logic
  deriving (Show, Eq, Typeable, Data)

type LogicAlg a =
  ( String -> a
  , [a] -> a
  , [a] -> a
  , a -> a -> a
  , a -> a
  )

foldLogic :: LogicAlg a -> Logic -> a
foldLogic (f1, f2, f3, f4, f5) = f where
  f (Var  a1   ) = f1 a1
  f (Or   a1) = f2 (map f a1)
  f (And  a1) = f3 (map f a1)
  f (Impl a1 a2) = f4 (f a1) (f a2)
  f (Not  a1   ) = f5 (f a1)

-- Language definition.

logicLanguage :: Language Logic
logicLanguage = language
  { variable  = Just Var
  , operators =
      [ Unary    Not  Prefix  0 "!"
      , Assoc    And          1 "&&"
      , Assoc    Or           2 "||"
      , Binary   Impl InfixR  3 "->"
      ]
  }

-- Evaluation.

type Environment = Set String

evalLogic :: Environment -> Logic -> Bool
evalLogic env = foldLogic ((`member` env), or, and, (||) . not, not)

readLogic :: Environment -> String -> Bool
readLogic env = evalLogic env . readExpression logicLanguage

-- Examples

appie :: ParseTree
appie = readParseTree logicLanguage "piet && klaas && maartje -> supermarkt"

demo1 = appie
demo2 = unparse $ appie
demo9 = evaluate logicLanguage appie
demo3 = printTree $ appie
demo4 = fromError $ follow appie root
demo5 = fromError $ follow appie [0]
demo6 = range $ fromError $ follow appie [0]
demo7 = lshow logicLanguage (And [Var "p", Var "q"])


text:
package ansiwriter

import (
	"fmt"
	"io"
)

// Modifier is any function capable of generating an opening escape sequence.
type Modifier func() string

func escapeANSI(code int) string {
	return fmt.Sprintf("\033[%dm", code)
}

func Bold() string    { return escapeANSI(1) }
func Black() string   { return escapeANSI(30) }
func Red() string     { return escapeANSI(31) }
func Green() string   { return escapeANSI(32) }
func Yellow() string  { return escapeANSI(33) }
func Blue() string    { return escapeANSI(34) }
func Magenta() string { return escapeANSI(35) }
func Cyan() string    { return escapeANSI(36) }
func White() string   { return escapeANSI(37) }

type awr struct {
	io.Writer
	modifiers []Modifier
}

// New wraps a provided io.Writer in one or more escape sequences. It only makes
// sense to combine a single color with boldness since if two colors are
// specified then only the latter one will in fact be visible.
func New(w io.Writer, mods ...Modifier) io.Writer {
	return awr{w, mods}
}

func (a awr) Write(b []byte) (n int, err error) {
	for _, mod := range a.modifiers {
		a.Writer.Write([]byte(mod()))
	}
	n, err = a.Writer.Write(b)
	_, _ = a.Writer.Write([]byte("\033[0m"))
	return
}


text:
.. _kproplog(8):

kproplog
========

SYNOPSIS
--------

**kproplog** [**-h**] [**-e** *num*] [-v]
**kproplog** [-R]

DESCRIPTION
-----------

The kproplog command displays the contents of the KDC database update
log to standard output.  It can be used to keep track of incremental
updates to the principal database.  The update log file contains the
update log maintained by the :ref:`kadmind(8)` process on the master
KDC server and the :ref:`kpropd(8)` process on the slave KDC servers.
When updates occur, they are logged to this file.  Subsequently any
KDC slave configured for incremental updates will request the current
data from the master KDC and update their log file with any updates
returned.

The kproplog command requires read access to the update log file.  It
will display update entries only for the KDC it runs on.

If no options are specified, kproplog displays a summary of the update
log.  If invoked on the master, kproplog also displays all of the
update entries.  If invoked on a slave KDC server, kproplog displays
only a summary of the updates, which includes the serial number of the
last update received and the associated time stamp of the last update.

OPTIONS
-------

**-R**
    Reset the update log.  This forces full resynchronization.  If used
    on a slave then that slave will request a full resync.  If used on
    the master then all slaves will request full resyncs.

**-h**
    Display a summary of the update log.  This information includes
    the database version number, state of the database, the number of
    updates in the log, the time stamp of the first and last update,
    and the version number of the first and last update entry.

**-e** *num*
    Display the last *num* update entries in the log.  This is useful
    when debugging synchronization between KDC servers.

**-v**
    Display individual attributes per update.  An example of the
    output generated for one entry:

     ::

        Update Entry
           Update serial # : 4
           Update operation : Add
           Update principal : test@EXAMPLE.COM
           Update size : 424
           Update committed : True
           Update time stamp : Fri Feb 20 23:37:42 2004
           Attributes changed : 6
                 Principal
                 Key data
                 Password last changed
                 Modifying principal
                 Modification time
                 TL data

ENVIRONMENT
-----------

kproplog uses the following environment variables:

* **KRB5_KDC_PROFILE**

SEE ALSO
--------

:ref:`kpropd(8)`


text:
@import "./_variables.css";

body {
  font-family: var(--fontAvenir);
}

.container {
  padding: var(--ms0);
}

.container__navMain,
.container__header,
.container__main,
.container__footer {
  width: 100%;
  margin: 0 auto;

  @media (--breakpoint-not-small) {
    width: 28rem;
  }
}


text:
require 'spec_helper'

describe Coercer::String, '.to_date' do
  subject { object.to_date(string) }

  let(:object) { described_class.new }

  context 'with a valid date string' do
    let(:string) { 'July, 22th, 2011' }

    it { is_expected.to be_instance_of(Date) }

    its(:year)  { should == 2011 }
    its(:month) { should == 7    }
    its(:day)   { should == 22   }
  end

  context 'with an invalid date string' do
    let(:string) { 'non-date' }

    specify do
      expect { subject }.to raise_error(UnsupportedCoercion)
    end
  end
end


text:
package org.interledger.cryptoconditions.der;

import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;

/**
 * An output stream for writing DER encoded data.
 */
public class DerOutputStream extends FilterOutputStream {

  public DerOutputStream(OutputStream out) {
    super(out);
  }

  /**
   * Writes a DER encoded length indicator to the stream.
   *
   * @param length The length value to writeFulfillment to the stream.
   * @throws IOException IOException
   */
  public void writeLength(int length) throws IOException {
    if (length > 127) {
      int size = 1;
      int val = length;

      while ((val >>>= 8) != 0) {
        size++;
      }

      write((byte) (size | 0x80));

      for (int i = (size - 1) * 8; i >= 0; i -= 8) {
        write((byte) (length >> i));
      }
    } else {
      write((byte) length);
    }
  }

  /**
   * Writes an encoded DER value to the stream.
   *
   * @param tag The DER tag that should accompany the value.
   * @param bytes The value to writeFulfillment to the stream.
   * @throws IOException IOException
   */
  public void writeEncoded(int tag, byte[] bytes) throws IOException {
    write(tag);
    writeLength(bytes.length);
    write(bytes);
  }

  /**
   * Writes the value as a DER tagged object.
   *
   * @param tagNumber The tag number for the object.
   * @param object The value to writeFulfillment to the stream.
   * @throws IOException IOException
   */
  public void writeTaggedObject(int tagNumber, byte[] object) throws IOException {
    writeEncoded(DerTag.TAGGED.getTag() + tagNumber, object);
  }

  /**
   * Writes the value as a DER tagged, constructed object.
   *
   * @param tagNumber The tag number for the object.
   * @param object The value to writeFulfillment to the stream.
   * @throws IOException IOException
   */
  public void writeTaggedConstructedObject(int tagNumber, byte[] object) throws IOException {
    writeEncoded(DerTag.TAGGED.getTag() + DerTag.CONSTRUCTED.getTag() + tagNumber, object);
  }

}


text:
@interface NotificationReceiver : NSObject
{
    int *_noteCounter;
}

- (id)initWithCounter: (int *)counter;

@end

@implementation NotificationReceiver

- (id)initWithCounter: (int *)counter
{
    _noteCounter = counter;
    return self;
}

- (void)gotNote: (NSNotification *)note
{
    (*_noteCounter)++;
}

@end

@interface KVOTargetSuperclass : NSObject {} @end
@implementation KVOTargetSuperclass @end

@interface KVOTarget : KVOTargetSuperclass {} @end
@implementation KVOTarget

- (void)setKey: (id)newValue
{
}

- (id)key
{
    return nil;
}

- (void)observeValueForKeyPath: (NSString *)keyPath ofObject: (id)object change: (NSDictionary *)change context: (void *)context
{
}

@end

static void WithPool(void (^block)(void))
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    block();
    [pool release];
}

static int gFailureCount;

void Test(void (*func)(void), const char *name)
{
    WithPool(^{
        int failureCount = gFailureCount;
        NSLog(@"Testing %s", name);
        func();
        NSLog(@"%s: %s", name, failureCount == gFailureCount ? "SUCCESS" : "FAILED");
    });
}

BOOL TestException(void (^block)(void))
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    @try {
        block();
        return NO;
    }
    @catch (NSException * e) {
        return YES;
    } @finally {
      [pool drain];
    }
}

#define TEST(func) Test(func, #func)

#define TEST_EXCEPTION(block) do { \
  if(!TestException(block)) { \
    gFailureCount++; \
    NSLog(@"%s:%d: assertion failed: exception was not thrown", __func__, __LINE__); \
  } \
} while(0)

#define TEST_ASSERT(cond, ...) do { \
    if(!(cond)) { \
        gFailureCount++; \
        NSString *message = [NSString stringWithFormat: @"" __VA_ARGS__]; \
        NSLog(@"%s:%d: assertion failed: %s %@", __func__, __LINE__, #cond, message); \
    } \
} while(0)

static BOOL WaitForNil(id (^block)(void))
{
    NSDate *start = [NSDate date];
    __block BOOL found;
    do
    {
        WithPool(^{
            found = block() != nil;
        });
    } while(found && [[NSDate date] timeIntervalSinceDate:start] < 10);

    return !found;
}

static BOOL NilTarget(MAZeroingWeakRef *ref)
{
    return WaitForNil(^{ return [ref target]; });
}

static void TestBasic(void)
{
    NSObject *obj = [[NSObject alloc] init];
    MAZeroingWeakRef *ref = [[MAZeroingWeakRef alloc] initWithTarget: obj];
    WithPool(^{
        TEST_ASSERT([ref target]);
        [obj release];
    });
    TEST_ASSERT(NilTarget(ref), @"ref target still live after object destroyed: %@", ref);
    [ref release];
}

static void TestRefDestroyedFirst(void)
{
    NSObject *obj = [[NSObject alloc] init];
    MAZeroingWeakRef *ref = [[MAZeroingWeakRef alloc] initWithTarget: obj];
    WithPool(^{
        TEST_ASSERT([ref target]);
        [ref release];
    });
    [obj release];
}

static void TestDoubleRef(void)
{
    NSObject *obj = [[NSObject alloc] init];
    MAZeroingWeakRef *ref1 = [[MAZeroingWeakRef alloc] initWithTarget: obj];
    MAZeroingWeakRef *ref2 = [[MAZeroingWeakRef alloc] initWithTarget: obj];
    WithPool(^{
        TEST_ASSERT([ref1 target]);
        TEST_ASSERT([ref2 target]);
        [obj release];
    });
    TEST_ASSERT(NilTarget(ref1), @"ref target still live after object destroyed: %@", ref1);
    TEST_ASSERT(NilTarget(ref2), @"ref target still live after object destroyed: %@", ref2);
    [ref1 release];
    [ref2 release];
}

static void TestRefToRef(void)
{
    NSObject *obj = [[NSObject alloc] init];
    MAZeroingWeakRef *ref1 = [[MAZeroingWeakRef alloc] initWithTarget: obj];
    MAZeroingWeakRef *ref2 = [[MAZeroingWeakRef alloc] initWithTarget: ref1];
    WithPool(^{
        TEST_ASSERT([ref1 target]);
        TEST_ASSERT([ref2 target]);
        [obj release];
    });
    WithPool(^{
        TEST_ASSERT(NilTarget(ref1), @"ref target still live after object destroyed: %@", ref1);
        TEST_ASSERT([ref2 target], @"ref target dead even though target ref still alive: %@", ref2);
        [ref1 release];
    });
    TEST_ASSERT(NilTarget(ref2), @"ref target still live after object destroyed: %@", ref2);
    [ref2 release];
}

static void TestNSArrayTarget(void)
{
    if(![MAZeroingWeakRef canRefCoreFoundationObjects])
    {
        NSLog(@"MAZeroingWeakRef can't reference CF objects, not testing them");
        return;
    }

    NSMutableArray *array = [[NSMutableArray alloc] init];
    MAZeroingWeakRef *ref = [[MAZeroingWeakRef alloc] initWithTarget: array];
    WithPool(^{
        TEST_ASSERT([ref target]);
        [array release];
    });
    TEST_ASSERT(NilTarget(ref), @"ref target still live after object destroyed: %@", ref);
    [ref release];
}

static void TestNSStringTarget(void)
{
    if(![MAZeroingWeakRef canRefCoreFoundationObjects])
    {
        NSLog(@"MAZeroingWeakRef can't reference CF objects, not testing them");
        return;
    }

    NSString *str = [[NSMutableString alloc] initWithString: @"Test String"];
    MAZeroingWeakRef *ref = [[MAZeroingWeakRef alloc] initWithTarget: str];
    WithPool(^{
        TEST_ASSERT([ref target]);
        [str release];
    });
    [ref release];
}

#if __APPLE__
static void TestNSConstantTarget(void)
{
    if ([MAZeroingWeakRef canRefCoreFoundationObjects]) {
      NSLog(@"MAZeroingWeakRef can reference CF objects, not testing constant object");
      return;
    }

    NSString *str = @"Constant String";
    MAZeroingWeakRef *ref = [[MAZeroingWeakRef alloc] initWithTarget: str];
    WithPool(^{
      TEST_ASSERT([ref target]);
      [str release];
    });
    [ref release];

    TEST_EXCEPTION(^{
      NSString *str = [[NSMutableString alloc] initWithString: @"Not Constant String"];
      MAZeroingWeakRef *ref = [[MAZeroingWeakRef alloc] initWithTarget: str];
      [ref release];
    });
}
#endif

static void TestCleanup(void)
{
    __block BOOL cleanedUp = NO;
    NSObject *obj = [[NSObject alloc] init];
    MAZeroingWeakRef *ref = [[MAZeroingWeakRef alloc] initWithTarget: obj];
    [ref setCleanupBlock: ^(id target) { cleanedUp = YES; }];
    [obj release];
    TEST_ASSERT(cleanedUp);
    [ref release];
}

static void TestCFCleanup(void)
{
    if(![MAZeroingWeakRef canRefCoreFoundationObjects])
    {
        NSLog(@"MAZeroingWeakRef can't reference CF objects, not testing them");
        return;
    }

    __block volatile BOOL cleanedUp = NO;
    NSObject *obj = [[NSMutableString alloc] init];
    MAZeroingWeakRef *ref = [[MAZeroingWeakRef alloc] initWithTarget: obj];
    [ref setCleanupBlock: ^(id target) { cleanedUp = YES; }];
    [obj release];
    TEST_ASSERT(WaitForNil(^{ return (id)(intptr_t)!cleanedUp; })
);
    [ref release];
}

#if __APPLE__
static void TestNotification(void)
{
    int notificationCounter = 0;
    NotificationReceiver *receiver = [[NotificationReceiver alloc] initWithCounter: &notificationCounter];
    [[NSNotificationCenter defaultCenter] addWeakObserver: receiver selector: @selector(gotNote:) name: @"name" object: @"object"];
    [[NSNotificationCenter defaultCenter] postNotificationName: @"name" object: @"object"];
    TEST_ASSERT(notificationCounter == 1);
    [receiver release];
    [[NSNotificationCenter defaultCenter] postNotificationName: @"name" object: @"object"];
    TEST_ASSERT(notificationCounter == 1);
}
#endif

static void TestWeakArray(void)
{
    if(![MAZeroingWeakRef canRefCoreFoundationObjects])
    {
        NSLog(@"MAZeroingWeakRef can't reference CF objects, not testing them");
        return;
    }

    NSString *str1 = [[NSMutableString alloc] initWithString: @"Test String 1"];
    NSString *str2 = [[NSMutableString alloc] initWithString: @"Test String 2"];
    NSString *str3 = [[NSMutableString alloc] initWithString: @"Test String 3"];

    MAWeakArray *array = [[MAWeakArray alloc] init];
    [array addObject: str1];
    [array addObject: str2];
    [array addObject: str3];

    [str2 release];

    WithPool(^{
        TEST_ASSERT([array objectAtIndex: 0]);
        TEST_ASSERT(WaitForNil(^{ return [array objectAtIndex: 1]; }));
        TEST_ASSERT([array objectAtIndex: 2]);
    });

    [str1 release];
    [str3 release];

    TEST_ASSERT(WaitForNil(^{ return [array objectAtIndex: 0]; }));
    TEST_ASSERT(WaitForNil(^{ return [array objectAtIndex: 1]; }));
    TEST_ASSERT(WaitForNil(^{ return [array objectAtIndex: 2]; }));
    [array release];
}

static void TestWeakDictionary(void)
{
    if(![MAZeroingWeakRef canRefCoreFoundationObjects])
    {
        NSLog(@"MAZeroingWeakRef can't reference CF objects, not testing them");
        return;
    }

    NSString *str1 = [[NSMutableString alloc] initWithString: @"Test String 1"];
    NSString *str2 = [[NSMutableString alloc] initWithString: @"Test String 2"];
    NSString *str3 = [[NSMutableString alloc] initWithString: @"Test String 3"];

    MAWeakDictionary *dict = [[MAWeakDictionary alloc] init];
    [dict setObject: str1 forKey: @"str1"];
    [dict setObject: str2 forKey: @"str2"];
    [dict setObject: str3 forKey: @"str3"];

    [str2 release];

    WithPool(^{
        TEST_ASSERT([dict objectForKey: @"str1"]);
        TEST_ASSERT([dict objectForKey: @"str2"] == nil);
        TEST_ASSERT([dict objectForKey: @"str3"]);
    });

    [str1 release];
    [str3 release];

    TEST_ASSERT([dict objectForKey: @"str1"] == nil);
    TEST_ASSERT([dict objectForKey: @"str2"] == nil);
    TEST_ASSERT([dict objectForKey: @"str3"] == nil);
    [dict release];
}

static void TestWeakProxy(void)
{
    if(![MAZeroingWeakRef canRefCoreFoundationObjects])
    {
        NSLog(@"MAZeroingWeakRef can't reference CF objects, not testing them");
        return;
    }

    NSMutableString *str = [[NSMutableString alloc] init];
    NSMutableString *proxy = (NSMutableString *)[[MAZeroingWeakProxy alloc] initWithTarget: str];

    WithPool(^{
        TEST_ASSERT([proxy isEqual: @""]);
        [proxy appendString: @"Hello, world!"];
        TEST_ASSERT([proxy isEqual: @"Hello, world!"]);
        TEST_ASSERT([proxy length] == [@"Hello, world!" length]);
        [proxy deleteCharactersInRange: NSMakeRange(0, 7)];
        TEST_ASSERT([proxy isEqual: @"world!"]);
    });

    [str release];

    TEST_ASSERT([proxy length] == 0);
    TEST_ASSERT([(id)proxy zeroingProxyTarget] == nil);
    [proxy release];
}

static void TestCleanupBlockReleasingZWR(void)
{
    NSObject *obj = [[NSObject alloc] init];
    WithPool(^{
        __block MAZeroingWeakRef *ref1 = [[MAZeroingWeakRef alloc] initWithTarget: obj];
        __block MAZeroingWeakRef *ref2 = [[MAZeroingWeakRef alloc] initWithTarget: obj];
        __block MAZeroingWeakRef *ref3 = [[MAZeroingWeakRef alloc] initWithTarget: obj];

        id cleanupBlock = ^{
            [ref1 release];
            ref1 = nil;
            [ref2 release];
            ref2 = nil;
            [ref3 release];
            ref3 = nil;
        };

        [ref1 setCleanupBlock: cleanupBlock];
        [ref2 setCleanupBlock: cleanupBlock];
        [ref3 setCleanupBlock: cleanupBlock];
    });
    [obj release];
}

static void TestAccidentalResurrectionInCleanupBlock(void)
{
    NSObject *obj = [[NSObject alloc] init];
    WithPool(^{
        __block MAZeroingWeakRef *ref1 = [[MAZeroingWeakRef alloc] initWithTarget: obj];
        __block MAZeroingWeakRef *ref2 = [[MAZeroingWeakRef alloc] initWithTarget: obj];

        id cleanupBlock = ^{
            [ref1 target];
            [ref2 target];
        };

        [ref1 setCleanupBlock: cleanupBlock];
        [ref2 setCleanupBlock: cleanupBlock];

        [obj release];
        [ref1 release];
        [ref2 release];
    });
}

static void TestKVOTarget(void)
{
    KVOTarget *target = [[KVOTarget alloc] init];
    [target addObserver: target forKeyPath: @"key" options: 0 context: NULL];

    MAZeroingWeakRef *ref = [[MAZeroingWeakRef alloc] initWithTarget: target];
    [target setKey: @"value"];
    [ref release];
}

static void TestKVOMultiLevelTarget(void)
{
    KVOTarget *target = [[KVOTarget alloc] init];
    [target addObserver: target forKeyPath: @"key.whatever" options: 0 context: NULL];

    MAZeroingWeakRef *ref = [[MAZeroingWeakRef alloc] initWithTarget: target];
	[target removeObserver:target forKeyPath:@"key.whatever"];
    [ref release];
}

static void TestClassForCoder(void)
{
    NSObject *obj = [[NSObject alloc] init];
    TEST_ASSERT([obj classForCoder] == [NSObject class]);
    [[[MAZeroingWeakRef alloc] initWithTarget: obj] autorelease];
    TEST_ASSERT([obj classForCoder] == [NSObject class]);
}

static void TestKVOReleaseNoCrash(void)
{
	KVOTarget *target = [[KVOTarget alloc] init];

	MAZeroingWeakRef *ref = [[MAZeroingWeakRef alloc] initWithTarget: target];

	[target addObserver: target forKeyPath: @"key" options: 0 context: NULL];

	[target setKey: @"value"];

    // destroying target without removing the observer tosses a warning to the console
    // but it's necessary in order to test this failure mode
	[target release];
	[ref target];

	[ref release];
}

static void TestKVOReleaseCrash(void)
{
	KVOTarget *target = [[KVOTarget alloc] init];

	[target addObserver: target forKeyPath: @"key" options: 0 context: NULL];

	MAZeroingWeakRef *ref = [[MAZeroingWeakRef alloc] initWithTarget: target];

	[target setKey: @"value"];

	[target release];
	[ref target];

	[ref release];
}

static void TestNativeZWRFallback(void)
{
    NSPort *port = [NSPort port];
    [MAZeroingWeakRef refWithTarget: port];
}

static void TestKVORemovalCrash(void)
{
    NSObject *obj = [[NSObject alloc] init];
    NSObject *observer = [[NSObject alloc] init];
    [obj addObserver:observer forKeyPath:@"description" options:NSKeyValueObservingOptionNew context:nil];
    MAZeroingWeakRef *weakRef = [MAZeroingWeakRef refWithTarget:obj];
    [obj removeObserver:observer forKeyPath:@"description"];
    [obj release];
    NSLog(@"weakRef %@", weakRef.target);
    [observer release];
}

int main(int argc, const char * argv[])
{
    WithPool(^{
        TEST(TestBasic);
        TEST(TestRefDestroyedFirst);
        TEST(TestDoubleRef);
        TEST(TestRefToRef);
        TEST(TestNSArrayTarget);
        TEST(TestNSStringTarget);
#if __APPLE__
        TEST(TestNSConstantTarget);
#endif
        TEST(TestCleanup);
        TEST(TestCFCleanup);
#if __APPLE__
        TEST(TestNotification);
#endif
        TEST(TestWeakArray);
        TEST(TestWeakDictionary);
        TEST(TestWeakProxy);
        TEST(TestCleanupBlockReleasingZWR);
        TEST(TestAccidentalResurrectionInCleanupBlock);
        TEST(TestKVOTarget);
        TEST(TestKVOMultiLevelTarget);
        TEST(TestClassForCoder);
        TEST(TestKVOReleaseNoCrash);
        TEST(TestKVOReleaseCrash);
        TEST(TestNativeZWRFallback);
        TEST(TestKVORemovalCrash);

        NSString *message;
        if(gFailureCount)
            message = [NSString stringWithFormat: @"FAILED: %d total assertion failure%s", gFailureCount, gFailureCount > 1 ? "s" : ""];
        else
            message = @"SUCCESS";
        NSLog(@"Tests complete: %@", message);
    });
    return 0;
}