ca.sqlpower.object.SPResolverRegistry.java Source code

Java tutorial

Introduction

Here is the source code for ca.sqlpower.object.SPResolverRegistry.java

Source

/*
 * Copyright (c) 2010, SQL Power Group Inc.
 *
 * This file is part of SQL Power Library.
 *
 * SQL Power Library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * SQL Power Library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 */

package ca.sqlpower.object;

import java.beans.PropertyChangeEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;

import org.apache.commons.collections.map.MultiValueMap;
import org.apache.log4j.Logger;

/**
 * This class keeps track of {@link SPVariableResolver}s that can resolve
 * namespaces and register to {@link SPObject} root objects.
 */
public class SPResolverRegistry {

    private static final Logger logger = Logger.getLogger(SPResolverRegistry.class);

    /**
     * Maps a root SPObject to a list of resolvers.
     */
    private static Map<String, List<SPVariableResolver>> resolvers = Collections
            .synchronizedMap(new WeakHashMap<String, List<SPVariableResolver>>());
    private static Map<String, TreeListener> listeners = Collections
            .synchronizedMap(new WeakHashMap<String, TreeListener>());

    private SPResolverRegistry() {
        // Everything in this class is static.
        // No need to create instances of it.
    }

    /**
     * Returns the root {@link SPObject} given a descendant {@link SPObject}.
     * 
     * @param treeElement
     *            The descendant {@link SPObject}.
     * @return The root {@link SPObject}, or null if the descendant
     *         {@link SPObject} is null.
     */
    private static SPObject getRoot(SPObject treeElement) {
        if (treeElement == null) {
            return null;
        }

        while (true) {
            if (treeElement.getParent() == null) {
                return treeElement;
            } else {
                treeElement = treeElement.getParent();
            }
        }
    }

    /**
     * Initializes the root of an {@link SPObject} in the resolver and listener
     * {@link Map}s by creating a new key (if it does not exist) by UUID, and
     * adds a tree listener to that root {@link SPObject}.
     * 
     * @param treeElement
     *            The {@link SPObject} whose root object is to be initialized.
     * @return The root {@link SPObject}, or null if the passed in
     *         {@link SPObject} is null.
     */
    public static SPObject init(SPObject treeElement) {
        if (treeElement == null) {
            return null;
        }

        synchronized (resolvers) {
            SPObject root = getRoot(treeElement);
            // No need to init this tree twice.
            if (!resolvers.containsKey(root.getUUID())) {
                // Init this placeholder with an empty list
                resolvers.put(root.getUUID(), Collections.synchronizedList(new ArrayList<SPVariableResolver>()));
                listeners.put(root.getUUID(), new TreeListener());
                // Now listen to the hierarchy for UUID change
                root.addSPListener(listeners.get(root.getUUID()));
            }
            return root;
        }
    }

    /**
     * Registers an {@link SPVariableResolver} for the root of a given
     * {@link SPObject} if it has not already been registered.
     * 
     * @param treeMember
     *            The {@link SPObject} whose root object the resolver should be
     *            registered to.
     * @param resolver
     *            The {@link SPVariableResolver} to register.
     */
    public static void register(SPObject treeMember, SPVariableResolver resolver) {
        if (treeMember != null && resolver != null) {
            synchronized (resolvers) {
                SPObject root = init(treeMember);
                if (!resolvers.get(root.getUUID()).contains(resolver)) {
                    logger.debug("Registering resolver - Namespace:" + resolver.getNamespace() + " bound to node:"
                            + treeMember.getName());
                    resolvers.get(root.getUUID()).add(resolver);
                }
            }
        }
    }

    /**
     * Deregisters an {@link SPVariableResolver} from the root of a given
     * {@link SPObject}.
     * 
     * @param treeMember
     *            The {@link SPObject} whose root object the resolver should be
     *            deregistered from.
     * @param resolver
     *            The {@link SPVariableResolver} to deregister.
     */
    public static void deregister(SPObject treeMember, SPVariableResolver resolver) {
        if (treeMember != null && resolver != null) {
            synchronized (resolvers) {
                SPObject root = init(treeMember);
                logger.debug("Deregistering resolver - Namespace:" + resolver.getNamespace() + " bound to node:"
                        + treeMember.getName());
                resolvers.get(root.getUUID()).remove(resolver);
            }
        }
    }

    /**
     * Creates a {@link List} of registered {@link SPVariableResolver}s that
     * resolves a given namespace for the root of a given {@link SPObject}.
     * 
     * @param treeMember
     *            The {@link SPObject} whose root to get the resolvers from.
     * @param namespace
     *            The namespace that the resolvers should resolve.
     * @return The created {@link List}.
     */
    public static List<SPVariableResolver> getResolvers(SPObject treeMember, String namespace) {
        if (treeMember == null) {
            return Collections.emptyList();
        }

        synchronized (resolvers) {
            SPObject root = init(treeMember);
            if (root != null) {
                List<SPVariableResolver> registeredResolvers = resolvers.get(root.getUUID());
                List<SPVariableResolver> matches = new ArrayList<SPVariableResolver>();
                for (SPVariableResolver resolver : registeredResolvers) {
                    if (resolver.resolvesNamespace(namespace)) {
                        matches.add(resolver);
                    }
                }
                return matches;
            } else {
                return Collections.emptyList();
            }
        }
    }

    /**
     * Finds the first {@link SPVariableResolver} that resolves a given
     * namespace for the root a given {@link SPObject}.
     * 
     * @param treeMember
     *            The {@link SPObject} whose root to get the resolver from.
     * @param namespace
     *            The namespace that the resolver should resolve.
     * @return The first {@link SPVariableResolver} found.
     */
    public static SPVariableResolver getResolver(SPObject treeMember, String namespace) {
        if (treeMember == null) {
            return null;
        }

        synchronized (resolvers) {
            SPObject root = init(treeMember);
            for (SPVariableResolver resolver : resolvers.get(root.getUUID())) {
                if (resolver.resolvesNamespace(namespace)) {
                    return resolver;
                }
            }
            return null;
        }
    }

    /**
     * Creates a {@link MultiValueMap} of {@link SPVariableResolver} user
     * friendly names to their respective namespace.
     * 
     * @param treeMember
     *            The {@link SPObject} whose root to get the namespaces from.
     * @return The created {@link MultiValueMap}.
     */
    public static MultiValueMap getNamespaces(SPObject treeMember) {
        if (treeMember == null) {
            return new MultiValueMap();
        }

        synchronized (resolvers) {
            MultiValueMap results = new MultiValueMap();
            SPObject root = init(treeMember);
            for (SPVariableResolver resolver : resolvers.get(root.getUUID())) {
                results.put(resolver.getUserFriendlyName(), resolver.getNamespace());
            }
            return results;
        }
    }

    /**
     * This {@link SPListener} listens to {@link SPObject}s whose UUID has
     * changed and has {@link SPVariableResolver}s registered to that object. It
     * updates the {@link Map}s of {@link SPResolverRegistry#resolvers} and
     * {@link SPResolverRegistry#listeners} to this new UUID.
     */
    private static class TreeListener extends AbstractPoolingSPListener {
        protected void propertyChangeImpl(PropertyChangeEvent e) {
            synchronized (resolvers) {
                if (e.getPropertyName().equalsIgnoreCase("uuid") && resolvers.containsKey(e.getOldValue())) {
                    // This means that the root object has changed it's UUID.
                    // Update the maps accordingly.
                    resolvers.put((String) e.getNewValue(), resolvers.get(e.getOldValue()));
                    listeners.put((String) e.getNewValue(), listeners.get(e.getOldValue()));
                    resolvers.remove(e.getOldValue());
                    listeners.remove(e.getOldValue());
                }
            }
        }
    }
}