/**
 * GenerateMetadataAction.java
 * Created on Jun 20, 2008
 * (C) Copyright TANDBERG Television Ltd.
 */
package com.tandbergtv.watchpoint.pmm.action.schedule.ingest;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.Date;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.log4j.Logger;
import org.jbpm.graph.def.ActionHandler;
import org.jbpm.graph.exe.ExecutionContext;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.tandbergtv.watchpoint.pmm.util.PathProperties;
import com.tandbergtv.watchpoint.pmm.util.schedule.ingest.ADIConstants;
import com.tandbergtv.watchpoint.pmm.util.schedule.ingest.ConversionPropertyReader;
import com.tandbergtv.watchpoint.pmm.util.schedule.ingest.FileType;
import com.tandbergtv.watchpoint.pmm.util.schedule.ingest.PlannerConstants;
import com.tandbergtv.watchpoint.pmm.util.schedule.ingest.ProgramListConstants;

/**
 * This class generates metadata file (ADI.xml) for every title in each of the
 * planners in the planners xml file. The path for this file is picked from the
 * context's 'covertedFilepath' variable.
 * 
 * @author spuranik
 * 
 */
public class GenerateMetadataAction implements ActionHandler {

	private static final long serialVersionUID = 1L;
	private static final Logger logger = Logger
			.getLogger(GenerateMetadataAction.class);

	// variables from the template
	private static final String CONVERTED_FILE_PATH = "convertedFilepath";
	private static final String SOURCE_PROVIDER_ID = "providerId";
	private static final String ERROR_MESSAGE = "error-message";
	private static String FILE_TYPE = "type";	

	// metadata file naming convention constants
	private static final String METADATA_FILE_PREFIX = "ADI_";
	private static final String METADATA_FILE_EXTN = ".xml";

	// this is the provider Id for which the metadata is being generated
	private String providerId;

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jbpm.graph.def.ActionHandler#execute(org.jbpm.graph.exe.ExecutionContext)
	 */
	public void execute(ExecutionContext context) throws Exception {

		String inputFilepath = (String) context.getVariable(CONVERTED_FILE_PATH);
		setProviderId((String) context.getVariable(SOURCE_PROVIDER_ID));		
		FileType type = FileType.getFileType((String) context.getVariable(FILE_TYPE));
		File inputFile = new File(inputFilepath);
		if (!inputFile.exists()) {
			String errorMsg = inputFile + " does not exist. Failed to generate metadata";
			throw new Exception(errorMsg);
		}

		String assetFolder = getAssetFolder(providerId);		
		try {
			generateMetadata(inputFilepath, type, assetFolder);
		} catch (RuntimeException e) {
			logger.error(e);
			context.setVariable(ERROR_MESSAGE, e.getMessage());
			throw new RuntimeException(e);
		}
	}

	/**
	 * gets the root folder name from path.properties and prefixes that to the
	 * asset folder name from conversion.properties
	 * 
	 * @return
	 */
	private String getAssetFolder(String providerId) {
		String contentPath = PathProperties
				.getProperty(PathProperties.CONTENT_PATH);
		String assetFolder = PathProperties
				.getProperty(PathProperties.INGEST_ASSET_PATH);

		String assetPath = contentPath + File.separator + providerId
				+ File.separator + assetFolder;

		// create the asset folder if not already present.
		File file = new File(assetPath);
		if (!file.exists()) {
			file.mkdirs();
		}
		return assetPath;
	}

	/**
	 * generates metadata files for each of the titles in the planner. Metadata
	 * files are written to the metadata folder created for each asset under the
	 * asset folder.
	 * 
	 * @param plannerFilepath
	 * @param metadataFolder
	 */
	public void generateMetadata(String inputFilepath, FileType type, String assetFolder) {
		
		NodeList titles = getTitles(inputFilepath, type);
		for (int i = 0; i < titles.getLength(); i++) {
			Element currTitle = (Element) titles.item(i);

			try {
				// for each title create a metadata file with name
				// ADI_[timestamp].xml in the asset folder.
				// Later this file should be renamed to ADI.xml by the
				// user of this file to ADI.xml
				Date now = new Date(System.currentTimeMillis());
				String metadataFilename = assetFolder + File.separator
						+ METADATA_FILE_PREFIX + now.getTime()
						+ METADATA_FILE_EXTN;
				createMetadata(currTitle, metadataFilename);
			} catch (RuntimeException e) {
				// if metadata for any one title fails, it will not affect
				// the others.
				String titleName = currTitle.getElementsByTagName(
						PlannerConstants.NAME_ELEMENT).item(0).getTextContent();
				String message = "Failed to generate metadata file for title: "
						+ titleName + ". Reason: " + e.getMessage();
				throw new RuntimeException(message, e);
			}
		}
	}

	/**
	 * Gets the list of top level title elements from the given file based on
	 * whether it's a planner or a program list. Any error while parsing the
	 * input xml will be thrown back.
	 * 
	 * @param inputFilepath
	 * @return
	 */
	private NodeList getTitles(String inputFilepath, FileType type) {

		try {
			DocumentBuilderFactory factory = DocumentBuilderFactory
					.newInstance();
			DocumentBuilder docBuilder = factory.newDocumentBuilder();
			Document doc = docBuilder.parse(inputFilepath);

			XPath xPath = XPathFactory.newInstance().newXPath();

			if (type == FileType.PLANNER) {
				return (NodeList) xPath.evaluate(
						PlannerConstants.TITLE_XPATH, doc,
						XPathConstants.NODESET);
			} else if (type == FileType.PROGRAM_LIST) {
				return (NodeList) xPath.evaluate(
						ProgramListConstants.PROGRAMLIST_TITLE_XPATH, doc,
						XPathConstants.NODESET);
			}
		} catch (XPathExpressionException e) {
			throw new RuntimeException(e);
		} catch (ParserConfigurationException e) {
			throw new RuntimeException(e);
		} catch (SAXException e) {
			throw new RuntimeException(e);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		return null;
	}

	/**
	 * prepares a ADI.xml for this title element (package). Any fields that are
	 * not present in the metadata are empty strings.
	 * 
	 * @param title
	 *            title for which metadata file needs to be generated.
	 * @param metadataFilename
	 *            complete path where the metadata file will be written.
	 * 
	 */
	private void createMetadata(Element title, String metadataFilename) {
		String pkgAssetId = getNodeValue(title, PlannerConstants.ASSET_ID_XPATH);
		if (pkgAssetId.trim().length() == 0) {
			throw new RuntimeException("Metadata file not generated for title as package Asset Id was missing");
		}

		Document doc = createADIDocument();
		// add package
		addPackage(title, doc);
		
		// add title
		Element children = (Element) title.getElementsByTagName(
				PlannerConstants.TITLE_CHILDREN_ELEMENT).item(0);
		Element childTitle = (Element) children.getElementsByTagName(
				PlannerConstants.TITLE_ELEMENT).item(0);
		addTitle(childTitle, doc);

		// add movie, poster, preview, box cover assets
		NodeList titleChildren = childTitle
				.getElementsByTagName(PlannerConstants.TITLE_ELEMENT);
		for (int i = 0; i < titleChildren.getLength(); i++) {
			Element currChild = (Element) titleChildren.item(i);
			String childName = currChild.getElementsByTagName(
					PlannerConstants.NAME_ELEMENT).item(0).getTextContent();
			if (childName.equals(PlannerConstants.MOVIE_TITLE_NAME)) {
				addMovie(currChild, doc);
			}
			if (childName.equals(PlannerConstants.PREVIEW_TITLE_NAME)) {
				addMovie(currChild, doc);
				addPreviewMetadata(doc, currChild);
			}
			if (childName.equals(PlannerConstants.POSTER_TITLE_NAME)) {
				addPoster(currChild, doc);
			}
			if (childName.equals(PlannerConstants.BOXCOVER_TITLE_NAME)) {
				addBoxCover(currChild, doc);
			}
		}
		// save the generated metadata file
		print(doc, metadataFilename);
		logger.info("Generated Metadata file: " + metadataFilename);		
	}

	/**
	 * Adds a box cover asset to the ADI.xml
	 * 
	 * @param boxCover
	 * @param doc
	 */
	private void addBoxCover(Element boxCover, Document doc) {
		Element asset = doc.createElement(ADIConstants.ASSET_ELEMENT);

		Element metadata = doc.createElement(ADIConstants.METADATA_ELEMENT);
		Element ams = createAMSElement(doc, boxCover);
		metadata.appendChild(ams);

		// Type
		Element appData = createAppDataElement(doc, getNodeValue(boxCover,
				PlannerConstants.PRODUCT_XPATH), ADIConstants.TYPE,
				getNodeValue(boxCover, PlannerConstants.NAME_XPATH));
		metadata.appendChild(appData);

		// Content_CheckSum
		appData = createAppDataElement(doc, getNodeValue(boxCover,
				ADIConstants.CONTENT_CHECKSUM), ADIConstants.CONTENT_CHECKSUM,
				getNodeValue(boxCover, PlannerConstants.CONTENT_CHECKSUM_XPATH));
		metadata.appendChild(appData);

		// Content_FileSize
		appData = createAppDataElement(doc, getNodeValue(boxCover,
				ADIConstants.CONTENT_FILESIZE), ADIConstants.CONTENT_FILESIZE,
				getNodeValue(boxCover, PlannerConstants.CONTENT_FILESIZE_XPATH));
		metadata.appendChild(appData);

		String boxCoverFilename = getNodeValue(boxCover,
				PlannerConstants.CONTENT_FILENAME_XPATH);
		Element content = prepareContent(doc, boxCoverFilename);

		asset.appendChild(metadata);
		asset.appendChild(content);
		getAsset(doc, ADIConstants.TITLE_ASSET_XPATH).appendChild(asset);
	}

	/**
	 * adds poster related info to the ADI.xml
	 * 
	 * @param poster
	 * @param doc
	 */
	private void addPoster(Element poster, Document doc) {
		Element asset = doc.createElement(ADIConstants.ASSET_ELEMENT);

		Element metadata = doc.createElement(ADIConstants.METADATA_ELEMENT);
		Element ams = createAMSElement(doc, poster);
		metadata.appendChild(ams);

		// Type
		Element appData = createAppDataElement(doc, getNodeValue(poster,
				PlannerConstants.PRODUCT_XPATH), ADIConstants.TYPE,
				getNodeValue(poster, PlannerConstants.NAME_XPATH));
		metadata.appendChild(appData);

		// Content_CheckSum
		appData = createAppDataElement(doc, getNodeValue(poster,
				PlannerConstants.PRODUCT_XPATH), ADIConstants.CONTENT_CHECKSUM,
				getNodeValue(poster, PlannerConstants.CONTENT_CHECKSUM_XPATH));
		metadata.appendChild(appData);

		// Content_FileSize
		appData = createAppDataElement(doc, getNodeValue(poster,
				PlannerConstants.PRODUCT_XPATH), ADIConstants.CONTENT_FILESIZE,
				getNodeValue(poster, PlannerConstants.CONTENT_FILESIZE_XPATH));
		metadata.appendChild(appData);

		String posterFilename = getNodeValue(poster,
				PlannerConstants.CONTENT_FILENAME_XPATH);
		Element content = prepareContent(doc, posterFilename);

		asset.appendChild(metadata);
		asset.appendChild(content);
		getAsset(doc, ADIConstants.TITLE_ASSET_XPATH).appendChild(asset);
	}

	/**
	 * Adds preview related metadata to the already created preview element in
	 * the ADI.xml These are the remaining metadata fields that need be added
	 * apart from the fields common to a movie
	 * 
	 * @param preview
	 */
	private void addPreviewMetadata(Document doc, Element preview) {
		// preview element should be the last element added before this is
		// called.
		Element appData = (Element) getAsset(doc, ADIConstants.PREVIEW_ASSET_XPATH);		
		Element metadata = (Element) appData.getElementsByTagName(ADIConstants.METADATA_ELEMENT).item(0);

		// rating
		appData = createAppDataElement(doc, getNodeValue(preview,
				PlannerConstants.PRODUCT_XPATH), ADIConstants.RATING,
				getNodeValue(preview, PlannerConstants.RATING_XPATH));
		metadata.appendChild(appData);

		// run_time
		appData = createAppDataElement(doc, getNodeValue(preview,
				PlannerConstants.PRODUCT_XPATH), ADIConstants.RUN_TIME,
				getNodeValue(preview, PlannerConstants.RUNTIME_XPATH));
		metadata.appendChild(appData);
	}

	/**
	 * adds movie related aDI info to the given doc
	 * 
	 * @param movie
	 * @param doc
	 */
	private void addMovie(Element movie, Document doc) {
		Element asset = doc.createElement(ADIConstants.ASSET_ELEMENT);

		Element metadata = doc.createElement(ADIConstants.METADATA_ELEMENT);
		Element ams = createAMSElement(doc, movie);
		metadata.appendChild(ams);

		// Type
		Element appData = createAppDataElement(doc, getNodeValue(movie,
				PlannerConstants.PRODUCT_XPATH), ADIConstants.TYPE,
				getNodeValue(movie, PlannerConstants.NAME_XPATH));
		metadata.appendChild(appData);

		// HDContent
		appData = createAppDataElement(doc, getNodeValue(movie,
				PlannerConstants.PRODUCT_XPATH), ADIConstants.HDCONTENT,
				getNodeValue(movie, PlannerConstants.HDCONTENT_XPATH));
		metadata.appendChild(appData);

		// Content_CheckSum
		appData = createAppDataElement(doc, getNodeValue(movie,
				PlannerConstants.PRODUCT_XPATH), ADIConstants.CONTENT_CHECKSUM,
				getNodeValue(movie, PlannerConstants.CONTENT_CHECKSUM_XPATH));
		metadata.appendChild(appData);

		// Content_FileSize
		appData = createAppDataElement(doc, getNodeValue(movie,
				PlannerConstants.PRODUCT_XPATH), ADIConstants.CONTENT_FILESIZE,
				getNodeValue(movie, PlannerConstants.CONTENT_FILESIZE_XPATH));
		metadata.appendChild(appData);

		// Audio_Type
		appData = createAppDataElement(doc, getNodeValue(movie,
				PlannerConstants.PRODUCT_XPATH), ADIConstants.AUDIO_TYPE,
				getNodeValue(movie, PlannerConstants.AUDIO_TYPE_XPATH));
		metadata.appendChild(appData);

		String movieFilename = getNodeValue(movie,
				PlannerConstants.CONTENT_FILENAME_XPATH);
		Element content = prepareContent(doc, movieFilename);

		asset.appendChild(metadata);
		asset.appendChild(content);

		getAsset(doc, ADIConstants.TITLE_ASSET_XPATH).appendChild(asset);
	}

	/**
	 * prepares a content element used in the ADI xml with the given value
	 * 
	 * @param value
	 * @return
	 */
	private Element prepareContent(Document doc, String value) {
		Element content = doc.createElement(ADIConstants.CONTENT_ELEMENT);
		content.setAttribute(ADIConstants.CONTENT_VALUE_ATTR, value);
		return content;
	}

	/**
	 * adds title required ADI info in the doc
	 * 
	 * @param title
	 *            title element representing the ADI title
	 * @param doc
	 *            doc to which the title will be added
	 */
	private void addTitle(Element title, Document doc) {

		Element asset = doc.createElement(ADIConstants.ASSET_ELEMENT);
		doc.getElementsByTagName(ADIConstants.ADI_ELEMENT).item(0).appendChild(
				asset);

		Element metadata = doc.createElement(ADIConstants.METADATA_ELEMENT);
		Element ams = createAMSElement(doc, title);
		metadata.appendChild(ams);

		// type
		Element appData = createAppDataElement(doc, getNodeValue(title,
				PlannerConstants.PRODUCT_XPATH), ADIConstants.TYPE,
				getNodeValue(title, PlannerConstants.NAME_XPATH));
		metadata.appendChild(appData);

		// title
		appData = createAppDataElement(doc, getNodeValue(title,
				PlannerConstants.PRODUCT_XPATH), ADIConstants.TITLE,
				getNodeValue(title, PlannerConstants.TITLE_TITLE_XPATH));
		metadata.appendChild(appData);

		// summary_short
		appData = createAppDataElement(doc, getNodeValue(title,
				PlannerConstants.PRODUCT_XPATH), ADIConstants.SUMMARY_SHORT,
				getNodeValue(title, PlannerConstants.SUMMARY_SHORT_XPATH));
		metadata.appendChild(appData);

		// summary_medium
		appData = createAppDataElement(doc, getNodeValue(title,
				PlannerConstants.PRODUCT_XPATH), ADIConstants.SUMMARY_MEDIUM,
				getNodeValue(title, PlannerConstants.SUMMARY_MEDIUM_XPATH));
		metadata.appendChild(appData);

		// diplay_run_time
		appData = createAppDataElement(doc, getNodeValue(title,
				PlannerConstants.PRODUCT_XPATH), ADIConstants.DISPLAY_RUN_TIME,
				getNodeValue(title, PlannerConstants.DISPLAY_RUNTIME_XPATH));
		metadata.appendChild(appData);

		// RUN_TIME
		appData = createAppDataElement(doc, getNodeValue(title,
				PlannerConstants.PRODUCT_XPATH), ADIConstants.RUN_TIME,
				getNodeValue(title, PlannerConstants.RUNTIME_XPATH));
		metadata.appendChild(appData);

		// Rating
		appData = createAppDataElement(doc, getNodeValue(title,
				PlannerConstants.PRODUCT_XPATH), ADIConstants.RATING,
				getNodeValue(title, PlannerConstants.RATING_XPATH));
		metadata.appendChild(appData);

		// billing_id
		appData = createAppDataElement(doc, getNodeValue(title,
				PlannerConstants.PRODUCT_XPATH), ADIConstants.BILLING_ID,
				getNodeValue(title, PlannerConstants.BILLING_ID_XPATH));
		metadata.appendChild(appData);

		// lis start
		appData = createAppDataElement(doc, getNodeValue(title,
				PlannerConstants.PRODUCT_XPATH),
				ADIConstants.LICENSE_WINDOW_START, getNodeValue(title,
						PlannerConstants.LICENSE_WINDOW_START_XPATH));
		metadata.appendChild(appData);

		// lis end
		appData = createAppDataElement(doc, getNodeValue(title,
				PlannerConstants.PRODUCT_XPATH),
				ADIConstants.LICENSE_WINDOW_END, getNodeValue(title,
						PlannerConstants.LICENSE_WINDOW_END_XPATH));
		metadata.appendChild(appData);

		// title brief
		appData = createAppDataElement(doc, getNodeValue(title,
				PlannerConstants.PRODUCT_XPATH), ADIConstants.TITLE_BRIEF,
				getNodeValue(title, PlannerConstants.TITLE_BRIEF_XPATH));
		metadata.appendChild(appData);

		// Preview_Period
		appData = createAppDataElement(doc, getNodeValue(title,
				PlannerConstants.PRODUCT_XPATH), ADIConstants.PREVIEW_PERIOD,
				getNodeValue(title, PlannerConstants.PREVIEW_PERIOD_XPATH));
		metadata.appendChild(appData);

		// qa contct
		appData = createAppDataElement(doc, getNodeValue(title,
				PlannerConstants.PRODUCT_XPATH),
				ADIConstants.PROVIDER_QA_CONTACT, getNodeValue(title,
						PlannerConstants.PROVIDER_QA_CONTACT_XPATH));
		metadata.appendChild(appData);

		asset.appendChild(metadata);
	}

	/**
	 * @return document for ADI xml with ADI element.
	 */
	private Document createADIDocument() {
		try {
			DocumentBuilderFactory factory = DocumentBuilderFactory
					.newInstance();
			DocumentBuilder docBuilder = factory.newDocumentBuilder();
			Document doc = docBuilder.newDocument();

			Element adi = doc.createElement(ADIConstants.ADI_ELEMENT);
			doc.appendChild(adi);
			return doc;
		} catch (DOMException e) {
			throw new RuntimeException(e);
		} catch (ParserConfigurationException e) {
			throw new RuntimeException(e);
		}	
	}

	/**
	 * adds all ADI info for the package
	 * 
	 * @param title
	 *            title representing the package
	 * @param doc
	 *            doc to which the package will be added
	 */
	private void addPackage(Element title, Document doc) {
		Element metadata = doc.createElement(ADIConstants.METADATA_ELEMENT);
		Element ams = createAMSElement(doc, title);
		metadata.appendChild(ams);

		Element appData = createAppDataElement(doc, getNodeValue(title,
				PlannerConstants.PRODUCT_XPATH),
				ADIConstants.SPECIFICATION_VERSION, getNodeValue(title,
						PlannerConstants.SPECIFICATION_XPATH));
		metadata.appendChild(appData);

		doc.getElementsByTagName(ADIConstants.ADI_ELEMENT).item(0).appendChild(
				metadata);

	}

	/**
	 * creates App_Data element with the given name and value
	 * 
	 * @param doc
	 * @return
	 */
	private Element createAppDataElement(Document doc, String app, String name,
			String value) {
		Element appData = doc.createElement(ADIConstants.APP_DATA_ELEMENT);

		appData.setAttribute(ADIConstants.APPDATA_APP_ATTR, app);
		appData.setAttribute(ADIConstants.APPDATA_NAME_ATTR, name);
		appData.setAttribute(ADIConstants.APPDATA_VALUE_ATTR, value);

		return appData;
	}

	/**
	 * Creates common AMS element used in ADI
	 * 
	 * @param doc
	 * @return
	 */
	private Element createAMSElement(Document doc, Element title) {
		Element ams = doc.createElement(ADIConstants.AMS_ELEMENT);

		ams.setAttribute(ADIConstants.AMS_ASSET_CLASS_ATTR, getNodeValue(title,
				PlannerConstants.NAME_XPATH));
		ams.setAttribute(ADIConstants.AMS_ASSET_ID_ATTR, getNodeValue(title,
				PlannerConstants.ASSET_ID_XPATH));
		ams.setAttribute(ADIConstants.AMS_ASSET_NAME_ATTR, getNodeValue(title,
				PlannerConstants.ASSET_NAME_XPATH));
		ams.setAttribute(ADIConstants.CREATION_DATE_ATTR, getNodeValue(title,
				PlannerConstants.CREATION_DATE_XPATH));
		ams.setAttribute(ADIConstants.AMS_ASSET_DESCRIPTION_ATTR, getNodeValue(
				title, PlannerConstants.DESCRIPTION_XPATH));
		ams.setAttribute(ADIConstants.AMS_PRODUCT_ATTR, getNodeValue(title,
				PlannerConstants.PRODUCT_XPATH));
		ams.setAttribute(ADIConstants.AMS_PROVIDER_ATTR, getNodeValue(title,
				PlannerConstants.PROVIDER_XPATH));
		ams.setAttribute(ADIConstants.AMS_PROVIDERID_ATTR, getNodeValue(title,
				PlannerConstants.PROVIDER_ID_XPATH));
		ams.setAttribute(ADIConstants.AMS_VERB_ATTR, getNodeValue(title,
				PlannerConstants.VERB_XPATH));
		ams.setAttribute(ADIConstants.AMS_VERSION_MAJOR_ATTR, getNodeValue(
				title, PlannerConstants.VERSION_MAJOR_XPATH));
		ams.setAttribute(ADIConstants.AMS_VERSION_MINOR_ATTR, getNodeValue(
				title, PlannerConstants.VERSION_MINOR_XPATH));

		return ams;
	}

	/**
	 * returns the value of the node that matches the xpression in the title
	 * 
	 * @param title
	 * @return
	 */
	private String getNodeValue(Element title, String expression) {
		try {
			XPathFactory factory = XPathFactory.newInstance();
			XPath xpath = factory.newXPath();
			XPathExpression expr = xpath.compile(expression);
			Object result = expr.evaluate(title, XPathConstants.NODE);
			return result == null ? null : ((Node) result).getTextContent();
		} catch (XPathExpressionException e) {
			logger.error(e);
		} catch (DOMException e) {
			logger.error(e);
		}
		return null;
	}

	/**
	 * returns the element in the doc that matches the given expression
	 * 
	 * @param doc
	 *            doc that needs to be looked up
	 * @param expression
	 *            xpath xpression to match
	 * @return node that matched the xpath
	 */
	private Node getAsset(Document doc, String expression) {
		try {
			XPathFactory factory = XPathFactory.newInstance();
			XPath xpath = factory.newXPath();
			XPathExpression expr = xpath.compile(expression);
			Object result = expr.evaluate(doc.getDocumentElement(),
					XPathConstants.NODE);
			return result == null ? null : ((Node) result);
		} catch (XPathExpressionException e) {
			// This should ideally not happen as we are searching in the doc that is currently
			// being built by this class.
			throw new RuntimeException(e);
		} catch (DOMException e) {
			// This should ideally not happen as we are searching in the doc that is currently
			// being built by this class.			
			throw new RuntimeException(e);
		}
	}

	/**
	 * writes out the xml document to the given file name
	 * 
	 * @param doc
	 *            doc to be written
	 * @param outputFile
	 *            complete filepath where the doc should be written
	 */
	private void print(Document doc, String outputFile) {
		try {
			TransformerFactory tFactory = TransformerFactory.newInstance();
			Transformer transformer = tFactory.newTransformer();

			// get the encoding charset from the config for this partner.
			// If not specified use the default from the config
			String encodingKey = ConversionPropertyReader
					.getEncodingKey(providerId);
			String encoding = ConversionPropertyReader.hasProperty(encodingKey) ? 
					ConversionPropertyReader.getProperty(encodingKey) :
					ConversionPropertyReader.getProperty(ConversionPropertyReader.DEFAULT_ENCODING); 
			transformer.setOutputProperty(OutputKeys.ENCODING, encoding);

			// <!DOCTYPE ADI SYSTEM 'ADI.DTD'>
			transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, "ADI.DTD");

			DOMSource source = new DOMSource(doc);

			FileOutputStream fs = new FileOutputStream(outputFile);
			StreamResult result = new StreamResult(fs);
			transformer.transform(source, result);

		} catch (TransformerConfigurationException e) {
			throw new RuntimeException(e);
		} catch (TransformerFactoryConfigurationError e) {
			throw new RuntimeException(e);
		} catch (TransformerException e) {
			throw new RuntimeException(e);
		} catch (FileNotFoundException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * @param providerId the providerId to set
	 */
	public void setProviderId(String providerId) {
		this.providerId = providerId;
	}
}
