# Path: siptools/utils.py
# def generate_digest(etree):
#     """Generate MD5 digest from etree.
# 
#     Identical metadata must
#     generate same digest even if attributes of any given element are
#     ordered differently. Also some metadata sections contain unique
#     identifiers that have to be removed if digest comparison is to work.
# 
#     This function creates a copy of the etree. All the attributes of the
#     copy are removed and collected to a separete list with path
#     information to the XML element the attributes belong to. This list
#     is sorted and appended to the end of the serialized XML string
#     without the attributes. Thus creating a string with all the original
#     information except the information about attribute ordering inside
#     any given XML element. This string is hashed and the digest
#     returned. For some PREMIS metadata identifiers are also removed.
# 
#     :etree: XML element for which the MD5 hash is generated
#     :returns: MD5 hash
#     """
#     # Creating copy of the original etree to avoid editing it
#     root = copy_etree(etree)
#     elem_tree = lxml.etree.ElementTree(root)
#     attrib_list = []
# 
#     # Remove premis identifiers and linking elements before metadata
#     # comparison
#     elem_tree = _remove_elements(elem_tree, 'eventIdentifierValue')
#     elem_tree = _remove_elements(elem_tree, 'agentIdentifierValue')
#     elem_tree = _remove_elements(elem_tree, 'linkingAgentIdentifier')
# 
#     # pop all attributes
#     for element in root.iter():
#         attributes = element.attrib
#         path = elem_tree.getpath(element)
#         _pop_attributes(attributes, attrib_list, path)
# 
#     attrib_list.sort()
#     xml_data = xml_helpers.utils.serialize(root)
# 
#     # Add the sorted attributes at the end of the serialized XML
#     attr_data = b"".join([attr.encode("utf-8") for attr in attrib_list])
#     xml_data = b"".join([xml_data, attr_data])
#     return hashlib.md5(xml_data).hexdigest()
# 
# def encode_path(path, suffix='', prefix='', safe=""):
#     """
#     Encode given path to URL encoding with given perfix and suffix.
# 
#     :path: Path to encode
#     :suffix: Suffix to add
#     :prefix: Prefix to add
#     :safe: Characters safe from URL quoting
#     :returns: Encoded string with given prefix and suffix
#     """
#     if isinstance(path, six.text_type):
#         path = path.encode("utf-8")
# 
#     if isinstance(safe, six.text_type):
#         safe = safe.encode("utf-8")
# 
#     quoted = quote_plus(path, safe=safe)
#     return "{}{}{}".format(prefix, quoted, suffix)


import os
import sys
import json
import six
import mets
import xml_helpers
from siptools.utils import generate_digest, encode_path
        # If temporary file was written, it'll replace the existing reference
        # file as a whole.
        if os.path.exists('%s.tmp' % reference_file):
            os.rename('%s.tmp' % reference_file, reference_file)

    # pylint: disable=too-many-arguments
    # pylint: disable=too-many-locals
    def write_md(self, metadata, mdtype, mdtypeversion, othermdtype=None,
                 section=None, stdout=False):
        """
        Wraps XML metadata into MD element and writes it to a lxml.etree XML
        file in the workspace. The output filename is
            <mdtype>-<hash>-othermd.xml,
        where <mdtype> is the type of metadata given as parameter and <hash>
        is a string generated from the metadata.

        Serializing and hashing the root xml element can be rather time
        consuming and as such this method should not be called for each file
        unless more efficient way of separating files by the metadata can't
        be easily implemented. This implementation should be done by the
        subclasses of metadata_creator.

        :metadata (Element): metadata XML element
        :mdtype (string): Value of mdWrap MDTYPE attribute
        :mdtypeversion (string): Value of mdWrap MDTYPEVERSION attribute
        :othermdtype (string): Value of mdWrap OTHERMDTYPE attribute
        :section (string): Type of mets metadata section
        :stdout (boolean): Print also to stdout
        :returns: md_id, filename - Metadata id and filename
        """

# Path: siptools/utils.py
# def generate_digest(etree):
#     """Generate MD5 digest from etree.
# 
#     Identical metadata must
#     generate same digest even if attributes of any given element are
#     ordered differently. Also some metadata sections contain unique
#     identifiers that have to be removed if digest comparison is to work.
# 
#     This function creates a copy of the etree. All the attributes of the
#     copy are removed and collected to a separete list with path
#     information to the XML element the attributes belong to. This list
#     is sorted and appended to the end of the serialized XML string
#     without the attributes. Thus creating a string with all the original
#     information except the information about attribute ordering inside
#     any given XML element. This string is hashed and the digest
#     returned. For some PREMIS metadata identifiers are also removed.
# 
#     :etree: XML element for which the MD5 hash is generated
#     :returns: MD5 hash
#     """
#     # Creating copy of the original etree to avoid editing it
#     root = copy_etree(etree)
#     elem_tree = lxml.etree.ElementTree(root)
#     attrib_list = []
# 
#     # Remove premis identifiers and linking elements before metadata
#     # comparison
#     elem_tree = _remove_elements(elem_tree, 'eventIdentifierValue')
#     elem_tree = _remove_elements(elem_tree, 'agentIdentifierValue')
#     elem_tree = _remove_elements(elem_tree, 'linkingAgentIdentifier')
# 
#     # pop all attributes
#     for element in root.iter():
#         attributes = element.attrib
#         path = elem_tree.getpath(element)
#         _pop_attributes(attributes, attrib_list, path)
# 
#     attrib_list.sort()
#     xml_data = xml_helpers.utils.serialize(root)
# 
#     # Add the sorted attributes at the end of the serialized XML
#     attr_data = b"".join([attr.encode("utf-8") for attr in attrib_list])
#     xml_data = b"".join([xml_data, attr_data])
#     return hashlib.md5(xml_data).hexdigest()
# 
# def encode_path(path, suffix='', prefix='', safe=""):
#     """
#     Encode given path to URL encoding with given perfix and suffix.
# 
#     :path: Path to encode
#     :suffix: Suffix to add
#     :prefix: Prefix to add
#     :safe: Characters safe from URL quoting
#     :returns: Encoded string with given prefix and suffix
#     """
#     if isinstance(path, six.text_type):
#         path = path.encode("utf-8")
# 
#     if isinstance(safe, six.text_type):
#         safe = safe.encode("utf-8")
# 
#     quoted = quote_plus(path, safe=safe)
#     return "{}{}{}".format(prefix, quoted, suffix)


# Path: siptools/mdcreator.py
import os
import sys
import json
import six
import mets
import xml_helpers
from siptools.utils import generate_digest, encode_path

        # If temporary file was written, it'll replace the existing reference
        # file as a whole.
        if os.path.exists('%s.tmp' % reference_file):
            os.rename('%s.tmp' % reference_file, reference_file)

    # pylint: disable=too-many-arguments
    # pylint: disable=too-many-locals
    def write_md(self, metadata, mdtype, mdtypeversion, othermdtype=None,
                 section=None, stdout=False):
        """
        Wraps XML metadata into MD element and writes it to a lxml.etree XML
        file in the workspace. The output filename is
            <mdtype>-<hash>-othermd.xml,
        where <mdtype> is the type of metadata given as parameter and <hash>
        is a string generated from the metadata.

        Serializing and hashing the root xml element can be rather time
        consuming and as such this method should not be called for each file
        unless more efficient way of separating files by the metadata can't
        be easily implemented. This implementation should be done by the
        subclasses of metadata_creator.

        :metadata (Element): metadata XML element
        :mdtype (string): Value of mdWrap MDTYPE attribute
        :mdtypeversion (string): Value of mdWrap MDTYPEVERSION attribute
        :othermdtype (string): Value of mdWrap OTHERMDTYPE attribute
        :section (string): Type of mets metadata section
        :stdout (boolean): Print also to stdout
        :returns: md_id, filename - Metadata id and filename
        """

        digest = generate_digest(metadata)