Axis.java

/*
 * Copyright (c) 1997, 2022 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Distribution License v. 1.0, which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

package com.sun.xml.xsom.impl.scd;

import com.sun.xml.xsom.XSAttContainer;
import com.sun.xml.xsom.XSAttGroupDecl;
import com.sun.xml.xsom.XSAttributeDecl;
import com.sun.xml.xsom.XSAttributeUse;
import com.sun.xml.xsom.XSComplexType;
import com.sun.xml.xsom.XSComponent;
import com.sun.xml.xsom.XSElementDecl;
import com.sun.xml.xsom.XSFacet;
import com.sun.xml.xsom.XSIdentityConstraint;
import com.sun.xml.xsom.XSListSimpleType;
import com.sun.xml.xsom.XSModelGroup;
import com.sun.xml.xsom.XSModelGroup.Compositor;
import com.sun.xml.xsom.XSModelGroupDecl;
import com.sun.xml.xsom.XSNotation;
import com.sun.xml.xsom.XSParticle;
import com.sun.xml.xsom.XSRestrictionSimpleType;
import com.sun.xml.xsom.XSSchema;
import com.sun.xml.xsom.XSSimpleType;
import com.sun.xml.xsom.XSType;
import com.sun.xml.xsom.XSUnionSimpleType;
import com.sun.xml.xsom.XSWildcard;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * Axis of traversal.
 *
 * @param <T>
 *      The kind of components that this axis may return.
 *
 * @author Kohsuke Kawaguchi
 */
public interface Axis<T extends XSComponent> {
    Iterator<T> iterator(XSComponent contextNode);

    Iterator<T> iterator(Iterator<? extends XSComponent> contextNodes);

    /**
     * Returns true if this is one of the model group axis.
     */
    boolean isModelGroup();


    /**
     * Pseudo-axis that selects all the {@link XSSchema}s in the current set.
     * Used to implement the absolute path expression
     */
    Axis<XSSchema> ROOT = new Axis<>() {
        public Iterator<XSSchema> iterator(XSComponent contextNode) {
            return contextNode.getRoot().iterateSchema();
        }

        public Iterator<XSSchema> iterator(Iterator<? extends XSComponent> contextNodes) {
            if (!contextNodes.hasNext())
                return Iterators.empty();
            else
                // this assumes that all current nodes belong to the same owner.
                return iterator(contextNodes.next());
        }

        public boolean isModelGroup() {
            return false;
        }

        public String toString() {
            return "root::";
        }
    };

    /**
     * Pseudo-axis that visits all skipped intermediate steps.
     * Those are:
     * <ol>
     *  <li>complex type reachable from element
     *  <li>model groups
     *  <li>combination of above.
     * </ol>
     */
    Axis<XSComponent> INTERMEDIATE_SKIP = new AbstractAxisImpl<>() {
        public Iterator<XSComponent> elementDecl(XSElementDecl decl) {
            XSComplexType ct = decl.getType().asComplexType();
            if (ct == null)
                return empty();
            else {
                // also pick up model groups inside this complex type
                return new Iterators.Union<>(singleton(ct), complexType(ct));
            }
        }

        public Iterator<XSComponent> modelGroupDecl(XSModelGroupDecl decl) {
            return descendants(decl.getModelGroup());
        }

        public Iterator<XSComponent> particle(XSParticle particle) {
            return descendants(particle.getTerm().asModelGroup());
        }

        /**
         * Iterate all descendant model groups of the given model group, including itself.
         */
        private Iterator<XSComponent> descendants(XSModelGroup mg) {
            // TODO: write a tree iterator
            // for now, we do it eagerly because I'm lazy
            List<XSComponent> r = new ArrayList<>();
            visit(mg, r);
            return r.iterator();
        }

        private void visit(XSModelGroup mg, List<XSComponent> r) {
            // since model groups never form a cycle, no cycle check is needed
            r.add(mg);
            for (XSParticle p : mg) {
                XSModelGroup child = p.getTerm().asModelGroup();
                if (child != null)
                    visit(child, r);
            }
        }

        public String toString() {
            return "(intermediateSkip)";
        }
    };

    /**
     * All descendants reachable via default axes. Used to implement the "//" semantics.
     *
     * So far the default axes together are guaranteed not to cause any cycle, so
     * no cycle check is needed (if it's needed, the life would be much harder!)
     */
    Axis<XSComponent> DESCENDANTS = new Axis<>() {
        public Iterator<XSComponent> iterator(XSComponent contextNode) {
            return new Visitor().iterator(contextNode);
        }

        public Iterator<XSComponent> iterator(Iterator<? extends XSComponent> contextNodes) {
            return new Visitor().iterator(contextNodes);
        }

        public boolean isModelGroup() {
            return false;
        }

        /**
         * Stateful visitor that remembers what's already traversed, to reduce the search space.
         */
        final class Visitor extends AbstractAxisImpl<XSComponent> {
            private final Set<XSComponent> visited = new HashSet<>();

            /**
             * Recursively apply the {@link Axis#DESCENDANTS} axis.
             */
            final class Recursion extends Iterators.Map<XSComponent, XSComponent> {
                public Recursion(Iterator<? extends XSComponent> core) {
                    super(core);
                }

                protected Iterator<XSComponent> apply(XSComponent u) {
                    return DESCENDANTS.iterator(u);
                }
            }

            public Iterator<XSComponent> schema(XSSchema schema) {
                if (visited.add(schema))
                    return ret(schema, new Recursion(schema.iterateElementDecls()));
                else
                    return empty();
            }

            public Iterator<XSComponent> elementDecl(XSElementDecl decl) {
                if (visited.add(decl))
                    return ret(decl, iterator(decl.getType()));
                else
                    return empty();
            }

            public Iterator<XSComponent> simpleType(XSSimpleType type) {
                if (visited.add(type))
                    return ret(type, FACET.iterator(type));
                else
                    return empty();
            }

            public Iterator<XSComponent> complexType(XSComplexType type) {
                if (visited.add(type))
                    return ret(type, iterator(type.getContentType()));
                else
                    return empty();
            }

            public Iterator<XSComponent> particle(XSParticle particle) {
                if (visited.add(particle))
                    return ret(particle, iterator(particle.getTerm()));
                else
                    return empty();
            }

            public Iterator<XSComponent> modelGroupDecl(XSModelGroupDecl decl) {
                if (visited.add(decl))
                    return ret(decl, iterator(decl.getModelGroup()));
                else
                    return empty();
            }

            public Iterator<XSComponent> modelGroup(XSModelGroup group) {
                if (visited.add(group))
                    return ret(group, new Recursion(group.iterator()));
                else
                    return empty();
            }

            public Iterator<XSComponent> attGroupDecl(XSAttGroupDecl decl) {
                if (visited.add(decl))
                    return ret(decl, new Recursion(decl.iterateAttributeUses()));
                else
                    return empty();
            }

            public Iterator<XSComponent> attributeUse(XSAttributeUse use) {
                if (visited.add(use))
                    return ret(use, iterator(use.getDecl()));
                else
                    return empty();
            }

            public Iterator<XSComponent> attributeDecl(XSAttributeDecl decl) {
                if (visited.add(decl))
                    return ret(decl, iterator(decl.getType()));
                else
                    return empty();
            }

            private Iterator<XSComponent> ret(XSComponent one, Iterator<? extends XSComponent> rest) {
                return union(singleton(one), rest);
            }
        }

        public String toString() {
            return "/";
        }
    };

    Axis<XSSchema> X_SCHEMA = new Axis<>() {
        public Iterator<XSSchema> iterator(XSComponent contextNode) {
            return Iterators.singleton(contextNode.getOwnerSchema());
        }

        public Iterator<XSSchema> iterator(Iterator<? extends XSComponent> contextNodes) {
            return new Iterators.Adapter<XSSchema, XSComponent>(contextNodes) {
                protected XSSchema filter(XSComponent u) {
                    return u.getOwnerSchema();
                }
            };
        }

        public boolean isModelGroup() {
            return false;
        }

        public String toString() {
            return "x-schema::";
        }
    };

    Axis<XSElementDecl> SUBSTITUTION_GROUP = new AbstractAxisImpl<>() {
        public Iterator<XSElementDecl> elementDecl(XSElementDecl decl) {
            return singleton(decl.getSubstAffiliation());
        }

        public String toString() {
            return "substitutionGroup::";
        }
    };

    Axis<XSAttributeDecl> ATTRIBUTE = new AbstractAxisImpl<>() {
        public Iterator<XSAttributeDecl> complexType(XSComplexType type) {
            return attributeHolder(type);
        }

        public Iterator<XSAttributeDecl> attGroupDecl(XSAttGroupDecl decl) {
            return attributeHolder(decl);
        }

        private Iterator<XSAttributeDecl> attributeHolder(final XSAttContainer atts) {
            // TODO: check spec. is this correct?
            return new Iterators.Adapter<XSAttributeDecl, XSAttributeUse>(atts.iterateAttributeUses()) {
                protected XSAttributeDecl filter(XSAttributeUse u) {
                    return u.getDecl();
                }
            };
        }

        public Iterator<XSAttributeDecl> schema(XSSchema schema) {
            return schema.iterateAttributeDecls();
        }

        public String toString() {
            return "@";
        }
    };

    Axis<XSElementDecl> ELEMENT = new AbstractAxisImpl<>() {
        public Iterator<XSElementDecl> particle(XSParticle particle) {
            return singleton(particle.getTerm().asElementDecl());
        }

        public Iterator<XSElementDecl> schema(XSSchema schema) {
            return schema.iterateElementDecls();
        }

        public Iterator<XSElementDecl> modelGroupDecl(XSModelGroupDecl decl) {
            return modelGroup(decl.getModelGroup());
        }

        //public Iterator<XSElementDecl> modelGroup(XSModelGroup group) {
        //    return new Iterators.Map<XSElementDecl,XSParticle>(group.iterator()) {
        //        protected Iterator<XSElementDecl> apply(XSParticle p) {
        //            return particle(p);
        //        }
        //    };
        //}

        @Override
        public String getName() {
            return "";
        }

        public String toString() {
            return "element::";
        }
    };


    Axis<XSType> TYPE_DEFINITION = new AbstractAxisImpl<>() {
        public Iterator<XSType> schema(XSSchema schema) {
            return schema.iterateTypes();
        }

        public Iterator<XSType> attributeDecl(XSAttributeDecl decl) {
            return singleton(decl.getType());
        }

        public Iterator<XSType> elementDecl(XSElementDecl decl) {
            return singleton(decl.getType());
        }

        public String toString() {
            return "~";
        }
    };

    Axis<XSType> BASETYPE = new AbstractAxisImpl<>() {
        public Iterator<XSType> simpleType(XSSimpleType type) {
            return singleton(type.getBaseType());
        }

        public Iterator<XSType> complexType(XSComplexType type) {
            return singleton(type.getBaseType());
        }

        public String toString() {
            return "baseType::";
        }
    };

    Axis<XSSimpleType> PRIMITIVE_TYPE = new AbstractAxisImpl<>() {
        public Iterator<XSSimpleType> simpleType(XSSimpleType type) {
            return singleton(type.getPrimitiveType());
        }

        public String toString() {
            return "primitiveType::";
        }
    };

    Axis<XSSimpleType> ITEM_TYPE = new AbstractAxisImpl<>() {
        public Iterator<XSSimpleType> simpleType(XSSimpleType type) {
            XSListSimpleType baseList = type.getBaseListType();
            if (baseList == null) return empty();
            return singleton(baseList.getItemType());
        }

        public String toString() {
            return "itemType::";
        }
    };

    Axis<XSSimpleType> MEMBER_TYPE = new AbstractAxisImpl<>() {
        public Iterator<XSSimpleType> simpleType(XSSimpleType type) {
            XSUnionSimpleType baseUnion = type.getBaseUnionType();
            if (baseUnion == null) return empty();
            return baseUnion.iterator();
        }

        public String toString() {
            return "memberType::";
        }
    };

    Axis<XSComponent> SCOPE = new AbstractAxisImpl<>() {
        public Iterator<XSComponent> complexType(XSComplexType type) {
            return singleton(type.getScope());
        }
        // TODO: attribute declaration has a scope, too.
        // TODO: element declaration has a scope

        public String toString() {
            return "scope::";
        }
    };

    Axis<XSAttGroupDecl> ATTRIBUTE_GROUP = new AbstractAxisImpl<>() {
        public Iterator<XSAttGroupDecl> schema(XSSchema schema) {
            return schema.iterateAttGroupDecls();
        }

        public String toString() {
            return "attributeGroup::";
        }
    };

    Axis<XSModelGroupDecl> MODEL_GROUP_DECL = new AbstractAxisImpl<>() {
        public Iterator<XSModelGroupDecl> schema(XSSchema schema) {
            return schema.iterateModelGroupDecls();
        }

        public Iterator<XSModelGroupDecl> particle(XSParticle particle) {
            return singleton(particle.getTerm().asModelGroupDecl());
        }

        public String toString() {
            return "group::";
        }
    };

    Axis<XSIdentityConstraint> IDENTITY_CONSTRAINT = new AbstractAxisImpl<>() {
        public Iterator<XSIdentityConstraint> elementDecl(XSElementDecl decl) {
            return decl.getIdentityConstraints().iterator();
        }

        public Iterator<XSIdentityConstraint> schema(XSSchema schema) {
            // TODO: iterate all elements in this schema (local or global!) and its identity constraints
            return super.schema(schema);
        }

        public String toString() {
            return "identityConstraint::";
        }
    };

    Axis<XSIdentityConstraint> REFERENCED_KEY = new AbstractAxisImpl<>() {
        public Iterator<XSIdentityConstraint> identityConstraint(XSIdentityConstraint decl) {
            return singleton(decl.getReferencedKey());
        }

        public String toString() {
            return "key::";
        }
    };

    Axis<XSNotation> NOTATION = new AbstractAxisImpl<>() {
        public Iterator<XSNotation> schema(XSSchema schema) {
            return schema.iterateNotations();
        }

        public String toString() {
            return "notation::";
        }
    };

    Axis<XSWildcard> WILDCARD = new AbstractAxisImpl<>() {
        public Iterator<XSWildcard> particle(XSParticle particle) {
            return singleton(particle.getTerm().asWildcard());
        }

        public String toString() {
            return "any::";
        }
    };

    Axis<XSWildcard> ATTRIBUTE_WILDCARD = new AbstractAxisImpl<>() {
        public Iterator<XSWildcard> complexType(XSComplexType type) {
            return singleton(type.getAttributeWildcard());
        }

        public Iterator<XSWildcard> attGroupDecl(XSAttGroupDecl decl) {
            return singleton(decl.getAttributeWildcard());
        }

        public String toString() {
            return "anyAttribute::";
        }
    };

    Axis<XSFacet> FACET = new AbstractAxisImpl<>() {
        public Iterator<XSFacet> simpleType(XSSimpleType type) {
            // TODO: it's not clear if "facets" mean all inherited facets or just declared facets
            XSRestrictionSimpleType r = type.asRestriction();
            if (r != null)
                return r.iterateDeclaredFacets();
            else
                return empty();
        }

        public String toString() {
            return "facet::";
        }
    };

    Axis<XSModelGroup> MODELGROUP_ALL = new ModelGroupAxis(Compositor.ALL);
    Axis<XSModelGroup> MODELGROUP_CHOICE = new ModelGroupAxis(Compositor.CHOICE);
    Axis<XSModelGroup> MODELGROUP_SEQUENCE = new ModelGroupAxis(Compositor.SEQUENCE);
    Axis<XSModelGroup> MODELGROUP_ANY = new ModelGroupAxis(null);

    final class ModelGroupAxis extends AbstractAxisImpl<XSModelGroup> {
        private final XSModelGroup.Compositor compositor;

        ModelGroupAxis(Compositor compositor) {
            this.compositor = compositor;
        }

        @Override
        public boolean isModelGroup() {
            return true;
        }

        public Iterator<XSModelGroup> particle(XSParticle particle) {
            return filter(particle.getTerm().asModelGroup());
        }

        public Iterator<XSModelGroup> modelGroupDecl(XSModelGroupDecl decl) {
            return filter(decl.getModelGroup());
        }

        private Iterator<XSModelGroup> filter(XSModelGroup mg) {
            if(mg==null)
                return empty();
            if(mg.getCompositor() == compositor || compositor == null)
                return singleton(mg);
            else
                return empty();
        }

        public String toString() {
            if(compositor==null)
                return "model::*";
            else
                return "model::"+compositor;
        }
    }
}