B
    dy                 @   s   d Z ddlZddlZddlZddlZddlmZ ddlm	Z	 ddl
mZmZmZ ddl
mZ ddl
mZ dd	l
mZ G d
d deZdd ZG dd dejZG dd dejZG dd dejeZdd ZdS )aN  
While the ligolw module provides classes and parser support for reading and
writing LIGO Light Weight XML documents, this module supplements that code
with classes and parsers that add intelligence to the in-RAM document
representation.

In particular, the document tree associated with a Table element is
enhanced.  During parsing, the Stream element in this module converts the
character data contained within it into a list of objects.  The list
contains one object for each row of the table, and the objects' attributes
are the names of the table's columns.  When the document is written out
again, the Stream element serializes the row objects back into character
data.

The Table element exports a list-like interface to the rows.  The Column
elements also provide list-like access to the values in the corresponding
columns of the table.
    N)escape)AttributesImpl   )
__author____date____version__)ligolw)	tokenizer)typesc                   s0   e Zd ZdZdZ fddZedd Z  ZS )next_idzB
	Type for .next_id attributes of tables with int_8s ID columns.
	Nc                s   t | tt| |S )N)typesuperr   __add__)selfother)	__class__ Z/work/yifan.wang/ringdown/master-ringdown-env/lib/python3.7/site-packages/ligo/lw/table.pyr   L   s    znext_id.__add__c             C   s   t td| | fd|iS )Nznext_%scolumn_name)r   str)clsr   r   r   r   r   O   s    znext_id.type)	__name__
__module____qualname____doc__r   r   classmethodr   __classcell__r   r   )r   r   r   F   s   r   c             C   sV   i }x*|  tjjD ]}|jdk	r|| qW x |  tjjD ]}|| q@W dS )a  
	Recurses over all Table elements below elem whose next_id
	attributes are not None, and uses the .get_next_id() method of each
	of those Tables to generate and assign new IDs to their rows.  The
	modifications are recorded, and finally all ID attributes in all
	rows of all tables are updated to fix cross references to the
	modified IDs.

	This function is used by ligolw_add to assign new IDs to rows when
	merging documents in order to make sure there are no ID collisions.
	Using this function in this way requires the .get_next_id() methods
	of all Table elements to yield unused IDs, otherwise collisions
	will result anyway.  See the .sync_next_id() method of the Table
	class for a way to initialize the .next_id attributes so that
	collisions will not occur.

	Example:

	>>> from ligo.lw import ligolw
	>>> from ligo.lw import lsctables
	>>> xmldoc = ligolw.Document()
	>>> xmldoc.appendChild(ligolw.LIGO_LW()).appendChild(lsctables.New(lsctables.SnglInspiralTable))
	[]
	>>> reassign_ids(xmldoc)
	N)getElementsByTagNamer   TabletagNamer   updateKeyMappingapplyKeyMapping)elemmappingtblr   r   r   reassign_idsT   s    
r%   c               @   s   e Zd ZdZG dd dejZejdejedZ	e
dd Zdd	 Zd
d Zdd Zdd Zdd Zdd Zdd Zdd Zdd Zedd ZdS )Columna  
	High-level column element that provides list-like access to the
	values in a column.

	Example:

	>>> from xml.sax.xmlreader import AttributesImpl
	>>> import sys
	>>> tbl = Table(AttributesImpl({u"Name": u"test"}))
	>>> col = tbl.appendChild(Column(AttributesImpl({u"Name": u"test:snr", u"Type": u"real_8"})))
	>>> tbl.appendChild(TableStream(AttributesImpl({u"Name": u"test"})))	# doctest: +ELLIPSIS
	<ligo.lw.table.TableStream object at ...>
	>>> print(col.Name)
	snr
	>>> print(col.Type)
	real_8
	>>> print(col.table_name)
	test
	>>> # append 3 rows (with nothing in them)
	>>> tbl.append(tbl.RowType())
	>>> tbl.append(tbl.RowType())
	>>> tbl.append(tbl.RowType())
	>>> # assign values to the rows, in order, in this column
	>>> col[:] = [8.0, 10.0, 12.0]
	>>> col[:]
	[8.0, 10.0, 12.0]
	>>> col.asarray()	# doctest: +NORMALIZE_WHITESPACE
	array([ 8., 10., 12.])
	>>> tbl.write(sys.stdout)	# doctest: +NORMALIZE_WHITESPACE
	<Table Name="test">
		<Column Name="test:snr" Type="real_8"/>
		<Stream Name="test">
			8,
			10,
			12
		</Stream>
	</Table>
	>>> col.index(10)
	1
	>>> 12 in col
	True
	>>> col[0] = 9.
	>>> col[1] = 9.
	>>> col[2] = 9.
	>>> tbl.write(sys.stdout)		# doctest: +NORMALIZE_WHITESPACE
	<Table Name="test">
		<Column Name="test:snr" Type="real_8"/>
		<Stream Name="test">
			9,
			9,
			9
		</Stream>
	</Table>
	>>> col.count(9)
	3

	NOTE:  the .Name attribute returns the stripped "Name" attribute of
	the element, e.g. with the table name prefix removed, but when
	assigning to the .Name attribute the value provided is stored
	without modification, i.e. there is no attempt to reattach the
	table's name to the string.  The calling code is responsible for
	doing the correct manipulations.  Therefore, the assignment
	operation below

	>>> print(col.Name)
	snr
	>>> print(col.getAttribute("Name"))
	test:snr
	>>> col.Name = col.Name
	>>> print(col.Name)
	snr
	>>> print(col.getAttribute("Name"))
	snr

	does not preserve the value of the "Name" attribute (though it does
	preserve the stripped form reported by the .Name property).  This
	asymmetry is necessary because the correct table name string to
	reattach to the attribute's value cannot always be known, e.g., if
	the Column object is not part of an XML tree and does not have a
	parent node.
	c               @   s&   e Zd ZedZdZedd ZdS )zColumn.ColumnNamez@(?:\A\w+:|\A)(?P<FullName>(?:(?P<Table>\w+):|\A)(?P<Name>\w+))\Zz%sc             C   s*   | j |d}|dkr&td| |S )a  
			Example:

			>>> Column.ColumnName.table_name("process:process_id")
			'process'
			>>> Column.ColumnName.table_name("process_id")
			Traceback (most recent call last):
			  File "<stdin>", line 1, in <module>
			ValueError: table name not found in 'process_id'
			r   Nztable name not found in '%s')dec_patternmatchgroup
ValueError)r   name
table_namer   r   r   r,      s    zColumn.ColumnName.table_nameN)	r   r   r   recompiler'   enc_patternr   r,   r   r   r   r   
ColumnName   s   
r0   Name)encdecc             C   s   | j | dS )Nr1   )r0   r,   getAttribute)r   r   r   r   r,      s    zColumn.table_namec             C   s
   t | jS )z*
		The number of values in this column.
		)len
parentNode)r   r   r   r   __len__   s    zColumn.__len__c                s8   t |tr" fdd j| D S t j|  jS dS )z1
		Retrieve the value in this column in row i.
		c                s   g | ]}t | jqS r   )getattrr1   ).0r)r   r   r   
<listcomp>
  s    z&Column.__getitem__.<locals>.<listcomp>N)
isinstanceslicer6   r8   r1   )r   ir   )r   r   __getitem__  s    
zColumn.__getitem__c             C   sP   t |tr8x@t| j| |D ]\}}t|| j| qW nt| j| | j| dS )a  
		Set the value in this column in row i.  i may be a slice.

		NOTE:  Unlike normal Python lists, the length of the Column
		cannot be changed as it is tied to the number of rows in
		the Table.  Therefore, if i is a slice, value should be an
		iterable with exactly the correct number of items.  No
		check is performed to ensure that this is true:  if value
		contains too many items the extras will be ignored, and if
		value contains too few items only as many rows will be
		updated as there are items.
		N)r<   r=   zipr6   setattrr1   )r   r>   valuer:   valr   r   r   __setitem__  s    
zColumn.__setitem__c             G   s   t d S )N)NotImplementedError)r   argsr   r   r   __delitem__!  s    zColumn.__delitem__c             c   s"   x| j D ]}t|| jV  qW dS )zK
		Return an iterator object for iterating over values in this
		column.
		N)r6   r8   r1   )r   rowr   r   r   __iter__$  s    zColumn.__iter__c                s   t  fdd| D S )z@
		Return the number of rows with this column equal to value.
		c             3   s   | ]}| kV  qd S )Nr   )r9   x)rB   r   r   	<genexpr>0  s    zColumn.count.<locals>.<genexpr>)sum)r   rB   r   )rB   r   count,  s    zColumn.countc             C   s.   x t | D ]\}}||kr
|S q
W t|dS )zP
		Return the smallest index of the row(s) with this column
		equal to value.
		N)	enumerater*   )r   rB   r>   rJ   r   r   r   index2  s    zColumn.indexc             C   s   |t | kS )zg
		Returns True or False if there is or is not, respectively,
		a row containing val in this column.
		)iter)r   rB   r   r   r   __contains__<  s    zColumn.__contains__c          
   C   s`   ddl }ytj| j }W n8 tk
rP } ztd| d|f W dd}~X Y nX |j| |dS )z
		Construct a numpy array from this column.  Note that this
		creates a copy of the data, so modifications made to the
		array will *not* be recorded in the original document.
		r   Nz0cannot determine numpy dtype for Column '%s': %sr1   )dtype)numpyligolwtypesZToNumPyTypeTypeKeyError	TypeErrorr4   Zfromiter)r   rS   rR   er   r   r   asarrayC  s    	(zColumn.asarrayc                s     | fddS )z=
		Return a list of Column elements named name under elem.
		c                s   | j  j ko| jkS )N)r   r1   )rX   )r   r+   r   r   <lambda>Y      z)Column.getColumnsByName.<locals>.<lambda>)r0   getElements)r   r"   r+   r   )r   r+   r   getColumnsByNameS  s    
zColumn.getColumnsByNameN)r   r   r   r   r   LLWNameAttrr0   attributeproxyr2   r1   propertyr,   r7   r?   rD   rG   rI   rM   rO   rQ   rY   r   r]   r   r   r   r   r&      s   Q	
r&   c               @   s>   e Zd ZdZejZdd Zdd Zdd Ze	j
dfd	d
ZdS )TableStreama  
	High-level Stream element for use inside Tables.  This element
	knows how to parse the delimited character stream into row objects
	that it appends into the list-like parent element, and knows how to
	turn the parent's rows back into a character stream.
	c                sx   t |j |jd k	r" t |jM  t| j| _| j fddt|j	|jD  | 
|j fdd|jD | _| S )Nc                s    g | ]\}}| kr|nd qS )Nr   )r9   Zpytypecolname)loadcolumnsr   r   r;     s    z&TableStream.config.<locals>.<listcomp>c                s   g | ]}| kr|qS r   r   )r9   r+   )rc   r   r   r;     s    )setcolumnnamesrc   r	   	Tokenizer	Delimiter
_tokenizerZ	set_typesr@   columnpytypes
RowBuilderRowType_rowbuilder)r   r6   r   )rc   r   configw  s    

$ zTableStream.configc             C   s2   | j j}x$| j| j|D ]}|| qW d S )N)r6   appendrl   rh   )r   contentZ
appendfuncrH   r   r   r   
appendData  s    zTableStream.appendDatac             C   s$   | j j s| | j | ` | `d S )N)rh   dataisspacerp   rg   rl   )r   r   r   r   
endElement  s    zTableStream.endElement c             C   s   |j }|| | t| jjdd | jjD | j}|| j yt	|}W n t
k
rb   Y nnX d| tj }|| |t| |j| }x |D ]}|| |t| qW |jr|jd dkr||j |d| | d  d S )Nc             S   s   g | ]}t j| qS r   )rT   Z
FormatFunc)r9   coltyper   r   r   r;     s    z%TableStream.write.<locals>.<listcomp>
rt   )writeZ	start_tagr	   Z	RowDumperr6   re   columntypesrg   dumpnextStopIterationr   ZIndent	xmlescape	delimitertokensZend_tag)r   fileobjindentwZ	rowdumperlinenewliner   r   r   rx     s$    "


zTableStream.writeN)r   r   r   r   r	   rj   rm   rp   rs   sysstdoutrx   r   r   r   r   ra   j  s   ra   c                   sJ  e Zd ZdZG dd dejZejdejedZ	dZ
dZdZdZdZG dd deZed	d
 Zedd Zedd Zedd Zedd Zed7ddZdd Zedd Zedd Zdd Zdd Zdd  Zd!d" Z fd#d$Z d%d& Z! fd'd(Z"d)d* Z#ed+d, Z$ed-d. Z%ed/d0 Z&d1d2 Z'd3d4 Z(d5d6 Z)  Z*S )8r   a  
	High-level Table element that knows about its columns and rows.

	Special Attributes
	------------------

	These are used by table-specific subclasses to provide information
	about the table they define.  Set to None when not used.

	.validcolumns:  Dictionary of column name/type pairs defining the
	set of columns instances of this table may have.

	.loadcolumns:  Sequence of names of columns to be loaded.  If not
	None, only names appearing in the list will be loaded, the rest
	will be skipped.  Can be used to reduce memory use.

	.constraints:  Text to be included as constraints in the SQL
	statement used to construct the table.

	.how_to_index:  Dictionary mapping SQL index name to an interable
	of column names over which to construct that index.

	.next_id:  object giving the next ID to assign to a row in this
	table, and carrying the ID column name as a .column_name attribute
	c               @   s   e Zd ZedZdZdS )zTable.TableNamez0(?:\A[a-z0-9_]+:|\A)(?P<Name>[a-z0-9_]+):table\Zz%s:tableN)r   r   r   r-   r.   r'   r/   r   r   r   r   	TableName  s   
r   r1   )r2   r3   Nc               @   s(   e Zd ZdZdd Zdd Zdd ZdS )	zTable.RowTypea"  
		Helpful parent class for row objects.  Also used as the
		default row class by Table instances.  Provides an
		__init__() method that accepts keyword arguments from which
		the object's attributes can be initialized.

		Example:

		>>> x = Table.RowType(a = 0.0, b = "test", c = True)
		>>> x.a
		0.0
		>>> x.b
		'test'
		>>> x.c
		True

		Also provides .__getstate__() and .__setstate__() methods
		to allow row objects to be pickled (otherwise, because they
		all use __slots__ to reduce their memory footprint, they
		aren't pickleable).
		c             K   s&   x |  D ]\}}t| || q
W d S )N)itemsrA   )r   kwargskeyrB   r   r   r   __init__  s    zTable.RowType.__init__c                s&   t  dstt fdd jD S )N	__slots__c             3   s&   | ]}t  |r|t |fV  qd S )N)hasattrr8   )r9   r   )r   r   r   rK     s    z-Table.RowType.__getstate__.<locals>.<genexpr>)r   rE   dictr   )r   r   )r   r   __getstate__
  s    
zTable.RowType.__getstate__c             C   s   | j f | d S )N)r   )r   stater   r   r   __setstate__  s    zTable.RowType.__setstate__N)r   r   r   r   r   r   r   r   r   r   r   rk     s   rk   c             C   s   dd |  tjjD S )z
		The stripped (without table prefixes attached) Name
		attributes of the Column elements in this table, in order.
		These are the names of the attributes that row objects in
		this taable possess.
		c             S   s   g | ]
}|j qS r   )r1   )r9   childr   r   r   r;     s    z%Table.columnnames.<locals>.<listcomp>)r   r   r&   r   )r   r   r   r   re     s    zTable.columnnamesc             C   s   dd |  tjjD S )z
		The non-stripped (with table prefixes attached) Name
		attributes of the Column elements in this table, in order.
		These are the Name attributes as they appear in the XML.
		c             S   s   g | ]}| d qS )r1   )r4   )r9   r   r   r   r   r;   #  s    z)Table.columnnamesreal.<locals>.<listcomp>)r   r   r&   r   )r   r   r   r   columnnamesreal  s    zTable.columnnamesrealc             C   s   dd |  tjjD S )zK
		The Type attributes of the Column elements in this table,
		in order.
		c             S   s   g | ]
}|j qS r   )rU   )r9   r   r   r   r   r;   +  s    z%Table.columntypes.<locals>.<listcomp>)r   r   r&   r   )r   r   r   r   ry   %  s    zTable.columntypesc             C   s   dd |  tjjD S )zm
		The Python types corresponding to the Type attributes of
		the Column elements in this table, in order.
		c             S   s   g | ]}t j|j qS r   )rT   ToPyTyperU   )r9   r   r   r   r   r;   3  s    z'Table.columnpytypes.<locals>.<listcomp>)r   r   r&   r   )r   r   r   r   ri   -  s    zTable.columnpytypesc                s     | fddS )zV
		Return a list of Table elements named name under elem.  See
		also .get_table().
		c                s   | j  j ko| jkS )N)r   r1   )rX   )r   r+   r   r   rZ   B  r[   z'Table.getTablesByName.<locals>.<lambda>)r   r\   )r   r"   r+   r   )r   r+   r   getTablesByName;  s    
zTable.getTablesByNamec             C   s@   |dkr| j }| ||}t|dkr8td| | |d S )a  
		Scan xmldoc for a Table element named name.  Raises
		ValueError if not exactly 1 such table is found.  If name
		is None (default), then the .tableName attribute of this
		class is used.  The Table class does not provide a
		.tableName attribute, but sub-classes, for example those in
		lsctables.py, do provide a value for that attribute.

		Example:

		>>> from ligo.lw import ligolw
		>>> from ligo.lw import lsctables
		>>> xmldoc = ligolw.Document()
		>>> xmldoc.appendChild(ligolw.LIGO_LW()).appendChild(lsctables.New(lsctables.SnglInspiralTable))
		[]
		>>> # find table with .get_table() class method (preferred)
		>>> sngl_inspiral_table = lsctables.SnglInspiralTable.get_table(xmldoc)

		See also .getTablesByName().
		Nr   z*document must contain exactly one %s tabler   )	tableNamer   r5   r*   r   )r   Zxmldocr+   Zelemsr   r   r   	get_tableD  s    zTable.get_tablec             C   sF   t  | }g |_x| jD ]}|t  | qW |dd= |  |S )a  
		Construct and return a new Table document subtree whose
		structure is the same as this table, that is it has the
		same columns etc..  The rows are not copied.  Note that a
		fair amount of metadata is shared between the original and
		new tables.  In particular, a copy of the Table object
		itself is created (but with no rows), and copies of the
		child nodes are created.  All other object references are
		shared between the two instances, such as the RowType
		attribute on the Table object.
		N)copy
childNodesappendChild_end_of_columns)r   newr"   r   r   r   r   a  s    

z
Table.copyc             C   s   |  |j|jS )z
		Return True if element is a Table element whose Name
		attribute matches the .tableName attribute of this class ;
		return False otherwise.  See also .CheckProperties().
		)CheckPropertiesr   
attributes)r   r"   r   r   r   CheckElementv  s    zTable.CheckElementc             C   s   || j ko| |d | jkS )a  
		Return True if tagname and attrs are the XML tag name and
		element attributes, respectively, of a Table element whose
		Name attribute matches the .tableName attribute of this
		class;  return False otherwise.  The Table parent class
		does not provide a .tableName attribute, but sub-classes,
		especially those in lsctables.py, do provide a value for
		that attribute.  See also .CheckElement()

		Example:

		>>> from ligo.lw import lsctables
		>>> lsctables.ProcessTable.CheckProperties(u"Table", {u"Name": u"process:table"})
		True
		r1   )r   r   r   )r   tagnameattrsr   r   r   r     s    zTable.CheckPropertiesc             C   s4   yt | |\}W n tk
r.   t|Y nX |S )a?  
		Retrieve and return the Column child element named name.
		The comparison is done using the stripped names.  Raises
		KeyError if this table has no column by that name.

		Example:

		>>> from ligo.lw import lsctables
		>>> tbl = lsctables.New(lsctables.SnglInspiralTable)
		>>> col = tbl.getColumnByName("mass1")
		)r&   r]   r*   rV   )r   r+   colr   r   r   getColumnByName  s
    zTable.getColumnByNamec             C   s   y|  | td| W n tk
r.   Y nX || jkrF| j| }n6t|| jkrh| jt| }ntd|| jf tt	d| |d}| 
tjj}|r| ||d  n
| | |S )az  
		Append a Column element named "name" to the table.  Returns
		the new child.  Raises ValueError if the table already has
		a column by that name, and KeyError if the validcolumns
		attribute of this table does not contain an entry for a
		column by that name.

		Example:

		>>> from ligo.lw import lsctables
		>>> tbl = lsctables.New(lsctables.ProcessParamsTable, [])
		>>> col = tbl.appendColumn("param")
		>>> print(col.getAttribute("Name"))
		param
		>>> print(col.Name)
		param
		>>> col = tbl.appendColumn(u"process:process_id")
		>>> print(col.getAttribute("Name"))
		process:process_id
		>>> print(col.Name)
		process_id
		zduplicate Column '%s'z"invalid Column '%s' for Table '%s'z%s)r1   rU   r   )r   r*   rV   validcolumnsr&   r0   r   ElementErrorr1   r   r   Streamr   ZinsertBeforer   )r   r+   ru   columnstreamsr   r   r   appendColumn  s     


zTable.appendColumnc             O   s   | j ||}| | |S )z
		Create and append a new row to this table, then return it

		All positional and keyword arguments are passed to the RowType
		constructor for this table.
		)rk   rn   )r   rF   r   rH   r   r   r   	appendRow  s    
zTable.appendRowc             C   s   dS )z#
		Deprecated stub.  Do not use.
		Nr   )r   r   r   r   _update_column_info  s    zTable._update_column_infoc          	      sf  t t| | | j| }|jtjjkr| jdk	r|j| jkrN| j|j }n8|	d| jkrp| j|	d }nt
d|j| jf ||jkrt
d|j|j| j|f ytj|j  W n. tk
r   t
d|j|j| jf Y nX tt| jt| jkrbt
d|j| jf nD|jtjjkrb|	d| 	dkrbt
d|	d| 	df dS )zA
		Used for validation during parsing.  For internal use only.
		Nr1   z"invalid Column '%s' for Table '%s'zCinvalid type '%s' for Column '%s' in Table '%s', expected type '%s'z4unrecognized Type '%s' for Column '%s' in Table '%s'z#duplicate Column '%s' in Table '%s'z/Stream Name '%s' does not match Table Name '%s')r   r   _verifyChildrenr   r   r   r&   r   r1   r4   r   rU   rT   r   rV   r5   rd   re   r   )r   r>   r   expected_type)r   r   r   r     s(    


 zTable._verifyChildrenc             C   s   dS )z
		Called during parsing to indicate that the last Column
		child element has been added.  Subclasses can override this
		to perform any special action that should occur following
		the addition of the last Column element.
		Nr   )r   r   r   r   r     s    zTable._end_of_columnsc                s   t t|   | dd= dS )zp
		Break internal references within the document tree rooted
		on this element to promote garbage collection.
		N)r   r   unlink)r   )r   r   r   r     s    zTable.unlinkc             C   s    | j d jtjjkr|   d S )Nrw   )r   r   r   r   r   )r   r   r   r   rs   %  s    zTable.endElementc             C   s   | j }|  j d7  _ |S )z
		Returns the current value of the next_id class attribute,
		and increments the next_id class attribute by 1.  Raises
		ValueError if the table does not have an ID generator
		associated with it.
		r   )r   )r   r   r   r   r   get_next_id2  s    	zTable.get_next_idc             C   s   t | j|| _dS )z
		Sets the value of the next_id class attribute.  This is a
		convenience function to help prevent accidentally assigning
		a value to an instance attribute instead of the class
		attribute.
		N)r   r   )r   r   r   r   r   set_next_id?  s    zTable.set_next_idc             C   s   | j dk	r| d dS )z
		If the current value of the next_id class attribute is not
		None then set it to 0, otherwise it is left unmodified.

		Example:

		>>> from ligo.lw import lsctables
		>>> for cls in lsctables.TableByName.values(): cls.reset_next_id()
		Nr   )r   r   )r   r   r   r   reset_next_idI  s    
zTable.reset_next_idc             C   sH   | j dk	rBt| r*t| | j jd }nd}|| j krB| | | j S )a  
		Determines the highest-numbered ID in this table, and sets
		the table's .next_id attribute to the next highest ID in
		sequence.  If the .next_id attribute is already set to a
		value greater than the highest value found, then it is left
		unmodified.  The return value is the ID identified by this
		method.  If the table's .next_id attribute is None, then
		this function is a no-op.

		Note that tables of the same name typically share a common
		.next_id attribute (it is a class attribute, not an
		attribute of each instance) so that IDs can be generated
		that are unique across all tables in the document.  Running
		sync_next_id() on all the tables in a document that are of
		the same type will have the effect of setting the ID to the
		next ID higher than any ID in any of those tables.

		Example:

		>>> from ligo.lw import lsctables
		>>> tbl = lsctables.New(lsctables.ProcessTable)
		>>> print(tbl.sync_next_id())
		0
		Nr   r   )r   r5   maxr   r   r   )r   nr   r   r   sync_next_idW  s    


zTable.sync_next_idc             C   s   | j dkrt| y| | j j}W n tk
r8   |S X | j}x`t|D ]T\}}|dkrltd| j|f ||f}||kr|| ||< qJ|   ||< ||< qJW |S )ac  
		Used as the first half of the row key reassignment
		algorithm.  Accepts a dictionary mapping old key --> new
		key.  Iterates over the rows in this table, using the
		table's next_id attribute to assign a new ID to each row,
		recording the changes in the mapping.  Returns the mapping.
		Raises ValueError if the table's next_id attribute is None.
		Nz-null row ID encountered in Table '%s', row %d)r   r*   r   r   rV   r1   rN   r   )r   r#   r   r,   r>   oldr   r   r   r   r    y  s    	
zTable.updateKeyMappingc          
   C   s   x| j D ]}| |}y
|j}W n tk
r6   wY nX | jdk	rP|| jjkrPqx>t|D ]2\}}y|||f ||< W qZ tk
r   Y qZX qZW qW dS )z
		Used as the second half of the key reassignment algorithm.
		Loops over each row in the table, replacing references to
		old row keys with the new values from the mapping.
		N)re   r   r,   r*   r   r   rN   rV   )r   r#   rb   r   r,   r>   r   r   r   r   r!     s    

zTable.applyKeyMapping)N)+r   r   r   r   r   r^   r   r_   r2   r1   r   rc   constraintsZhow_to_indexr   objectrk   r`   re   r   ry   ri   r   r   r   r   r   r   r   r   r   r   r   r   r   rs   r   r   r   r   r    r!   r   r   r   )r   r   r     sB   "
		
0!	
"r   c             C   s4   dd }| j fdd}dd }|| _|| _ || _| S )a  
	Modify ContentHandler, a sub-class of
	ligo.lw.ligolw.LIGOLWContentHandler, to cause it to use the Table,
	Column, and Stream classes defined in this module when parsing XML
	documents.

	Example:

	>>> from ligo.lw import ligolw
	>>> class LIGOLWContentHandler(ligolw.LIGOLWContentHandler):
	...	pass
	...
	>>> use_in(LIGOLWContentHandler)
	<class 'ligo.lw.table.LIGOLWContentHandler'>
	c             S   s   t |S )N)r&   )r   parentr   r   r   r   startColumn  s    zuse_in.<locals>.startColumnc             S   s0   |j tjj kr$|  t||S || ||S )N)r   r   r   r   ra   rm   )r   r   r   Z__orig_startStreamr   r   r   startStream  s    zuse_in.<locals>.startStreamc             S   s   t |S )N)r   )r   r   r   r   r   r   
startTable  s    zuse_in.<locals>.startTable)r   r   r   )ZContentHandlerr   r   r   r   r   r   use_in  s    r   )r   r   	itertoolsr-   r   Zxml.sax.saxutilsr   r}   Zxml.sax.xmlreaderr   rt   r   r   r   r   r	   r
   rT   intr   r%   r&   r   ra   r   listr   r   r   r   r   <module>,   s(   + l`   x