package org.apache.torque.templates.transformer;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.torque.generator.control.ControllerState;
import org.apache.torque.generator.source.SourceElement;
import org.apache.torque.generator.source.SourceException;
import org.apache.torque.generator.source.stream.FileSource;
import org.apache.torque.generator.source.stream.XmlSourceFormat;
import org.apache.torque.generator.source.transform.SourceTransformer;
import org.apache.torque.generator.source.transform.SourceTransformerException;
import org.apache.torque.templates.TorqueSchemaAttributeName;
import org.apache.torque.templates.TorqueSchemaElementName;

/**
 * A SourceTransformer which includes other schemata into the current graph.
 *
 * @version $Id: IncludeSchemaTransformer.java 1435917 2013-01-20 19:15:48Z tfischer $
 */
public class IncludeSchemaTransformer implements SourceTransformer
{
    /** The class log. */
    private static Log log
            = LogFactory.getLog(IncludeSchemaTransformer.class);

    /**
     * The base dir for the external schema,
     * or null to compute from the current source file.
     */
    private File baseDir;

    /**
     * Standard constructor.
     */
    public IncludeSchemaTransformer()
    {
    }

    /**
     * Constructor to override base dir.
     *
     * @param baseDir the new base dir.
     */
    public IncludeSchemaTransformer(File baseDir)
    {
        this.baseDir = baseDir;
    }

    /**
     * Loads the external schemata tables into the current graph.
     * The external database element is added as child of the
     * external-schema element.
     * Also, an all-tables child element is added to the root element,
     * which is filled with all tables from the external schema plus its own
     * tables.
     *
     * @param root the database root element of the source tree, not null.
     * @param controllerState the controller state, not null.
     *
     * @throws SourceTransformerException if the transformation fails.
     */
    public SourceElement transform(
            SourceElement root,
            ControllerState controllerState)
        throws SourceTransformerException
    {
        List<SourceElement> includeSchemaList
                = root.getChildren(TorqueSchemaElementName.INCLUDE_SCHEMA);
        List<SourceElement> childrenList = root.getChildren();

        for (SourceElement includeSchemaElement : includeSchemaList)
        {
            File includeSchemaBaseDir;
            if (this.baseDir == null)
            {
                File currentSourceFile = controllerState.getSourceFile();
                includeSchemaBaseDir = currentSourceFile.getParentFile();
            }
            else
            {
                includeSchemaBaseDir = baseDir;
            }
            String relativePath = includeSchemaElement.getAttribute(
                    TorqueSchemaAttributeName.FILENAME)
                .toString();
            File includeSchemaPath
                = new File(includeSchemaBaseDir, relativePath);
            log.trace("Trying to read included file " + includeSchemaPath);
            try
            {
                FileSource fileSource = new FileSource(
                        new XmlSourceFormat(),
                        includeSchemaPath,
                        controllerState);
                SourceElement includeSchemaRootElement
                        = fileSource.getRootElement();
                log.trace("successfully read included file "
                        + includeSchemaPath);

                this.transform(includeSchemaRootElement, controllerState);

                // disattach children from their current parent
                // so that the new parent is the primary parent
                List<SourceElement> toIncludeList
                        = new ArrayList<SourceElement>(
                                includeSchemaRootElement.getChildren());
                for (SourceElement childToInclude : toIncludeList)
                {
                    childToInclude.getParents().clear();
                }

                childrenList.addAll(toIncludeList);

                log.trace("finished processing included file "
                        + includeSchemaPath);
            }
            catch (SourceException e)
            {
                log.error("Could not construct source from schema file "
                        + includeSchemaPath,
                    e);
                throw new SourceTransformerException(e);
            }
        }
        return root;
    }
}
