/*******************************************************************************
 * Copyright (c) 2008 SAP AG 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:
 * Kaloyan Raev, kaloyan.raev@sap.com - initial API and implementation
 *******************************************************************************/
package org.eclipse.jst.j2ee.internal.web.operations;

import static org.eclipse.jst.j2ee.application.internal.operations.IAnnotationsDataModel.USE_ANNOTATIONS;

import java.util.Set;

import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jst.common.internal.annotations.controller.AnnotationsController;
import org.eclipse.jst.common.internal.annotations.controller.AnnotationsControllerManager;
import org.eclipse.jst.common.internal.annotations.controller.AnnotationsControllerManager.Descriptor;
import org.eclipse.jst.j2ee.internal.common.operations.NewJavaEEArtifactClassOperation;
import org.eclipse.jst.j2ee.internal.web.plugin.WebPlugin;
import org.eclipse.jst.j2ee.project.facet.IJ2EEFacetConstants;
import org.eclipse.jst.j2ee.web.project.facet.WebFacetUtils;
import org.eclipse.wst.common.componentcore.ComponentCore;
import org.eclipse.wst.common.componentcore.datamodel.FacetInstallDataModelProvider;
import org.eclipse.wst.common.componentcore.datamodel.FacetProjectCreationDataModelProvider;
import org.eclipse.wst.common.componentcore.datamodel.properties.IFacetDataModelProperties;
import org.eclipse.wst.common.componentcore.datamodel.properties.IFacetProjectCreationDataModelProperties;
import org.eclipse.wst.common.componentcore.datamodel.properties.IFacetProjectCreationDataModelProperties.FacetDataModelMap;
import org.eclipse.wst.common.componentcore.internal.operation.ArtifactEditProviderOperation;
import org.eclipse.wst.common.componentcore.resources.IVirtualComponent;
import org.eclipse.wst.common.frameworks.datamodel.AbstractDataModelOperation;
import org.eclipse.wst.common.frameworks.datamodel.DataModelFactory;
import org.eclipse.wst.common.frameworks.datamodel.IDataModel;
import org.eclipse.wst.common.frameworks.internal.enablement.nonui.WFTWrappedException;
import org.eclipse.wst.common.project.facet.core.IFacetedProject;
import org.eclipse.wst.common.project.facet.core.ProjectFacetsManager;

/**
 * The NewWebClassOperation is an IDataModelOperation following the IDataModel
 * wizard and operation framework.
 * 
 * @see org.eclipse.wst.common.frameworks.datamodel.IDataModelOperation
 * @see org.eclipse.wst.common.frameworks.datamodel.IDataModelProvider
 * 
 * It extends AbstractDataModelOperation to provide web artifact specific java
 * class generation.
 * @see AbstractDataModelOperation
 * 
 * This operation is used by the AddWebClassOperation to generate either an
 * annotated or non annotated java class for an added web artifact. It shares
 * the NewWebClassDataModelProvider with the AddWebClassOperation to store the
 * appropriate properties required to generate the new web artifact.
 * 
 * @see AddWebClassOperation
 * @see NewWebClassDataModelProvider
 * 
 * In the annotated case, a WTPJetEmitter listener template is created and used
 * to generate the listener java class with the embedded annotated tags.
 * @see org.eclipse.jst.j2ee.internal.project.WTPJETEmitter
 * @see CreateWebClassTemplateModel
 * 
 * In the non annotated case, the same emitter is used to create the class with
 * the non annotated web artifact template so the annotated tags are omitted.
 * 
 * Subclasses may extend this operation to provide their own specific web
 * artifact java class generation. The execute method may be extended to do so.
 * Also, generateUsingTemplates is exposed.
 * 
 * The use of this class is EXPERIMENTAL and is subject to substantial changes.
 */
public abstract class NewWebClassOperation extends NewJavaEEArtifactClassOperation {

	/**
	 * XDoclet facet constants
	 */
	private static final String JST_WEB_XDOCLET_VERSION = "1.2.3"; //$NON-NLS-1$
	
	/**
	 * variable for the web plugin
	 */
	protected static final String WEB_PLUGIN = "WEB_PLUGIN"; //$NON-NLS-1$

	/**
	 * This is the constructor which should be used when creating a
	 * NewWebClassOperation. An instance of the NewWebClassDataModel should be
	 * passed in. This does not accept null parameter. It will not return null.
	 * 
	 * @see ArtifactEditProviderOperation#ArtifactEditProviderOperation(IDataModel)
	 * @see NewListenerClassDataModel
	 * 
	 * @param dataModel
	 * @return NewListenerClassOperation
	 */
	public NewWebClassOperation(IDataModel dataModel) {
		super(dataModel);
	}

	/**
	 * Subclasses may extend this method to provide their own template based
	 * creation of an annotated web artifact java class file. This implementation
	 * uses the creation of a CreateWebClassTemplateModel and the WTPJetEmitter
	 * to create the java class with the annotated tags. This method accepts
	 * null for monitor, it does not accept null for fragment. If annotations
	 * are not being used the tags will be omitted from the class.
	 * 
	 * @see CreateWebClassTemplateModel
	 * @see #generateTemplateSource(CreateWebClassTemplateModel, IProgressMonitor)
	 * 
	 * @param monitor
	 * @param fragment
	 * @throws CoreException
	 * @throws WFTWrappedException
	 */
	protected void generateUsingTemplates(IProgressMonitor monitor, IPackageFragment fragment) throws WFTWrappedException, CoreException {
		// Create the web artifact template model
		CreateWebClassTemplateModel tempModel = createTemplateModel();
		IProject project = getTargetProject();
		String source;
		try {
			// generate the java source based on the template model
			source = generateTemplateSource(WebPlugin.getPlugin(), tempModel, getTemplateFile(), getTemplateImplementation(), monitor);
		} catch (Exception e) {
			throw new WFTWrappedException(e);
		}
		if (fragment != null) {
			// Create the java file
			String javaFileName = tempModel.getClassName() + DOT_JAVA;
			ICompilationUnit cu = fragment.getCompilationUnit(javaFileName);
			// Add the compilation unit to the java file
			if (cu == null || !cu.exists())
				cu = fragment.createCompilationUnit(javaFileName, source, true, monitor);
			IFile aFile = (IFile) cu.getResource();
			// Let the annotations controller process the annotated resource
			AnnotationsController controller = AnnotationsControllerManager.INSTANCE.getAnnotationsController(project);
			if (controller != null)
				controller.process(aFile);
		}
		
		// Add the xdoclet facet, if necessary, for xdoclet listener creation
		try {
			installXDocletFacetIfNecessary(monitor, project);
		} catch (Exception e) {
			throw new WFTWrappedException(e);
		}
	}

	/**
	 * This method is intended for internal use only. This will add an webdoclet facet to the project.
	 * 
	 * @throws CoreException 
	 * @throws ExecutionException 
	 */
	private void installXDocletFacet(IProgressMonitor monitor, IProject project) throws CoreException, ExecutionException {
		IFacetedProject facetedProject = ProjectFacetsManager.create(project);
		Set fixedFacets = facetedProject.getFixedProjectFacets();
		IDataModel dm = DataModelFactory.createDataModel(new FacetInstallDataModelProvider());
		dm.setProperty(IFacetDataModelProperties.FACET_ID, IJ2EEFacetConstants.DYNAMIC_WEB_XDOCLET);
		dm.setProperty(IFacetDataModelProperties.FACET_PROJECT_NAME, project.getName());
		dm.setProperty(IFacetDataModelProperties.FACET_VERSION_STR, JST_WEB_XDOCLET_VERSION);
		IDataModel fdm = DataModelFactory.createDataModel(new FacetProjectCreationDataModelProvider());
		fdm.setProperty(IFacetProjectCreationDataModelProperties.FACET_PROJECT_NAME, project.getName());

		FacetDataModelMap map = (FacetDataModelMap) fdm.getProperty(IFacetProjectCreationDataModelProperties.FACET_DM_MAP);
		map.add(dm);

		fdm.getDefaultOperation().execute(monitor, null);
		facetedProject.setFixedProjectFacets(fixedFacets);
	}
	
	/**
	 * This method is intended for internal use only. This will check to see if it needs to add an 
	 * webdoclet facet to the project.
	 * 
	 * @throws CoreException 
	 * @throws ExecutionException 
	 */
	private void installXDocletFacetIfNecessary(IProgressMonitor monitor, IProject project) throws CoreException, ExecutionException {

		// If not using annotations, ignore the xdoclet facet
		if (!model.getBooleanProperty(USE_ANNOTATIONS))
			return;
		
		// If an extended annotations processor is added, ignore the default xdoclet one
		Descriptor descriptor = AnnotationsControllerManager.INSTANCE.getDescriptor(getTargetComponent().getProject());
		if (descriptor != null)
			return;

		// Otherwise check and see if the xdoclet facet is on the project yet
		IFacetedProject facetedProject = ProjectFacetsManager.create(project);
		if (!facetedProject.hasProjectFacet(WebFacetUtils.WEB_XDOCLET_FACET))
			return;
			
		// Install xdoclet facet
		installXDocletFacet(monitor, project);
	}

	/**
	 * This method will create an instance of the CreateWebClassTemplateModel to
	 * be used in conjunction with the WTPJETEmitter. This method will not
	 * return null.
	 * 
	 * @see CreateWebClassTemplateModel
	 * @see NewWebClassOperation#generateUsingTemplates(IProgressMonitor,
	 *      IPackageFragment)
	 * 
	 * @return CreateWebClassTemplateModel
	 */
	protected abstract CreateWebClassTemplateModel createTemplateModel();

	/**
	 * This method will return the location of the template file that will be
	 * passed to the WTPJETEmitter to generate the template implementation
	 * class.
	 * 
	 * @return path to the template file. 
	 */
	protected abstract String getTemplateFile();

	/**
	 * This method will return an instance of the template implementation class
	 * that is statically compiled in the plugin. This instance can be used to
	 * generate the artifact's code without using the WTPJETEmitter.
	 * 
	 * @return an instance of the template implementation class. 
	 */
	protected abstract Object getTemplateImplementation();

	private IVirtualComponent getTargetComponent() {
		return ComponentCore.createComponent(getTargetProject());
	}
	
}
