Package com.alibaba.citrus.springext.support

Source Code of com.alibaba.citrus.springext.support.SchemaIncludes

/*
* 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.springext.support.SchemaUtil.*;
import static com.alibaba.citrus.util.ArrayUtil.*;
import static com.alibaba.citrus.util.Assert.*;
import static com.alibaba.citrus.util.CollectionUtil.*;
import static com.alibaba.citrus.util.StringUtil.*;
import static java.util.Collections.*;

import java.net.URI;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;

import com.alibaba.citrus.springext.Namespaces;
import com.alibaba.citrus.springext.Schema;
import com.alibaba.citrus.springext.Schema.Element;
import com.alibaba.citrus.springext.Schema.Transformer;
import com.alibaba.citrus.springext.Schemas;
import com.alibaba.citrus.util.ToStringBuilder;
import com.alibaba.citrus.util.internal.LazyLoader;
import com.alibaba.citrus.util.internal.LazyLoader.Loader;

/**
* 将一组<code>Schemas</code>整合在一起的集合。
*
* @author Michael Zhou
*/
public class SchemaSet implements Schemas, Namespaces, Iterable<Schemas> {
    private final List<Schemas> allSchemas;
    private final Map<String, Schema> nameToSchemas             = createHashMap();
    private final Map<String, Schema> nameToSchemasUnmodifiable = unmodifiableMap(nameToSchemas);
    private final SortedSet<String> names;
    private final SortedSet<String> namespaces;
    private final Set<String>       namespacesUnmodifiable;

    // 延迟加载namespace mappings,仅当有需要时再做。
    // 将所有相同namespace的schema放在一起,并按名称倒排序,即按:beans.xsd、beans-2.5.xsd、beans-2.0.xsd 顺序。
    private final LazyLoader<Map<String, Set<Schema>>, Object> nsToSchemas = LazyLoader.getDefault(new Loader<Map<String, Set<Schema>>, Object>() {
        public Map<String, Set<Schema>> load(Object context) {
            Map<String, Set<Schema>> nsToSchemasMappings = createTreeMap(); // 使用排序的map,使测试结果恒定。

            for (Schema schema : nameToSchemas.values()) {
                String namespace = schema.getTargetNamespace();

                if (namespace != null) {
                    Set<Schema> nsSchemas = nsToSchemasMappings.get(namespace);

                    if (nsSchemas == null) {
                        nsSchemas = createTreeSet(new Comparator<Schema>() {
                            public int compare(Schema o1, Schema o2) {
                                return o2.getName().compareTo(o1.getName());
                            }
                        });

                        nsToSchemasMappings.put(namespace, nsSchemas);
                        namespaces.add(namespace);
                    }

                    nsSchemas.add(schema);
                }
            }

            // getAvailableNamespaces()中可能包含一些namespace,它们没有对应的schemas。需要把它们特别地找出来。
            for (Schemas schemas : allSchemas) {
                if (schemas instanceof Namespaces) {
                    for (String namespace : ((Namespaces) schemas).getAvailableNamespaces()) {
                        if (!nsToSchemasMappings.containsKey(namespace)) {
                            nsToSchemasMappings.put(namespace, Collections.<Schema>emptySet());
                            namespaces.add(namespace);
                        }
                    }
                }
            }

            return unmodifiableMap(nsToSchemasMappings);
        }
    });

    public static SchemaSet getInstance(Schemas... schemasList) {
        if (schemasList != null && schemasList.length == 1 && schemasList[0] instanceof SchemaSet) {
            return (SchemaSet) schemasList[0];
        } else {
            return new SchemaSet(schemasList);
        }
    }

    public SchemaSet(Schemas... schemasList) {
        assertTrue(!isEmptyArray(schemasList), "schemasList");

        allSchemas = createArrayList(schemasList);

        for (Schemas schemas : schemasList) {
            this.nameToSchemas.putAll(schemas.getNamedMappings());
        }

        // sort by string length (descending) and name
        this.names = createTreeSet(new Comparator<String>() {
            public int compare(String o1, String o2) {
                int lengthCompare = o2.length() - o1.length();

                if (lengthCompare == 0) {
                    return o1.compareTo(o2);
                }

                return lengthCompare;
            }
        }, this.nameToSchemas.keySet());

        this.namespaces = createTreeSet();
        this.namespacesUnmodifiable = unmodifiableSet(namespaces);

        // 检查所有schema,将重复的include提到最上层
        processIncludes();
    }

    @Override
    public Iterator<Schemas> iterator() {
        return allSchemas.iterator();
    }

    @Override
    public Set<String> getAvailableNamespaces() {
        nsToSchemas.getInstance(); // ensure initialized
        return namespacesUnmodifiable;
    }

    /**
     * 检查所有schema,将所有includes提到最上层。
     * <p>
     * 例如:
     * </p>
     * <ul>
     * <li>all.xsd 包含 x.xsd 和 y.xsd</li>
     * <li>x.xsd 包含 z.xsd</li>
     * <li>y.xsd 包含 z.xsd</li>
     * </ul>
     * <p>
     * 在上面的例子中,z.xsd被包含了两遍,导致解析错误。 该方法做如下处理,从而避免了上述问题:
     * </p>
     * <ul>
     * <li>all.xsd 包含 x.xsd,y.xsd and z.xsd</li>
     * <li>x.xsd 不包含 z.xsd</li>
     * <li>y.xsd 不包含 z.xsd</li>
     * </ul>
     */
    private void processIncludes() {
        class SchemaIncludes {
            Map<String, Schema> allIncludes;
            boolean             removeAllIncludes;
        }

        Map<String, SchemaIncludes> nameToSchemaIncludes = createHashMap();

        // 所有包含了include的,并且被其它schema所包含的schema,其引用需要被移到最上层的schema中。
        for (Schema schema : createArrayList(nameToSchemas.values())) {
            Map<String, Schema> allIncludes = getAllIncludes(schema); // 直接或间接的所有includes,按依赖顺序排列
            Map<String, Element> allElements = getAllElements(schema, allIncludes.values());
            boolean withIndirectIncludes = false;

            foundIncludes(schema, allIncludes.values()); // 给子类一个机会处理includes

            for (Schema includedSchema : allIncludes.values()) {
                if (includedSchema.getIncludes().length > 0) {
                    withIndirectIncludes = true;

                    SchemaIncludes si = nameToSchemaIncludes.get(includedSchema.getName());

                    if (si == null) {
                        si = new SchemaIncludes();
                        nameToSchemaIncludes.put(includedSchema.getName(), si);
                    }

                    si.removeAllIncludes = true;
                }
            }

            if (withIndirectIncludes) {
                SchemaIncludes si = nameToSchemaIncludes.get(schema.getName());

                if (si == null) {
                    si = new SchemaIncludes();
                    nameToSchemaIncludes.put(schema.getName(), si);
                }

                si.allIncludes = allIncludes;
            }

            // 收集当前schema的所有elements
            schema.setElements(allElements.values());
        }

        for (Map.Entry<String, SchemaIncludes> entry : nameToSchemaIncludes.entrySet()) {
            Schema schema = nameToSchemas.get(entry.getKey());
            SchemaIncludes si = entry.getValue();

            // 立即执行以下所有的transformer,以防在多线程环境下出错。
            if (si.removeAllIncludes) {
                schema.transform(getTransformerWhoRemovesIncludes(), true);
            } else if (si.allIncludes != null) {
                schema.transform(getTransformerWhoAddsIndirectIncludes(si.allIncludes), true);
            }
        }

        finishProcessIncludes();
    }

    protected void foundIncludes(Schema schema, Collection<Schema> allIncludes) {
    }

    protected void finishProcessIncludes() {
    }

    private Map<String, Element> getAllElements(Schema schema, Collection<Schema> includes) {
        Map<String, Element> all = createHashMap();

        for (Element element : schema.getElements()) {
            all.put(element.getName(), element);
        }

        for (Schema include : includes) {
            for (Element element : include.getElements()) {
                all.put(element.getName(), element);
            }
        }

        return all;
    }

    /**
     * 取得所有的直接或间接的includes。
     * <p>
     * 使用深度优先的算法,被include的总是列在较前面。
     * </p>
     */
    private Map<String, Schema> getAllIncludes(Schema schema) {
        Map<String, Schema> includes = createLinkedHashMap();
        getAllIncludesDepthFirst(schema, includes);
        includes.remove(schema.getName()); // 不包含自身
        return includes;
    }

    private void getAllIncludesDepthFirst(Schema schema, Map<String, Schema> includes) {
        for (String include : schema.getIncludes()) {
            Schema includedSchema = findIncludedSchema(include, schema.getName());
            getAllIncludesDepthFirst(includedSchema, includes);
        }

        includes.put(schema.getName(), schema);
    }

    /** 查找include schema,如未找到,抛异常。 */
    private Schema findIncludedSchema(String include, String fromSchema) {
        return assertNotNull(findSchema(include), "Could not include schema \"%s\" in %s", include, fromSchema);
    }

    /** 添加一个schema。 */
    public void addSchema(Schema schema) {
        nameToSchemas.put(schema.getName(), schema);
        names.add(schema.getName());
    }

    /** 取得名称和schema的映射表。 */
    public Map<String, Schema> getNamedMappings() {
        return nameToSchemasUnmodifiable;
    }

    /** 取得namespace和schema的映射表。 */
    public Map<String, Set<Schema>> getNamespaceMappings() {
        return nsToSchemas.getInstance();
    }

    /** 查找systemId对应的schema,如未找到,则返回<code>null</code>。 */
    public Schema findSchema(String systemId) {
        systemId = assertNotNull(trimToNull(systemId), "systemId").replaceAll("\\\\", "/");

        try {
            systemId = URI.create(systemId).normalize().toString();
        } catch (Exception e) {
            // ignore
        }

        for (String schemaName : names) {
            if (systemId.endsWith(schemaName)) {
                return nameToSchemas.get(schemaName);
            }
        }

        return null;
    }

    /** 对所有的schema应用转换器。 */
    public void transformAll(Transformer transformer) {
        if (transformer == null) {
            transformer = getNoopTransformer();
        }

        for (Schema schema : nameToSchemas.values()) {
            schema.transform(transformer, true);
        }
    }

    @Override
    public String toString() {
        return new ToStringBuilder().append("SchemaSet").append(names).toString();
    }
}
TOP

Related Classes of com.alibaba.citrus.springext.support.SchemaIncludes

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.