content: /**
   * ﻿Copyright (C) 2007 - 2019 52°North Initiative for Geospatial Open Source
   * Software GmbH
   *
   * 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.n52.wps.client;
  
  import java.io.ByteArrayInputStream;
  import java.io.IOException;
  import java.io.InputStream;
  import java.net.MalformedURLException;
  import java.net.URL;
  
  import javax.xml.transform.TransformerException;
  import javax.xml.transform.TransformerFactoryConfigurationError;
  
  import net.opengis.ows.x11.ExceptionReportDocument.ExceptionReport;
  import net.opengis.wps.x100.DocumentOutputDefinitionType;
  import net.opengis.wps.x100.ExecuteDocument;
  import net.opengis.wps.x100.ExecuteResponseDocument;
  import net.opengis.wps.x100.OutputDataType;
  import net.opengis.wps.x100.OutputDescriptionType;
  import net.opengis.wps.x100.ProcessDescriptionType;
  
  import org.n52.wps.commons.XMLUtil;
  import org.n52.wps.io.IParser;
  import org.n52.wps.io.data.IData;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import org.w3c.dom.Node;
  import org.w3c.dom.NodeList;
  
  /*
   * 
   * returns the processOutputs according to the encoding of the process.
   * @author foerster
   *
   */
  public class ExecuteResponseAnalyser {
  	
  	private static Logger LOGGER = LoggerFactory.getLogger(ExecuteResponseAnalyser.class);
  	
  	ProcessDescriptionType processDesc;
  	ExecuteDocument exec;
  	Object response;
  	
  	public ExecuteResponseAnalyser(ExecuteDocument exec, Object response, ProcessDescriptionType processDesc) throws WPSClientException {
  		this.processDesc = processDesc;
  		this.exec= exec;
  		if(response instanceof ExceptionReport){
  			throw new WPSClientException("Output is not ComplexData but an Exception Report");
  		}
  		this.response = response;
  	}
  	
  	
  /**
   * delivers the parsed ComplexData by name
   * @param outputID id of the output element
   * @param binding Class of the output binding, e.g. GenericFileDataBinding
   * @return the parsed ComplexData in the requested format defined by the binding
   * @throws WPSClientException
   */
  	public IData getComplexData(String outputID, Class<?> binding) throws WPSClientException {
  		return parseProcessOutput(outputID, binding);
  		
  	}
  	
  	/**
  	 * delivers the parsed ComplexData by index
  	 * @param index index of the output element starting with 0
  	 * @param binding Class of the output binding, e.g. GenericFileDataBinding
  	 * @return the parsed ComplexData in the requested format defined by the binding
  	 * @throws WPSClientException
  	 */
  	public IData getComplexDataByIndex(int index, Class<?> binding) throws WPSClientException {
  		ExecuteResponseDocument doc = null;
  		if(response instanceof ExecuteResponseDocument){
  			doc = (ExecuteResponseDocument) response;
  		}else{
  			throw new WPSClientException("Output cannot be determined by index since it is either raw data or an exception report");
  		}
  		OutputDataType[] outputs = doc.getExecuteResponse().getProcessOutputs().getOutputArray();
  		int counter = 0; 
  		for(OutputDataType output : outputs) {
  			if(output.getData().getComplexData() != null) {
  				if(counter == index) {
  					return this.parseProcessOutput(output.getIdentifier().getStringValue(), binding);
  				}
  				counter ++;
  			}
  		}
  		return null;
  	}
  	
  	/**
  	 * delivers just the URL of a referenced output identified by index
  	 * @param index index of the output
  	 * @return URL of the stored output
  	 * @throws WPSClientException 
  	 */
  	public String getComplexReferenceByIndex(int index) throws WPSClientException {
  		ExecuteResponseDocument doc = null;
  		if(response instanceof ExecuteResponseDocument){
  			doc = (ExecuteResponseDocument) response;
  		}else{
  			throw new WPSClientException("Output cannot be determined by index since it is either raw data or an exception report");
  		}
  		OutputDataType[] outputs = doc.getExecuteResponse().getProcessOutputs().getOutputArray();
  		int counter = 0; 
  		for(OutputDataType output : outputs) {
  			if(output.getReference() != null) {
  				if(counter == index) {
  					return output.getReference().getHref();
  				}
  				counter ++;
  			}
  		}
  		RuntimeException rte = new RuntimeException("No reference found in response");		
  		LOGGER.error(rte.getMessage());
  		throw rte;
  	}
  		
  	
  		
  	
  	/**
  	 * parses a specific WPS output
  	 * 
  	 * @param outputID id of the output
  	 * @param outputDataBindingClass class of the desired output binding
  	 * @return parsed WPS output as IData
  	 * @throws WPSClientException
  	 */
  	private IData parseProcessOutput(String outputID, Class<?> outputDataBindingClass) throws WPSClientException {
  		OutputDescriptionType outputDesc = null;
  		
  		String schema = null;
  		String mimeType = null;
  		String encoding = null;
  		
  		if(exec.getExecute().isSetResponseForm() && exec.getExecute().getResponseForm().isSetRawDataOutput()){
  			// get data specification from request
  			schema = exec.getExecute().getResponseForm().getRawDataOutput().getSchema();
  			mimeType = exec.getExecute().getResponseForm().getRawDataOutput().getMimeType();
  			encoding = exec.getExecute().getResponseForm().getRawDataOutput().getEncoding();
  		}else if(exec.getExecute().isSetResponseForm() && exec.getExecute().getResponseForm().isSetResponseDocument()){
  			DocumentOutputDefinitionType[] outputs = exec.getExecute().getResponseForm().getResponseDocument().getOutputArray();
  			for(DocumentOutputDefinitionType output : outputs){
  				if(output.getIdentifier().getStringValue().equals(outputID)){
  					schema = output.getSchema();
  					mimeType = output.getMimeType();
  					encoding = output.getEncoding();
  				}
  			}
  			
  		}
  		
  		if(mimeType==null){
  			for(OutputDescriptionType tempDesc : processDesc.getProcessOutputs().getOutputArray()) {
  				if(outputID.equals(tempDesc.getIdentifier().getStringValue())) {
  					outputDesc = tempDesc;
  					break;
  				}
  			}
  			// get default data spec since mime type is not set
  			mimeType = outputDesc.getComplexOutput().getDefault().getFormat().getMimeType();
  			encoding = outputDesc.getComplexOutput().getDefault().getFormat().getEncoding();
  			schema = outputDesc.getComplexOutput().getDefault().getFormat().getSchema();
  		}
  		
  		IParser parser = StaticDataHandlerRepository.getParserFactory().getParser(schema, mimeType, encoding, outputDataBindingClass);
  		InputStream is = null;
  		if(response instanceof InputStream){
  			is = (InputStream)response;
  		}else if (response instanceof ExecuteResponseDocument){
  			ExecuteResponseDocument responseDocument = (ExecuteResponseDocument) response;
  			OutputDataType[] processOutputs = responseDocument.getExecuteResponse().getProcessOutputs().getOutputArray();
  			for(OutputDataType processOutput : processOutputs){
  				if(processOutput.getIdentifier().getStringValue().equalsIgnoreCase(outputID)){
  					if(processOutput.isSetReference()){
  						//request the reference
  						String urlString = processOutput.getReference().getHref();
  						URL url;
  						try {
  							url = new URL(urlString);
  							is = url.openStream();
  						} catch (MalformedURLException e) {
  							throw new WPSClientException("Could not fetch response from referenced URL", e);
  						} catch (IOException e) {
  							throw new WPSClientException("Could not fetch response from referenced URL", e);
  						}
  						
  					}else{
  						String complexDataContent;
  						try {
  							
  							NodeList candidateNodes = processOutput.getData().getComplexData().getDomNode().getChildNodes();
  							
  						    Node complexDataNode = candidateNodes.getLength() > 1 ? candidateNodes.item(1) : candidateNodes.item(0);							
  							
  							complexDataContent = XMLUtil.nodeToString(complexDataNode);
  							is = new ByteArrayInputStream(complexDataContent.getBytes());
  						} catch (TransformerFactoryConfigurationError e) {
  							LOGGER.error(e.getMessage());
  						} catch (TransformerException e) {
  							LOGGER.error(e.getMessage());
  						}
  					}
  					
  				}
  			}
  			
  		}else{
  			throw new WPSClientException("Wrong output type");
  		}
  		
  		
  		if(parser != null) {
  			if(encoding != null && encoding.equalsIgnoreCase("base64")){
  				return parser.parseBase64(is, mimeType, schema);
  			}else{
  				return parser.parse(is, mimeType, schema);
  			}
  		}
  		RuntimeException rte = new RuntimeException("Could not find suitable parser");		
  		LOGGER.error(rte.getMessage());
  		throw rte;
  	}
  }