/*
 *  Copyright (C) 2000-2015 aw2.0 LTD
 *
 *  This file is part of Open BlueDragon (OpenBD) CFML Server Engine.
 *
 *  OpenBD is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  Free Software Foundation,version 3.
 *
 *  OpenBD 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.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with OpenBD.  If not, see http://www.gnu.org/licenses/
 *
 *  Additional permission under GNU GPL version 3 section 7
 *
 *  If you modify this Program, or any covered work, by linking or combining
 *  it with any of the JARS listed in the README.txt (or a modified version of
 *  (that library), containing parts covered by the terms of that JAR, the
 *  licensors of this Program grant you additional permission to convey the
 *  resulting work.
 *  README.txt @ http://www.openbluedragon.org/license/README.txt
 *
 *  http://www.openbd.org/
 *  $Id: cfXmlData.java 2506 2015-02-08 22:25:59Z alan $
 */

package com.naryx.tagfusion.cfm.xml;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Serializable;
import java.io.StringWriter;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URL;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;

import org.w3c.dom.DOMException;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSOutput;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import com.naryx.tagfusion.cfm.engine.catchDataFactory;
import com.naryx.tagfusion.cfm.engine.cfData;
import com.naryx.tagfusion.cfm.engine.cfSession;
import com.naryx.tagfusion.cfm.engine.cfStructData;
import com.naryx.tagfusion.cfm.engine.cfmRunTimeException;
import com.naryx.tagfusion.cfm.tag.cfDUMP;
import com.naryx.tagfusion.cfm.xml.parse.NoValidationResolver;
import com.naryx.tagfusion.cfm.xml.parse.ValidationErrorHandler;
import com.naryx.tagfusion.cfm.xml.parse.ValidationInputSource;
import com.naryx.tagfusion.cfm.xml.parse.ValidationResolver;
import com.naryx.tagfusion.cfm.xml.parse.ValidatorSource;
import com.naryx.tagfusion.cfm.xml.parse.XmlSource;

public class cfXmlData extends cfStructData implements Serializable {

	private static final long	serialVersionUID	= 1;

	/* Used as the flag to check if current parser is JAXP 1.3 compliant */
	private static boolean		complianceChecked	= false;

	/* Used to get the JDK version number for working around Xerces bug in JDK 1.5 */
	private static String			jdkVer						= System.getProperty("java.version");



	/**
	 * Default constructor. Performs the JAXP 1.3 compliance check if not already checked.
	 * 
	 * @param n
	 *          Node to create a new cfXmlData with
	 * @param caseSensitive
	 *          true if the cfXmlData should be caseSensitive, false otherwise
	 */
	public cfXmlData(Node n, boolean caseSensitive) throws cfmRunTimeException {
		super(XmlHashtableFactory.newXmlHashtable(n, caseSensitive));
		if (!complianceChecked)
			checkCompliance();
	}



	/**
	 * Internal constructor that bypasses the compliance check.
	 * 
	 * @param xmlData
	 *          cfXmlData that is calling this method
	 * @param n
	 *          Node to create a new cfXmlData with
	 * @param caseSensitive
	 *          true if the cfXmlData should be caseSensitive, false otherwise
	 */
	protected cfXmlData(cfXmlData xmlData, Node n, boolean caseSensitive) {
		super(XmlHashtableFactory.newXmlHashtable(n, caseSensitive));
	}



	/**
	 * Internal constructor that bypasses the compliance check.
	 * 
	 * @param xmlHashtable
	 *          XmlHashtable that is calling this method
	 * @param n
	 *          Node to create a new cfXmlData with
	 * @param caseSensitive
	 *          true if the cfXmlData should be caseSensitive, false otherwise
	 */
	protected cfXmlData(XmlHashtable xmlHashtable, Node n, boolean caseSensitive) {
		super(XmlHashtableFactory.newXmlHashtable(n, caseSensitive));
	}



	/**
	 * Checks that the underlying XML parser is JAXP 1.3 compliant. Throws a cfmRunTimeException
	 * if it is not compliant.
	 * 
	 * @throws cfmRunTimeException
	 */
	protected static void checkCompliance() throws cfmRunTimeException {
		// Verify that the XML parser is JAX 1.3 compliant
		try {
			DOMImplementation domImpl = DocumentBuilderFactory.newInstance().newDocumentBuilder().getDOMImplementation();
			if (!DOMImplementationLS.class.isAssignableFrom(domImpl.getClass())) {
				String errMsg = "The configured XML parser does not support JAXP 1.3. Please configure the " + "JVM to use a JAXP 1.3 compliant XML parser. If using Sun Microsystems' JDK 1.5, " + "this can be done by setting the system property javax.xml.parsers.DocumentBuilderFactory " + "to \"com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl\".";
				throw new cfmRunTimeException(catchDataFactory.generalException("errorCode.runtimeError", errMsg));
			}
		} catch (Exception ex) {
			throw new cfmRunTimeException(catchDataFactory.javaMethodException("errorCode.javaException", ex.getClass().getName(), ex.getMessage(), ex));
		}
		complianceChecked = true;
	}



	public String getDataTypeName() {
		return "xml";
	}



	public cfData duplicate() {
		Node cn = getXMLNode().cloneNode(true);
		return new cfXmlData(this, cn, isCaseSensitive());
	}



	public void importXml(cfXmlData toAdd, boolean overwrite) throws cfmRunTimeException {
		try {
			// Get the document object (for importing)
			Document doc = null;
			if (getXMLNode().getNodeType() == Node.DOCUMENT_NODE)
				doc = (Document) getXMLNode();
			else
				doc = getXMLNode().getOwnerDocument();

			// Import the node
			Node child = doc.importNode(toAdd.getXMLNode(), true);
			if (!overwrite) {
				NodeList nl = getXMLNode().getChildNodes();
				for (int i = 0; i < nl.getLength(); i++) {
					if (nl.item(i).getNodeName().equals(child.getNodeName()))
						return;
				}
			}
			getXMLNode().appendChild(child);
		} catch (DOMException ex) {
			throw new cfmRunTimeException(catchDataFactory.javaMethodException("errorCode.javaException", ex.getClass().getName(), ex.getMessage(), ex));
		}
	}



	public Node getXMLNode() {
		return ((XmlHashtable) getHashData()).getXMLNode();
	}



	/*
	 * XmlNode api that needed to be exposed for other function manipulation
	 */
	public String getName() {
		return getXMLNode().getNodeName();
	}



	public cfXmlData getParent() throws cfmRunTimeException {
		if (getXMLNode().getParentNode() != null)
			return new cfXmlData(getXMLNode().getParentNode(), isCaseSensitive());
		else
			return null;
	}



	public void removeChild(cfXmlData child) throws cfmRunTimeException {
		try {
			getXMLNode().removeChild(child.getXMLNode());
		} catch (DOMException ex) {
			throw new cfmRunTimeException(catchDataFactory.javaMethodException("errorCode.javaException", ex.getClass().getName(), ex.getMessage(), ex));
		}
	}



	public void insertBefore(cfXmlData child1, cfXmlData child2) throws cfmRunTimeException {
		try {
			getXMLNode().insertBefore(child1.getXMLNode(), child2.getXMLNode());
		} catch (DOMException ex) {
			throw new cfmRunTimeException(catchDataFactory.javaMethodException("errorCode.javaException", ex.getClass().getName(), ex.getMessage(), ex));
		}
	}



	public void appendChild(cfXmlData child) throws cfmRunTimeException {
		try {
			getXMLNode().appendChild(child.getXMLNode());
		} catch (DOMException ex) {
			throw new cfmRunTimeException(catchDataFactory.javaMethodException("errorCode.javaException", ex.getClass().getName(), ex.getMessage(), ex));
		}
	}



	public String getString() {
		return toString();

		// this is the original code; it was modified to support the ToString()
		// function;
		// it's not clear if this method is ever invoked from anywhere else--if
		// it is,
		// we may need to put this code back and find a different solution for
		// ToString()

		// String val = getXMLNode().getNodeValue();
		// if (val == null)
		// return "";
		// else
		// return val;
	}



	public String toString() {
		return cfXmlData.toString(getXMLNode());
	}



	public static String toString(Node n) {
		Writer writer = new StringWriter();
		Node tmp = null;
		Document doc = n.getOwnerDocument();
		if (doc == null)
			doc = (Document) n;
		DOMImplementation impl = doc.getImplementation();
		DOMImplementationLS implls = null;
		if (DOMImplementationLS.class.isAssignableFrom(impl.getClass())) {
			implls = (DOMImplementationLS) impl;
			LSOutput lsout = implls.createLSOutput();
			lsout.setCharacterStream(writer);
			try {
				// Try to avoid serializing nodes that have no children.
				// This is a patch to avoid bug XERCESJ-1023.
				// See http://issues.apache.org/jira/browse/XERCESJ-1023
				if (n.getFirstChild() == null && jdkVer.startsWith("1.5")) {
					// Add an empty node child to n so that the call to prepareForSerialization()
					// traverses down before it walks back up. This is necessary for the
					// traversal to terminate back at n. Otherwise the traversal will encounter
					// a NPE at the document's parent.
					tmp = doc.createTextNode("");
					n.appendChild(tmp);
				}
				implls.createLSSerializer().write(n, lsout);
			} finally {
				if (tmp != null) {
					// Remove the temp node.
					n.removeChild(tmp);
				}
			}
		} else if (!complianceChecked) {
			// Very few ways to get here without a compliance check
			// but it is posible, so let's log the problem and move on.
			try {
				checkCompliance();
			} catch (cfmRunTimeException ex) {
				com.nary.Debug.printStackTrace(ex);
			}
		}

		/*
		 * Old Transformer way of doing things that doesn't handle namespace fix up.
		 * writer = new StringWriter();
		 * try
		 * {
		 * TransformerFactory transformerFactory = TransformerFactory.newInstance();
		 * Transformer transformer = transformerFactory.newTransformer();
		 * transformer.setOutputProperty(OutputKeys.METHOD, "xml");
		 * transformer.setOutputProperty(OutputKeys.INDENT, "yes");
		 * transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
		 * transformer.transform(new DOMSource(n), new StreamResult(writer));
		 * }
		 * catch (TransformerException ex)
		 * {
		 * com.nary.Debug.printStackTrace(ex);
		 * }
		 */
		return writer.toString().trim();
	}



	public void dump(PrintWriter out) {
		dump(out, "", cfDUMP.TOP_DEFAULT);
	}



	public void dump(PrintWriter out, String label, int _top) {
		((XmlHashtable) getHashData()).dump(out, label, _top);
	}



	public void dumpLong(PrintWriter out) {
		dumpLong(out, "", cfDUMP.TOP_DEFAULT);
	}



	public void dumpLong(PrintWriter out, String _label, int _top) {
		((XmlHashtable) getHashData()).dumpLong(out, _label, _top);
	}



	/**
	 * Parses and potentially validates a xml document using the specified
	 * ValidatorSource. The InputSourceGenerator produces InputSource objects that wrap
	 * the xml document. The ValiatorSource wraps the validation object (DTD/Schema).
	 * Returns a parsed and potentially validated cfXmlData instance.
	 * Note, because DTD validation occurs during parsing, and xml schema validation takes
	 * place after parsing, we need to separate the validation types into two steps.
	 * Also note, if a customHandler is specified, it may not throw any parse or
	 * validation errors/warnings. Which could lead to a null Document being returned.
	 * 
	 * @param xs
	 *          generator that creates new InputSource instances
	 * @param validator
	 *          wrapper for the validation object (DTD/Schema)
	 * @param customHandler
	 *          custom ErrorHandler, may be null
	 * @return parsed and potentially validated xml object, or null
	 * @throws IOException
	 * @throws SAXException
	 * @throws ParserConfigurationException
	 */
	protected static Document parseXml(XmlSource xs, ValidatorSource validator, ErrorHandler customHandler) throws IOException, SAXException, ParserConfigurationException {
		InputSource is = null;
		boolean schemaValidationRequired = false;
		boolean dtdRemovalRequired = false;
		EntityResolver dtdResolver = null;
		DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance();
		fact.setNamespaceAware(true);
		fact.setIgnoringElementContentWhitespace(true);

		if (validator.isNull()) {
			// Return empty content for all entity references (presumably from a <!DOCTYPE ...>
			// element) so the parser will parse without any DTD validation and not complain when
			// resolving <!DOCTYPE ...> entity references (if it exists).
			is = new ValidationInputSource(xs, ValidationInputSource.NO_CHANGE);
			dtdResolver = new NoValidationResolver();
		} else if (validator.isSchema()) {
			// Return empty content for all entity references (presumably from a <!DOCTYPE ...>
			// element) so the parser will parse without any DTD validation and not complain when
			// resolving <!DOCTYPE ...> entity references (if it exists).
			is = new ValidationInputSource(xs, ValidationInputSource.NO_CHANGE);
			dtdResolver = new NoValidationResolver();
			// Note that we must do some post parse xml schema validation.
			schemaValidationRequired = true;
		} else if (validator.isEmptyString() && !xs.hasDTD()) {
			// Return empty content for all entity references (presumably from a <!DOCTYPE ...>
			// element) so the parser will parse without any DTD validation and not complain when
			// resolving <!DOCTYPE ...> entity references (if it exists).
			is = new ValidationInputSource(xs, ValidationInputSource.NO_CHANGE);
			dtdResolver = new NoValidationResolver();
			// Note that we must do some post parse xml schema validation. This assumes
			// that the xml doc has some embedded xml schema reference.
			schemaValidationRequired = true;
		} else if (validator.isEmptyString()) {
			// Best have DTD referenced in the xml source. Set DTD validation to true,
			// leave the existing <!DOCTYPE ...> element intact.
			fact.setValidating(true);
			if (customHandler == null)
				customHandler = new ValidationErrorHandler();
			is = new ValidationInputSource(xs, ValidationInputSource.NO_CHANGE);
		} else {
			// Must have specified a DTD validator object so set DTD validation
			// to true, read the <!DOCTYPE ...> element while parsing and return
			// the specified DTD validator during entity reference lookup, or if
			// no <!DOCTYPE ..> element exists, add our own so we can return the
			// specified DTD validator content during entity reference lookup.
			fact.setValidating(true);
			if (customHandler == null)
				customHandler = new ValidationErrorHandler();
			dtdRemovalRequired = !xs.hasDTD();
			ValidationResolver vr = new ValidationResolver(validator);
			dtdResolver = vr;
			is = new ValidationInputSource(xs, vr, ValidationInputSource.READ_ADD);
		}

		DocumentBuilder parser = fact.newDocumentBuilder();
		parser.setEntityResolver(dtdResolver); // if these are null, it doesn't matter,
		parser.setErrorHandler(customHandler); // setting these won't change default behavior
		Document doc = parser.parse(is);
		
		if (doc != null) {
			doc.normalize();
		
			// Now see if we need to do any schema validation
			if (schemaValidationRequired)
				validateXml(doc, validator, customHandler);
		}

		// Remove the inserted DTD (if necessary)
		if (doc != null && dtdRemovalRequired && doc.getDoctype() != null)
			doc.removeChild(doc.getDoctype());

		// Return the parsed (and possibly validated Document)
		return doc;
	}



	/**
	 * Validates the specified Document against a ValidatorSource. The ValidatorSource
	 * could represent a DTD or xml schema document. If a validation exception arises, it
	 * will be thrown.
	 * 
	 * @param doc
	 *          Document to validate
	 * @param validator
	 *          ValidatorSource to validate against
	 * @throws cfmRunTimeException
	 */
	public static void validateXml(Node node, ValidatorSource validator) throws cfmRunTimeException {
		validateXml(node, validator, (cfStructData) null);
	}



	/**
	 * Validates the specified Document against a ValidatorSource. The ValidatorSource
	 * could represent a DTD or xml schema document. If a validation exception arises, it
	 * will be thrown. However, if the validationMsgs struct is specified a custom
	 * ErrorHandler will be used during validation that collects the validation exceptions
	 * and places them in the specified validationMsgs struct. No error/warning exceptions
	 * will be thrown.
	 * 
	 * @param doc
	 *          Document to validate
	 * @param validator
	 *          ValidatorSource to validate against
	 * @param validationMsgs
	 *          cfStructData to collect all the validation messages
	 * @throws cfmRunTimeException
	 */
	public static void validateXml(Node node, ValidatorSource validator, cfStructData validationMsgs) throws cfmRunTimeException {
		ErrorHandler customHandler = null;
		if (validationMsgs != null)
			customHandler = new ValidationErrorHandler(validationMsgs);
		try {
			validateXml(node, validator, customHandler);
		} catch (IOException ex) {
			throw new cfmRunTimeException(catchDataFactory.javaMethodException("errorCode.javaException", ex.getClass().getName(), ex.getMessage(), ex));
		} catch (SAXParseException ex) {
			// Since we will have already received/handled this in
			// our custom ErrorHandler, we don't need to re-handle it.
			if (customHandler == null) {
				throw new cfmRunTimeException(catchDataFactory.javaMethodException("errorCode.javaException", ex.getClass().getName(), ex.getMessage(), ex));
			}
		} catch (SAXException ex) {
			throw new cfmRunTimeException(catchDataFactory.javaMethodException("errorCode.javaException", ex.getClass().getName(), ex.getMessage(), ex));
		}
	}



	/**
	 * Validates the specified Document against a ValidatorSource. The ValidatorSource
	 * could represent a DTD or xml schema document. If a validation exception arises, it
	 * will be thrown. However, if the customHandler is specified and it does not throw
	 * error/warning exceptions, it may be the case that this method does not throw
	 * validation exceptions.
	 * 
	 * @param doc
	 *          Document to validate
	 * @param validator
	 *          ValidatorSource to validate against
	 * @param customHandler
	 *          custom ErrorHandler, or null
	 * @throws IOException
	 * @throws SAXException
	 */
	protected static void validateXml(Node node, ValidatorSource validator, ErrorHandler customHandler) throws IOException, SAXException {
		SchemaFactory fact = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
		if (customHandler == null)
			customHandler = new ValidationErrorHandler();
		fact.setErrorHandler(customHandler);
		Schema schema = null;

		// Either use the specified xml schema or assume the xml document
		// itself has xml schema location hints.
		if (validator.isEmptyString())
			schema = fact.newSchema();
		else
			schema = fact.newSchema(validator.getAsSource());

		// Validate it against xml schema
		Validator v = schema.newValidator();
		v.setErrorHandler(customHandler);
		v.validate(new DOMSource(node));
	}



	/**
	 * Supports parsing File instances, URL instances (that point to the xml content), and
	 * String instances (that represent the xml content itself). The validator object is
	 * also an instance of File, URL, or String. It represents a DTD or xml schema
	 * definition. Returns the parsed xml as an object.
	 * 
	 * @param value
	 *          a File, URL, or String
	 * @param caseSensitive
	 *          true if the cfXmlData is created with case sensitivity, false
	 *          otherwise
	 * @param validator
	 *          a File, URL, or String
	 * @return parsed xml as a cfXmlData object
	 * @throws cfmRunTimeException
	 */
	public static cfXmlData parseXml(Object value, boolean caseSensitive, Object validator) throws cfmRunTimeException {
		return parseXml(value, caseSensitive, validator, null);
	}



	/**
	 * Supports parsing File instances, URL instances (that point to the xml content), and
	 * String instances (that represent the xml content itself). The validator object is
	 * also an instance of File, URL, or String. It represents a DTD or xml schema
	 * definition. Returns the parsed xml as an object. If parseMsgs is specified, a
	 * custom ErrorHandler will be used during parsing and validation that will collect
	 * all the error/warning messages and add them to the specified parseMsgs struct.
	 * Note, no parsing or validation exceptions will be thrown if this parameter is
	 * non-null. Therefore the value of the return may be null.
	 * 
	 * @param value
	 *          a File, URL, or String
	 * @param caseSensitive
	 *          true if the cfXmlData is created with case sensitivity, false
	 *          otherwise
	 * @param validator
	 *          a File, URL, or String
	 * @param parseMsgs
	 *          cfStructData to collect all the parse/validation messages
	 * @return parsed xml as a cfXmlData object
	 * @throws cfmRunTimeException
	 */
	public static cfXmlData parseXml(Object value, boolean caseSensitive, Object validator, cfStructData parseMsgs) throws cfmRunTimeException {
		XmlSource isg = null;
		ValidatorSource vs = null;
		ValidationErrorHandler customHandler = null;
		String msg = null;

		if (value instanceof XmlSource)
			isg = (XmlSource) value;
		else
			isg = new XmlSource(value);
		if (validator instanceof ValidatorSource)
			vs = (ValidatorSource) validator;
		else
			vs = new ValidatorSource(validator);
		if (parseMsgs != null)
			customHandler = new ValidationErrorHandler(parseMsgs);

		Document doc = null;
		try {
			doc = parseXml(isg, vs, customHandler);
		} catch (IOException ex) {
			throw new cfmRunTimeException(catchDataFactory.javaMethodException("errorCode.javaException", ex.getClass().getName(), ex.getMessage(), ex));
		} catch (SAXParseException ex) {
			// Since we will have already received/handled this in
			// our custom ErrorHandler, we don't need to re-handle it.
			if (customHandler == null) {
				msg = ex.getMessage() + " Line: " + ex.getLineNumber() + " Column: " + ex.getColumnNumber();
				throw new cfmRunTimeException(catchDataFactory.javaMethodException("errorCode.javaException", ex.getClass().getName(), msg, ex));
			}
		} catch (SAXException ex) {
			throw new cfmRunTimeException(catchDataFactory.javaMethodException("errorCode.javaException", ex.getClass().getName(), ex.getMessage(), ex));
		} catch (ParserConfigurationException ex) {
			throw new cfmRunTimeException(catchDataFactory.javaMethodException("errorCode.javaException", ex.getClass().getName(), ex.getMessage(), ex));
		} catch (IllegalArgumentException ex) {
			throw new cfmRunTimeException(catchDataFactory.javaMethodException("errorCode.javaException", ex.getClass().getName(), ex.getMessage(), ex));
		}

		// Return
		if (doc != null)
			return new cfXmlData(doc, caseSensitive);
		else
			return null;
	}



	/**
	 * Interprets a String value to determine if it represents a File path, or a URL path,
	 * or xml data (DTD/Schema data) itself. Returns a File object, or URL object, or
	 * String object accordingly.
	 * 
	 * @param _session
	 *          cfSession to determine current working directory
	 * @param str
	 *          value to interpret
	 * @return a File, URL, or String object
	 * @throws cfmRunTimeException
	 */
	public static Object interpretString(cfSession _session, String str) throws cfmRunTimeException {
		if (str == null) {
			return null;
		} else {
			str = str.trim();
			if (str.isEmpty() || str.charAt(0) == '<') {
				return str;
			} else if (str.toLowerCase().startsWith("http:") || str.toLowerCase().startsWith("https:") || str.toLowerCase().startsWith("ftp:")) {
				// Open a URL to the data
				try {
					URL url = new URL(str);
					return url;
				} catch (MalformedURLException ex) {
					throw new cfmRunTimeException(catchDataFactory.javaMethodException("errorCode.javaException", ex.getClass().getName(), ex.getMessage(), ex));
				}
			} else {
				// Try it as a file
				File f = new File(str);
				if (!f.exists()){
					f = new File(_session.getPresentDirectory(), str);
					if (!f.exists())
						throw new cfmRunTimeException(catchDataFactory.generalException("errorCode.runtimeError", "Cannot locate: " + str));
				}
				return f;
			}
		}
	}



	public static cfXmlData newInstance(Object obj, boolean caseSensitive) {
		try {
			return new cfXmlData((Node) obj, caseSensitive);
		} catch (cfmRunTimeException ex) {
			// Just log it and return a cfXmlData object anyway.
			com.nary.Debug.printStackTrace(ex);
			return new cfXmlData((cfXmlData) null, (Node) obj, caseSensitive);
		}
	}



	public static boolean isXmlObject(Object obj) {
		if (obj == null)
			return false;
		else
			return (Node.class.isAssignableFrom(obj.getClass()));
	}

}