• spring中解析xml


    解析xml有SAX,Stax,dom等方式,那么spring中是如何解析xml文件的呢?

    Document doc = this.documentLoader.loadDocument(
                        inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());

    spring中采用的DOM的方式,所要做的一切就是得到org.w3c.dom.Document对象

    上面的documentLoader是DefaultDocumentLoader对象

    /*
     * Copyright 2002-2007 the original author or authors.
     *
     * 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 org.springframework.beans.factory.xml;
    
    import javax.xml.parsers.DocumentBuilder;
    import javax.xml.parsers.DocumentBuilderFactory;
    import javax.xml.parsers.ParserConfigurationException;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.springframework.util.xml.XmlValidationModeDetector;
    import org.w3c.dom.Document;
    import org.xml.sax.EntityResolver;
    import org.xml.sax.ErrorHandler;
    import org.xml.sax.InputSource;
    
    /**
     * Spring's default {@link DocumentLoader} implementation.
     *
     * <p>Simply loads {@link Document documents} using the standard JAXP-configured
     * XML parser. If you want to change the {@link DocumentBuilder} that is used to
     * load documents, then one strategy is to define a corresponding Java system property
     * when starting your JVM. For example, to use the Oracle {@link DocumentBuilder},
     * you might start your application like as follows:
     *
     * <pre code="class">java -Djavax.xml.parsers.DocumentBuilderFactory=oracle.xml.jaxp.JXDocumentBuilderFactory MyMainClass</pre>
     *
     * @author Rob Harrop
     * @author Juergen Hoeller
     * @since 2.0
     */
    public class DefaultDocumentLoader implements DocumentLoader {
    
        /**
         * JAXP attribute used to configure the schema language for validation.
         */
        private static final String SCHEMA_LANGUAGE_ATTRIBUTE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
    
        /**
         * JAXP attribute value indicating the XSD schema language.
         */
        private static final String XSD_SCHEMA_LANGUAGE = "http://www.w3.org/2001/XMLSchema";
    
    
        private static final Log logger = LogFactory.getLog(DefaultDocumentLoader.class);
    
    
        /**
         * Load the {@link Document} at the supplied {@link InputSource} using the standard JAXP-configured
         * XML parser.
         */
        public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
                ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
    
            DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
            if (logger.isDebugEnabled()) {
                logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
            }
            DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
            return builder.parse(inputSource);
        }
    
        /**
         * Create the {@link DocumentBuilderFactory} instance.
         * @param validationMode the type of validation: {@link XmlValidationModeDetector#VALIDATION_DTD DTD}
         * or {@link XmlValidationModeDetector#VALIDATION_XSD XSD})
         * @param namespaceAware whether the returned factory is to provide support for XML namespaces
         * @return the JAXP DocumentBuilderFactory
         * @throws ParserConfigurationException if we failed to build a proper DocumentBuilderFactory
         */
        protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
                throws ParserConfigurationException {
    
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setNamespaceAware(namespaceAware);
    
            if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
                factory.setValidating(true);
    
                if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
                    // Enforce namespace aware for XSD...
                    factory.setNamespaceAware(true);
                    try {
                        factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
                    }
                    catch (IllegalArgumentException ex) {
                        ParserConfigurationException pcex = new ParserConfigurationException(
                                "Unable to validate using XSD: Your JAXP provider [" + factory +
                                "] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? " +
                                "Upgrade to Apache Xerces (or Java 1.5) for full XSD support.");
                        pcex.initCause(ex);
                        throw pcex;
                    }
                }
            }
    
            return factory;
        }
    
        /**
         * Create a JAXP DocumentBuilder that this bean definition reader
         * will use for parsing XML documents. Can be overridden in subclasses,
         * adding further initialization of the builder.
         * @param factory the JAXP DocumentBuilderFactory that the DocumentBuilder
         * should be created with
         * @param entityResolver the SAX EntityResolver to use
         * @param errorHandler the SAX ErrorHandler to use
         * @return the JAXP DocumentBuilder
         * @throws ParserConfigurationException if thrown by JAXP methods
         */
        protected DocumentBuilder createDocumentBuilder(
                DocumentBuilderFactory factory, EntityResolver entityResolver, ErrorHandler errorHandler)
                throws ParserConfigurationException {
    
            DocumentBuilder docBuilder = factory.newDocumentBuilder();
            if (entityResolver != null) {
                docBuilder.setEntityResolver(entityResolver);
            }
            if (errorHandler != null) {
                docBuilder.setErrorHandler(errorHandler);
            }
            return docBuilder;
        }
    
    }

    loadDocument中有一个参数entityResolver,类型是EntityResolver,这也没有什么,DocumentBuilder本来就可以设置setEntityResolver,那么现在这个entityResolver是什么对象呢?是

    DelegatingEntityResolver ,
    /*
     * Copyright 2002-2012 the original author or authors.
     *
     * 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 org.springframework.beans.factory.xml;
    
    import java.io.IOException;
    
    import org.springframework.util.Assert;
    import org.xml.sax.EntityResolver;
    import org.xml.sax.InputSource;
    import org.xml.sax.SAXException;
    
    /**
     * {@link EntityResolver} implementation that delegates to a {@link BeansDtdResolver}
     * and a {@link PluggableSchemaResolver} for DTDs and XML schemas, respectively.
     *
     * @author Rob Harrop
     * @author Juergen Hoeller
     * @author Rick Evans
     * @since 2.0
     * @see BeansDtdResolver
     * @see PluggableSchemaResolver
     */
    public class DelegatingEntityResolver implements EntityResolver {
    
        /** Suffix for DTD files */
        public static final String DTD_SUFFIX = ".dtd";
    
        /** Suffix for schema definition files */
        public static final String XSD_SUFFIX = ".xsd";
    
    
        private final EntityResolver dtdResolver;
    
        private final EntityResolver schemaResolver;
    
    
        /**
         * Create a new DelegatingEntityResolver that delegates to
         * a default {@link BeansDtdResolver} and a default {@link PluggableSchemaResolver}.
         * <p>Configures the {@link PluggableSchemaResolver} with the supplied
         * {@link ClassLoader}.
         * @param classLoader the ClassLoader to use for loading
         * (can be {@code null}) to use the default ClassLoader)
         */
        public DelegatingEntityResolver(ClassLoader classLoader) {
            this.dtdResolver = new BeansDtdResolver();
            this.schemaResolver = new PluggableSchemaResolver(classLoader);
        }
    
        /**
         * Create a new DelegatingEntityResolver that delegates to
         * the given {@link EntityResolver EntityResolvers}.
         * @param dtdResolver the EntityResolver to resolve DTDs with
         * @param schemaResolver the EntityResolver to resolve XML schemas with
         */
        public DelegatingEntityResolver(EntityResolver dtdResolver, EntityResolver schemaResolver) {
            Assert.notNull(dtdResolver, "'dtdResolver' is required");
            Assert.notNull(schemaResolver, "'schemaResolver' is required");
            this.dtdResolver = dtdResolver;
            this.schemaResolver = schemaResolver;
        }
    
    
        public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
            if (systemId != null) {
                if (systemId.endsWith(DTD_SUFFIX)) {
                    return this.dtdResolver.resolveEntity(publicId, systemId);
                }
                else if (systemId.endsWith(XSD_SUFFIX)) {
                    return this.schemaResolver.resolveEntity(publicId, systemId);
                }
            }
            return null;
        }
    
    
        @Override
        public String toString() {
            return "EntityResolver delegating " + XSD_SUFFIX + " to " + this.schemaResolver +
                    " and " + DTD_SUFFIX + " to " + this.dtdResolver;
        }
    
    }

    在DocumentBuilder调用parse方法的时候会调用EntityResolver(这里是DelegatingEntityResolver)的resolveEntity方法:

        public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
            if (systemId != null) {
                if (systemId.endsWith(DTD_SUFFIX)) {
                    return this.dtdResolver.resolveEntity(publicId, systemId);
                }
                else if (systemId.endsWith(XSD_SUFFIX)) {
                    return this.schemaResolver.resolveEntity(publicId, systemId);
                }
            }
            return null;
        }

    请注意resolveEntity方法会根据systemId来返回不同的对象,systemId即是xsi:schemaLocation种的值

    /*
     * Copyright 2002-2012 the original author or authors.
     *
     * 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 org.springframework.beans.factory.xml;
    
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.util.Map;
    import java.util.Properties;
    import java.util.concurrent.ConcurrentHashMap;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.core.io.Resource;
    import org.springframework.core.io.support.PropertiesLoaderUtils;
    import org.springframework.util.Assert;
    import org.springframework.util.CollectionUtils;
    import org.xml.sax.EntityResolver;
    import org.xml.sax.InputSource;
    
    /**
     * {@link EntityResolver} implementation that attempts to resolve schema URLs into
     * local {@link ClassPathResource classpath resources} using a set of mappings files.
     *
     * <p>By default, this class will look for mapping files in the classpath using the pattern:
     * {@code META-INF/spring.schemas} allowing for multiple files to exist on the
     * classpath at any one time.
     *
     * The format of {@code META-INF/spring.schemas} is a properties
     * file where each line should be of the form {@code systemId=schema-location}
     * where {@code schema-location} should also be a schema file in the classpath.
     * Since systemId is commonly a URL, one must be careful to escape any ':' characters
     * which are treated as delimiters in properties files.
     *
     * <p>The pattern for the mapping files can be overidden using the
     * {@link #PluggableSchemaResolver(ClassLoader, String)} constructor
     *
     * @author Rob Harrop
     * @author Juergen Hoeller
     * @since 2.0
     */
    public class PluggableSchemaResolver implements EntityResolver {
    
        /**
         * The location of the file that defines schema mappings.
         * Can be present in multiple JAR files.
         */
        public static final String DEFAULT_SCHEMA_MAPPINGS_LOCATION = "META-INF/spring.schemas";
    
    
        private static final Log logger = LogFactory.getLog(PluggableSchemaResolver.class);
    
        private final ClassLoader classLoader;
    
        private final String schemaMappingsLocation;
    
        /** Stores the mapping of schema URL -> local schema path */
        private volatile Map<String, String> schemaMappings;
    
    
        /**
         * Loads the schema URL -> schema file location mappings using the default
         * mapping file pattern "META-INF/spring.schemas".
         * @param classLoader the ClassLoader to use for loading
         * (can be {@code null}) to use the default ClassLoader)
         * @see PropertiesLoaderUtils#loadAllProperties(String, ClassLoader)
         */
        public PluggableSchemaResolver(ClassLoader classLoader) {
            this.classLoader = classLoader;
            this.schemaMappingsLocation = DEFAULT_SCHEMA_MAPPINGS_LOCATION;
        }
    
        /**
         * Loads the schema URL -> schema file location mappings using the given
         * mapping file pattern.
         * @param classLoader the ClassLoader to use for loading
         * (can be {@code null}) to use the default ClassLoader)
         * @param schemaMappingsLocation the location of the file that defines schema mappings
         * (must not be empty)
         * @see PropertiesLoaderUtils#loadAllProperties(String, ClassLoader)
         */
        public PluggableSchemaResolver(ClassLoader classLoader, String schemaMappingsLocation) {
            Assert.hasText(schemaMappingsLocation, "'schemaMappingsLocation' must not be empty");
            this.classLoader = classLoader;
            this.schemaMappingsLocation = schemaMappingsLocation;
        }
    
        public InputSource resolveEntity(String publicId, String systemId) throws IOException {
            if (logger.isTraceEnabled()) {
                logger.trace("Trying to resolve XML entity with public id [" + publicId +
                        "] and system id [" + systemId + "]");
            }
    
            if (systemId != null) {
                String resourceLocation = getSchemaMappings().get(systemId);
                if (resourceLocation != null) {
                    Resource resource = new ClassPathResource(resourceLocation, this.classLoader);
                    try {
                        InputSource source = new InputSource(resource.getInputStream());
                        source.setPublicId(publicId);
                        source.setSystemId(systemId);
                        if (logger.isDebugEnabled()) {
                            logger.debug("Found XML schema [" + systemId + "] in classpath: " + resourceLocation);
                        }
                        return source;
                    }
                    catch (FileNotFoundException ex) {
                        if (logger.isDebugEnabled()) {
                            logger.debug("Couldn't find XML schema [" + systemId + "]: " + resource, ex);
                        }
                    }
                }
            }
            return null;
        }
    
        /**
         * Load the specified schema mappings lazily.
         */
        private Map<String, String> getSchemaMappings() {
            if (this.schemaMappings == null) {
                synchronized (this) {
                    if (this.schemaMappings == null) {
                        if (logger.isDebugEnabled()) {
                            logger.debug("Loading schema mappings from [" + this.schemaMappingsLocation + "]");
                        }
                        try {
                            Properties mappings =
                                    PropertiesLoaderUtils.loadAllProperties(this.schemaMappingsLocation, this.classLoader);
                            if (logger.isDebugEnabled()) {
                                logger.debug("Loaded schema mappings: " + mappings);
                            }
                            Map<String, String> schemaMappings = new ConcurrentHashMap<String, String>(mappings.size());
                            CollectionUtils.mergePropertiesIntoMap(mappings, schemaMappings);
                            this.schemaMappings = schemaMappings;
                        }
                        catch (IOException ex) {
                            throw new IllegalStateException(
                                    "Unable to load schema mappings from location [" + this.schemaMappingsLocation + "]", ex);
                        }
                    }
                }
            }
            return this.schemaMappings;
        }
    
    
        @Override
        public String toString() {
            return "EntityResolver using mappings " + getSchemaMappings();
        }
    
    }
    /*
     * Copyright 2002-2012 the original author or authors.
     *
     * 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 org.springframework.beans.factory.xml;
    
    import java.io.IOException;
    import java.util.Arrays;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.core.io.Resource;
    import org.xml.sax.EntityResolver;
    import org.xml.sax.InputSource;
    
    /**
     * EntityResolver implementation for the Spring beans DTD,
     * to load the DTD from the Spring class path (or JAR file).
     *
     * <p>Fetches "spring-beans-2.0.dtd" from the class path resource
     * "/org/springframework/beans/factory/xml/spring-beans-2.0.dtd",
     * no matter whether specified as some local URL that includes "spring-beans"
     * in the DTD name or as "http://www.springframework.org/dtd/spring-beans-2.0.dtd".
     *
     * @author Juergen Hoeller
     * @author Colin Sampaleanu
     * @since 04.06.2003
     * @see ResourceEntityResolver
     */
    public class BeansDtdResolver implements EntityResolver {
    
        private static final String DTD_EXTENSION = ".dtd";
    
        private static final String[] DTD_NAMES = {"spring-beans-2.0", "spring-beans"};
    
        private static final Log logger = LogFactory.getLog(BeansDtdResolver.class);
    
    
        public InputSource resolveEntity(String publicId, String systemId) throws IOException {
            if (logger.isTraceEnabled()) {
                logger.trace("Trying to resolve XML entity with public ID [" + publicId +
                        "] and system ID [" + systemId + "]");
            }
            if (systemId != null && systemId.endsWith(DTD_EXTENSION)) {
                int lastPathSeparator = systemId.lastIndexOf("/");
                for (String DTD_NAME : DTD_NAMES) {
                    int dtdNameStart = systemId.indexOf(DTD_NAME);
                    if (dtdNameStart > lastPathSeparator) {
                        String dtdFile = systemId.substring(dtdNameStart);
                        if (logger.isTraceEnabled()) {
                            logger.trace("Trying to locate [" + dtdFile + "] in Spring jar");
                        }
                        try {
                            Resource resource = new ClassPathResource(dtdFile, getClass());
                            InputSource source = new InputSource(resource.getInputStream());
                            source.setPublicId(publicId);
                            source.setSystemId(systemId);
                            if (logger.isDebugEnabled()) {
                                logger.debug("Found beans DTD [" + systemId + "] in classpath: " + dtdFile);
                            }
                            return source;
                        }
                        catch (IOException ex) {
                            if (logger.isDebugEnabled()) {
                                logger.debug("Could not resolve beans DTD [" + systemId + "]: not found in class path", ex);
                            }
                        }
    
                    }
                }
            }
    
            // Use the default behavior -> download from website or wherever.
            return null;
        }
    
    
        @Override
        public String toString() {
            return "EntityResolver for DTDs " + Arrays.toString(DTD_NAMES);
        }
    
    }

    在PluggableSchemaResolver类中会去加载META-INF/spring.schemas

    http://www.springframework.org/schema/beans/spring-beans-2.0.xsd=org/springframework/beans/factory/xml/spring-beans-2.0.xsd
    http://www.springframework.org/schema/beans/spring-beans-2.5.xsd=org/springframework/beans/factory/xml/spring-beans-2.5.xsd
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd=org/springframework/beans/factory/xml/spring-beans-3.0.xsd
    http://www.springframework.org/schema/beans/spring-beans-3.1.xsd=org/springframework/beans/factory/xml/spring-beans-3.1.xsd
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd=org/springframework/beans/factory/xml/spring-beans-3.2.xsd
    http://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans-3.2.xsd
    http://www.springframework.org/schema/tool/spring-tool-2.0.xsd=org/springframework/beans/factory/xml/spring-tool-2.0.xsd
    http://www.springframework.org/schema/tool/spring-tool-2.5.xsd=org/springframework/beans/factory/xml/spring-tool-2.5.xsd
    http://www.springframework.org/schema/tool/spring-tool-3.0.xsd=org/springframework/beans/factory/xml/spring-tool-3.0.xsd
    http://www.springframework.org/schema/tool/spring-tool-3.1.xsd=org/springframework/beans/factory/xml/spring-tool-3.1.xsd
    http://www.springframework.org/schema/tool/spring-tool-3.2.xsd=org/springframework/beans/factory/xml/spring-tool-3.2.xsd
    http://www.springframework.org/schema/tool/spring-tool.xsd=org/springframework/beans/factory/xml/spring-tool-3.2.xsd
    http://www.springframework.org/schema/util/spring-util-2.0.xsd=org/springframework/beans/factory/xml/spring-util-2.0.xsd
    http://www.springframework.org/schema/util/spring-util-2.5.xsd=org/springframework/beans/factory/xml/spring-util-2.5.xsd
    http://www.springframework.org/schema/util/spring-util-3.0.xsd=org/springframework/beans/factory/xml/spring-util-3.0.xsd
    http://www.springframework.org/schema/util/spring-util-3.1.xsd=org/springframework/beans/factory/xml/spring-util-3.1.xsd
    http://www.springframework.org/schema/util/spring-util-3.2.xsd=org/springframework/beans/factory/xml/spring-util-3.2.xsd
    http://www.springframework.org/schema/util/spring-util.xsd=org/springframework/beans/factory/xml/spring-util-3.2.xsd

    然后根据systemId去对象的位置获取对应xsd文件

    为什么要设置EntityResolver呢?如果xml文件不符合xsd文件,则解析时候会报错,spring中对这种错误的情况只是报异常而已

    /*
     * Copyright 2002-2012 the original author or authors.
     *
     * 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 org.springframework.util.xml;
    
    import org.apache.commons.logging.Log;
    import org.xml.sax.ErrorHandler;
    import org.xml.sax.SAXException;
    import org.xml.sax.SAXParseException;
    
    /**
     * Simple {@code org.xml.sax.ErrorHandler} implementation:
     * logs warnings using the given Commons Logging logger instance,
     * and rethrows errors to discontinue the XML transformation.
     *
     * @author Juergen Hoeller
     * @since 1.2
     */
    public class SimpleSaxErrorHandler implements ErrorHandler {
    
        private final Log logger;
    
    
        /**
         * Create a new SimpleSaxErrorHandler for the given
         * Commons Logging logger instance.
         */
        public SimpleSaxErrorHandler(Log logger) {
            this.logger = logger;
        }
    
    
        public void warning(SAXParseException ex) throws SAXException {
            logger.warn("Ignored XML validation warning", ex);
        }
    
        public void error(SAXParseException ex) throws SAXException {
            throw ex;
        }
    
        public void fatalError(SAXParseException ex) throws SAXException {
            throw ex;
        }
    
    }
  • 相关阅读:
    [BZOJ4755][JSOI2016]扭动的回文串(manacher+Hash)
    十二省联考2019部分题解
    [BZOJ2959]长跑(LCT+并查集)
    [BZOJ4541][HNOI2016]矿区(平面图转对偶图)
    笛卡尔树
    [CF896C]Willem, Chtholly and Seniorious(珂朵莉树)
    [BZOJ4349]最小树形图
    [BZOJ1858][SCOI2010]序列操作(线段树)
    [PA2014]Parking
    [PA2014]Budowa
  • 原文地址:https://www.cnblogs.com/ghgyj/p/4027791.html
Copyright © 2020-2023  润新知