package textbender.a.u.branch; // Copyright 2007, Michael Allan. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Textbender Software"), to deal in the Textbender Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Textbender Software, and to permit persons to whom the Textbender Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Textbender Software. THE TEXTBENDER SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE TEXTBENDER SOFTWARE OR THE USE OR OTHER DEALINGS IN THE TEXTBENDER SOFTWARE. import java.io.*; import java.util.*; import javax.xml.transform.*; import javax.xml.transform.dom.*; import javax.xml.transform.stream.*; import org.w3c.dom.*; import org.w3c.dom.ls.*; import textbender._.*; import textbender.d.gene.*; import textbender.d.gene.xhtml.*; import textbender.d.revision.*; import textbender.d.transfer.*; import textbender.g.util.*; import textbender.g.xml.dom.*; import textbender.g.xml.dom.bootstrap.*; import static textbender._.Textbender.TEXTBENDER_NAMESPACE; /** A revision brancher. */ public final class Brancher { /** Constructs a Brancher for a document. * * @param gg the 'g' list ('gg') element of the document * @param gList live list of gene meta-data ('g') elements in 'gg' * (e.g. backed by a NodeList) * @param domLS DOM implementation to use * @param mutantAbstractor to use * @param parser to use * @param stringBuilder string builder to use, overwriting its existing content * @param transformerFactory to use * @param uuidStringifier UUID stringifier to use */ Brancher( Element gg, List gList, DOMImplementationLS domLS, MutantAbstractor mutantAbstractor, LSParser parser, StringBuilder stringBuilder, TransformerFactory transformerFactory, UUIDStringifier uuidStringifier ) throws TransformerConfigurationException // TransformerConfigurationException newTransformer { this.gg = gg; this.gList = gList; this.domLS = domLS; this.mutantAbstractor = mutantAbstractor; this.parser = parser; this.uuidStringifier = uuidStringifier; _stringBuilder = stringBuilder; document = gg.getOwnerDocument(); eSS = transformerFactory.newTransformer(); // identity eSS.setOutputProperty( OutputKeys.OMIT_XML_DECLARATION, "yes" ); eSSWriter = new StringWriter(); eSSResult = new StreamResult( eSSWriter ); } // ------------------------------------------------------------------------------------ /** Assigns the document to a new revision line. *
    *
  1. If the document already has a revision line, * then branch processing is done, * in order to update the ancestry records.
  2. *
  3. The document receives a new, unique revision line ID.
  4. *
  5. Its 'gR' gene reindex counter is effectively reset to zero.
  6. *
*/ public void branch() throws TransformerException { final Element metaData = (Element)gg.getParentNode(); final Element revisionLine = Revision.ensureRevisionLine( metaData, _stringBuilder ); // if it already has a revision line ID, do branch processing // ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` if( revisionLine.getAttributeNS( null, "id" ).length() > 0 ) { recordTransferRecursively( document.getDocumentElement() ); } // set a new, unique revision line ID // ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` revisionLine.setAttributeNS( null, "id", uuidStringifier.toBase64Name( UUID.randomUUID() )); // reset 'gR' gene reindex counter // ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` if( !Gene.gROf(gg).equals( "0" )) gg.removeAttributeNS( null, "gR" ); } /** Returns a Recombinant XHTML document read from an input stream, * and then {@linkplain #branch branched}. Convenience method. * * @param in stream to read document from * @param transformerFactory to use * @param errorHandler to use * * @return document, suitable for passing to * RecombinantXHTML.{@linkplain RecombinantXHTML#write(Document,TransformerFactory,Result) write} * * @throws LSException from parse, also reported * to the provided errorHandler * @throws TextbenderException if document not in recombinant form * @throws TransformerException from branch, newTransformer */ static Document branched( final InputStream in, final TransformerFactory transformerFactory, final DOMErrorHandler errorHandler ) throws LSException, TextbenderException, TransformerException { // Read from input. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Document document = null; // till read final DOMImplementationLS domLS = DOMImplementationRegistryX.implementationLS(); final LSParser parser = domLS.createLSParser ( domLS.MODE_SYNCHRONOUS, "http://www.w3.org/TR/REC-xml" ); { final DOMConfiguration domConfig = parser.getDomConfig(); domConfig.setParameter( "error-handler", errorHandler ); // domConfig.setParameter( "validate", true ); domConfig.setParameter ( "resource-resolver", // new textbender.d.gene.xhtml.RecombinantXHTML.DOMResourceResolver( domLS ) new textbender.d.gene.xhtml.RecombinantXHTML.DOMResourceResolverMin( domLS ) ); final LSInput lsInput = domLS.createLSInput(); lsInput.setByteStream( in ); document = parser.parse( lsInput ); } // Branch. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - { final StringBuilder b = new StringBuilder(); // // final Element metaData = DocumentRT.findMetaData( document ); // // if( metaData == null ) throw new TextbenderException( DocumentRT.FIND_META_DATA_FAILURE_MESSAGE ); // //// but allow new, blank texts to be loaded, without meta-data: final Element metaData = RecombinantXHTML.ensureMetaData( document, b ); final Element gg = Gene.ensureGG( metaData, b ); final List gList = new ElementListNL ( gg.getElementsByTagNameNS( TEXTBENDER_NAMESPACE, "g" )); final UUIDStringifier uuidStringifier = new UUIDStringifier(); Brancher brancher = new Brancher ( gg, gList, domLS, RecombinantXHTML.i(), parser, b, transformerFactory, uuidStringifier ); brancher.branch(); } return document; } //// P r i v a t e /////////////////////////////////////////////////////////////////////// private final Document document; private final DOMImplementationLS domLS; private final Transformer eSS; // identity, string serializer for embedded sequence copies private final StreamResult eSSResult; private final StringWriter eSSWriter; private final Element gg; private final List gList; private final MutantAbstractor mutantAbstractor; private final DOMSource nodeTransformSource = new DOMSource(); // for setting via setNode() only private final LSParser parser; private void recordTransferRecursively( final Node node ) throws TransformerException // TransformerException recordAsTransferSourceSequence { record: if( node instanceof Element ) { final Element element = (Element)node; int gIndex = Gene.gIndexOf( element ); if( gIndex < 0 ) break record; final Element g = gList.get( gIndex ); if( !Transfer.isTransferMutant( element, g, gList, mutantAbstractor, domLS, parser, _stringBuilder )) break record; Transfer.recordAsTransferSource ( element, gg, gList, g, nodeTransformSource, eSS, eSSWriter, eSSResult, _stringBuilder ); } for( Node child = node.getFirstChild(); child != null; child = child.getNextSibling() ) { recordTransferRecursively( child ); } } /** Common string builder. */ private final StringBuilder _stringBuilder; /** Empties and returns the common string builder. */ private StringBuilder _stringBuilderEmpty() { _stringBuilder.delete( 0, Integer.MAX_VALUE ); return _stringBuilder; } private final UUIDStringifier uuidStringifier; }