/*
 * Copyright 2007 Werner Guttmann
 *
 * 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.castor.anttask;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Vector;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.taskdefs.MatchingTask;
import org.apache.tools.ant.types.FileSet;
import org.castor.ddlgen.Generator;
import org.castor.ddlgen.GeneratorException;
import org.castor.ddlgen.GeneratorFactory;
import org.castor.mapping.MappingUnmarshaller;
import org.exolab.castor.mapping.Mapping;
import org.exolab.castor.mapping.MappingException;

/**
 * An <a href="http://ant.apache.org/">Ant</a> task to call the Castor
 * DDL Generator. It can be passed a file, a directory, a Fileset or all
 * three.
 *
 * @author <a href="mailto:werner DOT guttmann AT gmx DOT net">Werner Guttmann</a>
 * @version $Revision: 6543 $ $Date: 2005-03-05 06:42:06 -0700 (Sat, 05 Mar 2005) $
 */
public final class CastorDDLGenTask extends MatchingTask {
    //--------------------------------------------------------------------------

    /** Error message -- no mapping to run ddl generator on. */
    private static final String NO_MAPPING_MSG =
        "A mapping file must be provided for DDL generation.";

    //--------------------------------------------------------------------------

    /** If processing one schema file, this lists the file. */
    private File _mappingFile = null;
    
    /** If processing all mapping files in a directory, this lists the directory. */
    private File _mappingDir = null;
    
    /** If processing a fileset, this lists the fileset. */
    private Vector _mappingFilesets = new Vector();

    // Begin DDL Generator parameters
    
    /** The package that generated code will belong to. */
    private String _ddlFileName;
    
    /** Name of database engine to generate DDL for. */
    private String _databaseEngine;
    
    /** Global properties file to be used during DDL generation. */
    private String _globalProperties;
    
    /** Database specific properties file to be used during DDL generatrion. */
    private String _databaseEngineProperties;
    
    /** OutputStream used for writing the generated DDL statements. */
    private OutputStream _outputStream; 

    //--------------------------------------------------------------------------

    /**
     * Sets the individual schema that will have code generated for it.
     * 
     * @param file One schema file.
     */
    public void setFile(final File file) {
        _mappingFile = file;
    }

    /**
     * Sets the directory such that all schemas in this directory will have code
     * generated for them.
     * 
     * @param dir The directory containing schemas to process.
     */
    public void setDir(final File dir) {
        _mappingDir = dir;
    }

    /**
     * Adds a fileset to process that contains schemas to process.
     * 
     * @param set An individual file set containing schemas.
     */
    public void addFileset(final FileSet set) {
        _mappingFilesets.addElement(set);
    }

    /**
     * Specifies the name of database engine to generate DDL for.
     * @param databaseEngine Name of the database engine
     */
    public void setDatabaseEngine(final String databaseEngine) {
        _databaseEngine = databaseEngine;
    }

    /**
     * Specifies the name of a database specific properties file to be used 
     * during DDL generation.
     * @param databaseEngineProperties Database specific properties
     */
    public void setDatabaseEngineProperties(final String databaseEngineProperties) {
        _databaseEngineProperties = databaseEngineProperties;
    }

    /**
     * Specifies the name of the DDL file to be generated.
     * @param ddlFileName Name of the DDL file to be generated
     */
    public void setDdlFileName(final String ddlFileName) {
        _ddlFileName = ddlFileName;
    }

    /**
     * Specifies the name of a global properties file to be used during 
     * DDL generation.
     * @param globalProperties Custom global properties for DDL generation.
     */
    public void setGlobalProperties(final String globalProperties) {
        _globalProperties = globalProperties;
    }

    //--------------------------------------------------------------------------

    /**
     * Configured the code generator. If anything goes wrong during configuration of the
     * Ant task a BuildException will be thrown.
     */
    private void config() {
        if (_databaseEngine == null) {
            throw new BuildException("No database engine specified.");
        }
        
        try {
            _outputStream = new FileOutputStream(_ddlFileName);
        } catch (IOException e) {
            throw new BuildException("Problem finding the Castor JDO mapping file " 
                    + _mappingFile.getAbsolutePath(), e);
        }
    }

    /**
     * Runs source generation. If anything goes wrong during source generation a
     * BuildException will be thrown.
     * 
     * @param filePath an individual Schema to generate code for.
     */
    private void processFile(final String filePath) {
        log("Processing " + filePath);
        try {
            Generator ddlgen = GeneratorFactory.createDDLGenerator(
                    _databaseEngine, _globalProperties, _databaseEngineProperties);
            
            Mapping mapping = new Mapping();
            mapping.loadMapping(filePath);
            new MappingUnmarshaller().loadMappingOnly(mapping);
            // TODO: Joachim 2007-09-07 the InternalContext should be set into the unmarshaller!
            
            ddlgen.setMapping(mapping);
            ddlgen.generateDDL(_outputStream);
        } catch (IOException e) {
            throw new BuildException ("Problem finding the Castor JDO mapping file " 
                    + _mappingFile.getAbsolutePath(), e);
        } catch (MappingException e) {
            throw new BuildException ("Problem loading the Castor JDO mapping file " 
                    + _mappingFile.getAbsolutePath(), e);
        } catch (GeneratorException e) {
            throw new BuildException ("Problem generating DDL script for " 
                    + _mappingFile.getAbsolutePath(), e);
        }
    }

    /**
     * Public execute method -- entry point for the Ant task.  Loops over all
     * schema that need code generated and creates needed code generators, then
     * executes them. If anything goes wrong during execution of the Ant task a
     * BuildException will be thrown.
     * 
     * @see org.apache.tools.ant.Task#execute()
     */
    public void execute() {
        // Must have something to run the source generator on
        if (_mappingFile == null && _mappingDir == null && _mappingFilesets.size() == 0) {
            throw new BuildException(NO_MAPPING_MSG);
        }

        config();

        // Run DDL generator on file
        if (_mappingFile != null) {
            processFile(_mappingFile.getAbsolutePath());
        }

        // Run source generator on all files in directory
        if (_mappingDir != null && _mappingDir.exists() && _mappingDir.isDirectory()) {
            DirectoryScanner ds = this.getDirectoryScanner(_mappingDir);

            String[] files = ds.getIncludedFiles();
            for (int i = 0; i < files.length; i++) {
                String filePath = _mappingDir.getAbsolutePath() + File.separator + files[i];
                processFile(filePath);
            }
        }

        // Run source generator on all files in FileSet
        for (int i = 0; i < _mappingFilesets.size(); i++) {
            FileSet fs = (FileSet) _mappingFilesets.elementAt(i);
            DirectoryScanner ds = fs.getDirectoryScanner(getProject());
            File subdir = fs.getDir(getProject());

            String[] files = ds.getIncludedFiles();
            for (int j = 0; j < files.length; j++) {
                String filePath = subdir.getAbsolutePath() + File.separator + files[j];
                processFile(filePath);
            }
        }
    }
}
