##
# File: PdbxWriter.py
# Date: 2011-10-09 Jdw Adapted from PdbxParser.py
#
# Updates:
# 5-Apr-2011 jdw Using the double quote format preference
# 23-Oct-2012 jdw update path details and reorganize.
#
###
"""Classes for writing data and dictionary containers in PDBx/mmCIF format."""
from sys import stdout
from .containers import DefinitionContainer, DataContainer
from .errors import PdbxError
__docformat__ = "restructuredtext en"
__author__ = "John Westbrook"
__email__ = "jwest@rcsb.rutgers.edu"
MAXIMUM_LINE_LENGTH = 2048
SPACING = 2
INDENT_DEFINITION = 3
DO_DEFINITION_INDENT = False
[docs]class PdbxWriter:
"""Write PDBx data files or dictionaries.
Use the input container or container list.
"""
def __init__(self, output_file=stdout):
"""Initialize.
:param file output_file: file object ready for writing
"""
self.__output_file = output_file
self.__container_list = []
self.__maximum_line_length = MAXIMUM_LINE_LENGTH
self.__spacing = SPACING
self.__indent_definition = INDENT_DEFINITION
self.__indent_space = " " * self.__indent_definition
self._do_definition_indent = DO_DEFINITION_INDENT
# Maximum number of rows checked for value length and format
self.__row_partition = None
[docs] def set_row_partition(self, num_rows):
"""Maximum number of rows checked for value length and format.
:param int num_rows: maximum number of rows
"""
self.__row_partition = num_rows
[docs] def write(self, container_list):
"""Write out a list of containers.
:param list container_list: list of
:class:`~pdbx.containers.ContainerBase` objects to write.
"""
self.__container_list = container_list
for container in self.__container_list:
self.write_container(container)
[docs] def write_container(self, container):
"""Write out information for an individual container.
:param container: container to write
:type container: :class:`~pdbx.containers.ContainerBase`
"""
indent_string = " " * self.__indent_definition
if isinstance(container, DefinitionContainer):
self.__write("save_%s\n" % container.name)
self._do_definition_indent = True
self.__write(indent_string + "#\n")
elif isinstance(container, DataContainer):
if container.get_global():
self.__write("global_\n")
self._do_definition_indent = False
self.__write("\n")
else:
self.__write("data_%s\n" % container.name)
self._do_definition_indent = False
self.__write("#\n")
for name in container.get_object_name_list():
obj = container.get_object(name)
object_list = obj.row_list
# Skip empty objects
if not object_list:
continue
# Item - value formattting
if len(object_list) == 1:
self.__write_item_value_format(obj)
# Table formatting
elif len(object_list) > 1 and obj.attribute_list:
self.__write_table_format(obj)
else:
raise PdbxError(
"len(object_list) = %d and len(obj.attribute_list) = %d"
% (len(object_list), len(obj.attribute_list))
)
if self._do_definition_indent:
self.__write(indent_string + "#")
else:
self.__write("#")
# Add a trailing saveframe reserved word
if isinstance(container, DefinitionContainer):
self.__write("\nsave_\n")
self.__write("#\n")
def __write(self, string_):
"""Write a string.
:param str string_: string to write
"""
self.__output_file.write(string_)
def __write_item_value_format(self, category):
"""Write items and values for the given category.
:param category: category to write
:type category: :class:`~pdbx.containers.DataCategory`
"""
# Compute the maximum item name length within this category -
attribute_name_max_length = 0
for attribute_name in category.attribute_list:
attribute_name_max_length = max(
attribute_name_max_length, len(attribute_name)
)
item_name_max_length = (
self.__spacing + len(category.name) + attribute_name_max_length + 2
)
line_list = []
line_list.append("#\n")
for attribute_name, _ in category.attribute_list_with_order:
if self._do_definition_indent:
# - add indent --
line_list.append(self.__indent_space)
item_name = "_%s.%s" % (category.name, attribute_name)
line_list.append(item_name.ljust(item_name_max_length))
line_list.append(category.get_value_formatted(attribute_name, 0))
line_list.append("\n")
self.__write("".join(line_list))
def __write_table_format(self, category):
"""Write table format data.
:param category: category to write
:type category: :class:`~pdbx.containers.DataCategory`
"""
# Write the declaration of the loop_
line_list = []
line_list.append("#\n")
if self._do_definition_indent:
line_list.append(self.__indent_space)
line_list.append("loop_")
for attribute_name in category.attribute_list:
line_list.append("\n")
if self._do_definition_indent:
line_list.append(self.__indent_space)
item_name = "_%s.%s" % (category.name, attribute_name)
line_list.append(item_name)
self.__write("".join(line_list))
# Write the data in tabular format
# For speed make the following evaluation on a portion of the table
if self.__row_partition is not None:
num_steps = max(1, category.row_count / self.__row_partition)
else:
num_steps = 1
format_type_list, _ = category.get_format_type_list(steps=num_steps)
max_length_list = category.get_max_attribute_list_length(
steps=num_steps
)
spacing = " " * self.__spacing
for irow in range(category.row_count):
line_list = []
line_list.append("\n")
if self._do_definition_indent:
line_list.append(self.__indent_space + " ")
for iattr in range(category.attribute_count):
format_type = format_type_list[iattr]
max_length = max_length_list[iattr]
if format_type in ("FT_UNQUOTED_STRING", "FT_NULL_VALUE"):
val = category.get_value_formatted_by_index(iattr, irow)
line_list.append(val.ljust(max_length))
elif format_type == "FT_NUMBER":
val = category.get_value_formatted_by_index(iattr, irow)
line_list.append(val.rjust(max_length))
elif format_type == "FT_QUOTED_STRING":
val = category.get_value_formatted_by_index(iattr, irow)
line_list.append(val.ljust(max_length + 2))
elif format_type == "FT_MULTI_LINE_STRING":
val = category.get_value_formatted_by_index(iattr, irow)
line_list.append(val)
line_list.append(spacing)
self.__write("".join(line_list))
self.__write("\n")