/*
 *    GeoTools - OpenSource mapping toolkit
 *    http://geotools.org
 *    (C) 2002-2006, GeoTools Project Managment Committee (PMC)
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation;
 *    version 2.1 of the License.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 */
package org.geotools.gml3;

import org.eclipse.xsd.XSDSchema;
import org.eclipse.xsd.util.XSDSchemaLocationResolver;
import java.io.File;
import java.io.IOException;
import org.geotools.xml.BindingConfiguration;
import org.geotools.xml.Configuration;
import org.geotools.xs.XSConfiguration;


/**
 * An xml configuration for application schemas.
 * <p>
 * This Configuration expects the namespace and schema location URI of the main
 * xsd file for a given application schema and is able to resolve the schema
 * location for the includes and imports as well as they're defined as relative
 * paths and the provided <code>schemaLocation</code> is a file URI.
 * </p>
 *
 * @author Justin Deoliveira, The Open Planning Project
 * @author Gabriel Roldan, Axios Engineering
 * @version $Id: ApplicationSchemaConfiguration.java 27504 2007-10-15 22:38:02Z
 *          groldan $
 * @since 2.4
 */
public class ApplicationSchemaConfiguration extends Configuration {
    /** application schema namespace */
    private String namespace;

    /** location of the application schema itself */
    private String schemaLocation;

    public ApplicationSchemaConfiguration(String namespace, String schemaLocation) {
        assert namespace != null;
        assert schemaLocation != null;
        this.namespace = namespace;
        this.schemaLocation = schemaLocation;
        addDependency(new XSConfiguration());
        addDependency(new GMLConfiguration());
    }

    public String getNamespaceURI() {
        return namespace;
    }

    public String getSchemaFileURL() {
        return schemaLocation;
    }

    public BindingConfiguration getBindingConfiguration() {
        return null;
    }

    /**
     * Returns a {@link XSDSchemaLocationResolver} that expects to be asked for
     * the location of the schema file for the imports and includes of the
     * schema targeted by this configuration, and is able to resolve their
     * actual locations as relative paths to
     * {@link #getSchemaFileURL() this schema location}, if available.
     *
     * @return a {@link XSDSchemaLocationResolver} that
     */
    public XSDSchemaLocationResolver getSchemaLocationResolver() {
        return new XSDSchemaLocationResolver() {
                /**
                 * Uses the <code>schema.getSchemaLocation()</code>'s parent
                 * folder as the base folder to resolve <code>location</code> as a
                 * relative URI of.
                 * <p>
                 * This way, application schemas splitted over multiple files can be
                 * resolved based on the relative location of a given import or
                 * include.
                 * </p>
                 * <p>
                 * If the <code>location</code> is provided as argument but cannot
                 * be resolved to a relative location to
                 * <code>schema.getSchemaLocation()</code>, then
                 * <code>null</code> is returned and the caller shall utilize the
                 * absolute URL location to get the resource from, or some other
                 * sort of finding the actual schema location.
                 * </p>
                 *
                 * @param schema
                 *            the schema being resolved
                 * @param uri
                 *            the namespace being resolved. If its an empty string
                 *            (i.e. the location refers to an include, and thus the
                 *            uri to the same one than the schema), the schema one
                 *            is used.
                 * @param location
                 *            the xsd location, either of <code>schema</code>, an
                 *            import or an include, for which to try resolving it as
                 *            a relative path of the <code>schema</code> location.
                 * @return a file: style uri with the resolved schema location for
                 *         the given one, or <code>null</code> if
                 *         <code>location</code> can't be resolved as a relative
                 *         path of the <code>schema</code> location.
                 */
                public String resolveSchemaLocation(final XSDSchema schema, final String uri,
                    final String location) {
                    if (uri == null) {
                        throw new NullPointerException("got null uri");
                    }

                    if (location == null) {
                        throw new NullPointerException("got null location");
                    }

                    String schemaLocation;

                    if (schema == null) {
                        if (ApplicationSchemaConfiguration.this.namespace.equals(uri)) {
                            //about to resolve the root schema this configuration is meant for
                            schemaLocation = ApplicationSchemaConfiguration.this.schemaLocation;
                        } else {
                            throw new NullPointerException("got null schema");
                        }
                    } else {
                        // try to resolve location as a relative path
                        schemaLocation = schema.getSchemaLocation();

                        if ("".equals(schemaLocation)) {
                            // is it an include of the root schema being resolved?
                            schemaLocation = ApplicationSchemaConfiguration.this.schemaLocation;
                        }
                    }

                    String schemaLocationFolder;
                    int lastSlash = schemaLocation.lastIndexOf('/');

                    if (lastSlash > 0) {
                        schemaLocationFolder = schemaLocation.substring(0, lastSlash);
                    } else {
                        schemaLocationFolder = ".";
                    }

                    if (schemaLocationFolder.startsWith("file:")) {
                        schemaLocationFolder = schemaLocationFolder.substring(5);
                    }

                    File locationFile = new File(schemaLocationFolder, location);

                    if (locationFile.exists()) {
                        try {
                            locationFile = locationFile.getCanonicalFile();
                        } catch (IOException e) {
                            throw new RuntimeException("Can't access file, might be "
                                + "access to file denied by security manager: "
                                + locationFile.getAbsolutePath());
                        }

                        String resolvedLocationUri = locationFile.toURI().toString();

                        return resolvedLocationUri;
                    }

                    return null;
                }
            };
    }
}
