org.prebake.fs.GlobDispatcher.java Source code

Java tutorial

Introduction

Here is the source code for org.prebake.fs.GlobDispatcher.java

Source

// Copyright 2010, Mike Samuel
//
// 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 org.prebake.fs;

import org.prebake.core.ArtifactListener;
import org.prebake.core.Glob;
import org.prebake.core.MutableGlobSet;

import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.annotation.ParametersAreNonnullByDefault;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Supplier;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Sets;

/**
 * Dispatches changed paths to listeners based on unions of globs.
 *
 * @author Mike Samuel <mikesamuel@gmail.com>
 */
@ParametersAreNonnullByDefault
final class GlobDispatcher {
    private final Multimap<Glob, GlobUnion> globsContaining = Multimaps.synchronizedListMultimap(Multimaps
            .newListMultimap(Maps.<Glob, Collection<GlobUnion>>newHashMap(), new ArrayListSupplier<GlobUnion>()));
    private final Multimap<GlobUnion, ArtifactListener<GlobUnion>> listeners = Multimaps.synchronizedListMultimap(
            Multimaps.newListMultimap(Maps.<GlobUnion, Collection<ArtifactListener<GlobUnion>>>newHashMap(),
                    new ArrayListSupplier<ArtifactListener<GlobUnion>>()));
    private final MutableGlobSet gset = new MutableGlobSet();
    private final Logger logger;

    /** @param logger receives exceptions thrown by listeners. */
    GlobDispatcher(Logger logger) {
        this.logger = logger;
    }

    void dispatch(Iterable<Path> paths) {
        // Order of dispatch here is non-deterministic
        Set<GlobUnion> unions = Sets.newLinkedHashSet();
        for (Path p : paths) {
            for (Glob g : gset.matching(p)) {
                synchronized (globsContaining) {
                    unions.addAll(globsContaining.get(g));
                }
            }
        }
        for (GlobUnion union : unions) {
            synchronized (listeners) {
                for (ArtifactListener<GlobUnion> unionListener : listeners.get(union)) {
                    try {
                        unionListener.artifactChanged(union);
                    } catch (RuntimeException ex) {
                        logger.log(Level.SEVERE, "Internal error", ex);
                    }
                }
            }
        }
    }

    void watch(GlobUnion union, ArtifactListener<GlobUnion> listener) {
        synchronized (listeners) {
            if (!listeners.containsKey(union)) {
                for (Glob glob : union.globs) {
                    Collection<GlobUnion> unions = globsContaining.get(glob);
                    if (unions.isEmpty()) {
                        gset.add(glob);
                    }
                    unions.add(union);
                }
            }
            listeners.put(union, listener);
        }
    }

    void unwatch(GlobUnion union, ArtifactListener<GlobUnion> listener) {
        synchronized (listeners) {
            Collection<ArtifactListener<GlobUnion>> listenerList = listeners.get(union);
            listenerList.remove(listener);
            if (listenerList.isEmpty()) {
                for (Glob glob : union.globs) {
                    Collection<GlobUnion> unions = globsContaining.get(glob);
                    unions.remove(union);
                    if (unions.isEmpty()) {
                        gset.remove(glob);
                    }
                }
            }
        }
    }

    @VisibleForTesting
    String unittestBackdoorGlobKeys() {
        List<Glob> globKeys;
        synchronized (globsContaining) {
            globKeys = Lists.newArrayList(globsContaining.keySet());
        }
        Collections.sort(globKeys);
        return globKeys.toString();
    }

    private static class ArrayListSupplier<T> implements Supplier<List<T>> {
        public List<T> get() {
            return Lists.newArrayList();
        }
    }
}