/* * BioJava development code * * This code may be freely distributed and modified under the * terms of the GNU Lesser General Public Licence. This should * be distributed with the code. If you do not have a copy, * see: * * http://www.gnu.org/copyleft/lesser.html * * Copyright for this code is held jointly by the individual * authors. These should be listed in @author doc comments. * * For more information on the BioJava project and its aims, * or to join the biojava-l mailing list, visit the home page * at: * * http://www.biojava.org/ * */ package org.biojava.bio.program.blast2html; import java.io.PrintWriter; import java.util.Arrays; import java.util.List; import java.util.Properties; /** * Renders HTML version of blast-like output.

* * Makes an assumption that the gap character is '-', * should parameterize. * * * Primary author - * Colin Hardman (CAT) * Other authors - * Tim Dilks (CAT) * Simon Brocklehurst (CAT) * Stuart Johnston (CAT) * Lawerence Bower (CAT) * Derek Crockford (CAT) * Neil Benn (CAT) * * Copyright 2001 Cambridge Antibody Technology Group plc. * * This code released to the biojava project, May 2001 * under the LGPL license. * * @author Cambridge Antibody Technology Group plc * @author Greg Cox * @version 1.0 */ public class HTMLRenderer { private StringBuffer align = new StringBuffer(); private StringBuffer alignMarkUp1 = new StringBuffer(); private StringBuffer alignMarkUp2 = new StringBuffer(); private PrintWriter out = null; private String oStyleDef; private StringBuffer pcDataBuffer = new StringBuffer( 80 ); private boolean tAlternateSummary = false; private int iSummaryCount =0; private boolean tNeedComma = false; /** * The width, in characters, of the sequence alignments */ private int iAlignLen = 50; private List oURLGeneratorList = null; private DatabaseURLGenerator oFirstURLGenerator = null; private AlignmentMarker oAlignmentMarker; private static String oJavaScriptDef = ""; private char[] padding = new char[100]; { Arrays.fill( padding, 0, 100, ' ' ); } private Properties oOptions = null; /** * Flag to detect if HTML output was empty. */ private boolean wasEmpty = true; /** * Creates an HTMLRenderer, that outputs the HTML to the specified * PrintWriter.

* * The style definition is expected to defines the following styles *

* These URL's are used in two places, in the summary and detail sections. * The first URLGenerator is used for the summary, and all for the detail. * * * @param poPrintWriter - the output stream to write the HTML to. * @param poStyleDef - definition of styles, if null uses hard coded. * @param piAlignmentWidth width in characters of the alignment regions * of output * @param poFactory URLGeneratorFactory provides an array of * DatabaseURLGenerators, null for no links * @param poAlignmentMarker - for configurable markup of alignments, * for no markup use null. * @param poOptions properties of */ public HTMLRenderer( PrintWriter poPrintWriter, String poStyleDef, int piAlignmentWidth, URLGeneratorFactory poFactory, AlignmentMarker poAlignmentMarker, Properties poOptions ) { oOptions = poOptions; if ( poFactory != null ) { oURLGeneratorList = poFactory.getDatabaseURLGenerators(); if ( !oURLGeneratorList.isEmpty() ) { oFirstURLGenerator = (DatabaseURLGenerator) oURLGeneratorList.get( 0 ); } } if ( piAlignmentWidth <= 0 ) { throw new IllegalArgumentException ( "Alignment length must be > 0, not " + piAlignmentWidth ); } iAlignLen = piAlignmentWidth; oStyleDef = poStyleDef ; if ( poPrintWriter == null ) { throw new IllegalArgumentException ( "PrintWriter cannot be null" ); } out = poPrintWriter; oAlignmentMarker = poAlignmentMarker; } /** * Set the PrintWriter to output the HTML * to. * * @param poPrintWriter a PrintWriter */ public void setPrintWriter( PrintWriter poPrintWriter ) { out = poPrintWriter; // initialize other stuff tAlternateSummary = false; iSummaryCount =0; tNeedComma = false; wasEmpty = true; align.setLength( 0 ); alignMarkUp1.setLength( 0 ); alignMarkUp2.setLength( 0 ); pcDataBuffer.setLength( 0 ); } /** * Returns true if no HitSummary and no Hit elements were * encountered * * @return boolean - true if no summary of Hit elements * where encountered. */ public boolean wasEmpty() { return wasEmpty; } /** * Writes out the header section of the report. * * @param oProgram a String - eg 'ncbi-blastp' * @param oVersion a String - eg '2.2.1' * @param oQuery a String - eg 'my query id' * @param oDatabase a String- eg 'embl' */ void writeTitleAndHeader( String oProgram, String oVersion, String oQuery, String oDatabase ) { out.print( "" ); out.print( oProgram ); out.print( " version " ); out.print( oVersion ); out.print( " Search Results Query ID : " ); out.print( oQuery ); out.println( "" ); out.println( "" ); out.println( " " ); out.println( " " ); out.println( " " ); out.print( " " ); out.println( " " ); out.println( " " ); out.print( " " ); out.println( " " ); out.println( " " ); out.print( " " ); out.println( " " ); out.println( "
Sequence Similarity Report Query Id:" ); out.print( oQuery ); out.println( " 
Search Program:" ); out.print( oProgram ); out.print( " v. " ); out.print( oVersion ); out.println( "
Database:" ); out.print( oDatabase ); out.println( "
" ); } /** * Returns the appropriate style and javascript definitions for this * renderer. * * @return a String value */ public String getHeaderDefinitions() { StringBuffer sb = new StringBuffer(); sb.append("\n" ); sb.append( oJavaScriptDef ); return sb.substring(0); } /** * Called when first summary item is reached. * * @param oHitSummary a HitSummary - the first Summary item. */ void startSummaryTable( HitSummary oHitSummary ) { wasEmpty = false; out.println( "
" ); out.println( "" ); out.println( " " ); out.println( "" ); out.println( "" ); out.println( "
Overview of Results
" ); out.println( "" ); out.println( " " ); if ( oHitSummary.readingFrame != null ) { out.println( "" ); } out.println( "" ); if ( oHitSummary.expectValue != null ) { out.println( "" ); } if ( oHitSummary.smallestSumProbability != null ) { out.println( "" ); } if ( oHitSummary.numberOfHSPs != null ) { out.println( "" ); } if ( oHitSummary.numberOfContributingHSPs != null ) { out.println( "" ); } out.println( "" ); // start a new summary table iSummaryCount = 10; } /** * Called when a hit summary is reached ( except first ). * * @param oHitSummary a HitSummary value */ void writeCurrentSummary( HitSummary oHitSummary ) { if ( iSummaryCount == 10 ) { // lets break up that big table iSummaryCount = 0; out.println( "
" ); out.println( "" ); out.println( " " ); out.println( "" ); out.println( "" ); out.println( "" ); out.println( "
Hit IdHit Description
" ); out.println( "
FrameScoreE valueP(N)HSPsHSPs
" ); out.println (""); } out.println( " " ); out.print( " " ); // reading frame this.writeSummaryColumn( oHitSummary.readingFrame, "center" ); // score out.print( "" ); // Expect Value this.writeSummaryColumn( oHitSummary.expectValue, "center" ); // Smallest sum prob this.writeSummaryColumn( oHitSummary.smallestSumProbability, "center" ); // numberOfHSPs this.writeSummaryColumn( oHitSummary.numberOfHSPs, "center"); // numberOfContributingHSPs this.writeSummaryColumn( oHitSummary.numberOfContributingHSPs, "center" ); out.println( "" ); tAlternateSummary = !tAlternateSummary; iSummaryCount ++; } /** * Outputs a summary column item. * */ void writeSummaryColumn( String poValue, String poAlign ) { if ( poValue != null ) { out.print( "" ); } } /** * Called when summary table is complete. * */ void endSummaryTable() { out.println( "
" ); out.println (""); out.println( " " ); out.print( "" ); } else { out.print( oHitSummary.oHitId.id ); out.println( "" ); } out.print( "" ); out.println( "" ); out.println( "
" ); if ( oFirstURLGenerator!= null ) { out.print( "" ); out.print( oHitSummary.oHitId.id ); out.print( "" ); this.writeContentChars( oHitSummary.oDesc.hitDescription ); out.println( "
" ); out.println( "
" ); out.print( oHitSummary.score ); out.println( "
" ); out.print( poValue ); out.println( "
" ); out.println( "
" ); } /** * Start the Detail table. * */ void startDetailTable() { wasEmpty = false; out.println( "
" ); out.println( "" ); out.println( " " ); out.println( "" ); out.println( "" ); out.println( "
Detailed Analysis of Results
" ); } /** * Write the current detail. * */ void writeCurrentDetail( DetailHit oDetailHit ) { out.println ( "" ); out.println( " " ); out.print( "" ); out.println( "" ); out.println( "" ); out.println( " " ); out.println( "" ); out.println( "
" ); if ( oURLGeneratorList != null && !oURLGeneratorList.isEmpty() ) { out.println( " " ); out.print( "" ); out.println( "" ); } out.print( "
Hit Id : " ); out.print( oDetailHit.oHitId.id ); out.println( "
" ); this.writeContentChars( oDetailHit.oDesc.hitDescription ); out.println( "
Top
" ); out.print( "

Sequence length of hit = " ); out.print( oDetailHit.sequenceLength); out.println( "

" ); for ( int i =0 ; i < oURLGeneratorList.size() ; i++ ) { DatabaseURLGenerator oURLGenerator = ( DatabaseURLGenerator ) oURLGeneratorList.get( i ); out.print( oURLGenerator.toLink( oDetailHit.oHitId.id, oOptions )); } out.println( "

" ); // out.println( "
" ); } /** * Utility for writing out each HSP info item, such as * score ot number of identities. * */ void writeHSPInfo( String poName, String poValue ) { if ( poValue != null ) { if ( tNeedComma ) { out.print( ", " ); } out.print( poName ); out.print( " = " ); out.print( poValue ); tNeedComma = true; } } /** * Writes out the current HSP. * */ void writeCurrentHSP( HSPSummary oHSPSummary, BlastLikeAlignment oAlignment ) { tNeedComma = false; out.println ( "" ); out.println( " " ); out.println ( "" ); out.println( "" ); out.println( "
High-scoring segment pair (HSP) group
" ); out.println( "

" ); out.println ( "" ); out.println( " " ); out.println( "" ); out.println( "" ); out.println( "
" ); out.println( "

HSP Information
" ); this.writeHSPInfo( "Score", oHSPSummary.score ); this.writeHSPInfo( "E", oHSPSummary.expectValue ); this.writeHSPInfo( "P", oHSPSummary.pValue ); if ( oHSPSummary.numberOfIdentities != null || oHSPSummary.percentageIdentity != null ) { out.print( ", Identities = " ); if ( oHSPSummary.numberOfIdentities != null ) { out.print( oHSPSummary.numberOfIdentities ); if ( oHSPSummary.alignmentSize != null ) { out.print( "/" ); out.print( oHSPSummary.alignmentSize ); } if ( oHSPSummary.percentageIdentity != null ) { out.print( " (" ); out.print( oHSPSummary.percentageIdentity ); out.print( "%)" ); } } } if ( oHSPSummary.numberOfPositives != null || oHSPSummary.percentagePositives != null ) { out.print( ", Positives = " ); if ( oHSPSummary.numberOfPositives != null ) { out.print( oHSPSummary.numberOfPositives ); if ( oHSPSummary.alignmentSize != null ) { out.print( "/" ); out.print( oHSPSummary.alignmentSize ); } if ( oHSPSummary.percentagePositives != null ) { out.print( " (" ); out.print( oHSPSummary.percentagePositives ); out.print( "%)" ); } } } this.writeHSPInfo( "Length", oHSPSummary.alignmentSize ); this.writeHSPInfo( "Query Frame", this.toSign( oHSPSummary.queryFrame ) ); this.writeHSPInfo( "Hit Frame", this.toSign( oHSPSummary.hitFrame ) ); if ( oHSPSummary.queryFrame == null ) { this.writeHSPInfo( "Query Strand", this.toSign( oHSPSummary.queryStrand ) ); } if ( oHSPSummary.hitFrame == null ) { this.writeHSPInfo( "Hit Strand", this.toSign( oHSPSummary.hitStrand )); } this.writeHSPInfo( "P(N)", oHSPSummary.sumPValues ); this.writeHSPInfo( "No. of gaps", oHSPSummary.numberOfGaps ); out.println( "

" ); out.println( "
" ); out.println( "
" ); out.println( "
" ); out.println( "" ); out.println( " " ); out.println( "" ); out.println( "" ); this.drawCurrentAlignment( oAlignment ); out.println( "
" ); out.println( "
" ); } /** * Draws one block of the alignment. */ String drawSubAlignment( String piQueryStart, String piQueryStop, String piHitStart, String piHitStop, String poQuery, String poConsensus, String poHit ) { align.setLength( 0 ); int iMax = 4; if ( piQueryStart.length() > iMax ) { iMax = piQueryStart.length(); } if ( piHitStart.length() > iMax ) { iMax = piHitStart.length(); } iMax = iMax+2; String[] oFormattedSeq = new String[]{ poQuery, poHit, poConsensus }; if ( oAlignmentMarker != null ) { oAlignmentMarker.alignment2HTML( oFormattedSeq ); } String oConsensusPad = " "; oConsensusPad = oConsensusPad.concat( this.padTo( "", iMax) ); align.append( " " ); align.append( "\n"); align.append( "" ); align.append( "\n"); align.append( "
Query: " );
	align.append( this.padTo( piQueryStart, iMax) );

	align.append( oFormattedSeq[0] );
	align.append( "  ");
	align.append( this.padTo( piQueryStop, iMax) );

	align.append( "\n");

	align.append( oConsensusPad );

	align.append( oFormattedSeq[2] );
	align.append( "\n");
	align.append( "Hit  : " );
	align.append( this.padTo( piHitStart, iMax ) );

	align.append( oFormattedSeq[1] );
	align.append( "  ");
	align.append( this.padTo( piHitStop, iMax) );
	align.append( "
" ); align.append( "\n"); align.append( "" ); align.append( "\n"); align.append( "" ); align.append( "\n"); align.append( "" ); align.append( "\n"); return align.substring(0); } /** * Draws a full alignment block. */ void drawCurrentAlignment( BlastLikeAlignment oAlignment ) { int i = 0; int iCurrentQueryStart = Integer.parseInt ( oAlignment.oQuerySeq.startPosition ); int iCurrentHitStart = Integer.parseInt ( oAlignment.oHitSeq.startPosition ); int iQueryLen = oAlignment.oQuerySeq.seq.length(); int iHitLen = oAlignment.oHitSeq.seq.length(); int iNumberOfQueryGaps = 0; int iNumberOfHitGaps = 0; int index = -1; while ( ( index = oAlignment.oQuerySeq.seq.indexOf( '-', index+1 ) ) != -1 ) { iNumberOfQueryGaps++; } index = -1; while ( ( index = oAlignment.oHitSeq.seq.indexOf( '-', index+1 ) ) != -1 ) { iNumberOfHitGaps++; } int iQStop = Integer.parseInt( oAlignment.oQuerySeq.stopPosition ); int iHStop = Integer.parseInt( oAlignment.oHitSeq.stopPosition ); int queryDirection = 1; int hitDirection = 1; if ( iQStop < iCurrentQueryStart ) { queryDirection = -1; } if ( iHStop < iCurrentHitStart ) { hitDirection = -1; } int iQueryMultiplier = (( iQStop - iCurrentQueryStart ) + queryDirection)/ ( iQueryLen - iNumberOfQueryGaps ); int iHitMultiplier = (( iHStop - iCurrentHitStart ) + hitDirection )/ ( iHitLen - iNumberOfHitGaps ); int iCurrentQueryEnd = 0; int iCurrentHitEnd = 0; // // // Substring ( i*iAlignLen, (i+1)*iAlignLen ) // // Increment the end number by ( (iAlign-numberofgaps)* multiplier ) // // The end check should be the current end number // while( ((i+1)*iAlignLen) < iQueryLen ) { String oCurrentQueryString = oAlignment.oQuerySeq.seq.substring ( i*iAlignLen, (i+1)*iAlignLen ); String oCurrentHitString = oAlignment.oHitSeq.seq.substring ( i*iAlignLen, (i+1)*iAlignLen ); iNumberOfQueryGaps = this.countNumberOfGaps( oCurrentQueryString ); iNumberOfHitGaps = this.countNumberOfGaps( oCurrentHitString ); iCurrentQueryEnd = iCurrentQueryStart + ( ( iAlignLen - iNumberOfQueryGaps ) * iQueryMultiplier ); iCurrentHitEnd = iCurrentHitStart + (( iAlignLen - iNumberOfHitGaps ) * iHitMultiplier ); out.println( drawSubAlignment ( iCurrentQueryStart + "", (iCurrentQueryEnd - queryDirection) + "", iCurrentHitStart + "", (iCurrentHitEnd - hitDirection) + "", oCurrentQueryString, oAlignment.oConsensus.substring ( i*iAlignLen, (i+1)*iAlignLen ) , oCurrentHitString ) ); i++; iCurrentQueryStart += ( ( iAlignLen - iNumberOfQueryGaps ) * iQueryMultiplier ); iCurrentHitStart += ( ( iAlignLen - iNumberOfHitGaps ) * iHitMultiplier ); } // end while if ( iQStop != iCurrentQueryEnd ) { iCurrentQueryEnd = iQStop; iCurrentHitEnd = iHStop; out.println( drawSubAlignment( iCurrentQueryStart + "", iCurrentQueryEnd + "", iCurrentHitStart + "", iCurrentHitEnd + "", oAlignment.oQuerySeq.seq.substring ( i*iAlignLen ) , oAlignment.oConsensus.substring ( i*iAlignLen ) , oAlignment.oHitSeq.seq.substring ( i*iAlignLen ) ) ); } } /** * Renderers end of detail table * */ void endDetailTable() { out.println ( "" ); out.println( " " ); out.println( "" ); out.println( "" ); out.println( "
" ); out.println ("
" ); out.println ( "Produced using biojava " ); out.println( "- www.biojava.org
" ); out.println( "
" ); } /** * Convert from 'plus' and 'minus' to '+' and '-' * * @param poString String to convert. * @return new String */ private String toSign( String poString ) { String oSign = null; if ( poString != null ) { if ( poString.startsWith( "plus" ) ) { oSign = "+"; if ( poString.length() > 4 ) { oSign = oSign.concat( poString.substring( 4 )); } } else if ( poString.startsWith( "minus" )) { oSign = "-"; if ( poString.length() > 5 ) { oSign = oSign.concat( poString.substring( 5 )); } } } return oSign; } // ************************************************************ // // * Escape HTML chars * // // * * // // * Probably replace this with something in a apache or * // // * java lib. * // // * * // // * * // // ************************************************************ // private void writeContentChars( String poLine ) { poLine = this.replace( poLine, '&', "&"); poLine = this.replaceGtLtAndQuote( poLine ); out.print( poLine ); } private String replaceGtLtAndQuote( String poInputString ) { pcDataBuffer.setLength( 0 ); pcDataBuffer.append( poInputString ); int iLength = ( pcDataBuffer.length() ); for (int i = iLength; --i >= 0; ) { if ( (pcDataBuffer.charAt(i) == '<') ) { pcDataBuffer.deleteCharAt(i); // then insert escape char to the LHS pcDataBuffer.insert(i, "<" ); } else if ( (pcDataBuffer.charAt(i) == '>') ) { pcDataBuffer.deleteCharAt(i); // then insert escape char to the LHS pcDataBuffer.insert(i, ">" ); } else if ( (pcDataBuffer.charAt(i) == '\\') ) { pcDataBuffer.deleteCharAt(i); // then insert escape char to the LHS pcDataBuffer.insert(i, """ ); } // end if } // end for return pcDataBuffer.substring(0); } private String replace( String poInputString, char pcCharToReplace, String poReplacementString ) { pcDataBuffer.setLength( 0 ); pcDataBuffer.append( poInputString ); int iLength = ( pcDataBuffer.length() ); for (int i = iLength; --i >= 0; ) { if ( pcDataBuffer.charAt(i) == pcCharToReplace ) { pcDataBuffer.deleteCharAt(i); // then insert escape char to the LHS pcDataBuffer.insert(i, poReplacementString ); } // end if } // end for return pcDataBuffer.substring(0); } /** * Makes assumption about the gap character. * */ int countNumberOfGaps( String poString ) { int index = -1; int iNumberOfGaps = 0; while ( ( index = poString.indexOf( '-', index+1 ) ) != -1 ) { iNumberOfGaps++; } return iNumberOfGaps; } /** * Ensures the given string is the correct length. * */ String padTo( String poString, int iNumberOfChars ) { int iLen = iNumberOfChars - poString.length(); if ( iLen > padding.length ) { padding = new char[ iLen ]; Arrays.fill( padding, 0, iLen, ' ' ); } return poString.concat( String.copyValueOf( padding, 0, iLen ) ); } } // end class