001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 */
021package org.biojava.bio.program.blast2html;
022
023
024/**
025 * <p>
026 * Class to do simple HTML colouring of sequence alignments.
027 * </p>
028 *
029 * <p>
030 * For any particular alignment position, the colouring depends only on the two
031 * characters at that position. The decision is made in two stages:-
032 * <ol>
033 *   <li>Whether to colour or not in the <CODE>ColourCommand.isColoured()</CODE>
034 *    method</li>
035 *   <li>what the colours are in the <CODE>AlignmentStyler.getStyle()</CODE>
036 *    method</li>
037 * </ol>
038 * </p>
039 *
040 * <p>
041 * This allows simple choices to highlight the mismatches, the identities or
042 * simply colour up everything.
043 * </p>
044 *
045 * <p>
046 * The current implemention of step 2. is a simple colour lookup.
047 * </p>
048 *
049 * <p>
050 * Limitations:
051 * </p>
052 *
053 * <p>
054 * As the FONT styles need to be defined before being used in the HTML,
055 * it means the all colours to be used have to calculated up front.
056 * </p>
057 *
058 * <p>
059 * The position in the alignment is not passed in so position dependent
060 * colouring ( say against a HMM profile ) would be involve either
061 * changing the interfaces or implementing them such that they knew the
062 * required information via another route.
063 * </p>
064 *
065 * <p><pre>
066 * Primary author -
067 *                 Colin Hardman      (CAT)
068 * Other authors  -
069 *                 Tim Dilks          (CAT)
070 *                 Simon Brocklehurst (CAT)
071 *                 Stuart Johnston    (CAT)
072 *                 Lawerence Bower    (CAT)
073 *                 Derek Crockford    (CAT)
074 *                 Neil Benn          (CAT)
075 *
076 * Copyright 2001 Cambridge Antibody Technology Group plc.
077 * </pre></p>
078 *
079 * <p>
080 * This code released to the biojava project, May 2001
081 * under the LGPL license.
082 * </p>
083 *
084 * @author Cambridge Antibody Technology Group plc
085 * @author Greg Cox
086 * @version 1.0
087 */
088public class AlignmentMarker {
089
090    /**
091     * Resuable StringBuffers for the markup of the alignments
092     */
093    private StringBuffer[] markedUp  = new StringBuffer[ 3 ];
094    {
095        markedUp[0] = new StringBuffer( 150 );
096        markedUp[1] = new StringBuffer( 150 );
097        markedUp[2] = new StringBuffer( 150 );
098    }
099
100
101    /**
102     * Holds the current style for query, hit and consensus
103     */
104    private String[] oCurrentStyle = new String[3];
105    {
106        oCurrentStyle[0] = null;
107        oCurrentStyle[1] = null;
108        oCurrentStyle[2] = null;
109    }
110
111
112    /**
113     * Holds the NewStyles for query, hit and consensus
114     */
115    private String[]  oNewStyle = new String[ 3 ];
116
117    /**
118     * Single method interface for deciding whether to colour
119     * a particular alignment pair.
120     */
121    private ColourCommand oColourCommand;
122
123    /**
124     * Class that determines the style for each char
125     */
126    private AlignmentStyler oStyler;
127
128    /**
129     * Creates a new <code>AlignmentMarker</code> instance.
130     *
131     * @param poColourCommand - controls whether a particular alignment pair
132     *                          should be coloured
133     * @param poStyler - specifies all possible styles and where each style
134     *                   is used
135     */
136    public AlignmentMarker( ColourCommand poColourCommand,
137                            AlignmentStyler  poStyler ) {
138
139        oColourCommand = poColourCommand;
140        oStyler  = poStyler;
141    }
142
143
144    /**
145     * <p>
146     * Delegate to the AlignmentStyler
147     * </p>
148     *
149     * <p>
150     * Returns a fragment of HTML that defines the FONT
151     * styles to be used in the alignment markup.
152     * </p>
153     *
154     * <p>
155     * For example:
156     * <PRE>
157     * FONT.C2-S{background-color:#FFFC50;color:#000000}
158     * FONT.C4-S{background-color:#FC50FF;color:#000000}
159     * FONT.C3-S{background-color:#FF7272;color:#000000}
160     * FONT.C0-S{background-color:#50FF78;color:#000000}
161     * FONT.C1-S{background-color:#FFCA50;color:#000000}
162     * FONT.C5-S{background-color:#A5A5FF;color:#000000}
163     * </PRE>
164     * </p>
165     *
166     * @return String - the HTML
167     */
168    String getAlignmentStyles() {
169
170         return oStyler.getAlignmentStyles();
171     }
172
173    /**
174     * Takes three sequences ( of the same length ) as strings and
175     * returns three strings with colour markup in HTML.
176     *
177     * @param poAlignment - three strings representing a pairwise alignment
178     *                      query, hit, consensus
179     */
180    void alignment2HTML( String[] poAlignment ) {
181
182        if ( poAlignment == null || poAlignment.length != 3
183             || poAlignment[0].length() != poAlignment[1].length()
184             || poAlignment[0].length() != poAlignment[2].length() ) {
185
186            System.err.println( "-->" + poAlignment[0] + "<--" );
187            System.err.println( "-->" + poAlignment[1] + "<--" );
188            System.err.println( "-->" + poAlignment[2] + "<--" );
189
190            throw new IllegalArgumentException
191                ( "Only accept array of three strings, all of same length" );
192        }
193
194        if ( oStyler == null ) { // then no styles
195            return;
196        }
197
198        // Initialise
199
200        markedUp[0].setLength(0);
201        markedUp[1].setLength(0);
202        markedUp[2].setLength(0);
203
204        oCurrentStyle[0] = null;
205        oCurrentStyle[1] = null;
206        oCurrentStyle[2] = null;
207
208        // For each position
209
210        for( int i= 0, n = poAlignment[0].length(); i < n ; i++) {
211
212            String oFirst  = String.valueOf( poAlignment[0].charAt( i ) );
213            String oSecond = String.valueOf( poAlignment[1].charAt( i ) );
214
215            // Decide whether to apply colours.
216            if ( oColourCommand.isColoured( oFirst, oSecond ) ) {
217                oStyler.getStyle( oFirst, oSecond,
218                                  oNewStyle );
219
220            } else {
221                // put in a loop if want to change the number
222                oNewStyle[0] = null;
223                oNewStyle[1] = null;
224                oNewStyle[2] = null;
225            }
226
227            this.applyStyles( oCurrentStyle, oNewStyle, markedUp );
228
229            markedUp[0].append( oFirst );
230            markedUp[1].append( oSecond );
231            markedUp[2].append( poAlignment[2].charAt( i ) );
232
233            System.arraycopy( oNewStyle, 0, oCurrentStyle,
234                              0, oNewStyle.length );
235
236        } // for each char in sequence
237
238        this.flushStyles( oCurrentStyle, markedUp );
239
240        poAlignment[0] = markedUp[0].substring(0);
241        poAlignment[1] = markedUp[1].substring(0);
242        poAlignment[2] = markedUp[2].substring(0);
243    }
244
245
246    /**
247     * Simple utility function to call applyStyle on seq1, markup &
248     * seq2.
249     *
250     * @param poCurrentStyle a <code>String[]</code>
251     * @param poNewStyle a <code>String[]</code>
252     * @param poMarkedUp a <code>StringBuffer[]</code>
253     */
254    private void applyStyles( String[] poCurrentStyle,
255                              String[] poNewStyle,
256                              StringBuffer[] poMarkedUp ) {
257
258        for ( int i=0, n = poCurrentStyle.length; i < n ; i++ ) {
259            this.applyStyle( poCurrentStyle[i], poNewStyle[i], poMarkedUp[i] );
260        }
261    }
262
263    /**
264     * <p>
265     * Apply the new style to the output.
266     * </p>
267     *
268     * <p>
269     * Takes care of runs of the same style and
270     * changing styles.
271     * </p>
272     *
273     * @param poCurrentStyle  <code>String</code> - the current style
274     * @param poNewStyle      <code>String</code> - the new style
275     * @param poOutput        <code>StringBuffer</code> - the styled output
276     */
277    private void applyStyle( String poCurrentStyle,
278                             String poNewStyle,
279                             StringBuffer poOutput ) {
280        //
281        // If new style is null we just need to close the old style
282        // if it was different.
283        //
284        //   else if the new style is different from the old
285        //     start a new style.
286        //
287        if ( poNewStyle == null ) {
288
289            if ( poCurrentStyle != null ) {
290                poOutput.append( "</FONT>" );
291            }
292        } else if ( !poNewStyle.equals( poCurrentStyle ) ) {
293            if ( poCurrentStyle != null ) {
294                poOutput.append( "</FONT>" );
295            }
296            // start a new one
297            poOutput.append("<FONT CLASS=" + poNewStyle + ">");
298        }
299    }
300
301    /**
302     * If the last style is not null then close it.
303     *
304     */
305    private void flushStyles( String[] poCurrentStyle,
306                              StringBuffer[] poMarkedUp ) {
307
308        for ( int i=0, n = poCurrentStyle.length; i < n ; i++ ) {
309            if ( poCurrentStyle[i] != null ) { //then close
310                poMarkedUp[i].append( "</FONT>" );
311            }
312        }
313    }
314}
315
316