InheritanceModel.java

/*
 *   Copyright (C) Christian Schulte <cs@schulte.it>, 2011-325
 *   All rights reserved.
 *
 *   Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the following conditions
 *   are met:
 *
 *     o Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *     o Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in
 *       the documentation and/or other materials provided with the
 *       distribution.
 *
 *   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 *   AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 *   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
 *   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 *   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *   $JOMC: InheritanceModel.java 5043 2015-05-27 07:03:39Z schulte $
 *
 */
package org.jomc.model;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;
import org.w3c.dom.Element;

/**
 * Inheritance model.
 *
 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
 * @version $JOMC: InheritanceModel.java 5043 2015-05-27 07:03:39Z schulte $
 * @since 1.2
 */
public class InheritanceModel
{

    /**
     * Inheritance model node.
     *
     * @param <T> The type of the model object of the node.
     *
     * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
     * @version $JOMC: InheritanceModel.java 5043 2015-05-27 07:03:39Z schulte $
     * @since 1.2
     */
    public static class Node<T>
    {

        /**
         * The implementation the node originates from.
         */
        private final Implementation implementation;

        /**
         * The specification the node originates from.
         */
        private final Specification specification;

        /**
         * The class declaration the node originates from.
         */
        private final Implementation classDeclaration;

        /**
         * The direct descendant node.
         */
        private final Node<Implementation> descendant;

        /**
         * The model object of the node.
         */
        private final T modelObject;

        /**
         * Flag indicating the node is the final node in an inheritance hierarchy.
         */
        private final boolean _final;

        /**
         * Flag indicating the node is intended to override an ancestor node.
         */
        private final boolean override;

        /**
         * The path to the node.
         */
        private final LinkedList<Node<Implementation>> path = new LinkedList<Node<Implementation>>();

        /**
         * The nodes overridden by the node.
         */
        private final Set<Node<T>> overriddenNodes = new HashSet<Node<T>>();

        /**
         * Creates a new {@code Node} instance.
         *
         * @param implementation The implementation the node originates from.
         * @param specification The specification the node originates from or {@code null}.
         * @param classDeclaration The class declaration the node originates from or {@code null}.
         * @param descendant The direct descendant node of the node or {@code null}.
         * @param modelObject The model object of the node.
         * @param finalNode {@code true}, if the node is the final node in an inheritance hierarchy; {@code false},
         * else.
         * @param overrideNode {@code true}, if the node is intended to override an ancestor node; {@code false}, else.
         */
        public Node( final Implementation implementation, final Specification specification,
                     final Implementation classDeclaration, final Node<Implementation> descendant, final T modelObject,
                     final boolean finalNode, final boolean overrideNode )
        {
            super();
            this.implementation = implementation;
            this.specification = specification;
            this.classDeclaration = classDeclaration;
            this.descendant = descendant;
            this.modelObject = modelObject;
            this._final = finalNode;
            this.override = overrideNode;
        }

        /**
         * Gets the implementation the node originates from.
         *
         * @return The implementation the node originates from.
         */
        public final Implementation getImplementation()
        {
            return this.implementation;
        }

        /**
         * Gets the specification the node originates from.
         *
         * @return The specification the node originates from or {@code null}, if the node does not originate from a
         * specification.
         */
        public final Specification getSpecification()
        {
            return this.specification;
        }

        /**
         * Gets the class declaration the node originates from.
         *
         * @return The class declaration the node originates from or {@code null}, if the node does not originate from a
         * class declaration.
         */
        public final Implementation getClassDeclaration()
        {
            return this.classDeclaration;
        }

        /**
         * Gets the direct descendant node of the node.
         *
         * @return The direct descendant node of the node or {@code null}.
         *
         * @see InheritanceModel#getSourceNodes(java.lang.String)
         */
        public final Node<Implementation> getDescendant()
        {
            return this.descendant;
        }

        /**
         * Gets the model object of the node.
         *
         * @return The model object of the node.
         */
        public final T getModelObject()
        {
            return this.modelObject;
        }

        /**
         * Gets a flag indicating the node is the final node in an inheritance hierarchy.
         *
         * @return {@code true}, if the node is the final node in an inheritance hierarchy; {@code false}, else.
         */
        public final boolean isFinal()
        {
            return this._final;
        }

        /**
         * Gets a flag indicating the node is intended to override an ancestor node.
         *
         * @return {@code true}, if the node is intended to override an ancestor; {@code false} else.
         */
        public final boolean isOverride()
        {
            return this.override;
        }

        /**
         * Gets a set of nodes overridden by the node.
         *
         * @return An unmodifiable set holding nodes overridden by the node.
         */
        public final Set<Node<T>> getOverriddenNodes()
        {
            return Collections.unmodifiableSet( this.overriddenNodes );
        }

        /**
         * Gets the path to the node.
         *
         * @return An unmodifiable list holding path elements.
         */
        public final List<Node<Implementation>> getPath()
        {
            return Collections.unmodifiableList( this.path );
        }

        /**
         * Gets a set of nodes overridden by the node.
         *
         * @return A modifiable set holding nodes overridden by the node.
         *
         * @see #getOverriddenNodes()
         */
        private Set<Node<T>> getModifiableOverriddenNodes()
        {
            return this.overriddenNodes;
        }

        /**
         * Gets the path to the node.
         *
         * @return A modifiable list holding path nodes of the node.
         *
         * @see #getPath()
         */
        private LinkedList<Node<Implementation>> getModifiablePath()
        {
            return this.path;
        }

    }

    /**
     * Enumeration of context states.
     */
    private enum ContextState
    {

        PREPARING,
        PREPARED

    }

    /**
     * The modules backing the model.
     */
    private final Modules modules;

    /**
     * {@code Dependency} nodes by context and dependency name.
     */
    private final Map<String, Map<String, Set<Node<Dependency>>>> dependencies = newMap();

    /**
     * {@code Dependency} nodes by context and implementation identifier.
     */
    private final Map<String, Map<String, Map<String, Set<Node<Dependency>>>>> effDependencies = newMap();

    /**
     * {@code Message} nodes by context and message name.
     */
    private final Map<String, Map<String, Set<Node<Message>>>> messages = newMap();

    /**
     * {@code Message} nodes by context and implementation identifier.
     */
    private final Map<String, Map<String, Map<String, Set<Node<Message>>>>> effMessages = newMap();

    /**
     * {@code Property} nodes by context and property name.
     */
    private final Map<String, Map<String, Set<Node<Property>>>> properties = newMap();

    /**
     * {@code Property} nodes by context and implementation identifier.
     */
    private final Map<String, Map<String, Map<String, Set<Node<Property>>>>> effProperties = newMap();

    /**
     * {@code SpecificationReference} nodes by context and specification identifier.
     */
    private final Map<String, Map<String, Set<Node<SpecificationReference>>>> specReferences = newMap();

    /**
     * {@code SpecificationReference} nodes by context and implementation identifier.
     */
    private final Map<String, Map<String, Map<String, Set<Node<SpecificationReference>>>>> effSpecReferences =
        newMap();

    /**
     * {@code ImplementationReference} nodes by context and implementation reference identifier.
     */
    private final Map<String, Map<String, Set<Node<ImplementationReference>>>> implReferences = newMap();

    /**
     * {@code ImplementationReference} nodes by context and implementation reference identifier.
     */
    private final Map<String, Set<Node<ImplementationReference>>> cyclicImplReferences = newMap();

    /**
     * {@code ImplementationReference} nodes by context and implementation identifier.
     */
    private final Map<String, Map<String, Map<String, Set<Node<ImplementationReference>>>>> effImplReferences =
        newMap();

    /**
     * {@code Element} nodes by context and qualified name.
     */
    private final Map<String, Map<QName, Set<Node<Element>>>> xmlElements = newMap();

    /**
     * {@code Element} nodes by context and implementation identifier.
     */
    private final Map<String, Map<String, Map<QName, Set<Node<Element>>>>> effXmlElements = newMap();

    /**
     * {@code JAXBElement} nodes by context and qualified name.
     */
    private final Map<String, Map<QName, Set<Node<JAXBElement<?>>>>> jaxbElements = newMap();

    /**
     * {@code JAXBElement} nodes by context and implementation identifier.
     */
    private final Map<String, Map<String, Map<QName, Set<Node<JAXBElement<?>>>>>> effJaxbElements =
        newMap();

    /**
     * {@code Implementation} nodes by context and implementation identifier.
     */
    private final Map<String, Map<String, Node<Implementation>>> implementations = newMap();

    /**
     * Source nodes of a hierarchy by context and implementation identifier.
     */
    private final Map<String, Map<String, Node<Implementation>>> sourceNodes = newMap();

    /**
     * Context states by context identifier.
     */
    private final Map<String, ContextState> contextStates = newMap();

    /**
     * Creates a new {@code InheritanceModel} instance.
     *
     * @param modules The modules backing the model.
     *
     * @throws NullPointerException if {@code modules} is {@code null}.
     *
     * @see Modules#clone()
     */
    public InheritanceModel( final Modules modules )
    {
        super();

        if ( modules == null )
        {
            throw new NullPointerException( "modules" );
        }

        this.modules = modules.clone();
    }

    /**
     * Gets a set holding source nodes of an implementation.
     *
     * @param implementation The identifier of the implementation to get source nodes of.
     *
     * @return An unmodifiable set holding source nodes of the implementation identified by {@code implementation}.
     *
     * @throws NullPointerException if {@code implementation} is {@code null}.
     *
     * @see Node#getDescendant()
     */
    public Set<Node<Implementation>> getSourceNodes( final String implementation )
    {
        if ( implementation == null )
        {
            throw new NullPointerException( "implementation" );
        }

        this.prepareContext( implementation );
        final Collection<Node<Implementation>> col = map( this.sourceNodes, implementation ).values();
        return unmodifiableSet( newSet( col ) );
    }

    /**
     * Gets a set holding implementation reference nodes of an implementation causing a cycle.
     *
     * @param implementation The identifier of the implementation to get implementation reference nodes causing a cycle
     * of.
     *
     * @return An unmodifiable set holding implementation reference nodes of the implementation identified by
     * {@code implementation} causing a cycle.
     *
     * @throws NullPointerException if {@code implementation} is {@code null}.
     *
     * @since 1.5
     *
     * @see Node#getPath()
     */
    public Set<Node<ImplementationReference>> getCycleNodes( final String implementation )
    {
        if ( implementation == null )
        {
            throw new NullPointerException( "implementation" );
        }

        this.prepareContext( implementation );
        return unmodifiableSet( nodes( this.cyclicImplReferences, implementation ) );
    }

    /**
     * Gets a set holding the names of all dependencies of an implementation.
     *
     * @param implementation The identifier of the implementation to get the names of all dependencies of.
     *
     * @return An unmodifiable set holding the names of all dependencies of the implementation identified by
     * {@code implementation}.
     *
     * @throws NullPointerException if {@code implementation} is {@code null}.
     */
    public Set<String> getDependencyNames( final String implementation )
    {
        if ( implementation == null )
        {
            throw new NullPointerException( "implementation" );
        }

        this.prepareContext( implementation );
        return Collections.unmodifiableSet( map( this.dependencies, implementation ).keySet() );
    }

    /**
     * Gets a set holding effective dependency nodes of an implementation.
     *
     * @param implementation The identifier of the implementation to get effective dependency nodes of.
     * @param name The dependency name to get effective nodes for.
     *
     * @return An unmodifiable set holding effective dependency nodes matching {@code name} of the implementation
     * identified by {@code implementation}.
     *
     * @throws NullPointerException if {@code implementation} or {@code name} is {@code null}.
     *
     * @see #getDependencyNames(java.lang.String)
     */
    public Set<Node<Dependency>> getDependencyNodes( final String implementation, final String name )
    {
        if ( implementation == null )
        {
            throw new NullPointerException( "implementation" );
        }
        if ( name == null )
        {
            throw new NullPointerException( "name" );
        }

        this.prepareContext( implementation );
        Set<Node<Dependency>> set = null;

        final Map<String, Set<Node<Dependency>>> map =
            getEffectiveNodes( this.effDependencies, implementation, implementation );

        if ( map != null )
        {
            set = map.get( name );
        }

        return unmodifiableSet( set );
    }

    /**
     * Gets a set holding the identifiers of all implementation references of an implementation.
     *
     * @param implementation The identifier of the implementation to get the identifiers of all implementation
     * references of.
     *
     * @return An unmodifiable set holding the identifiers of all implementation references of the implementation
     * identified by {@code implementation}.
     *
     * @throws NullPointerException if {@code implementation} is {@code null}.
     */
    public Set<String> getImplementationReferenceIdentifiers( final String implementation )
    {
        if ( implementation == null )
        {
            throw new NullPointerException( "implementation" );
        }

        this.prepareContext( implementation );
        return Collections.unmodifiableSet( map( this.implReferences, implementation ).keySet() );
    }

    /**
     * Gets a set holding effective implementation reference nodes of an implementation.
     *
     * @param implementation The identifier of the implementation to get effective implementation reference nodes of.
     * @param identifier The implementation reference identifier to get effective nodes for.
     *
     * @return An unmodifiable set holding effective implementation reference nodes matching {@code identifier} of the
     * implementation identified by {@code implementation}.
     *
     * @throws NullPointerException if {@code implementation} or {@code identifier} is {@code null}.
     *
     * @see #getImplementationReferenceIdentifiers(java.lang.String)
     */
    public Set<Node<ImplementationReference>> getImplementationReferenceNodes( final String implementation,
                                                                               final String identifier )
    {
        if ( implementation == null )
        {
            throw new NullPointerException( "implementation" );
        }
        if ( identifier == null )
        {
            throw new NullPointerException( "identifier" );
        }

        this.prepareContext( implementation );
        Set<Node<ImplementationReference>> set = null;
        final Map<String, Set<Node<ImplementationReference>>> map =
            getEffectiveNodes( this.effImplReferences, implementation, implementation );

        if ( map != null )
        {
            set = map.get( identifier );
        }

        return unmodifiableSet( set );
    }

    /**
     * Gets a set holding the qualified names of all XML elements of an implementation.
     *
     * @param implementation The identifier of the implementation to get the qualified names of all XML elements of.
     *
     * @return An unmodifiable set holding the qualified names of all XML elements of the implementation identified by
     * {@code implementation}.
     *
     * @throws NullPointerException if {@code implementation} is {@code null}.
     */
    public Set<QName> getJaxbElementNames( final String implementation )
    {
        if ( implementation == null )
        {
            throw new NullPointerException( "implementation" );
        }

        this.prepareContext( implementation );
        return Collections.unmodifiableSet( map( this.jaxbElements, implementation ).keySet() );
    }

    /**
     * Gets a set holding effective JAXB element nodes of an implementation.
     *
     * @param implementation The identifier of the implementation to get effective JAXB element nodes of.
     * @param name The qualified JAXB element name to get effective nodes for.
     *
     * @return An unmodifiable set holding effective JAXB element nodes matching {@code name} of the implementation
     * identified by {@code implementation}.
     *
     * @throws NullPointerException if {@code implementation} or {@code name} is {@code null}.
     *
     * @see #getJaxbElementNames(java.lang.String)
     */
    public Set<Node<JAXBElement<?>>> getJaxbElementNodes( final String implementation, final QName name )
    {
        if ( implementation == null )
        {
            throw new NullPointerException( "implementation" );
        }
        if ( name == null )
        {
            throw new NullPointerException( "name" );
        }

        this.prepareContext( implementation );
        Set<Node<JAXBElement<?>>> set = null;
        final Map<QName, Set<Node<JAXBElement<?>>>> map =
            getEffectiveNodes( this.effJaxbElements, implementation, implementation );

        if ( map != null )
        {
            set = map.get( name );
        }

        return unmodifiableSet( set );
    }

    /**
     * Gets a set holding the names of all messages of an implementation.
     *
     * @param implementation The identifier of the implementation to get the names of all messages of.
     *
     * @return An unmodifiable set holding the names of all messages of the implementation identified by
     * {@code implementation}.
     *
     * @throws NullPointerException if {@code implementation} is {@code null}.
     */
    public Set<String> getMessageNames( final String implementation )
    {
        if ( implementation == null )
        {
            throw new NullPointerException( "implementation" );
        }

        this.prepareContext( implementation );
        return Collections.unmodifiableSet( map( this.messages, implementation ).keySet() );
    }

    /**
     * Gets a set holding effective message nodes of an implementation.
     *
     * @param implementation The identifier of the implementation to get effective message nodes of.
     * @param name The message name to get effective nodes for.
     *
     * @return An unmodifiable set holding effective message nodes matching {@code name} of the implementation
     * identified by {@code implementation}.
     *
     * @throws NullPointerException if {@code implementation} or {@code name} is {@code null}.
     *
     * @see #getMessageNames(java.lang.String)
     */
    public Set<Node<Message>> getMessageNodes( final String implementation, final String name )
    {
        if ( implementation == null )
        {
            throw new NullPointerException( "implementation" );
        }
        if ( name == null )
        {
            throw new NullPointerException( "name" );
        }

        this.prepareContext( implementation );
        Set<Node<Message>> set = null;
        final Map<String, Set<Node<Message>>> map =
            getEffectiveNodes( this.effMessages, implementation, implementation );

        if ( map != null )
        {
            set = map.get( name );
        }

        return unmodifiableSet( set );
    }

    /**
     * Gets a set holding the names of all properties of an implementation.
     *
     * @param implementation The identifier of the implementation to get the names of all properties of.
     *
     * @return An unmodifiable set holding the names of all properties of the implementation identified by
     * {@code implementation}.
     *
     * @throws NullPointerException if {@code implementation} is {@code null}.
     */
    public Set<String> getPropertyNames( final String implementation )
    {
        if ( implementation == null )
        {
            throw new NullPointerException( "implementation" );
        }

        this.prepareContext( implementation );
        return Collections.unmodifiableSet( map( this.properties, implementation ).keySet() );
    }

    /**
     * Gets a set holding effective property nodes of an implementation.
     *
     * @param implementation The identifier of the implementation to get effective property nodes of.
     * @param name The property name to get effective nodes for.
     *
     * @return An unmodifiable set holding effective property nodes matching {@code name} of the implementation
     * identified by {@code implementation}.
     *
     * @throws NullPointerException if {@code implementation} or {@code name} is {@code null}.
     *
     * @see #getPropertyNames(java.lang.String)
     */
    public Set<Node<Property>> getPropertyNodes( final String implementation, final String name )
    {
        if ( implementation == null )
        {
            throw new NullPointerException( "implementation" );
        }
        if ( name == null )
        {
            throw new NullPointerException( "name" );
        }

        this.prepareContext( implementation );
        Set<Node<Property>> set = null;
        final Map<String, Set<Node<Property>>> map =
            getEffectiveNodes( this.effProperties, implementation, implementation );

        if ( map != null )
        {
            set = map.get( name );
        }

        return unmodifiableSet( set );
    }

    /**
     * Gets a set holding the identifiers of all specification references of an implementation.
     *
     * @param implementation The identifier of the implementation to get the identifiers of all specification references
     * of.
     *
     * @return An unmodifiable set holding the identifiers of all specification references of the implementation
     * identified by {@code implementation}.
     *
     * @throws NullPointerException if {@code implementation} is {@code null}.
     */
    public Set<String> getSpecificationReferenceIdentifiers( final String implementation )
    {
        if ( implementation == null )
        {
            throw new NullPointerException( "implementation" );
        }

        this.prepareContext( implementation );
        return Collections.unmodifiableSet( map( this.specReferences, implementation ).keySet() );
    }

    /**
     * Gets a set holding effective specification reference nodes of an implementation.
     *
     * @param implementation The identifier of the implementation to get effective specification reference nodes of.
     * @param identifier The specification reference identifier to get effective nodes for.
     *
     * @return An unmodifiable set holding effective specification reference nodes matching {@code identifier} of the
     * implementation identified by {@code implementation}.
     *
     * @throws NullPointerException if {@code implementation} or {@code identifier} is {@code null}.
     *
     * @see #getSpecificationReferenceIdentifiers(java.lang.String)
     */
    public Set<Node<SpecificationReference>> getSpecificationReferenceNodes( final String implementation,
                                                                             final String identifier )
    {
        if ( implementation == null )
        {
            throw new NullPointerException( "implementation" );
        }
        if ( identifier == null )
        {
            throw new NullPointerException( "identifier" );
        }

        this.prepareContext( implementation );
        Set<Node<SpecificationReference>> set = null;
        final Map<String, Set<Node<SpecificationReference>>> map =
            getEffectiveNodes( this.effSpecReferences, implementation, implementation );

        if ( map != null )
        {
            set = map.get( identifier );
        }

        return unmodifiableSet( set );
    }

    /**
     * Gets a set holding the qualified names of all XML elements of an implementation.
     *
     * @param implementation The identifier of the implementation to get the qualified names of all XML elements of.
     *
     * @return An unmodifiable set holding the qualified names of all XML elements of the implementation identified by
     * {@code implementation}.
     *
     * @throws NullPointerException if {@code implementation} is {@code null}.
     */
    public Set<QName> getXmlElementNames( final String implementation )
    {
        if ( implementation == null )
        {
            throw new NullPointerException( "implementation" );
        }

        this.prepareContext( implementation );
        return Collections.unmodifiableSet( map( this.xmlElements, implementation ).keySet() );
    }

    /**
     * Gets a set holding effective XML element nodes of an implementation.
     *
     * @param implementation The identifier of the implementation to get effective XML element nodes of.
     * @param name The qualified XML element name to get effective nodes for.
     *
     * @return An unmodifiable set holding effective XML element nodes matching {@code name} of the implementation
     * identified by {@code implementation}.
     *
     * @throws NullPointerException if {@code implementation} or {@code name} is {@code null}.
     *
     * @see #getXmlElementNames(java.lang.String)
     */
    public Set<Node<Element>> getXmlElementNodes( final String implementation, final QName name )
    {
        if ( implementation == null )
        {
            throw new NullPointerException( "implementation" );
        }
        if ( name == null )
        {
            throw new NullPointerException( "name" );
        }

        this.prepareContext( implementation );
        Set<Node<Element>> set = null;
        final Map<QName, Set<Node<Element>>> map =
            getEffectiveNodes( this.effXmlElements, implementation, implementation );

        if ( map != null )
        {
            set = map.get( name );
        }

        return unmodifiableSet( set );
    }

    private void prepareContext( final String context )
    {
        ContextState state = this.contextStates.get( context );

        if ( state == null )
        {
            state = ContextState.PREPARING;
            this.contextStates.put( context, state );

            final Implementation i = this.modules.getImplementation( context );

            if ( i != null )
            {
                this.collectNodes( context, i, null, null );

                for ( final Node<Implementation> source : map( this.sourceNodes, context ).values() )
                {
                    this.collectEffectiveNodes( context, source );
                }
            }

            state = ContextState.PREPARED;
            this.contextStates.put( context, state );
        }

        assert state == ContextState.PREPARED :
            "Unexpected context state '" + state + "' for context '" + context + "'.";

    }

    private void collectNodes( final String context, final Implementation declaration,
                               final Node<Implementation> descendant, LinkedList<Node<Implementation>> path )
    {
        if ( path == null )
        {
            path = new LinkedList<Node<Implementation>>();
        }

        final Map<String, Node<Implementation>> contextImplementations = map( this.implementations, context );

        if ( declaration != null && !contextImplementations.containsKey( declaration.getIdentifier() ) )
        {
            final Node<Implementation> declarationNode = new Node<Implementation>(
                declaration, null, null, descendant, declaration, declaration.isFinal(), false );

            declarationNode.getModifiablePath().addAll( path );

            contextImplementations.put( declaration.getIdentifier(), declarationNode );

            path.addLast( declarationNode );

            if ( declaration.getDependencies() != null )
            {
                for ( int i = 0, s0 = declaration.getDependencies().getDependency().size(); i < s0; i++ )
                {
                    final Dependency d = declaration.getDependencies().getDependency().get( i );
                    final Node<Dependency> node =
                        new Node<Dependency>( declaration, null, null, descendant, d, d.isFinal(), d.isOverride() );

                    node.getModifiablePath().addAll( path );

                    addNode( map( this.dependencies, context ), node, node.getModelObject().getName() );
                }
            }

            if ( declaration.getMessages() != null )
            {
                for ( int i = 0, s0 = declaration.getMessages().getMessage().size(); i < s0; i++ )
                {
                    final Message m = declaration.getMessages().getMessage().get( i );
                    final Node<Message> node =
                        new Node<Message>( declaration, null, null, descendant, m, m.isFinal(), m.isOverride() );

                    node.getModifiablePath().addAll( path );

                    addNode( map( this.messages, context ), node, node.getModelObject().getName() );
                }

                if ( !declaration.getMessages().getReference().isEmpty() )
                {
                    final Module m = this.modules.getModuleOfImplementation( declaration.getIdentifier() );

                    if ( m != null && m.getMessages() != null )
                    {
                        for ( int i = 0, s0 = declaration.getMessages().getReference().size(); i < s0; i++ )
                        {
                            final MessageReference r = declaration.getMessages().getReference().get( i );
                            Message msg = m.getMessages().getMessage( r.getName() );

                            if ( msg != null )
                            {
                                msg = msg.clone();
                                msg.setFinal( r.isFinal() );
                                msg.setOverride( r.isOverride() );

                                final Node<Message> node = new Node<Message>(
                                    declaration, null, null, descendant, msg, msg.isFinal(), msg.isOverride() );

                                node.getModifiablePath().addAll( path );

                                addNode( map( this.messages, context ), node, node.getModelObject().getName() );
                            }
                        }
                    }
                }
            }

            if ( declaration.getProperties() != null )
            {
                for ( int i = 0, s0 = declaration.getProperties().getProperty().size(); i < s0; i++ )
                {
                    final Property p = declaration.getProperties().getProperty().get( i );
                    final Node<Property> node =
                        new Node<Property>( declaration, null, null, descendant, p, p.isFinal(), p.isOverride() );

                    node.getModifiablePath().addAll( path );

                    addNode( map( this.properties, context ), node, node.getModelObject().getName() );
                }

                if ( !declaration.getProperties().getReference().isEmpty() )
                {
                    final Module m = this.modules.getModuleOfImplementation( declaration.getIdentifier() );

                    if ( m != null && m.getProperties() != null )
                    {
                        for ( int i = 0, s0 = declaration.getProperties().getReference().size(); i < s0; i++ )
                        {
                            final PropertyReference r = declaration.getProperties().getReference().get( i );
                            Property p = m.getProperties().getProperty( r.getName() );

                            if ( p != null )
                            {
                                p = p.clone();
                                p.setFinal( r.isFinal() );
                                p.setOverride( r.isOverride() );

                                final Node<Property> node = new Node<Property>(
                                    declaration, null, null, descendant, p, p.isFinal(), p.isOverride() );

                                node.getModifiablePath().addAll( path );

                                addNode( map( this.properties, context ), node, node.getModelObject().getName() );
                            }
                        }
                    }
                }
            }

            if ( declaration.getSpecifications() != null )
            {
                for ( int i = 0, s0 = declaration.getSpecifications().getReference().size(); i < s0; i++ )
                {
                    final SpecificationReference r = declaration.getSpecifications().getReference().get( i );
                    final Node<SpecificationReference> node = new Node<SpecificationReference>(
                        declaration, null, null, descendant, r, r.isFinal(), r.isOverride() );

                    node.getModifiablePath().addAll( path );

                    addNode( map( this.specReferences, context ), node, node.getModelObject().getIdentifier() );

                    final Specification s = this.modules.getSpecification( r.getIdentifier() );

                    if ( s != null && s.getProperties() != null )
                    {
                        for ( int j = 0, s1 = s.getProperties().getProperty().size(); j < s1; j++ )
                        {
                            final Property p = s.getProperties().getProperty().get( j );
                            final Node<Property> n =
                                new Node<Property>( declaration, s, null, descendant, p, p.isFinal(), p.isOverride() );

                            n.getModifiablePath().addAll( path );

                            addNode( map( this.properties, context ), n, n.getModelObject().getName() );
                        }
                    }
                }
            }

            if ( !declaration.getAny().isEmpty() )
            {
                for ( int i = 0, s0 = declaration.getAny().size(); i < s0; i++ )
                {
                    final Object any = declaration.getAny().get( i );

                    if ( any instanceof Element )
                    {
                        final Element e = (Element) any;
                        final Node<Element> node =
                            new Node<Element>( declaration, null, null, descendant, e, false, false );

                        node.getModifiablePath().addAll( path );

                        addNode( map( this.xmlElements, context ), node, getXmlElementName( e ) );
                        continue;
                    }

                    if ( any instanceof JAXBElement<?> )
                    {
                        final JAXBElement<?> e = (JAXBElement<?>) any;
                        boolean _final = false;
                        boolean override = false;

                        if ( e.getValue() instanceof Inheritable )
                        {
                            _final = ( (Inheritable) e.getValue() ).isFinal();
                            override = ( (Inheritable) e.getValue() ).isOverride();
                        }

                        final Node<JAXBElement<?>> node =
                            new Node<JAXBElement<?>>( declaration, null, null, descendant, e, _final, override );

                        node.getModifiablePath().addAll( path );

                        addNode( map( this.jaxbElements, context ), node, e.getName() );
                        continue;
                    }
                }
            }

            if ( declaration.getImplementations() != null
                     && !declaration.getImplementations().getReference().isEmpty() )
            {
                boolean all_cyclic = true;

                for ( int i = 0, s0 = declaration.getImplementations().getReference().size(); i < s0; i++ )
                {
                    final ImplementationReference r = declaration.getImplementations().getReference().get( i );
                    final Node<ImplementationReference> node = new Node<ImplementationReference>(
                        declaration, null, null, descendant, r, r.isFinal(), r.isOverride() );

                    node.getModifiablePath().addAll( path );

                    final Implementation ancestor = this.modules.getImplementation( r.getIdentifier() );

                    boolean cycle = false;
                    if ( ancestor != null && contextImplementations.containsKey( ancestor.getIdentifier() ) )
                    {
                        for ( int j = 0, s1 = path.size(); j < s1; j++ )
                        {
                            final Node<Implementation> n = path.get( j );

                            if ( n.getModelObject().getIdentifier().equals( ancestor.getIdentifier() ) )
                            {
                                cycle = true;
                                node.getModifiablePath().add( n );
                                break;
                            }
                        }
                    }

                    if ( cycle )
                    {
                        addNode( this.cyclicImplReferences, node, context );
                    }
                    else
                    {
                        all_cyclic = false;
                        addNode( map( this.implReferences, context ), node, node.getModelObject().getIdentifier() );
                        this.collectNodes( context, ancestor, declarationNode, path );
                    }
                }

                if ( all_cyclic )
                {
                    map( this.sourceNodes, context ).
                        put( declarationNode.getModelObject().getIdentifier(), declarationNode );

                }
            }
            else
            {
                map( this.sourceNodes, context ).
                    put( declarationNode.getModelObject().getIdentifier(), declarationNode );

            }

            path.removeLast();
        }
    }

    private void collectEffectiveNodes( final String context, final Node<Implementation> node )
    {
        final Map<String, Set<Node<SpecificationReference>>> directSpecificationReferences =
            getDirectEffectiveNodes( map( this.specReferences, context ), node.getModelObject().getIdentifier() );

        final Map<String, Set<Node<Dependency>>> directDependencies =
            getDirectEffectiveNodes( map( this.dependencies, context ), node.getModelObject().getIdentifier() );

        final Map<String, Set<Node<Message>>> directMessages =
            getDirectEffectiveNodes( map( this.messages, context ), node.getModelObject().getIdentifier() );

        final Map<String, Set<Node<Property>>> directProperties =
            getDirectEffectiveNodes( map( this.properties, context ), node.getModelObject().getIdentifier() );

        final Map<String, Set<Node<ImplementationReference>>> directImplementationReferences =
            getDirectEffectiveNodes( map( this.implReferences, context ), node.getModelObject().getIdentifier() );

        final Map<QName, Set<Node<Element>>> directXmlElements =
            getDirectEffectiveNodes( map( this.xmlElements, context ), node.getModelObject().getIdentifier() );

        final Map<QName, Set<Node<JAXBElement<?>>>> directJaxbElements =
            getDirectEffectiveNodes( map( this.jaxbElements, context ), node.getModelObject().getIdentifier() );

        overrideNodes( map( this.effSpecReferences, context ), node, directSpecificationReferences );
        overrideNodes( map( this.effImplReferences, context ), node, directImplementationReferences );
        overrideNodes( map( this.effDependencies, context ), node, directDependencies );
        overrideNodes( map( this.effMessages, context ), node, directMessages );
        overrideNodes( map( this.effProperties, context ), node, directProperties );
        overrideNodes( map( this.effJaxbElements, context ), node, directJaxbElements );
        overrideNodes( map( this.effXmlElements, context ), node, directXmlElements );

        this.addClassDeclarationNodes( context, node );

        final Map<String, Set<Node<SpecificationReference>>> ancestorSpecificationReferences =
            getEffectiveNodes( this.effSpecReferences, context, node.getModelObject().getIdentifier() );

        final Map<String, Set<Node<Dependency>>> ancestorDependencies =
            getEffectiveNodes( this.effDependencies, context, node.getModelObject().getIdentifier() );

        final Map<String, Set<Node<Message>>> ancestorMessages =
            getEffectiveNodes( this.effMessages, context, node.getModelObject().getIdentifier() );

        final Map<String, Set<Node<Property>>> ancestorProperties =
            getEffectiveNodes( this.effProperties, context, node.getModelObject().getIdentifier() );

        final Map<String, Set<Node<ImplementationReference>>> ancestorImplementationReferences =
            getEffectiveNodes( this.effImplReferences, context, node.getModelObject().getIdentifier() );

        final Map<QName, Set<Node<Element>>> ancestorXmlElements =
            getEffectiveNodes( this.effXmlElements, context, node.getModelObject().getIdentifier() );

        final Map<QName, Set<Node<JAXBElement<?>>>> ancestorJaxbElements =
            getEffectiveNodes( this.effJaxbElements, context, node.getModelObject().getIdentifier() );

        if ( node.getDescendant() != null )
        {
            if ( ancestorSpecificationReferences != null )
            {
                inheritNodes( map( this.effSpecReferences, context ), ancestorSpecificationReferences,
                              node.getDescendant() );

            }

            if ( ancestorDependencies != null )
            {
                inheritNodes( map( this.effDependencies, context ), ancestorDependencies,
                              node.getDescendant() );

            }

            if ( ancestorProperties != null )
            {
                inheritNodes( map( this.effProperties, context ), ancestorProperties, node.getDescendant() );
            }

            if ( ancestorMessages != null )
            {
                inheritNodes( map( this.effMessages, context ), ancestorMessages, node.getDescendant() );
            }

            if ( ancestorImplementationReferences != null )
            {
                inheritNodes( map( this.effImplReferences, context ), ancestorImplementationReferences,
                              node.getDescendant() );

            }

            if ( ancestorXmlElements != null )
            {
                inheritNodes( map( this.effXmlElements, context ), ancestorXmlElements,
                              node.getDescendant() );

            }

            if ( ancestorJaxbElements != null )
            {
                inheritNodes( map( this.effJaxbElements, context ), ancestorJaxbElements,
                              node.getDescendant() );

            }

            collectEffectiveNodes( context, node.getDescendant() );
        }
    }

    private void addClassDeclarationNodes( final String context, final Node<Implementation> node )
    {
        final Implementation classDeclaration = this.getClassDeclaration( node.getModelObject() );

        if ( classDeclaration != null )
        {
            this.prepareContext( classDeclaration.getIdentifier() );

            Map<String, Set<Node<Dependency>>> effectiveDependencies =
                getEffectiveNodes( this.effDependencies, context, node.getModelObject().getIdentifier() );

            Map<String, Set<Node<Message>>> effectiveMessages =
                getEffectiveNodes( this.effMessages, context, node.getModelObject().getIdentifier() );

            Map<String, Set<Node<Property>>> effectiveProperties =
                getEffectiveNodes( this.effProperties, context, node.getModelObject().getIdentifier() );

            Map<String, Set<Node<SpecificationReference>>> effectiveSpecificationReferences =
                getEffectiveNodes( this.effSpecReferences, context, node.getModelObject().getIdentifier() );

            Map<QName, Set<Node<Element>>> effectiveXmlElements =
                getEffectiveNodes( this.effXmlElements, context, node.getModelObject().getIdentifier() );

            Map<QName, Set<Node<JAXBElement<?>>>> effectiveJaxbElements =
                getEffectiveNodes( this.effJaxbElements, context, node.getModelObject().getIdentifier() );

            final Map<String, Set<Node<Dependency>>> declDependencies =
                getEffectiveNodes( this.effDependencies, classDeclaration.getIdentifier(),
                                   classDeclaration.getIdentifier() );

            final Map<String, Set<Node<Message>>> declMessages =
                getEffectiveNodes( this.effMessages, classDeclaration.getIdentifier(),
                                   classDeclaration.getIdentifier() );

            final Map<String, Set<Node<Property>>> declProperties =
                getEffectiveNodes( this.effProperties, classDeclaration.getIdentifier(),
                                   classDeclaration.getIdentifier() );

            final Map<String, Set<Node<SpecificationReference>>> declSpecReferences =
                getEffectiveNodes( this.effSpecReferences, classDeclaration.getIdentifier(),
                                   classDeclaration.getIdentifier() );

            final Map<QName, Set<Node<Element>>> declXmlElements =
                getEffectiveNodes( this.effXmlElements, classDeclaration.getIdentifier(),
                                   classDeclaration.getIdentifier() );

            final Map<QName, Set<Node<JAXBElement<?>>>> declJaxbElements =
                getEffectiveNodes( this.effJaxbElements, classDeclaration.getIdentifier(),
                                   classDeclaration.getIdentifier() );

            if ( declDependencies != null )
            {
                if ( effectiveDependencies == null )
                {
                    effectiveDependencies = newMap();
                    map( this.effDependencies, context ).
                        put( node.getModelObject().getIdentifier(), effectiveDependencies );

                }

                for ( final Map.Entry<String, Set<Node<Dependency>>> e : declDependencies.entrySet() )
                {
                    final Set<Node<Dependency>> set = newSet( e.getValue().size() );

                    for ( final Node<Dependency> n : e.getValue() )
                    {
                        final Node<Dependency> effNode = new Node<Dependency>(
                            node.getModelObject(), n.getSpecification(), classDeclaration, null, n.getModelObject(),
                            n.isFinal(), n.isOverride() );

                        effNode.getModifiablePath().addAll( n.getPath() );
                        set.add( effNode );

                        addNode( map( this.dependencies, context ), effNode, e.getKey() );
                    }

                    if ( effectiveDependencies.containsKey( e.getKey() ) )
                    {
                        for ( final Node<Dependency> effNode : effectiveDependencies.get( e.getKey() ) )
                        {
                            effNode.getModifiableOverriddenNodes().addAll( set );
                        }
                    }
                    else
                    {
                        effectiveDependencies.put( e.getKey(), set );
                    }
                }
            }

            if ( declSpecReferences != null )
            {
                if ( effectiveSpecificationReferences == null )
                {
                    effectiveSpecificationReferences = newMap();
                    map( this.effSpecReferences, context ).
                        put( node.getModelObject().getIdentifier(), effectiveSpecificationReferences );

                }

                for ( final Map.Entry<String, Set<Node<SpecificationReference>>> e : declSpecReferences.entrySet() )
                {
                    final Set<Node<SpecificationReference>> set = newSet( e.getValue().size() );

                    for ( final Node<SpecificationReference> n : e.getValue() )
                    {
                        final Node<SpecificationReference> effNode = new Node<SpecificationReference>(
                            node.getModelObject(), n.getSpecification(), classDeclaration, null, n.getModelObject(),
                            n.isFinal(), n.isOverride() );

                        effNode.getModifiablePath().addAll( n.getPath() );
                        set.add( effNode );

                        addNode( map( this.specReferences, context ), effNode, e.getKey() );
                    }

                    if ( effectiveSpecificationReferences.containsKey( e.getKey() ) )
                    {
                        for ( final Node<SpecificationReference> effNode
                                  : effectiveSpecificationReferences.get( e.getKey() ) )
                        {
                            effNode.getModifiableOverriddenNodes().addAll( set );
                        }
                    }
                    else
                    {
                        effectiveSpecificationReferences.put( e.getKey(), set );
                    }
                }
            }

            if ( declMessages != null )
            {
                if ( effectiveMessages == null )
                {
                    effectiveMessages = newMap();
                    map( this.effMessages, context ).
                        put( node.getModelObject().getIdentifier(), effectiveMessages );

                }

                for ( final Map.Entry<String, Set<Node<Message>>> e : declMessages.entrySet() )
                {
                    final Set<Node<Message>> set = newSet( e.getValue().size() );

                    for ( final Node<Message> n : e.getValue() )
                    {
                        final Node<Message> effNode = new Node<Message>(
                            node.getModelObject(), n.getSpecification(), classDeclaration, null, n.getModelObject(),
                            n.isFinal(), n.isOverride() );

                        effNode.getModifiablePath().addAll( n.getPath() );
                        set.add( effNode );

                        addNode( map( this.messages, context ), effNode, e.getKey() );
                    }

                    if ( effectiveMessages.containsKey( e.getKey() ) )
                    {
                        for ( final Node<Message> effNode : effectiveMessages.get( e.getKey() ) )
                        {
                            effNode.getModifiableOverriddenNodes().addAll( set );
                        }
                    }
                    else
                    {
                        effectiveMessages.put( e.getKey(), set );
                    }
                }
            }

            if ( declProperties != null )
            {
                if ( effectiveProperties == null )
                {
                    effectiveProperties = newMap();
                    map( this.effProperties, context ).
                        put( node.getModelObject().getIdentifier(), effectiveProperties );

                }

                for ( final Map.Entry<String, Set<Node<Property>>> e : declProperties.entrySet() )
                {
                    final Set<Node<Property>> set = newSet( e.getValue().size() );

                    for ( final Node<Property> n : e.getValue() )
                    {
                        final Node<Property> effNode = new Node<Property>(
                            node.getModelObject(), n.getSpecification(), classDeclaration, null, n.getModelObject(),
                            n.isFinal(), n.isOverride() );

                        effNode.getModifiablePath().addAll( n.getPath() );
                        set.add( effNode );

                        addNode( map( this.properties, context ), effNode, e.getKey() );
                    }

                    if ( effectiveProperties.containsKey( e.getKey() ) )
                    {
                        for ( final Node<Property> effNode : effectiveProperties.get( e.getKey() ) )
                        {
                            effNode.getModifiableOverriddenNodes().addAll( set );
                        }
                    }
                    else
                    {
                        effectiveProperties.put( e.getKey(), set );
                    }
                }
            }

            if ( declXmlElements != null )
            {
                if ( effectiveXmlElements == null )
                {
                    effectiveXmlElements = newMap();
                    map( this.effXmlElements, context ).
                        put( node.getModelObject().getIdentifier(), effectiveXmlElements );

                }

                for ( final Map.Entry<QName, Set<Node<Element>>> e : declXmlElements.entrySet() )
                {
                    final Set<Node<Element>> set = newSet( e.getValue().size() );

                    for ( final Node<Element> n : e.getValue() )
                    {
                        final Node<Element> effNode = new Node<Element>(
                            node.getModelObject(), n.getSpecification(), classDeclaration, null, n.getModelObject(),
                            n.isFinal(), n.isOverride() );

                        effNode.getModifiablePath().addAll( n.getPath() );
                        set.add( effNode );

                        addNode( map( this.xmlElements, context ), effNode, e.getKey() );
                    }

                    if ( effectiveXmlElements.containsKey( e.getKey() ) )
                    {
                        for ( final Node<Element> effNode : effectiveXmlElements.get( e.getKey() ) )
                        {
                            effNode.getModifiableOverriddenNodes().addAll( set );
                        }
                    }
                    else
                    {
                        effectiveXmlElements.put( e.getKey(), set );
                    }
                }
            }

            if ( declJaxbElements != null )
            {
                if ( effectiveJaxbElements == null )
                {
                    effectiveJaxbElements = newMap();
                    map( this.effJaxbElements, context ).
                        put( node.getModelObject().getIdentifier(), effectiveJaxbElements );

                }

                for ( final Map.Entry<QName, Set<Node<JAXBElement<?>>>> e : declJaxbElements.entrySet() )
                {
                    final Set<Node<JAXBElement<?>>> set = newSet( e.getValue().size() );

                    for ( final Node<JAXBElement<?>> n : e.getValue() )
                    {
                        final Node<JAXBElement<?>> effNode = new Node<JAXBElement<?>>(
                            node.getModelObject(), n.getSpecification(), classDeclaration, null, n.getModelObject(),
                            n.isFinal(), n.isOverride() );

                        effNode.getModifiablePath().addAll( n.getPath() );
                        set.add( effNode );

                        addNode( map( this.jaxbElements, context ), effNode, e.getKey() );
                    }

                    if ( effectiveJaxbElements.containsKey( e.getKey() ) )
                    {
                        for ( final Node<JAXBElement<?>> effNode : effectiveJaxbElements.get( e.getKey() ) )
                        {
                            effNode.getModifiableOverriddenNodes().addAll( set );
                        }
                    }
                    else
                    {
                        effectiveJaxbElements.put( e.getKey(), set );
                    }
                }
            }
        }
    }

    private Implementation getClassDeclaration( final Implementation implementation )
    {
        Implementation declaration = null;

        if ( implementation.getClazz() != null && !implementation.isClassDeclaration() )
        {
            find:
            for ( int i = 0, s0 = this.modules.getModule().size(); i < s0; i++ )
            {
                final Module candidateModule = this.modules.getModule().get( i );

                if ( candidateModule.getImplementations() != null )
                {
                    for ( int j = 0, s1 = candidateModule.getImplementations().getImplementation().size(); j < s1; j++ )
                    {
                        final Implementation candidate =
                            candidateModule.getImplementations().getImplementation().get( j );

                        if ( candidate.isClassDeclaration()
                                 && candidate.getClazz().equals( implementation.getClazz() ) )
                        {
                            declaration = candidate;
                            break find;
                        }
                    }
                }
            }
        }

        return declaration;
    }

    private static <T, K> void addNode( final Map<K, Set<Node<T>>> map, final Node<T> node, final K key )
    {
        Set<Node<T>> set = map.get( key );

        if ( set == null )
        {
            set = newSet();
            map.put( key, set );
        }

        set.add( node );
    }

    private static <T, K> void overrideNodes( final Map<String, Map<K, Set<Node<T>>>> effective,
                                              final Node<Implementation> implementation,
                                              final Map<K, Set<Node<T>>> directNodes )
    {
        for ( final Map.Entry<K, Set<Node<T>>> e : directNodes.entrySet() )
        {
            final Set<Node<T>> effectiveNodes =
                effectiveNodes( effective, implementation.getModelObject().getIdentifier(), e.getKey() );

            final Set<Node<T>> overridingNodes = newSet();

            for ( final Node<T> directNode : e.getValue() )
            {
                for ( final Iterator<Node<T>> it = effectiveNodes.iterator(); it.hasNext(); )
                {
                    final Node<T> effectiveNode = it.next();

                    if ( isOverriding( effectiveNode, directNode ) )
                    {
                        it.remove();

                        if ( directNode != effectiveNode )
                        {
                            directNode.getModifiableOverriddenNodes().add( effectiveNode );
                        }
                    }
                }

                boolean overriddenByAncestor = false;

                if ( directNode.getSpecification() != null )
                {
                    for ( final Node<T> effectiveNode : effectiveNodes )
                    {
                        if ( effectiveNode.getSpecification() == null )
                        {
                            overriddenByAncestor = true;
                            effectiveNode.getModifiableOverriddenNodes().add( directNode );
                        }
                    }
                }

                if ( !overriddenByAncestor )
                {
                    overridingNodes.add( directNode );
                }
            }

            effectiveNodes.addAll( overridingNodes );
        }
    }

    private static <K, V, T> Map<K, V> map( final Map<T, Map<K, V>> map, final T context )
    {
        Map<K, V> contextMap = map.get( context );

        if ( contextMap == null )
        {
            contextMap = newMap();
            map.put( context, contextMap );
        }

        return contextMap;
    }

    private static <K, V> Set<Node<V>> nodes( final Map<K, Set<Node<V>>> map, final K key )
    {
        Set<Node<V>> nodes = map.get( key );

        if ( nodes == null )
        {
            nodes = newSet();
            map.put( key, nodes );
        }

        return nodes;
    }

    private static <K, V> Set<Node<V>> effectiveNodes( final Map<String, Map<K, Set<Node<V>>>> map,
                                                       final String context, final K key )
    {
        return nodes( map( map, context ), key );
    }

    private static <T, K> void inheritNodes(
        final Map<String, Map<K, Set<Node<T>>>> effective, final Map<K, Set<Node<T>>> ancestor,
        final Node<Implementation> descendant )
    {
        for ( Map.Entry<K, Set<Node<T>>> e : ancestor.entrySet() )
        {
            for ( final Node<T> inherit : e.getValue() )
            {
                if ( isInheritableNode( inherit ) )
                {
                    effectiveNodes( effective, descendant.getModelObject().getIdentifier(), e.getKey() ).add( inherit );
                }
            }
        }
    }

    private static <T, K> Map<K, Set<Node<T>>> getDirectEffectiveNodes( final Map<K, Set<Node<T>>> map,
                                                                        final String origin )
    {
        final Map<K, Set<Node<T>>> declarationMap = newMap( map.size() );

        for ( final Map.Entry<K, Set<Node<T>>> e : map.entrySet() )
        {
            final Set<Node<T>> set = nodes( declarationMap, e.getKey() );

            for ( final Node<T> n : e.getValue() )
            {
                if ( isDirectEffectiveNode( n, origin ) )
                {
                    set.add( n );
                }
            }

            for ( final Node<T> n : e.getValue() )
            {
                if ( isDirectSpecifiedNode( n, origin ) )
                {
                    boolean add = true;

                    for ( final Node<T> override : set )
                    {
                        if ( override.getSpecification() == null )
                        {
                            override.getModifiableOverriddenNodes().add( n );
                            add = false;
                        }
                    }

                    if ( add )
                    {
                        set.add( n );
                    }
                }
            }
        }

        return declarationMap;
    }

    private static <T, K> Map<K, Set<Node<T>>> getEffectiveNodes(
        final Map<String, Map<String, Map<K, Set<Node<T>>>>> effective, final String context,
        final String implementation )
    {
        return map( effective, context ).get( implementation );
    }

    private static boolean isDirectNode( final Node<?> node, final String implementation )
    {
        return implementation.equals( node.getImplementation().getIdentifier() );
    }

    private static boolean isDirectEffectiveNode( final Node<?> node, final String implementation )
    {
        return isDirectNode( node, implementation ) && node.getClassDeclaration() == null
                   && node.getSpecification() == null;

    }

    private static boolean isDirectSpecifiedNode( final Node<?> node, final String implementation )
    {
        return isDirectNode( node, implementation ) && node.getClassDeclaration() == null
                   && node.getSpecification() != null;

    }

    private static boolean isOverriding( final Node<?> node, final Node<?> override )
    {
        if ( override.getSpecification() != null )
        {
            if ( node.getSpecification() == null )
            {
                return false;
            }
            else if ( !override.getSpecification().getIdentifier().equals( node.getSpecification().getIdentifier() ) )
            {
                return false;
            }
        }

        return true;
    }

    private static boolean isInheritableNode( final Node<?> node )
    {
        return node.getClassDeclaration() == null;
    }

    private static <K, V> Map<K, V> newMap()
    {
        return new HashMap<K, V>();
    }

    private static <K, V> Map<K, V> newMap( final int initialCapacity )
    {
        return new HashMap<K, V>( initialCapacity );
    }

    private static <T> Set<T> newSet()
    {
        return new HashSet<T>();
    }

    private static <T> Set<T> newSet( final int initialCapacity )
    {
        return new HashSet<T>( initialCapacity );
    }

    private static <T> Set<T> newSet( final Collection<? extends T> col )
    {
        return new HashSet<T>( col );
    }

    private static <T> Set<T> unmodifiableSet( final Set<T> set )
    {
        return set != null ? Collections.unmodifiableSet( set ) : Collections.<T>emptySet();
    }

    private static QName getXmlElementName( final Element element )
    {
        if ( element.getNamespaceURI() != null )
        {
            return new QName( element.getNamespaceURI(), element.getLocalName() );
        }
        else
        {
            return new QName( element.getLocalName() );
        }
    }

}