Package com.alibaba.citrus.springext.support

Source Code of com.alibaba.citrus.springext.support.SpringExtSchemaSet$IncludedSchemaInfo

/*
* Copyright (c) 2002-2012 Alibaba Group Holding Limited.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*    http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.alibaba.citrus.springext.support;

import static com.alibaba.citrus.util.Assert.*;
import static com.alibaba.citrus.util.CollectionUtil.*;

import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;

import com.alibaba.citrus.springext.ConfigurationPoint;
import com.alibaba.citrus.springext.ConfigurationPoints;
import com.alibaba.citrus.springext.Contribution;
import com.alibaba.citrus.springext.ResourceResolver;
import com.alibaba.citrus.springext.Schema;
import com.alibaba.citrus.springext.Schemas;
import com.alibaba.citrus.springext.impl.ConfigurationPointImpl;
import com.alibaba.citrus.springext.impl.ConfigurationPointsImpl;
import com.alibaba.citrus.springext.impl.SpringPluggableSchemas;
import com.alibaba.citrus.springext.support.SchemaUtil.AnyElementVisitor;
import com.alibaba.citrus.util.ToStringBuilder;
import org.jetbrains.annotations.NotNull;

/**
* 创建一个schema set,其中包含以下所有的schemas:
* <ul>
* <li>所有的SpringExt扩展点、捐献的schemas。</li>
* <li>所有的Spring原有的schemas。</li>
* </ul>
*
* @author Michael Zhou
*/
public class SpringExtSchemaSet extends SchemaSet {
    private NamespaceItem[] treeItems;

    /** 通过默认的<code>ClassLoader</code>来装载schemas。 */
    public SpringExtSchemaSet() {
        this(new ConfigurationPointsImpl(), new SpringPluggableSchemas());
    }

    /** 通过指定的<code>ClassLoader</code>来装载schemas。 */
    public SpringExtSchemaSet(ClassLoader classLoader) {
        this(new ConfigurationPointsImpl(classLoader), new SpringPluggableSchemas(classLoader));
    }

    /** 通过指定的<code>ResourceResolver</code>来装载schemas(IDE plugins mode)。 */
    public SpringExtSchemaSet(ResourceResolver resourceResolver) {
        this(new ConfigurationPointsImpl(resourceResolver), new SpringPluggableSchemas(resourceResolver));
    }

    /** for test only */
    SpringExtSchemaSet(String location) {
        this(new ConfigurationPointsImpl((ClassLoader) null, location), new SpringPluggableSchemas());
    }

    private SpringExtSchemaSet(ConfigurationPointsImpl configurationPoints, SpringPluggableSchemas springPluggableSchemas) {
        super(configurationPoints, springPluggableSchemas);
    }

    public ConfigurationPoints getConfigurationPoints() {
        for (Schemas schemas : this) {
            if (schemas instanceof ConfigurationPoints) {
                return (ConfigurationPoints) schemas;
            }
        }

        unreachableCode("no ConfigurationPoints found");
        return null;
    }

    /*
     * 处理以下情形:
     * <p/>
     * 一个contribution schema引用了一个spring pluggable schema。如果spring pluggable schema中包含一个anyElement,引用了另一个configuration point。
     * 则需要将anyElement展开,并添加configuration point对于contribution的依赖。
     */
    private static class IncludedSchemaInfo implements Iterable<Contribution> {
        private final Map<String, Contribution> includingContributions = createHashMap();
        private final Schema includedSchema;

        private IncludedSchemaInfo(Schema includedSchema) {
            this.includedSchema = includedSchema;
        }

        public Schema getIncludedSchema() {
            return includedSchema;
        }

        public Iterator<Contribution> iterator() {
            return includingContributions.values().iterator();
        }
    }

    private Map<String, IncludedSchemaInfo> includedSchemaInfoMap;

    @Override
    protected void foundIncludes(Schema schema, Collection<Schema> allIncludes) {
        if (schema instanceof ContributionSchemaSourceInfo) {
            Contribution contribution = (Contribution) ((ContributionSchemaSourceInfo) schema).getParent();

            if (contribution != null) {
                for (Schema includedSchema : allIncludes) {
                    if (includedSchema instanceof SpringPluggableSchemaSourceInfo) {
                        if (includedSchemaInfoMap == null) {
                            includedSchemaInfoMap = createHashMap();
                        }

                        String includedSchemaName = includedSchema.getName();
                        IncludedSchemaInfo includedSchemaInfo = includedSchemaInfoMap.get(includedSchemaName);

                        if (includedSchemaInfo == null) {
                            includedSchemaInfo = new IncludedSchemaInfo(includedSchema);
                            includedSchemaInfoMap.put(includedSchemaName, includedSchemaInfo);
                        }

                        includedSchemaInfo.includingContributions.put(schema.getName(), contribution);
                    }
                }
            }
        }
    }

    @Override
    protected void finishProcessIncludes() {
        if (includedSchemaInfoMap != null) {
            for (final IncludedSchemaInfo includedSchemaInfo : includedSchemaInfoMap.values()) {
                Schema includedSchema = includedSchemaInfo.getIncludedSchema();

                includedSchema.transform(SchemaUtil.getAnyElementTransformer(getConfigurationPoints(), new AnyElementVisitor() {
                    public void visitAnyElement(ConfigurationPoint cp) {
                        for (Contribution contribution : includedSchemaInfo) {
                            if (cp instanceof ConfigurationPointImpl) {
                                ((ConfigurationPointImpl) cp).addDependingContribution(contribution);
                            }
                        }
                    }
                }), true);
            }
        }

        includedSchemaInfoMap = null;
    }

    /** 取得所有独立的schemas。 */
    public synchronized NamespaceItem[] getIndependentItems() {
        if (treeItems == null) {
            treeItems = new TreeBuilder().build();
        }

        return treeItems;
    }

    public static interface TreeItem {
        boolean hasChildren();

        @NotNull
        TreeItem[] getChildren();
    }

    public static interface NamespaceItem extends TreeItem {
        @NotNull
        String getNamespace();

        @NotNull
        Set<Schema> getSchemas();
    }

    public static interface ParentOf<C extends TreeItem> {
    }

    public static class ConfigurationPointItem extends AbstractNamespaceItem<ContributionItem> {
        private final ConfigurationPoint configurationPoint;

        public ConfigurationPointItem(@NotNull String namespace, @NotNull Set<Schema> schemas, @NotNull ConfigurationPoint configurationPoint) {
            super(namespace, schemas);
            this.configurationPoint = configurationPoint;
        }

        @NotNull
        public ConfigurationPoint getConfigurationPoint() {
            return configurationPoint;
        }
    }

    public static class ContributionItem extends AbstractTreeItem<ConfigurationPointItem> {
        private final Contribution contribution;

        public ContributionItem(@NotNull Contribution contribution) {
            this.contribution = contribution;
        }

        @NotNull
        public Contribution getContribution() {
            return contribution;
        }

        @Override
        public String toString() {
            return contribution.getName();
        }
    }

    public static class SpringPluggableItem extends AbstractNamespaceItem<TreeItem> {
        public SpringPluggableItem(@NotNull String namespace, @NotNull Set<Schema> schemas) {
            super(namespace, schemas);
        }
    }

    private static abstract class AbstractTreeItem<C extends TreeItem> implements TreeItem, ParentOf<C> {
        protected final Map<String, C> children = createTreeMap();

        @Override
        public boolean hasChildren() {
            return !children.isEmpty();
        }

        @NotNull
        public TreeItem[] getChildren() {
            return children.values().toArray(new TreeItem[children.size()]);
        }

        @NotNull
        public String dump() {
            ToStringBuilder buf = new ToStringBuilder();
            dump(buf);
            return buf.toString();
        }

        void dump(ToStringBuilder buf) {
            buf.append(this).start("{", "}");

            int i = 0;
            for (TreeItem child : getChildren()) {
                if (i++ > 0) {
                    buf.append("\n");
                }

                ((AbstractTreeItem) child).dump(buf);
            }

            buf.end();
        }
    }

    private static abstract class AbstractNamespaceItem<C extends TreeItem> extends AbstractTreeItem<C>
            implements NamespaceItem {
        private final String      namespace;
        private final Set<Schema> schemas;

        protected AbstractNamespaceItem(@NotNull String namespace, @NotNull Set<Schema> schemas) {
            this.namespace = namespace;
            this.schemas = schemas;
        }

        @NotNull
        public String getNamespace() {
            return namespace;
        }

        @NotNull
        public Set<Schema> getSchemas() {
            return schemas;
        }

        @Override
        public String toString() {
            return namespace;
        }
    }

    private class TreeBuilder {
        private final Map<String, Set<Schema>>   namespaceMappings  = getNamespaceMappings();
        private final Map<String, NamespaceItem> items              = createHashMap();
        private final Map<String, NamespaceItem> independentItems   = createTreeMap();
        private final LinkedList<String>         buildingNamespaces = createLinkedList();

        public NamespaceItem[] build() {
            for (String namespace : namespaceMappings.keySet()) {
                buildNamespaceItemRecursively(namespace);
            }

            return independentItems.values().toArray(new NamespaceItem[independentItems.size()]);
        }

        private boolean buildNamespaceItemRecursively(@NotNull String namespace) {
            if (buildingNamespaces.contains(namespace)) {
                return false; // 防止递归失控
            }

            try {
                buildingNamespaces.push(namespace);
                buildNamespaceItem(namespace);
            } finally {
                buildingNamespaces.pop();
            }

            return true;
        }

        private void buildNamespaceItem(@NotNull String namespace) {
            if (items.containsKey(namespace)) {
                return; // 防止重复build
            }

            Set<Schema> schemas = namespaceMappings.get(namespace);

            assertTrue(schemas != null, "%s not exist", namespace); // namespace必须存在

            Schema schema = null;

            if (!schemas.isEmpty()) {
                schema = schemas.iterator().next(); // 取得第一个schema作为main schema。
            }

            // spring.schemas
            if (schema == null || schema instanceof SpringPluggableSchemaSourceInfo) {
                buildSpringPluggableItem(namespace, schemas);
            }

            // configuration point
            else if (schema instanceof ConfigurationPointSchemaSourceInfo) {
                buildConfigurationPointItem(namespace, schemas, (ConfigurationPointSchemaSourceInfo) schema);
            }

            // 不会发生
            else {
                unreachableCode();
            }
        }

        private void buildSpringPluggableItem(@NotNull String namespace, @NotNull Set<Schema> schemas) {
            NamespaceItem item = new SpringPluggableItem(namespace, schemas);
            items.put(namespace, item);
            independentItems.put(namespace, item);
        }

        private void buildConfigurationPointItem(String namespace, Set<Schema> schemas, ConfigurationPointSchemaSourceInfo schema) {
            ConfigurationPoint configurationPoint = (ConfigurationPoint) schema.getParent();
            ConfigurationPointItem item = new ConfigurationPointItem(namespace, schemas, configurationPoint);
            items.put(namespace, item);

            Collection<Contribution> dependingContributions = configurationPoint.getDependingContributions();
            int count = 0;

            for (Contribution contribution : dependingContributions) {
                String dependingNamespace = contribution.getConfigurationPoint().getNamespaceUri();

                if (buildNamespaceItemRecursively(dependingNamespace)) {
                    count++;
                    NamespaceItem parentItem = assertNotNull(items.get(dependingNamespace), "no item for namespace %s", namespace);

                    if (parentItem instanceof ConfigurationPointItem) {
                        item = buildContributionItem(namespace, item, contribution, (ConfigurationPointItem) parentItem);
                    }
                }
            }

            if (count == 0) {
                independentItems.put(namespace, item);
            }
        }

        private ConfigurationPointItem buildContributionItem(String namespace, ConfigurationPointItem item, Contribution contribution, ConfigurationPointItem parentItem) {
            String contributionName = contribution.getName();
            ContributionItem parentContributionItem = parentItem.children.get(contributionName);

            if (parentContributionItem == null) {
                parentContributionItem = new ContributionItem(contribution);
                parentItem.children.put(contributionName, parentContributionItem);
            }

            if (!parentContributionItem.children.containsKey(namespace)) {
                parentContributionItem.children.put(namespace, item);
            }

            return item;
        }
    }
}
TOP

Related Classes of com.alibaba.citrus.springext.support.SpringExtSchemaSet$IncludedSchemaInfo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.