package gov.grants.apply.support.bizobj;

import gov.grants.apply.support.vo.AttachmentDetails;
import gov.grants.apply.support.vo.AttachmentXPathInfo;
import gov.grants.commons.XMLConstants;
import gov.grants.commons.util.VtdXmlUtil;

import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import com.ximpleware.AutoPilot;
import com.ximpleware.VTDGen;
import com.ximpleware.VTDNav;
import com.ximpleware.XMLModifier;

public abstract class VtdXml {
	private static final transient Logger log = Logger.getLogger( VtdXml.class.getName() );
	
	
	// XML element constants
	public static final String XMLNS_ATTR = "xmlns";
	public static final String XMLNS_PREFIX = XMLNS_ATTR + ":";
	
	public static final String APPLICATION_ELEMENT_LOCAL_PART = "Application";
	public static final String GRANT_APPLICATION_ELEMENT_LOCAL_PART = "GrantApplication";
	public static final String OVERALL_APPLICATION_ELEMENT_LOCAL_PART = "OverallApplication";
	public static final String OVERALL_APPLICATION_ID_ELEMENT_LOCAL_PART = "OverallApplicationID";
	public static final String APPLICATION_PACKAGE_ELEMENT_LOCAL_PART = "ApplicationPackage";
	public static final String SUB_APPLICATION_GROUP_ID_ELEMENT_LOCAL_PART = "SubApplicationGroupID";
	public static final String SUB_APPLICATION_ID_ELEMENT_LOCAL_PART = "SubApplicationID";
	
	public static final String FORMS_ELEMENT_LOCAL_PART = "Forms";
	
	public static final String META_MULTI_GRANT_APPLICATION_NS_PREFIX = "mpgrant";
	public static final String META_MULTI_GRANT_APPLICATION_NS_URI = "http://apply.grants.gov/system/MetaMultiGrantApplication";
	public static final String META_GRANT_APPLICATION_NS_PREFIX = "grant";
	public static final String META_GRANT_APPLICATION_NS_URI = "http://apply.grants.gov/system/MetaGrantApplication";
	
	public static final String XML_SCHEMA_INSTANCE_NS_URL = "http://www.w3.org/2001/XMLSchema-instance";
	public static final String XML_SCHEMA_INSTANCE_DEFAULT_NS_PREFIX = "xsi";
	public static final String XML_SCHEMA_INSTANCE_DEFAULT_NS_DECLARATION = "xmlns:" + XML_SCHEMA_INSTANCE_DEFAULT_NS_PREFIX + "=\"" + XML_SCHEMA_INSTANCE_NS_URL + "\"";
	
	public static final String SCHEMA_LOCATION_ATTR_LOCAL_PART = "schemaLocation";
	public static final String SCHEMA_LOCATION_NS_SINGLE_PROJECT = "http://apply.grants.gov/system/MetaGrantApplication";
	
	public static final String GLOBAL_V1_XMLNS_URI = "http://apply.grants.gov/system/Global-V1.0";
	
	public static final String ATT_PREFIX_OVERALL = "OverallApplication";
	public static final String ATT_PREFIX_SUB_APPLICATION = "SubApplication";
	
	public static final String DEFAULT_HASH_ALGORITHM = "SHA-1";
	public static final String SCHEMA_VERSION = "1.0";
	
	public static final Map<String, String> BASE_FORM_NS_MAP;
	public static final String BASE_NS_STR;
	
	// use backup in case 'xsi' is in use
	protected String XML_SCHEMA_INSTANCE_BACKUP_NS_PREFIX = new java.util.Date().getTime() + "_xsi";
	protected String XML_SCHEMA_INSTANCE_BACKUP_NS_DECLARATION = "xmlns:" + XML_SCHEMA_INSTANCE_BACKUP_NS_PREFIX + "=\"" + XML_SCHEMA_INSTANCE_NS_URL + "\"";
	
	
	protected VTDNav vn;
	protected AutoPilot ap = new AutoPilot();
//	protected boolean namespacesAdded = false;
	
	protected XMLModifier xm = new XMLModifier();
	
	protected boolean isMultiProject = false;
	
	
	/**
	 * key: CID (att:href attribute value)
	 * <br>
	 * value: <code>AttachmentDetails</code>
	 */
	protected Map<String, AttachmentDetails> attachmentDetailsMap = new HashMap<String, AttachmentDetails>();
	
	
	static {
		BASE_FORM_NS_MAP = new HashMap<String, String>();
		BASE_FORM_NS_MAP.put( "glob", "http://apply.grants.gov/system/Global-V1.0" );
		BASE_FORM_NS_MAP.put( "globLib", "http://apply.grants.gov/system/GlobalLibrary-V2.0" );
		BASE_FORM_NS_MAP.put( "att", "http://apply.grants.gov/system/Attachments-V1.0" );
		BASE_NS_STR = createBaseNsStr();
		
	}// static
	
	
	/* Public Method(s) */
	

	
	/**
	 * Includes prolog: <?xml version="1.0"?>
	 * 
	 * @return
	 * @throws Exception
	 */
	public byte[] getXmlBytes() throws Exception {
		try {
			return VtdXmlUtil.getXmlBytes( vn );
			
		} catch ( Exception e ) {
			log.error( e );
			throw new Exception( e );
		}// try-catch
		
	}// getXmlBytes
	
	public void addAllNamespaces( AutoPilot ap ) {
		try {
			
			int size = vn.getTokenCount();
			Map<String, String> nsMap = getXmlNsMap();
			int count = 0;
			
			nsMap.putAll( XMLConstants.getCommonXmlNsMap() );
			log.debug( "nsMap size: " + nsMap.size() );
			
			Set<String> nsPrefixes = nsMap.keySet();
			for ( String prefix : nsPrefixes ) {
				log.debug( "adding namespace: " + prefix + "=" + nsMap.get( prefix ) );
				ap.declareXPathNameSpace( prefix, nsMap.get( prefix ) );
			}// for
			
//			namespacesAdded = true;
			
		} catch ( Exception e ) {
			log.error( "Cannot add all namespaces: " + e.getMessage() );
		}// try-catch
	}// addAllNamespaces
	
	
	/**
	 * This method is used mainly for making sure namespace prefixes are accounted for
	 * in sub-project GrantApplication xml elements.  Validation is not performed so
	 * correct namespace declarations are not important.
	 * <br><br>
	 * If two namespaces have the same prefix but different URI values, the first one will not be
	 * overwritten in the Map.  Successive ones will be ignored.
	 * 
	 * @return
	 */
	public Map<String, String> getXmlNsMap() {
		Map<String, String> xmlNsMap = new HashMap<String, String>();
		try {
			int size = vn.getTokenCount();
			String nsPrefix = null;
			String nsUrl = null;
			
			String mapNsUrl = null;
			
			for ( int i= 0; i < size; i++ ) {
				String token = vn.toNormalizedString2( i );
				
				if ( vn.startsWith( i, XMLNS_PREFIX ) ) {
					nsPrefix = token.substring( token.indexOf( ":" ) + 1 );
					nsUrl = vn.toNormalizedString2( i + 1 );
					
					mapNsUrl = xmlNsMap.get( nsPrefix );
					if ( StringUtils.isBlank( mapNsUrl ) ) {
						log.debug( "add ns: " + nsPrefix + "=" + nsUrl );
						xmlNsMap.put( nsPrefix, nsUrl );
					} else {
						if ( !mapNsUrl.equals( nsUrl ) ) {
						}// if
					}// if-else
				}// if
			}// for
			
		} catch ( Exception e ) {
			log.error( "Cannot add all namespaces: " + e.getMessage() );
		}// try-catch
		
		return xmlNsMap;
		
	}// getXmlNsMap

	
	/**
	 * Retrieves 'xmlns' attributes between the two index parameters.
	 * This is ideal for using to retrieve 'xmlns' attributes for a particular
	 * element (i.e. root element)
	 * 
	 * @param beginIndex - the begin index, inclusive
	 * @param endIndex - the end index, exclusive.
	 * @return
	 */
	public Map<String, String> getXmlNsMap( VTDNav vn, int beginIndex, int endIndex ) {
		Map<String, String> xmlNsMap = new HashMap<String, String>();
		log.debug( "begin index: " + beginIndex );
		log.debug( "end index: " + endIndex );
		
		try {
			String nsPrefix = null;
			String nsUrl = null;
			String mapNsUrl = null;
			
			for ( int i = beginIndex; i < endIndex; i++ ) {
				String token = vn.toNormalizedString2( i );
				
				if ( vn.startsWith( i, XMLNS_ATTR ) ) {
					nsPrefix = token.substring( token.indexOf( ":" ) + 1 );
					nsUrl = vn.toNormalizedString2( i + 1 );
					
					mapNsUrl = xmlNsMap.get( nsPrefix );
					if ( StringUtils.isBlank( mapNsUrl ) ) {
						log.debug( "add ns: " + nsPrefix + "=" + nsUrl );
						xmlNsMap.put( nsPrefix, nsUrl );
					} else {
						if ( !mapNsUrl.equals( nsUrl ) ) {
							log.debug( nsPrefix + " [" + mapNsUrl + "] --- [" + nsUrl + "]" );
						}// if
						
					}// if-else
					
				}// if
				
			}// for
			
		} catch ( Exception e ) {
			log.error( "Cannot add all namespaces: " + e.getMessage() );
		}// try-catch
		
		return xmlNsMap;
		
	}// getXmlNsMap
	
	/* Protected Method(s) */
	
	protected void init( byte[] xmlBytes ) throws Exception {
		try {
			
			if ( xmlBytes == null || xmlBytes.length == 0 ) {
				throw new Exception( "XML bytes are blank" );
			}// if
			
    		log.debug( "xml size: " + xmlBytes.length );
    		VTDGen vGen = new VTDGen();
    		vGen.setDoc( xmlBytes );
    		vGen.parse( true );//set namespace awareness
    		vn = vGen.getNav();
    		log.debug( "loaded xml successfully" );
    		
    		addAllNamespaces( ap );
    		ap.bind( vn );
    		
		} catch ( Exception e ) {
			log.error( "Exception parsing xml bytes: " + e.getMessage() );
			throw new Exception( e.getMessage() );
		}// try-catch
		
	}// init
	
	
	/**
	 * Returns the XML (at the current vNav index) as a string without 
	 * resolving entities (i.e. '&amp;' will NOT be resolved to '&')
	 * 
	 * @return
	 * @throws Exception
	 */
	protected String getRawXmlStr() throws Exception {
		String rawXml = null;
		try {
//			resetXmlNav();
			log.debug( "get raw xml for element: " + vn.toNormalizedString2( vn.getCurrentIndex() ) + " @ index: " + vn.getCurrentIndex() );
			long k = vn.getElementFragment();
			log.debug( "k: " + k );
			rawXml = vn.toRawString( ( int ) k, ( int )( k >> 32 ) );
		} catch ( Exception e ) {
			String s = "Exception caught: " + e.getMessage();
			log.error( s );
			throw new Exception( s );
		}// try-catch
		
		return rawXml;
		
	}// getRawXmlStr
	
	
	protected String addNamespacesToRoot( String xmlStr ) throws Exception {
		String rawXml = null;
		if ( StringUtils.isBlank( xmlStr ) ) {
			throw new Exception( "XML is blank" );
		}// if
		
		try {
			byte[] xmlBytes = addNamespacesToRoot( xmlStr.getBytes() );
			rawXml = IOUtils.toString( xmlBytes, "UTF-8" );
			return rawXml;
		} catch ( Exception e ) {
			log.error( e );
			throw new Exception( e );
		}// try-catch
		
	}// addNamespacesToRoot
	
	
	protected byte[] addNamespacesToRoot( byte[] xmlBytes ) throws Exception {
//		String rawXml = null;
		String nsStr = "";
		try {
			if ( xmlBytes == null || xmlBytes.length == 0 ) {
				throw new Exception( "XML is blank" );
			}// if
			
			
    		log.debug( "xml size: " + xmlBytes.length );
    		VTDGen vGen = new VTDGen();
    		vGen.setDoc( xmlBytes );
    		/*
    		 * set namespace awareness to 'false' to avoid:
    		 * 		Exception: Name space qualification Exception: prefixed attribute not qualified
    		 * 
    		 * This happens when the xml element does not have all of the required namespaces.
    		 * Using this method usually implies this scenario.
    		 */
    		vGen.parse( false );//set namespace awareness
    		VTDNav vNav = vGen.getNav();
    		log.debug( "loaded xml successfully" );
    		
    		log.debug( "root element: " + vNav.toNormalizedString( vNav.getCurrentIndex() ) );
    		log.debug( "root index: " + vNav.getCurrentIndex() );
    		
    		int beginIndex = vNav.getCurrentIndex();
    		
    		/*
    		 * get attributes (ns declarations) from root element
    		 */
    		int attrCount = vNav.getAttrCount();
    		log.debug( "root attribute count: " + attrCount );
    		/*
    		 * one ns attribute will requires 2 indexes, one for the key
    		 * and one for the value. Some attributes might not be key-value.
    		 * Therefore, 5 attributes will require 10 indexes.
    		 * Add 1 to get the value of the last attribute because the first index = 1.
    		 */
    		int endIndex = vNav.getCurrentIndex() + ( attrCount * 2 );
    		for ( int i = 1; i < endIndex; i++ ) {
    			log.debug( "element @ " + ( vNav.getCurrentIndex() + i ) + ": " + vNav.toNormalizedString( vNav.getCurrentIndex() + i ) );
    		}// for
    		
			Map<String, String> rootNsMap = getXmlNsMap( vNav, beginIndex, endIndex );
			log.debug( "root element ns attrs: " + rootNsMap );
			if ( rootNsMap.size() > 0 ) {
				StringBuilder ns = new StringBuilder();
				Set<Map.Entry<String, String>> entries = BASE_FORM_NS_MAP.entrySet();
				for ( Map.Entry<String, String> entry : entries ) {
					if ( !rootNsMap.containsKey( entry.getKey() ) ) {
    					ns.append( " xmlns:" )
    						.append( entry.getKey() )
    						.append( "=\"" )
    						.append( entry.getValue() )
    						.append( "\"" );
					} else {
						log.debug( "root ns already has: " + entry.getKey() + "=" + entry.getValue() );
					}// if-else
					
				}// for
				
				log.debug( "missing ns attrs: " + ns );
    			if ( ns.length() > 0 ) {
    				nsStr = ns.toString();
    			}// if
				
			} else {
				nsStr = BASE_NS_STR;
			}// if-else
    		
    		if ( StringUtils.isNotBlank( nsStr ) ) {
    			log.debug( "adding ns declarations to form root element: " + nsStr );
	    		vNav.toElement( VTDNav.R );
	    		XMLModifier xm = new XMLModifier();
	    		xm.bind( vNav );
	    		xm.insertAttribute( " " + nsStr );
				
				ByteArrayOutputStream baos = new ByteArrayOutputStream();
				xm.output( baos );
				xmlBytes = baos.toByteArray();
    		}// if
		

		} catch ( Exception e ) {
			log.error( e );
			throw new Exception( e );
		}// try-catch
		
		log.info( "returning original form xml" );
		return xmlBytes;
		
	}// addNamespacesToRoot
	
	
	protected boolean initAttachmentDetailsMap() throws Exception {
		String cid = null;
		int count = 0;
		
		AttachmentDetails attachmentDetails = null;
		
		try {
			resetXmlNav();
			// find all attachment FileLocation elements
			String xpath = "//" + AttachmentDetails.FILE_LOCATION_ELEMENT;
			
			log.debug( "xpath: " + xpath );
			
			ap.selectXPath( xpath );
			int i = -1;
			
			while ( ( i = ap.evalXPath() ) > -1 ) {
				attachmentDetails = new AttachmentDetails();
				log.debug( "--- " + vn.toNormalizedString2( vn.getCurrentIndex() ) );
				
				/*
				 * preserve cid ('href' attribute) value (do NOT resolve entities and preserve spaces)
				 */
				cid = vn.toRawString( vn.getAttrValNS( AttachmentDetails.ATTACHMENT_NS_URI, AttachmentDetails.CID_ATTRIBUTE_LOCAL_PART ) );
				log.debug( "cid: " + cid );
				
				if ( this.attachmentDetailsMap.containsKey( cid ) ) {
					log.error( "continue --- duplicate cid: " + cid );
					continue;
				}// if
				
				attachmentDetails.setContentID( cid );
				
				// get MimeType
				vn.toElement( VTDNav.PARENT );
				
				// MimeType: required
				if ( vn.toElementNS( VTDNav.FIRST_CHILD, AttachmentDetails.ATTACHMENT_NS_URI, AttachmentDetails.MIME_TYPE_ELEMENT_LOCAL_PART ) ) {
					if ( vn.getText() > -1 ) {
						attachmentDetails.setMimeType( vn.toNormalizedString2( vn.getText() ) );
					} else {
						log.info( "Attachment MimeType element is blank." );
					}// if-else
					vn.toElement( VTDNav.PARENT );
				} else {
					throw new Exception( "Attachment MimeType element is missing for CID: " + cid );
				}// if-else
				
				// HashValue: required
				// hash value for PDF submissions are calculated in PdfExtractionService.getFifAttachments()
				if ( vn.toElementNS( VTDNav.FIRST_CHILD, CommonConstants.GLOBAL_NS_URI, AttachmentDetails.HASH_VALUE_ELEMENT_LOCAL_PART ) ) {
					if ( vn.getText() > -1 ) {
						attachmentDetails.setHashValue( vn.toNormalizedString2( vn.getText() ) );
					} else {
						log.info( "Attachment HashValue element is blank." );
					}// if-else
					vn.toElement( VTDNav.PARENT );
				} else {
					throw new Exception( "Attachment HashValue element is missing for CID: " + cid );
				}// if-else
				
				// get FileName
				if ( vn.toElementNS( VTDNav.FIRST_CHILD, AttachmentDetails.ATTACHMENT_NS_URI, AttachmentDetails.FILENAME_ELEMENT_LOCAL_PART ) ) {
					if ( vn.getText() > -1 ) {
						attachmentDetails.setRawFileName( vn.toRawString( vn.getText() ) );
						attachmentDetails.setFileName( vn.toNormalizedString2( vn.getText() ) );
					} else {
						log.info( "Attachment FileName element is blank." );
					}// if-else
					vn.toElement( VTDNav.PARENT );
				} else {
					throw new Exception( "Attachment FileName element is missing for CID: " + cid );
				}// if-else
				
				// generate xpath + filename
				// must do last because this process moves the pointer to sub-project group id element
				// NOTE: set pointer to an attachment element because 'createAttXPathInfo()' ignores the first parent element
				vn.toElement( VTDNav.FIRST_CHILD );
				AttachmentXPathInfo attXPathInfo = createAttXPathInfo();
				attachmentDetails.setXpathPrefix( attXPathInfo.getXpathPrefix() );
				attachmentDetails.setParentFormName( attXPathInfo.getParentFormName() );
				
				log.debug( "adding attachment: " + attachmentDetails );
				this.attachmentDetailsMap.put( cid, attachmentDetails );
				count++;
				
			}// while
			
		} catch ( Exception e ) {
			String s = "Exception creating attachment details map: " + e.getMessage();
			log.error( s );
			throw new Exception( s );
		}// try-catch
		
		log.debug( "att count: " + count );
		
		if ( count > 0 ) { return true; }
		return false;
		
	}// initAttachmentDetailsMap
	
	
	/**
	 * Sets the XML navigation pointer to the root element
	 */
	protected void resetXmlNav() throws Exception {
		try {
			vn.toElement( VTDNav.ROOT );
		} catch ( Exception e ) {
			String s = "Exception caught resetting XML navigation to root: " + e.getMessage();
			log.error( s );
			throw new Exception( s );
		}// try-catch
		
	}// resetXmlNav
	
	
	/**
	 * NOTE: Uses CURRENT vNav index as starting point.
	 * 
	 * Usually starts at att:FileLocation element.
	 * <br>
	 * Ignores the immediate parent element.
	 * 
	 * @return
	 * @throws GrantsBusinessException
	 */
	protected AttachmentXPathInfo createAttXPathInfo() throws Exception {
		AttachmentXPathInfo attXPathInfo = new AttachmentXPathInfo();
		StringBuilder attXPathPrefix = new StringBuilder();
		
		try {
			log.debug( "begin element name: " + vn.toNormalizedString2( vn.getCurrentIndex() ) + " @ index " + vn.getCurrentIndex()  );
			if ( vn.getText() > -1 ) {
				log.debug( "begin element value: " + vn.toNormalizedString2( vn.getText() ) );
			}// if
			
			// build xpath filename prefix
			vn.toElement( VTDNav.PARENT );// ignore immediate parent
			boolean prefixComplete = false;
			String fullElementName = null;
			String elementName = null;
			
			while ( !prefixComplete ) {
				vn.toElement( VTDNav.PARENT );
				fullElementName = vn.toNormalizedString2( vn.getCurrentIndex() );
				elementName = fullElementName.substring( fullElementName.indexOf( ":" ) + 1 );
				
				if ( FORMS_ELEMENT_LOCAL_PART.equals( elementName ) ) {
					
					if ( isMultiProject ) {
						vn.toElement( VTDNav.PARENT );// grant:GrantApplication element
						vn.toElement( VTDNav.PREV_SIBLING );// overall/sub ApplicationHeader element
						vn.toElement( VTDNav.FIRST_CHILD );// overall/sub ApplicationID
						
						fullElementName = vn.toNormalizedString2( vn.getCurrentIndex() );
						elementName = fullElementName.substring( fullElementName.indexOf( ":" ) + 1 );
						log.debug( "parent element: " + elementName );
						attXPathPrefix.insert( 0, vn.toNormalizedString2( vn.getText() ) + "-" );
						
						if ( OVERALL_APPLICATION_ID_ELEMENT_LOCAL_PART.equals( elementName ) ) {
							attXPathPrefix.insert( 0, ATT_PREFIX_OVERALL + "-" );
							
						} else if ( SUB_APPLICATION_ID_ELEMENT_LOCAL_PART.equals( elementName ) ) {
							vn.toElement( VTDNav.PARENT );// SubApplicationHeader
							vn.toElement( VTDNav.PARENT );// SubApplication
							vn.toElement( VTDNav.PARENT );// SubApplicationGroup
							vn.toElement( VTDNav.FIRST_CHILD );// SubApplicationGroupHeader
							vn.toElement( VTDNav.FIRST_CHILD );// SubApplicationGroupID
							attXPathPrefix.insert( 0, vn.toNormalizedString2( vn.getText() ) + "-" );
							
						}// if-else
						
					}// if
					
//					log.debug( "prefix complete --- done" );
					prefixComplete = true;
				} else {
					// keep setting element name
					// last entry into this 'else' will be child of 'Forms' element
					attXPathInfo.setParentFormName( elementName );
					
					// pre-pend element name to attachment xpath prefix
//					attXPathPrefix.insert( 0, elementName + "-" );
					// uncomment to leave off the '-' at the end of the final prefix
					if ( attXPathPrefix.length() > 0 ) {
						attXPathPrefix.insert( 0, elementName + "-" );
					} else {
						attXPathPrefix.insert( 0, elementName );
					}// if-else
					
				}// if-else
				
			}// if
			
		} catch ( Exception e ) {
			String s = "Exception generating att prefix: " + e.getMessage();
			log.error( s );
			throw new Exception( s );
		}// try-catch
		
		attXPathInfo.setXpathPrefix( attXPathPrefix.toString() );
		log.debug( "returning att xpath info: " + attXPathInfo );
		return attXPathInfo;
		
	}// createAttXPathInfo
	
	
	protected String getElementByXPath( String xpath) throws Exception {
		
		String retVal = null;
		// Always reset cursor to root element just to be sure...
		try {
			vn.toElement( VTDNav.R );
			ap.bind( vn );
			ap.selectXPath( xpath );
			int result = ap.evalXPath();
			if(result != -1) {
				log.debug( String.format(" XPath element %s returned index: %d ", xpath, result));
				log.debug( "XPath element Text: " + vn.toNormalizedString( result));
				retVal = getRawXmlStr();
			}
		} catch (Exception e) {
			String s = "Exception getting Element by XPath: " + e.getMessage();
			log.error( s );
			throw new Exception( s );
		}// try-catch

		return retVal;
		
	}// getElementByXPath
	
	
	protected static String createBaseNsStr() {
		StringBuilder sb = new StringBuilder();
		Set<Map.Entry<String, String>> entries = BASE_FORM_NS_MAP.entrySet();
		for ( Map.Entry<String, String> entry : entries ) {
			sb.append( " xmlns:" )
				.append( entry.getKey() )
				.append( "=\"" )
				.append( entry.getValue() )
				.append( "\"" );
		}// for
		
		log.debug( "base ns str: " + sb );
		return sb.toString();
	}// createBaseNsStr
	
	protected String createAttHashValue( String attFilePath ) 
	throws Exception {
		String attHashValue = null;
    	try {
    		attHashValue = VtdXmlUtil.createAttHashValue( attFilePath );
			
    	} catch ( Exception e ) {
    		log.error( e );
    		throw new Exception( e );
    	}// try-catch
		
		log.debug( "created attachment hash value: " + attHashValue );
		return attHashValue;
	}// createAttHashValue
	
	
}
