/*
 * $Header: /home/cvspublic/jakarta-slide/src/stores/org/apache/slide/store/ojb/tools/SchemaReorderTask.java,v 1.1 2005/01/23 14:13:48 cvillegas Exp $
 * $Revision: 1.1 $
 * $Date: 2005/01/23 14:13:48 $
 *
 * ====================================================================
 *
 * Copyright 1999-2002 The Apache Software Foundation
 *
 * 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.apache.slide.store.ojb.tools;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import java.io.File;
import java.io.FileOutputStream;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.output.XMLOutputter;
import org.jdom.input.SAXBuilder;
import java.util.List;
import java.util.Iterator;
import java.util.HashSet;

/**
 * Reorder the table elements in the XML DDL schema so that tables in foreign key
 * constraints appear before tables that declares them. This is required
 * because Torque generates create statements in the order they appear in the 
 * schema file. Since this schema file is also generated, the order is undeterminate.
 *
 */
public class SchemaReorderTask extends Task {

    private File schema;
    private File output;
    private HashSet seen = new HashSet();
    
    /**
     * @return Returns the schema.
     */
    public File getSchema() {
        return schema;
    }
    /**
     * @param schema The database schema in Torque XML format for reorder
     */
    public void setSchema(File schema) {
        this.schema = schema;
    }
    
    /**
     * @return Returns the output.
     */
    public File getOutput() {
        return output;
    }
    /**
     * @param output The output to set.
     */
    public void setOutput(File output) {
        this.output = output;
    }
    
    /**
     * Checks that settings exist and in valid combinations
     * 
     * @throws BuildException if parameters are incorrect
     */
    private void assertValidSettings() throws BuildException {
        if (schema == null ) {
            throw new BuildException( "Must specify an schema attribute" );
        }
        if (output == null) {
            throw new BuildException( "Must specify an output attribute" );
        }
    }
    
    /**
     * Reorder the table elements in the XML schema so that dependencies appear
     * before the tables that require them. 
     * 
     * @see org.apache.tools.ant.Task#execute()
     */
    public void execute() throws BuildException {
        assertValidSettings();
        try {
            if ( output.exists() && output.lastModified() > schema.lastModified() )
                return;
            Document doc = new SAXBuilder().build(schema);
            List tables = doc.getRootElement().getChildren("table");
            for(int i=0; i<tables.size(); i++) {
                i = doTable(tables, i);
            }
            XMLOutputter outp = new XMLOutputter();
            outp.output(doc, new FileOutputStream(output));            
        } catch (Exception e) {
            throw new BuildException(e);
        }
    } 
    
    private int doTable(List tables, int i) {
        Element table = (Element)tables.get(i);
        List fks = table.getChildren("foreign-key");
        for (Iterator k=fks.iterator();k.hasNext();) {
            Element fk = (Element)k.next();
            String ft = fk.getAttributeValue("foreignTable");
            if ( ! seen.contains(ft) ) {
                Element dep = findTable(tables, i+1, ft);
                if ( dep != null ) {
                    tables.add(i, dep.detach());
                    i = doTable(tables, i) + 1;
                }
            }
        }
        seen.add(table.getAttributeValue("name"));
        return i;
    }
    
    private Element findTable(List tables, int start, String name) {
        for(int i=start; i<tables.size();i++) {
            Element table = (Element)tables.get(i);
            if ( table.getAttributeValue("name").equals(name) )
                return table;
        }
        return null;
    }
}
