/*******************************************************************************
 * Copyright (c) 2009 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

package org.eclipse.pde.api.tools.internal.tasks;

import java.io.PrintWriter;
import java.io.StringWriter;

import org.apache.tools.ant.BuildException;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiBaseline;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiElement;
import org.eclipse.pde.api.tools.internal.search.XMLApiSearchReporter;

/**
 * Ant task for performing the API use analysis of a given Eclipse SDK
 * 
 * @since 1.0.1
 * @noextend This class is not intended to be subclassed by clients.
 */
public class ApiUseTask extends UseTask {
	
	/**
	 * Set the flag to indicate if the usage scan should try to proceed when an error is encountered
	 * or stop.
	 * <p>
	 * The default value is <code>false</code>.
	 * </p>
	 * @param proceed if the scan should try to continue in the face of errors. Valid values 
	 * are <code>true</code> or <code>false</code>.
	 */
	public void setProceedOnError(String proceed) {
		this.proceedonerror = Boolean.valueOf(proceed).booleanValue();
	}
	
	/**
	 * Set the flag to indicate if the usage search should include system libraries in the scope and baseline.
	 * <p>
	 * The default value is <code>false</code>.
	 * </p>
	 * @param include if system libraries should be considered in the search scope and baseline. Valid values 
	 * are <code>true</code> or <code>false</code>.
	 */
	public void setIncludeSystemLibraries(String include) {
		this.includesystemlibraries = Boolean.valueOf(include).booleanValue();
	}
	
	/**
	 * Set the location of the current product or baseline that you want to search.
	 * 
	 * <p>It can be a .zip, .jar, .tgz, .tar.gz file, or a directory that corresponds to 
	 * the Eclipse installation folder. This is the directory is which you can find the 
	 * Eclipse executable.
	 * </p>
	 *
	 * @param baselineLocation the given location for the baseline to analyze
	 */
	public void setBaseline(String baselineLocation) {
		this.currentBaselineLocation = baselineLocation;
	}
	
	/**
	 * Sets the location of the scope to search against. If a scope is not provided, the baseline
	 * will act a both a scope and a baseline: i.e. the baseline will be searched for a complete reference graph.
	 * 
	 * <p>It can be a .zip, .jar, .tgz, .tar.gz file, or a directory that corresponds to
	 * a location of bundles that make a complete resolved baseline (i.e. bundles and all of their dependencies).
	 * </p>
	 * 
	 * @param scopeLocation the given location for the scope to search against
	 */
	public void setScope(String scopeLocation) {
		this.scopeLocation = scopeLocation;
	}
	
	/**
	 * Sets a flag to indicate if projects that have not been set up for API tooling should be allowed 
	 * in the search scope.
	 * <p>
	 * The default value is <code>false</code>.
	 * </p>
	 * @param includenonapi if non- API enabled projects should be included in the search scope. Valid values 
	 * are <code>true</code> or <code>false</code>.
	 */
	public void setIncludeNonApiProjects(String includenonapi) {
		this.includenonapi = Boolean.valueOf(includenonapi).booleanValue();
	}
	
	/**
	 * Set the exclude list location.
	 * 
	 * <p>The exclude list is used to know what bundles should excluded from the xml report generated by the task
	 * execution. Lines starting with '#' are ignored from the excluded elements.</p>
	 * <p>The format of the exclude list file looks like this:</p>
	 * <pre>
	 * # DOC BUNDLES
	 * org.eclipse.jdt.doc.isv
	 * org.eclipse.jdt.doc.user
	 * org.eclipse.pde.doc.user
	 * org.eclipse.platform.doc.isv
	 * org.eclipse.platform.doc.user
	 * # NON-ECLIPSE BUNDLES
	 * com.ibm.icu
	 * com.jcraft.jsch
	 * javax.servlet
	 * javax.servlet.jsp
	 * ...
	 * </pre>
	 * <p>The location is set using an absolute path.</p>
	 *
	 * @param excludeListLocation the given location for the excluded list file
	 */
	public void setExcludeList(String excludeListLocation) {
		this.excludeListLocation = excludeListLocation;
	}

	/**
	 * Set the output location where the reports will be generated.
	 * 
	 * <p>Once the task is completed, reports are available in this directory using a structure
	 * similar to the filter root. A sub-folder is created for each component that has problems
	 * to be reported. Each sub-folder contains a file called "report.xml". </p>
	 * 
	 * <p>A special folder called "allNonApiBundles" is also created in this folder that contains a xml file called
	 * "report.xml". This file lists all the bundles that are not using the api tooling nature.</p>
	 * 
	 * @param baselineLocation the given location for the reference baseline to analyze
	 */
	public void setReport(String reportLocation) {
		this.reportLocation = reportLocation;
	}
	
	/**
	 * Set the execution environment file to use.
	 * <p>By default, an execution environment file corresponding to a JavaSE-1.6 execution environment
	 * is used.</p>
	 * <p>The file is specified using an absolute path. This is optional.</p> 
	 *
	 * @param eeFileLocation the given execution environment file
	 */
	public void setEEFile(String eeFileLocation) {
		this.eeFileLocation = eeFileLocation;
	}
	
	/**
	 * Set the debug value.
	 * <p>The possible values are: <code>true</code>, <code>false</code></p>
	 * <p>Default is <code>false</code>.</p>
	 *
	 * @param debugValue the given debug value
	 */
	public void setDebug(String debugValue) {
		this.debug = Boolean.toString(true).equals(debugValue); 
	}
	
	/**
	 * Sets if references to API types should be considered in the search.
	 * <p>The possible values are: <code>true</code>, <code>false</code></p>
	 * <p>Default is <code>false</code>.</p>
	 * 
	 * @param considerapi the given value
	 */
	public void setConsiderAPI(String considerapi) {
		this.considerapi = Boolean.toString(true).equals(considerapi);
	}
	
	/**
	 * Sets if references to internal types should be considered in the search.
	 * <p>The possible values are: <code>true</code>, <code>false</code></p>
	 * <p>Default is <code>false</code>.</p>
	 * 
	 * @param considerapi the given value
	 */
	public void setConsiderInternal(String considerinternal) {
		this.considerinternal = Boolean.toString(true).equals(considerinternal);
	}
	
	/**
	 * @see org.eclipse.pde.api.tools.internal.tasks.UseTask#assertParameters()
	 */
	protected void assertParameters() throws BuildException {
		super.assertParameters();
		if (this.reportLocation == null) {
			StringWriter out = new StringWriter();
			PrintWriter writer = new PrintWriter(out);
			writer.println(Messages.bind(
					Messages.ApiUseTask_missing_report_location, 
					new String[] {this.reportLocation}));
			writer.flush();
			writer.close();
			throw new BuildException(String.valueOf(out.getBuffer()));
		}
	}
	
	/* (non-Javadoc)
	 * @see org.apache.tools.ant.Task#execute()
	 */
	public void execute() throws BuildException {
		assertParameters();
		writeDebugHeader();
		cleanReportLocation();
		
		IApiBaseline baseline = getBaseline(CURRENT_BASELINE_NAME, this.currentBaselineLocation);
		IApiBaseline scope = getBaseline(SCOPE_BASELINE_NAME, this.scopeLocation);
		if(scope == null) {
			scope = baseline;
		}
		initializeExcludeSet(scope);
		this.reporter = new XMLApiSearchReporter(this.reportLocation, this.debug);
		try {
			doSearch(baseline, scope, this.reporter);
		}
		catch(CoreException ce) {
			throw new BuildException(Messages.ApiUseTask_search_engine_problem, ce);
		}
		finally {
			if(baseline != null) {
				baseline.dispose();
				deleteBaseline(this.currentBaselineLocation, getBaselineInstallDir(CURRENT_BASELINE_NAME));
			}
			if(scope != null && this.scopeLocation != null) {
				scope.dispose();
				deleteBaseline(this.scopeLocation, getBaselineInstallDir(SCOPE_BASELINE_NAME));
			}
			this.reporter.reportNotSearched((IApiElement[]) this.notsearched.toArray(new IApiElement[this.notsearched.size()]));
		}
	}
}
