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

// Contributors:
//     Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.sdo.helper;

import java.net.URI;
import java.net.URL;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import org.eclipse.persistence.logging.AbstractSessionLog;
import org.xml.sax.InputSource;

/**
 * <p><b>Purpose</b>: Default implementation of the org.eclipse.persistence.sdo.helper.SchemaResolver interface
 * <p><b>Responsibilities</b>:<ul>
 * <li> Given the source schema and namespace and schemaLocation values from an import or include return the referenced Schema
 * <li> If a baseSchemaLocation is set it will be prepended to all schemaLocations passed into the resovleSchema methods
 * <li> This implementation will try to open an Inputstream to a URL created from the schemaLocation and return a StreamSource based on that inputstream
 * </ul>
 *
 * @see org.eclipse.persistence.sdo.helper.SchemaResolver
 */
public class DefaultSchemaResolver implements SchemaResolver {
    private String baseSchemaLocation;

    public DefaultSchemaResolver() {
    }

    /**
     * Given the source schema and namespace and schemaLocation values from an import
     * or include return the referenced Schema.
     *
     * @param sourceXSD The Source object of the source schema
     * @param namespace The namespace portion of the import/include
     * @param schemaLocation The schemaLocation portion of the import/include
     * @return Source for the referenced Schema or null if processing the referenced
     * schema should be skipped
     */
    public Source resolveSchema(Source sourceXSD, String namespace, String schemaLocation) {
        try {
            URL schemaUrl = null;
            String baseLoc = getBaseSchemaLocation();
            if (baseLoc == null) {
                // No base location, use schema location

                URI schemaUri = new URI(schemaLocation);
                if(!(schemaUri.isAbsolute()) && sourceXSD.getSystemId() != null) {
                    baseLoc = sourceXSD.getSystemId();
                    schemaUrl = new URI(baseLoc).resolve(schemaUri).toURL();
                } else {
                    // No base location, use schema location
                    schemaUrl = new URI(schemaLocation).toURL();
                }
            } else {
                // May need to resolve against the base location
                URI schemaUri = new URI(schemaLocation);
                // If the schema location is absolute, ignore base location
                if (schemaUri.isAbsolute()) {
                    schemaUrl = schemaUri.toURL();
                } else {
                    // Attempt to resolve the schema location against the base location
                    URI baseUri = new URI(baseLoc);
                    // Handle 'jar:' case - we will need to try and create the schema URL manually
                    if (baseUri.isOpaque() && baseUri.getScheme().equals("jar")) {
                        // Example - jar:file:/C:/schema.jar!/my.xsd
                        // Strip off anything after the last '/' character (base location could represent a file)
                        schemaUrl = new URI(baseLoc.substring(0, baseLoc.lastIndexOf("/") + 1) + schemaLocation).toURL();
                    } else {
                        schemaUrl = new URI(baseLoc).resolve(schemaUri).toURL();
                    }
                }
            }
            return new StreamSource(schemaUrl.toExternalForm());
        } catch (Exception e) {
            AbstractSessionLog.getLog().log(AbstractSessionLog.WARNING, "sdo_error_processing_referenced_schema", new Object[] { e.getClass().getName(), namespace, schemaLocation });
            AbstractSessionLog.getLog().logThrowable(AbstractSessionLog.FINEST, e);
        }
        return null;
    }

    /**
     * Satisfy EntityResolver interface implementation.
     * Allow resolution of external entities.
     *
     * @param publicId
     * @param systemId
     * @return null
     */
    public InputSource resolveEntity(String publicId, String systemId) {
        return null;
    }

    /**
    * Optional baseSchemaLocation can be specified
    * If set, all schemaLocations passed into the resolveSchema methods will be resolved
    * against this base location according to the java.net.URI API
    *
    * @param baseSchemaLocation optional baseSchemaLocation
    */
    public void setBaseSchemaLocation(String baseSchemaLocation) {
        this.baseSchemaLocation = baseSchemaLocation;
    }

    public String getBaseSchemaLocation() {
        return baseSchemaLocation;
    }
}
