content: /**
   * ﻿Copyright (C) 2009 - 2019 52°North Initiative for Geospatial Open Source
   * Software GmbH
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 as published
   * by the Free Software Foundation.
   *
   * If the program is linked with libraries which are licensed under one of
   * the following licenses, the combination of the program with the linked
   * library is not considered a "derivative work" of the program:
   *
   *       • Apache License, version 2.0
   *       • Apache Software License, version 1.0
   *       • GNU Lesser General Public License, version 3
   *       • Mozilla Public License, versions 1.0, 1.1 and 2.0
   *       • Common Development and Distribution License (CDDL), version 1.0
   *
   * Therefore the distribution of the program linked with libraries licensed
   * under the aforementioned licenses, is permitted by the copyright holders
   * if the distribution is compliant with both the GNU General Public
   * License version 2 and the aforementioned licenses.
   *
   * This program is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
   * Public License for more details.
   */
  package org.n52.wps.ags.algorithmpackage;
  
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.StringReader;
  import java.io.StringWriter;
  import java.net.URI;
  import java.util.List;
  
  import javax.xml.bind.JAXBContext;
  import javax.xml.bind.JAXBException;
  import javax.xml.bind.Unmarshaller;
  import javax.xml.transform.Source;
  import javax.xml.transform.Transformer;
  import javax.xml.transform.TransformerException;
  import javax.xml.transform.TransformerFactory;
  import javax.xml.transform.stream.StreamResult;
  import javax.xml.transform.stream.StreamSource;
  
  import org.apache.commons.io.FileUtils;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import org.apache.xmlbeans.XmlException;
  import org.apache.xmlbeans.XmlOptions;
  
  import net.opengis.wps.x100.InputDescriptionType;
  import net.opengis.wps.x100.OutputDescriptionType;
  import net.opengis.wps.x100.ProcessDescriptionType;
  import net.opengis.wps.x100.ProcessDescriptionsDocument;
  
  /**
   * @author Matthias Mueller, TU Dresden
   *
   */
  public class AlgorithmPackage {
  	private final File algorithmWorkspace;
  
  	private ProcessDescriptionType processDescription;
  	private AlgorithmDescription algorithmDescription;
  
  	static Logger LOGGER = LoggerFactory.getLogger(AlgorithmPackage.class);
  
  	public AlgorithmPackage (ProcessDescriptionType pd, AlgorithmDescription ad, File algorithmWorkspace){
  
  		// create descriptions
  		algorithmDescription = ad;
  		processDescription = pd;
  
  		// assign workspace
  		this.algorithmWorkspace = algorithmWorkspace;
  
  	}
  
  	public AlgorithmPackage (File processDescriptionXML, File workspaceTemplateRoot){
  
  		// create descriptions
  		processDescription = createProcessDescription(processDescriptionXML);
  		algorithmDescription = createAlgorithmDescription(processDescriptionXML);
  
  		// assemble template workspace
  		String wsDirString = workspaceTemplateRoot.getAbsolutePath() + File.separator + getWorkspacePathFragment();
  		try {
  			wsDirString = new File(wsDirString).getCanonicalPath();
  		} catch (IOException e) {
  			LOGGER.warn("Could not determine the workspace's canonical path. Possibly a malformed AlgorithmDescription.\n"
  					+ "rootDirectory is: " + workspaceTemplateRoot.getAbsolutePath() + "\n"
  					+ "workspacePathFragment is: " + getWorkspacePathFragment());
  		}
  		algorithmWorkspace = new File(wsDirString);
  	}
  
  	public AlgorithmPackage createChild (File destinationRoot) throws IOException{
  		FileUtils.copyDirectoryToDirectory(algorithmWorkspace, destinationRoot);
  		File wsDir = new File(destinationRoot.getAbsolutePath() + File.separator + getWorkspacePathFragment());
  		AlgorithmPackage child = new AlgorithmPackage(processDescription, algorithmDescription, wsDir);
  		return child;
  	}
  
  	public String getProcessID(){
  		return processDescription.getIdentifier().getStringValue();
  	}
  
  	public boolean isContainer(URI containerURN){
  		String str = containerURN.toString();
  		if (str.equalsIgnoreCase(algorithmDescription.getContainerType())){
  			return true;
  		} else {
  			return false;
  		}
  	}
  
  	public boolean isContainer(URI[] containerURNs){
  		for(URI currentContainer : containerURNs){
  			if (isContainer(currentContainer)){
  				return true;
  			}
  		}
  		return false;
  	}
  
  	public boolean isSufficientRuntimeEnvironment (URI[] runtimeURNs){
  		boolean doublecheck = true;
  		for (String myCurrentRuntime : algorithmDescription.getRequiredRuntimeComponent()){
  			boolean check = false;
  			for (URI yourCurrentRuntime : runtimeURNs){
  				if (yourCurrentRuntime.toString().equalsIgnoreCase(myCurrentRuntime)){
  					check = true;
  				}
  			}
  			doublecheck = check && doublecheck;
  		}
  
  		return doublecheck;
  	}
  
  	public ProcessDescriptionType getProcessDescription(){
  		return processDescription;
  	}
  
  	public File getInstanceWorkspace(){
  		return algorithmWorkspace;
  	}
  
  	public AlgorithmURL getAlgorithmURL(){
  		return new AlgorithmURL(algorithmDescription.getAlgorithmLocation());
  	}
  
  	public  List<AlgorithmParameterType> getParameters(){
  		return algorithmDescription.getAlgorithmParameters().getParameter();
  	}
  
  	public String getDefaultMimeType(String paramID){
  		String mimeType = null;
  
  		// check inputs for a match
  		for (InputDescriptionType currentInput : processDescription.getDataInputs().getInputArray()){
  			if (currentInput.getIdentifier().getStringValue().equalsIgnoreCase(paramID)){
  				mimeType = currentInput.getComplexData().getDefault().getFormat().getMimeType();
  			}
  		}
  
  		for (OutputDescriptionType currentOutput : processDescription.getProcessOutputs().getOutputArray()){
  			if (currentOutput.getIdentifier().getStringValue().equalsIgnoreCase(paramID)){
  				mimeType = currentOutput.getComplexOutput().getDefault().getFormat().getMimeType();
  			}
  		}
  
  		return mimeType;
  	}
  
  	private String getWorkspacePathFragment(){
  		return algorithmDescription.getWorkspaceLocation();
  	}
  
  	private static AlgorithmDescription createAlgorithmDescription (File xmlFile) {
  
  		// create the AlgorithmDescription document from DescribeProcess file
  		StringReader adReader = generateAlgorithmDescription(xmlFile);
  
  		try {
  			JAXBContext jc = JAXBContext.newInstance(AlgorithmDescription.class);
  			Unmarshaller unmarshaller = jc.createUnmarshaller();
  			AlgorithmDescription ad = (AlgorithmDescription) unmarshaller.unmarshal(adReader);
  			return ad;
  		} catch (JAXBException e) {
  			LOGGER.error("Unable to create AlgorithmDescription from xmlFile: " + xmlFile.getAbsolutePath());
  			e.printStackTrace();
  			return null;
  		}
  
  	}
  
  	private static ProcessDescriptionType createProcessDescription (File describeProcessFile){
  
  		try {
  			InputStream xmlDesc = new FileInputStream(describeProcessFile);
  			XmlOptions option = new XmlOptions();
  			option.setLoadTrimTextBuffer();
  			ProcessDescriptionsDocument doc = ProcessDescriptionsDocument.Factory.parse(xmlDesc, option);
  			if(doc.getProcessDescriptions().getProcessDescriptionArray().length == 0) {
  				LOGGER.warn("ProcessDescription is empty!");
  				return null;
  			}
  			return doc.getProcessDescriptions().getProcessDescriptionArray(0);
  		}
  		catch(IOException e) {
  			LOGGER.warn("Could not initialize algorithm, parsing error! ", e);
  		}
  		catch(XmlException e) {
  			LOGGER.warn("Could not initialize algorithm, parsing error! ", e);
  		}
  		return null;
  	}
  
  
  	/**
       * Generates an XML file. Uses a ProcessDescription file and a XSLT file to generate an
       * AlgorithmDescription.
       *
       * @param xmlFile - the ProcessDescription in XML
       * @param xsltFile - the transformation rules in XSLT
       * @throws Exception - the exceptions
       */
      private static StringReader generateAlgorithmDescription(File xmlFile) {
  
      	Source xmlSource = new StreamSource(xmlFile);
      	TransformerFactory transFact = TransformerFactory.newInstance();
      	StringWriter sw = new StringWriter();
          StreamResult transformResult = new StreamResult(sw);
  
      	try {
      		Source xsltSource = transFact.getAssociatedStylesheet(xmlSource, null, null, null);
  			Transformer trans = transFact.newTransformer(xsltSource);
  			trans.transform(xmlSource, transformResult);
  			StringReader sr = new StringReader(sw.toString());
  			return sr;
  
  		} catch (TransformerException e) {
  			LOGGER.error("Error evaluating ProcessDescription XML.");
  			e.printStackTrace();
  		}
  		return null;
      }
  
  }
  