content: /*****************************************************************
   *   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
   *
   *    https://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.cayenne.tools;
  
  import org.apache.cayenne.access.DbGenerator;
  import org.apache.cayenne.datasource.DriverDataSource;
  import org.apache.cayenne.dba.DbAdapter;
  import org.apache.cayenne.dbsync.DbSyncModule;
  import org.apache.cayenne.di.DIBootstrap;
  import org.apache.cayenne.di.Injector;
  import org.apache.cayenne.log.NoopJdbcEventLogger;
  import org.apache.cayenne.map.DataMap;
  import org.apache.cayenne.map.DbEntity;
  import org.apache.cayenne.dbsync.reverse.configuration.ToolsModule;
  import org.apache.cayenne.util.Util;
  import org.slf4j.Logger;
  import org.apache.tools.ant.BuildException;
  import org.apache.tools.ant.Project;
  
  import java.sql.Driver;
  import java.util.Collections;
  
  /**
   * An Ant Task that is a frontend to Cayenne DbGenerator allowing schema
   * generation from DataMap using Ant.
   * 
   * @since 1.2
   */
  // TODO: support classpath attribute for loading the driver
  public class DbGeneratorTask extends CayenneTask {
  
  	// DbGenerator options... setup defaults similar to DbGenerator itself:
  	// all DROP set to false, all CREATE - to true
  	protected boolean dropTables;
  	protected boolean dropPK;
  	protected boolean createTables = true;
  	protected boolean createPK = true;
  	protected boolean createFK = true;
  
  	@Override
  	public void execute() {
  
  		Logger logger = new AntLogger(this);
  
  		log(String.format("connection settings - [driver: %s, url: %s, username: %s]", driver, url, userName),
  				Project.MSG_VERBOSE);
  
  		log(String.format(
  				"generator options - [dropTables: %s, dropPK: %s, createTables: %s, createPK: %s, createFK: %s]",
  				dropTables, dropPK, createTables, createPK, createFK), Project.MSG_VERBOSE);
  
  		validateAttributes();
  
  		ClassLoader loader = null;
  		Injector injector = DIBootstrap.createInjector(new DbSyncModule(), new ToolsModule(logger));
  		try {
  			loader = Thread.currentThread().getContextClassLoader();
  			Thread.currentThread().setContextClassLoader(DbGeneratorTask.class.getClassLoader());
  
  			// Load the data map and run the db generator.
  			DataMap dataMap = loadDataMap(injector);
  
  			// load driver taking custom CLASSPATH into account...
  			DriverDataSource dataSource = new DriverDataSource((Driver) Class.forName(driver).getDeclaredConstructor().newInstance(), url,
  					userName, password);
  
  			DbAdapter adapter = getAdapter(injector, dataSource);
  
  			DbGenerator generator = new DbGenerator(adapter, dataMap, Collections.<DbEntity> emptyList(), null,
  					NoopJdbcEventLogger.getInstance());
  			generator.setShouldCreateFKConstraints(createFK);
  			generator.setShouldCreatePKSupport(createPK);
  			generator.setShouldCreateTables(createTables);
  			generator.setShouldDropPKSupport(dropPK);
  			generator.setShouldDropTables(dropTables);
  
  			generator.runGenerator(dataSource);
  		} catch (Exception ex) {
  			Throwable th = Util.unwindException(ex);
  
  			String message = "Error generating database";
  
  			if (th.getLocalizedMessage() != null) {
  				message += ": " + th.getLocalizedMessage();
  			}
  
  			log(message, Project.MSG_ERR);
  			throw new BuildException(message, th);
  		} finally {
  			Thread.currentThread().setContextClassLoader(loader);
  			injector.shutdown();
  		}
  	}
  
  	/**
  	 * Validates attributes that are not related to internal
  	 * DefaultClassGenerator. Throws BuildException if attributes are invalid.
  	 */
  	protected void validateAttributes() throws BuildException {
  		StringBuilder error = new StringBuilder("");
  
  		if (map == null) {
  			error.append("The 'map' attribute must be set.\n");
  		}
  
  		if (driver == null) {
  			error.append("The 'driver' attribute must be set.\n");
  		}
  
  		if (url == null) {
  			error.append("The 'adapter' attribute must be set.\n");
  		}
  
  		if (error.length() > 0) {
  			throw new BuildException(error.toString());
  		}
  	}
  
  	public void setCreateFK(boolean createFK) {
  		this.createFK = createFK;
  	}
  
  	public void setCreatePK(boolean createPK) {
  		this.createPK = createPK;
  	}
  
  	public void setCreateTables(boolean createTables) {
  		this.createTables = createTables;
  	}
  
  	public void setDropPK(boolean dropPK) {
  		this.dropPK = dropPK;
  	}
  
  	public void setDropTables(boolean dropTables) {
  		this.dropTables = dropTables;
  	}
  }
  