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 * Created on Feb 15, 2010
021 * Author: Andreas Prlic
022 *
023 */
024
025package org.biojava.nbio.structure.align.model;
026
027import org.biojava.nbio.structure.*;
028import org.biojava.nbio.structure.align.ce.CeMain;
029import org.biojava.nbio.structure.align.ce.CeSideChainMain;
030import org.biojava.nbio.structure.align.fatcat.FatCatFlexible;
031import org.biojava.nbio.structure.align.seq.SmithWaterman3Daligner;
032import org.biojava.nbio.structure.align.util.AFPAlignmentDisplay;
033import org.biojava.nbio.structure.jama.Matrix;
034
035import java.io.StringWriter;
036import java.util.List;
037import java.util.Locale;
038
039/**
040 * A class to convert the data in an AfpChain object to various String outputs.
041 *
042 * @author Andreas Prlic
043 *
044 */
045public class AfpChainWriter
046{
047
048        public static final String newline = System.getProperty("line.separator");
049
050        private static final int LINELENGTH = 70;
051
052        public static String toFatCat(AFPChain afpChain, Atom[] ca1, Atom[] ca2)
053        {
054
055                boolean printLegend = true;
056                boolean longHeader  = true;
057                boolean showHTML = false;
058                boolean showAlignmentBlock = false;
059
060                return toFatCatCore(afpChain, ca1, ca2, printLegend, longHeader, showHTML, showAlignmentBlock);
061        }
062
063        public static String toScoresList(AFPChain afpChain){
064
065                // see sippl On distance and similarity in fold space 2008 bioinformatics
066
067                StringWriter writer = new StringWriter();
068
069                if ( afpChain.getAlgorithmName().startsWith("CE")) {
070                        writer.append("Z-score " );
071                        writer.append(String.format(Locale.US, "%.2f", afpChain.getProbability()));
072                        writer.append(newline);
073                }
074
075
076                writer.append("Sab (nr. equivalent residues): " );
077                writer.append(String.valueOf(afpChain.getNrEQR())).append("");
078                writer.append(newline);
079
080                writer.append("Dab (distance between folds a,b): ");
081                int dab = afpChain.getCa1Length()+afpChain.getCa2Length() - 2 * afpChain.getNrEQR();
082                writer.append(String.valueOf(dab)).append("");
083                writer.append(newline);
084
085                writer.append("sab (relative similarity): ");
086                double sab = 2 * afpChain.getNrEQR() / (double)( afpChain.getCa1Length() + afpChain.getCa2Length());
087                writer.append(String.valueOf(sab)).append("");
088                writer.append(newline);
089
090                writer.append("cab (coverage a): ");
091                double cab = afpChain.getNrEQR() / (double) afpChain.getCa1Length();
092                writer.append(String.valueOf(cab)).append("");
093                writer.append(newline);
094
095                writer.append("cba (coverage b): ");
096                double cba = afpChain.getNrEQR() / (double) afpChain.getCa2Length();
097                writer.append(String.valueOf(cba)).append("");
098                writer.append(newline);
099
100                writer.append("seq similarity: ");
101                writer.append(String.valueOf(afpChain.getSimilarity())).append("");
102                writer.append(newline);
103
104                writer.append("TM-score: ");
105                writer.append(String.valueOf(afpChain.getTMScore())).append("");
106                writer.append(newline);
107
108                return writer.toString();
109        }
110
111        /**
112         * Output in FatCatCore format
113         *
114         * <p>Note that if a circular permutation has occured the residue numbers may
115         * be innaccurate.
116         *
117         * @param afpChain
118         * @param ca1
119         * @param ca2
120         * @param printLegend
121         * @param longHeader
122         * @param showHTML
123         * @param showAlignmentBlock
124         * @return
125         */
126        public static String toFatCatCore(
127                        AFPChain afpChain,
128                        Atom[] ca1,
129                        Atom[] ca2,
130                        boolean printLegend, boolean longHeader, boolean showHTML, boolean showAlignmentBlock){
131
132                //TODO The sequence numbers are inaccurate if a !afpChain.isSequential()
133
134                String name1 = afpChain.getName1();
135                String name2 = afpChain.getName2();
136                int ca1Length = afpChain.getCa1Length();
137                int ca2Length = afpChain.getCa2Length();
138
139                int blockNum = afpChain.getBlockNum();
140                int totalLenIni = afpChain.getTotalLenIni();
141                double totalRmsdIni = afpChain.getTotalRmsdIni();
142                int optLength = afpChain.getOptLength();
143                double totalRmsdOpt = afpChain.getTotalRmsdOpt();
144                double chainRmsd = afpChain.getChainRmsd();
145                double alignScore = afpChain.getAlignScore();
146                int alnLength = afpChain.getAlnLength();
147                int gapLen = afpChain.getGapLen();
148                List<AFP> afpSet = afpChain.getAfpSet();
149
150                double similarity = afpChain.getSimilarity();
151                double identity = afpChain.getIdentity();
152
153                if (similarity <0  || identity < 0){
154                        afpChain.calcSimilarity();
155                        similarity = afpChain.getSimilarity();
156                        identity = afpChain.getIdentity();
157                }
158
159
160
161                String algorithmName = afpChain.getAlgorithmName();
162                //String version = afpChain.getVersion();
163
164                double probability = afpChain.getProbability();
165
166
167                int afpNum = afpSet.size();
168
169                int[] blockGap = afpChain.getBlockGap();
170
171
172                double[] blockScore = afpChain.getBlockScore();
173                double[] blockRmsd = afpChain.getBlockRmsd();
174                int[] blockSize = afpChain.getBlockSize();
175
176
177                int alnbeg1 = afpChain.getAlnbeg1();
178                int alnbeg2 = afpChain.getAlnbeg2();
179
180                char[] alnseq1 = afpChain.getAlnseq1();
181                char[] alnseq2 = afpChain.getAlnseq2();
182                char[] alnsymb = afpChain.getAlnsymb();
183
184                // == end of extractation of data values from afpChain
185                ////////////////////////////////
186
187                StringBuffer txt = new StringBuffer();
188
189                if ( longHeader) {
190                        txt.append(String.format("Align %s.pdb %d with %s.pdb %d", name1, ca1Length, name2, ca2Length));
191                }
192                else {
193                        txt.append(String.format("Align %s.pdb Length1: %d with %s.pdb Length2: %d", name1, ca1Length, name2, ca2Length));
194                }
195                txt.append(newline);
196                if ( afpChain.isShortAlign()){
197                        txt.append("Short match");
198                        return txt.toString();
199                }
200                //txt.append(String.format(Locale.US, "raw-score: %.2f norm.-score: %.2f ", alignScore, normAlignScore));
201
202                if ( longHeader ) {
203                        txt.append(String.format(Locale.US, "Twists %d ini-len %d ini-rmsd %.2f opt-equ %d opt-rmsd %.2f chain-rmsd %.2f Score %.2f align-len %d gaps %d (%.2f%%)",
204                                        blockNum - 1, totalLenIni, totalRmsdIni, optLength, totalRmsdOpt, chainRmsd, alignScore,
205                                        alnLength, gapLen, (100.0 * gapLen/alnLength)) );
206                        txt.append(newline);
207
208                }  else {
209
210                        if ( ! longHeader)
211                                printScore(txt,algorithmName,probability,longHeader);
212
213                        printScoresInLines(afpChain, blockNum, optLength, totalRmsdOpt, alignScore, alnLength, gapLen, identity, similarity,txt);
214                }
215
216
217                //txt.append(String.format(Locale.US, "P-value %.2e Afp-num %d Identity %.2f%% Similarity %.2f%% norm.-score: %.2f"+newline, probability, afpNum, identity * 100, similarity * 100, normAlignScore));
218
219                if ( longHeader) {
220                        printScore(txt,algorithmName,probability,longHeader);
221
222                        txt.append(String.format(Locale.US, "Afp-num %d Identity %.2f%% Similarity %.2f%%", afpNum, identity * 100, similarity * 100));
223                        txt.append(newline);
224                }
225
226                int i;
227                double gap;
228
229                if ( longHeader ){
230                        int fragLen = 8 ; // FatCatParameters.DEFAULT_FRAGLEN;
231                        for(i = 0; i < blockNum; i ++)  {
232                                gap = blockGap[i] /( (double)blockGap[i] + fragLen * blockSize[i]);
233                                txt.append(String.format(Locale.US, "Block %2d afp %2d score %5.2f rmsd %5.2f gap %d (%.2f%%)",
234                                                i, blockSize[i], blockScore[i], blockRmsd[i], blockGap[i], gap));
235                                txt.append(newline);
236                        }
237                }
238
239                int     linelen = 70;
240                String a;
241                String b;
242                String c;
243
244
245                int     t = 0;
246                int     ap = alnbeg1;
247                int     bp = alnbeg2;
248                int     k, len;
249
250                //System.out.println(alnseq1.length + " " + alnseq1.toString());
251
252                while((alnLength - t) > 0)      {
253                        if(alnLength - t > linelen)     len = linelen;
254                        else    len = alnLength - t;
255
256                        if ( ap >= ca1.length)
257                                break;
258                        if ( bp >= ca2.length)
259                                break;
260
261                        String pdb1 = ca1[ap].getGroup().getResidueNumber().toString();
262                        String pdb2 = ca2[bp].getGroup().getResidueNumber().toString();
263
264
265                        //System.err.println("t,len:"+t+":"+len);
266                        String lseq1 = new String(alnseq1).substring(t,t+len);
267                        String lseq2 = new String(alnseq2).substring(t,t+len);
268                        String lsymb = new String(alnsymb).substring(t,t+len);
269
270                        //System.err.println("B:" + b);
271
272
273                        // check conservation and color accordingly, if requested by user.
274                        if ( showHTML ) {
275                                a = "";
276                                b = "";
277                                c = "";
278
279                                //      <span class=\"m\">|</span> ... Structurally equivalent and identical residues
280                                //  <span class=\"sm\">:</span> ... Structurally equivalent and similar residues
281                                //  <span class=\"qg\">.</span> ... Structurally equivalent, but not similar residues.
282
283                                for (int pos = 0 ; pos < lseq1.length() ; pos ++){
284                                        char c1 = lseq1.charAt(pos);
285                                        char c2 = lseq2.charAt(pos);
286                                        char cl = lsymb.charAt(pos);
287                                        int block = -1 ;
288                                        if ( cl != ' ') {
289                                                try {
290                                                        block = Integer.parseInt(String.valueOf(cl));
291                                                } catch (Exception e){
292                                                        //
293                                                }
294                                        }
295                                        if ( cl != ' ' ){
296
297                                                if ( showAlignmentBlock && block > -1 ) {
298                                                        a += "<span class=\"alignmentBlock1"+block+"\">" + c1 + "</span>";
299                                                        b += "<span class=\"alignmentBlock2"+block+"\">" + c2 + "</span>";
300                                                        c += "<span class=\"m\">" + cl + "</span>";
301                                                } else {
302                                                        a += getPrefix(c1, c2, 0, block, false).toString() + c1 + "</span>";
303                                                        b += getPrefix(c1, c2, 1, block, false).toString() + c2 + "</span>";
304                                                        c += "<span class=\"m\">" + cl + "</span>";
305                                                }
306
307                                        } else if ( c1 != '-' && c2 != '-') {
308
309                                                a += "<span class=\"sm\">" + c1 + "</span>";
310                                                b += "<span class=\"sm\">" + c2 + "</span>";
311                                                c += "<span class=\"sm\">" + cl + "</span>";
312
313
314                                        } else {
315
316                                                a += "<span class=\"qg\">" + c1 + "</span>";
317                                                b += "<span class=\"qg\">" + c2 + "</span>";
318                                                c += "<span class=\"qg\">" + cl + "</span>";
319
320                                        }
321
322                                        if(c1 != '-') ap ++;
323                                        if(c2 != '-') bp ++;
324                                }
325
326
327                        } else {
328
329                                a = lseq1;
330                                b = lseq2;
331                                c = lsymb;
332                        }
333
334                        txt.append(newline);
335                        if ( longHeader )
336                                txt.append(String.format("%14s", " "));
337                        else
338                                txt.append(String.format("%14s", " "));
339
340                        if (  longHeader ) {
341                                for(k = 10; k <= len; k += 10)
342                                        txt.append("    .    :");
343                                if(k <= len + 5) txt.append("    .");
344
345                        } else {
346
347                                for(k = 10; k <= len; k += 10)
348                                        txt.append("----+----|");
349                                if(k <= len + 5) txt.append("----+");
350
351
352                        }
353
354
355
356                        txt.append(newline);
357                        txt.append(String.format("Chain 1:%5s %s"+newline +"%14s%s"+newline+"Chain 2:%5s %s",
358                                        pdb1, a, " ", c, pdb2, b));
359
360                        txt.append(newline);
361
362                        if ( ! showHTML){
363                                for(k = 0; k < len; k ++)       {
364                                        if(a.charAt(k) != '-') ap ++;
365                                        if(b.charAt(k) != '-') bp ++;
366                                }
367                        }
368                        t += len;
369
370
371
372                }
373                txt.append(newline);
374                if ( printLegend ){
375                        if ( algorithmName.equalsIgnoreCase(CeMain.algorithmName) ||
376                                        algorithmName.equalsIgnoreCase(SmithWaterman3Daligner.algorithmName)){
377                                txt.append("Note: positions are from PDB; | means alignment of identical amino acids, : of similar amino acids ");
378
379                        } else {
380                                txt.append("Note: positions are from PDB; the numbers between alignments are block index");
381                        }
382                        txt.append(newline);
383
384                }
385                return txt.toString();
386
387        }
388
389        public static void printScoresInLines(AFPChain afpChain, int blockNum, int optLength, double totalRmsdOpt, double alignScore,
390                        int alnLength, int gapLen, double identity, double similarity, StringBuffer txt)
391        {
392                if ( blockNum - 1 > 0) {
393                        txt.append(String.format(Locale.US, "Twists %d ", blockNum -1 ));
394                        txt.append(newline);
395                }
396
397                txt.append(String.format(Locale.US, "Equ: %d ", optLength));
398                txt.append(newline);
399                txt.append(String.format(Locale.US, "RMSD: %.2f ", totalRmsdOpt));
400                txt.append(newline);
401                txt.append(String.format(Locale.US, "Score: %.2f ", alignScore));
402                txt.append(newline);
403                txt.append(String.format(Locale.US, "Align-len: %d ", alnLength));
404                txt.append(newline);
405                txt.append(String.format(Locale.US, "Gaps: %d (%.2f%%)",
406                                gapLen, (100.0 * gapLen/alnLength)) );
407                txt.append(newline);
408                if ( afpChain.getTMScore() >= 0) {
409                        txt.append(String.format(Locale.US, "TM-score: %.2f",afpChain.getTMScore()));
410                        txt.append(newline);
411                }
412                txt.append(newline);
413                txt.append(String.format(Locale.US, "Identity: %.2f%% ", identity * 100 ));
414                txt.append(newline);
415                txt.append(String.format(Locale.US, "Similarity: %.2f%%", similarity * 100));
416                txt.append(newline);
417        }
418
419        private static void printScore(StringBuffer txt,
420                        String algorithmName,
421                        double probability,
422                        boolean longHeader)
423        {
424                if ( algorithmName.equalsIgnoreCase(CeMain.algorithmName) || algorithmName.equalsIgnoreCase(CeSideChainMain.algorithmName) ){
425                        txt.append(String.format(Locale.US, "Z-score %.2f ", probability));
426                        if ( ! longHeader)
427                                txt.append(newline);
428                } else if ( algorithmName.equalsIgnoreCase(SmithWaterman3Daligner.algorithmName)) {
429
430                } else {
431                        if ( longHeader ){
432                                txt.append(String.format(Locale.US, "P-value %.2e ",probability));
433                        }  else {
434                                txt.append(String.format(Locale.US, "P-value: %.2e ",probability));
435                                txt.append(newline);
436                        }
437                }
438
439
440
441        }
442
443        /**
444         * Prints the afpChain as a nicely formatted alignment, including alignment
445         * statistics, the aligned sequences themselves, and information about the
446         * superposition.
447         * @param afpChain
448         * @param ca1
449         * @param ca2
450         * @return a String representation as it is used on the RCSB PDB web site for display.
451         */
452        public static String toWebSiteDisplay(AFPChain afpChain, Atom[] ca1, Atom[] ca2 ){
453                boolean showAlignmentBlock   = true;
454                return toWebSiteDisplay(afpChain, ca1, ca2, showAlignmentBlock);
455        }
456
457        /**
458         * Prints the afpChain as a nicely formatted alignment, including alignment
459         * statistics, the aligned sequences themselves, and information about the
460         * superposition.
461         * @param afpChain
462         * @param ca1
463         * @param ca2
464         *
465         * @return a String representation as it is used on the RCSB PDB web site for display.
466         */
467        public static String toWebSiteDisplay(AFPChain afpChain, Atom[] ca1, Atom[] ca2, boolean showAlignmentBlock){
468
469                boolean printLegend = true;
470                boolean longHeader  = true;
471                boolean showHTML = true;
472
473                if ( afpChain.getAlgorithmName().equalsIgnoreCase(FatCatFlexible.algorithmName)) {
474
475
476                        String msg =  toFatCatCore(afpChain,ca1,ca2,printLegend,longHeader,showHTML, showAlignmentBlock);
477
478                        return msg;
479                }
480
481                boolean showSeq = true;
482
483                AFPAlignmentDisplay.getAlign(afpChain, ca1, ca2, showSeq);
484
485
486                //      String msg= toFatCatCore(afpChain,ca1,ca2, printLegend,longHeader);
487                //
488
489
490                String msg = toPrettyAlignment(afpChain, ca1, ca2, showHTML, showAlignmentBlock);
491
492
493                msg = msg + newline +
494                "     <span class=\"m\">|</span> ... Structurally equivalent and identical residues " + newline +
495                "     <span class=\"sm\">:</span> ... Structurally equivalent and similar residues " + newline +
496                "     <span class=\"qg\">.</span> ... Structurally equivalent, but not similar residues. " + newline;
497
498                msg += newline;
499                msg += "     To calculate the coordinates of chain 2 aligned on chain 1 apply the following transformation: ";
500                msg += newline;
501                msg += newline;
502                msg += toRotMat(afpChain);
503                return msg;
504
505
506        }
507
508        public static String toPrettyAlignment(AFPChain afpChain, Atom[] ca1, Atom[] ca2, boolean showHTML, boolean showAlignmentBlock) {
509                String name1 = afpChain.getName1();
510                String name2 = afpChain.getName2();
511                int ca1Length = afpChain.getCa1Length();
512                int ca2Length = afpChain.getCa2Length();
513
514                int blockNum = afpChain.getBlockNum();
515
516
517                int optLength = afpChain.getOptLength();
518                double totalRmsdOpt = afpChain.getTotalRmsdOpt();
519
520                double alignScore = afpChain.getAlignScore();
521                int alnLength = afpChain.getAlnLength();
522                int gapLen = afpChain.getGapLen();
523
524
525                double similarity = afpChain.getSimilarity();
526                double identity = afpChain.getIdentity();
527
528                if (similarity <0  || identity < 0){
529                        afpChain.calcSimilarity();
530                        similarity = afpChain.getSimilarity();
531                        identity = afpChain.getIdentity();
532                }
533
534
535                String algorithmName = afpChain.getAlgorithmName();
536                //String version = afpChain.getVersion();
537
538                double probability = afpChain.getProbability();
539
540
541                // == end of extractation of data values from afpChain
542
543                StringBuffer txt = new StringBuffer();
544
545                txt.append(String.format("Align %s.pdb Length1: %d with %s.pdb Length2: %d", name1, ca1Length, name2, ca2Length));
546
547                txt.append(newline);
548
549                if ( afpChain.isShortAlign()){
550                        txt.append("Short match");
551                        return txt.toString();
552                }
553
554                printScore(txt, algorithmName, probability, false);
555                printScoresInLines(afpChain, blockNum, optLength, totalRmsdOpt, alignScore, alnLength, gapLen,identity, similarity, txt);
556                txt.append(newline);
557
558                int[] optLen = afpChain.getOptLen();
559                int[][][] optAln = afpChain.getOptAln();
560
561
562                int i, j,p1, p2;
563
564                int k;
565                int p1b = 0;
566                int p2b = 0;
567
568                int     len = 0;
569                StringWriter alnseq1 = new StringWriter();
570                StringWriter alnseq2 = new StringWriter();
571                StringWriter alnsymb = new StringWriter();
572                StringWriter header1  = new StringWriter();
573                StringWriter footer1  = new StringWriter();
574                StringWriter header2  = new StringWriter();
575                StringWriter footer2  = new StringWriter();
576                StringWriter block    = new StringWriter();
577
578                int aligPos = -1;
579                for(i = 0; i < blockNum; i ++)  {
580
581                        for(j = 0; j < optLen[i]; j ++) {
582
583                                p1 = optAln[i][0][j];
584                                p2 = optAln[i][1][j];
585                                aligPos++;
586
587                                //               System.out.println(p1 + " " + p2 + " " +  footer2.toString());
588
589                                if ( len == 0){
590                                        //the first position of sequence in alignment
591                                        formatStartingText(p1,p2,header1,header2,footer1,footer2,ca1,ca2);
592                                } else {
593                                        // check for gapped region
594                                        int lmax = (p1 - p1b - 1)>(p2 - p2b - 1)?(p1 - p1b - 1):(p2 - p2b - 1);
595                                        for(k = 0; k < lmax; k ++)      {
596
597
598                                                formatGappedRegion(ca1, ca2, txt, p1, p2, k, p1b, p2b, alnseq1, alnseq2, alnsymb, header1, footer1, header2,
599                                                                footer2, block,len, showHTML);
600                                                len++;
601                                                doLenCheck(len,txt,header1,header2,alnseq1,alnsymb,alnseq2,footer1, footer2,block, showHTML)  ;
602                                        }
603                                }
604
605                                // ALIGNED REGION
606                                //           System.out.println(len + " >" + header1.toString() +"< ");
607                                //           System.out.println(len + " >" + header2.toString() +"< ");
608                                //           System.out.println(len + " >" + alnseq1.toString() +"< ");
609                                //           System.out.println(len + " >" + alnsymb.toString() +"< ");
610                                //           System.out.println(len + " >" + alnseq2.toString() +"< ");
611                                //           System.out.println(len + " >" + footer1.toString() +"< ");
612                                formatAlignedRegion(afpChain, ca1, ca2, p1, p2, alnseq1, alnseq2, alnsymb, header1, footer1, header2, footer2, block,len, aligPos, showHTML, showAlignmentBlock);
613                                //            System.out.println(len + " >" + header1.toString() +"< ");
614                                //            System.out.println(len + " >" + header2.toString() +"< ");
615                                //            System.out.println(len + " >" + alnseq1.toString() +"< ");
616                                //            System.out.println(len + " >" + alnsymb.toString() +"< ");
617                                //            System.out.println(len + " >" + alnseq2.toString() +"< ");
618                                //            System.out.println(len + " >" + footer1.toString() +"< ");
619
620                                len++;
621
622                                doLenCheck(len,txt,header1,header2,alnseq1,alnsymb,alnseq2,footer1, footer2,block, showHTML)  ;
623
624                                p1b = p1;
625                                p2b = p2;
626
627                                //header1.append(newline);
628                                //header2.append(newline);
629
630                        }
631
632                }
633
634                alnLength = len;
635
636                doLenCheck(LINELENGTH,txt,header1,header2,alnseq1,alnsymb,alnseq2,footer1, footer2,block, showHTML);
637                return txt.toString();
638        }
639
640        /**
641         * Prints the alignment in the simplest form: a list of aligned residues.
642         * Format is one line per residue pair, tab delimited:
643         * <ul><li>1. PDB number. Includes insertion code</li>
644         * <li>1. Chain.</li>
645         * <li>1. Amino Acid. Three letter code.</li>
646         * <li>2. PDB number.</li>
647         * <li>2. Chain.</li>
648         * <li>2. Amino Acid.</li>
649         * </ul>
650         * example:
651         * <code>152    A       ALA     161S    A       VAL</code>
652         * <p>Note that this format loses information about blocks.
653         * @param afpChain
654         * @param ca1
655         * @param ca2
656         * @return a String representation of the aligned pairs.
657         */
658        public static String toAlignedPairs(AFPChain afpChain, Atom[] ca1, Atom[] ca2) {
659                StringWriter pairs = new StringWriter();
660
661                //Write structure names & PDB codes
662                pairs.append("#Struct1:\t");
663                pairs.append(afpChain.getName1());
664                pairs.append("\n");
665                pairs.append("#Struct2:\t");
666                pairs.append(afpChain.getName2());
667                pairs.append("\n");
668
669                //Write optimally aligned pairs
670                pairs.append("#Num1\tChain1\tAA1\tNum2\tChain2\tAA2\n");
671                int[][][] optAln = afpChain.getOptAln();
672                int[] blockLen = afpChain.getOptLen();
673                for( int block=0;block<afpChain.getBlockNum(); block++) {
674                        for(int i=0;i<blockLen[block];i++) {
675                                Atom atom1 = ca1[ optAln[block][0][i] ];
676                                Atom atom2 = ca2[ optAln[block][1][i] ];
677
678                                pairs.append(atom1.getGroup().getResidueNumber().toString());
679                                pairs.append('\t');
680                                pairs.append(atom1.getGroup().getChain().getName());
681                                pairs.append('\t');
682                                pairs.append(atom1.getGroup().getPDBName());
683                                pairs.append('\t');
684                                pairs.append(atom2.getGroup().getResidueNumber().toString());
685                                pairs.append('\t');
686                                pairs.append(atom2.getGroup().getChain().getName());
687                                pairs.append('\t');
688                                pairs.append(atom2.getGroup().getPDBName());
689                                pairs.append('\n');
690                        }
691                }
692
693                return pairs.toString();
694        }
695
696        private static void formatGappedRegion(Atom[] ca1, Atom[] ca2, StringBuffer txt, int p1, int p2, int k, int p1b, int p2b,
697                        StringWriter alnseq1, StringWriter alnseq2, StringWriter alnsymb, StringWriter header1, StringWriter footer1,
698                        StringWriter header2, StringWriter footer2, StringWriter block, int len, boolean formatHTML)    {
699
700                // DEAL WITH GAPS
701                int tmppos = (p1 - p1b - 1);
702                block.append("g");
703
704                int  pos1=p1b+1+k ;
705                char oneletter1 = ' ';
706                try {
707                        oneletter1 = getOneLetter(ca1[pos1].getGroup());
708                } catch (Exception e){}
709                int pos2=p2b+1+k;
710                char oneletter2 = ' ';
711                try {
712                        oneletter2 = getOneLetter(ca2[pos2].getGroup());
713                } catch (Exception e){}
714
715
716                if(k >= tmppos) {
717                        //alnseq1[len] = '-';
718                        if (  formatHTML){
719                                alnseq1.append("<span class=\"qg\">-</span>");
720                                header1.append(" ");
721                                header2.append(" ");
722
723
724                        } else {
725                                alnseq1.append('-');
726                                header1.append(" ");
727                                header2.append(" ");
728                        }
729
730                }
731                else {
732                        if ( formatHTML){
733                                alnseq1.append(getPrefix(oneletter1,oneletter2,0,-1, false));
734                        }
735                        alnseq1.append(oneletter1);
736                        if (formatHTML){
737                                alnseq1.append("</span>");
738                        }
739                        formatPosition(pos1,ca1, len, header1, header2);
740
741                }
742
743                if(k >= (p2 - p2b - 1)) {
744                        //alnseq2[len] = '-';
745                        if ( formatHTML){
746                                alnseq2.append("<span class=\"qg\">-</span>");
747                                footer1.append(" ");
748                                footer2.append(" ");
749                        } else {
750                                alnseq2.append('-');
751                                footer1.append(" ");
752                                footer2.append(" ");
753                        }
754
755                }
756                else  {
757                        if ( formatHTML){
758                                alnseq2.append(getPrefix(oneletter1,oneletter2,1, -1, false));
759                        }
760                        alnseq2.append(oneletter2);
761                        if (formatHTML){
762                                alnseq2.append("</span>");
763                        }
764                        formatPosition(pos2, ca2, len, footer1, footer2);
765
766                }
767                //alnsymb[len ++] = ' ';
768                alnsymb.append(' ');
769
770        }
771
772
773
774        private static CharSequence getPrefix(char oneletter1, char oneletter2,
775                        int i, int blockNr, boolean showAlignmentBlock) {
776
777                if ( oneletter1 == '-' || oneletter2 == '-' ) {
778                        // a gap in the alignment.
779                        // label as mismatch
780                        return "<span class=\"qg\">";
781                }
782
783                // an aligned position
784
785                if ( showAlignmentBlock && blockNr > -1){
786                        return "<span class=\"alignmentBlock"+(i+1)+(blockNr+1)+"\">";
787                }
788
789                // we return the "default" sequence alignment view...
790
791                if ( oneletter1 == oneletter2)
792                        return "<span class=\"m\">";
793
794                double score = AFPAlignmentDisplay.aaScore(oneletter1,oneletter2);
795                if ( score > 0 )
796                        return "<span class=\"sm\">";
797
798                // not similar
799                return "<span class=\"qg\">";
800        }
801
802        private static void formatPosition(int pos1, Atom[] ca, int len, StringWriter header1, StringWriter header2)
803        {
804                int linePos = len % LINELENGTH;
805
806                if ( header1.getBuffer().length() < linePos) {
807                        // fill up the buffer, we are probably shortly after the start...
808                        for ( int i = header1.getBuffer().length() ; i< linePos ; i++){
809                                header1.append(" ");
810                        }
811                }
812
813
814
815                Atom a = ca[pos1];
816                Group g = a.getGroup();
817
818                ResidueNumber residueNumber = g.getResidueNumber();
819                pos1 = residueNumber.getSeqNum();
820                boolean hasInsertionCode = false;
821                if ( residueNumber.getInsCode() != null) {
822                        hasInsertionCode = true;
823                }
824
825                if ( (pos1 %10  == 0) && ( ! hasInsertionCode)) {
826                        CharSequence display = getPDBPos(a);
827
828                        boolean ignoreH1 = false;
829
830                        // make sure we don't have a problem with the left boundary...
831                        if ( header1.getBuffer().length()-1 > linePos) {
832                                ignoreH1 = true;
833                                //System.out.println("Ignore h1: " + len + " " + header1.getBuffer().length() + " linePos: " + linePos +"  >" + header1.toString() +"<");
834                        }
835                        //System.out.println(len + " p1:" + tmp + " = " + pos1 + " " + " " + display + " " + ignoreH1);
836                        if ( ! ignoreH1) {
837                                header1.append(String.format("%-13s",display ));
838                                header2.append("|");
839                        } else {
840                                header2.append("|");
841                        }
842
843                } else if ( hasInsertionCode){
844                        Character insCode = g.getResidueNumber().getInsCode();
845                        if ( insCode != null)
846                                header2.append(insCode);
847                        else {
848                                header2.append("!");
849                        }
850                } else if ( ((pos1) %5 ) == 0 && len > 5) {
851                        header2.append(".");
852                } else {
853                        if ( len > 0)
854                                header2.append(" ");
855                }
856
857        }
858
859
860
861        private static void formatAlignedRegion(AFPChain afpChain, Atom[] ca1, Atom[] ca2, int p1, int p2,
862                        StringWriter alnseq1, StringWriter alnseq2,
863                        StringWriter alnsymb, StringWriter header1, StringWriter footer1, StringWriter header2,
864                        StringWriter footer2, StringWriter block, int len, int aligPos,
865                        boolean showHTML, boolean showAlignmentBlock)
866        {
867                char c1;
868                char c2;
869                if (( p1 < ca1.length) && (p2< ca2.length)){
870                        c1=  getOneLetter(ca1[p1].getGroup());
871                        c2 =  getOneLetter(ca2[p2].getGroup());
872                } else {
873                        c1 = 'X';
874                        c2 = 'X';
875                }
876
877                int blockPos = -1;
878                if ( afpChain.getBlockNum() > 0) {
879                        blockPos = AFPAlignmentDisplay.getBlockNrForAlignPos(afpChain, aligPos);
880                }
881
882
883
884                double score = AFPAlignmentDisplay.aaScore(c1,c2);
885
886                if ( showHTML) {
887                        alnseq1.append(getPrefix(c1,c2,  0, blockPos, showAlignmentBlock));
888                        alnseq2.append(getPrefix(c1,c2,  1, blockPos, showAlignmentBlock));
889                }
890
891                alnseq1.append(c1);
892                alnseq2.append(c2);
893
894                if ( showHTML){
895                        alnseq1.append("</span>");
896                        alnseq2.append("</span>");
897                }
898
899                if ( c1 == c2){
900                        if ( showHTML){
901
902                                alnsymb.append("<span class=\"m\">|</span>");
903
904                        } else {
905                                alnsymb.append('|');
906                        }
907                        //alnsymb[len ++] = '|';
908                } else {
909
910
911                        if ( score > 1) {
912                                if ( showHTML){
913                                        alnsymb.append( "<span class=\"sm\">:</span>");
914                                } else {
915                                        alnsymb.append( ':');
916                                }
917                        }
918                        else {
919                                if ( showHTML)
920                                        alnsymb.append( "<span class=\"qg\">.</span>");
921                                else
922                                        alnsymb.append( '.');
923                        }
924                }
925
926                if ( p1 < ca1.length)
927                        formatPosition(p1, ca1,len, header1, header2);
928
929                if ( p2 < ca2.length)
930                        formatPosition(p2,ca2,len, footer1, footer2);
931
932        }
933
934        private static void formatStartingText(int p1, int p2, StringWriter header1, StringWriter header2, StringWriter footer1,
935                        StringWriter footer2, Atom[] ca1, Atom[] ca2)
936        {
937
938                header1.append(String.format("%-13s", getPDBPos(ca1[p1])));
939                header2.append("|");
940                footer1.append(String.format("%-13s", getPDBPos(ca2[p2])));
941                footer2.append("|");
942
943
944        }
945
946        private static boolean doLenCheck(int len, StringBuffer txt, StringWriter header1, StringWriter header2, StringWriter alnseq1,
947                        StringWriter alnsymb, StringWriter alnseq2, StringWriter footer1, StringWriter footer2, StringWriter block, boolean formatHTML)
948        {
949
950                if ( len % LINELENGTH  == 0) {
951
952                        //txt.append("|");
953                        txt.append(header1);
954                        //txt.append("|");
955                        txt.append(newline);
956                        //txt.append("|");
957                        txt.append(header2);
958                        //txt.append("|");
959                        txt.append(newline);
960                        //txt.append("|");
961                        txt.append(alnseq1);
962                        //txt.append("|");
963                        txt.append(newline);
964
965                        //txt.append("|");
966                        txt.append(alnsymb);
967                        //         txt.append(newline);
968                        //         txt.append(block);
969                        //txt.append("|");
970                        txt.append(newline);
971                        //txt.append("|");
972                        txt.append(alnseq2);
973                        //txt.append("|");
974                        txt.append(newline);
975                        //txt.append("|");
976                        txt.append(footer2);
977                        //txt.append("|");
978                        txt.append(newline);
979                        //txt.append("|");
980                        txt.append(footer1);
981                        //txt.append("|");
982                        txt.append(newline);
983                        txt.append(newline);
984                        txt.append(newline);
985
986                        if (formatHTML ) {
987
988                                int len1 = alnseq1.getBuffer().length();
989                                int len2 = alnseq2.getBuffer().length();
990                                int lens = alnsymb.getBuffer().length();
991                                alnseq1.getBuffer().replace(0, len1, "");
992                                alnseq2.getBuffer().replace(0, len2, "");
993                                alnsymb.getBuffer().replace(0, lens, "");
994
995
996
997                                header1.getBuffer().replace(0, LINELENGTH, "");
998                                header2.getBuffer().replace(0, LINELENGTH , "");
999                                footer1.getBuffer().replace(0, LINELENGTH, "");
1000                                footer2.getBuffer().replace(0, LINELENGTH, "");
1001                                block.getBuffer().replace(0, LINELENGTH, "");
1002                        } else {
1003                                alnseq1.getBuffer().replace(0, LINELENGTH, "");
1004                                alnseq2.getBuffer().replace(0, LINELENGTH, "");
1005                                alnsymb.getBuffer().replace(0, LINELENGTH, "");
1006                                header1.getBuffer().replace(0, LINELENGTH, "");
1007                                header2.getBuffer().replace(0, LINELENGTH , "");
1008                                footer1.getBuffer().replace(0, LINELENGTH, "");
1009                                footer2.getBuffer().replace(0, LINELENGTH, "");
1010                                block.getBuffer().replace(0, LINELENGTH, "");
1011                        }
1012                        StringBuffer buf = header1.getBuffer();
1013                        for ( int i=0;i<buf.length();i++){
1014                                char c = buf.charAt(i);
1015                                if ( c != ' '){
1016                                        buf.setCharAt(i, ' ');
1017                                }
1018                        }
1019                        buf = footer1.getBuffer();
1020                        for ( int i=0;i<buf.length();i++){
1021                                char c = buf.charAt(i);
1022                                if ( c != ' '){
1023                                        buf.setCharAt(i, ' ');
1024                                }
1025                        }
1026
1027                        return true;
1028                }
1029
1030                return false;
1031
1032
1033        }
1034
1035        private static CharSequence getPDBPos(Atom atom)
1036        {
1037
1038                Group g = atom.getGroup();
1039                if ( g!= null){
1040                        Chain c = g.getChain();
1041                        if (c != null){
1042                                return g.getResidueNumber().toString()+":" + c.getName() ;
1043                                //return g.getPDBCode()+":" + c.getName() + "." + getOneLetter(g) ;
1044                        }
1045                }
1046                return "!";
1047        }
1048
1049        private static char getOneLetter(Group g){
1050
1051
1052                if (g==null) return StructureTools.UNKNOWN_GROUP_LABEL;
1053
1054
1055                return StructureTools.get1LetterCode(g.getPDBName());
1056
1057
1058        }
1059
1060
1061        public static String toDBSearchResult(AFPChain afpChain)
1062        {
1063                StringBuffer str = new StringBuffer();
1064
1065                str.append(afpChain.getName1());
1066                str.append("\t");
1067                str.append(afpChain.getName2());
1068                str.append("\t");
1069                str.append(String.format(Locale.US, "%.2f",afpChain.getAlignScore()));
1070                str.append("\t");
1071                if ( afpChain.getAlgorithmName().equalsIgnoreCase(CeMain.algorithmName)){
1072                        str.append(String.format(Locale.US, "%.2f",afpChain.getProbability()));
1073                } else {
1074                        str.append(String.format(Locale.US, "%.2e",afpChain.getProbability()));
1075                }
1076                str.append("\t");
1077                str.append(String.format(Locale.US, "%.2f",afpChain.getTotalRmsdOpt()));
1078                str.append("\t");
1079                str.append(afpChain.getCa1Length());
1080                str.append("\t");
1081                str.append(afpChain.getCa2Length());
1082                str.append("\t");
1083                str.append(afpChain.getCoverage1());
1084                str.append("\t");
1085                str.append(afpChain.getCoverage2());
1086                str.append("\t");
1087                str.append(String.format(Locale.US, "%.2f",afpChain.getIdentity()));
1088                str.append("\t");
1089                str.append(afpChain.getDescription2());
1090                str.append("\t");
1091                str.append(newline);
1092
1093                return str.toString();
1094        }
1095
1096        public static String toRotMat(AFPChain afpChain)
1097        {
1098
1099                Matrix[] blockRotationMatrix = afpChain.getBlockRotationMatrix();
1100                int blockNum = afpChain.getBlockNum();
1101                Atom[] blockShiftVector = afpChain.getBlockShiftVector();
1102
1103                StringBuffer txt = new StringBuffer();
1104
1105                if ( blockRotationMatrix == null || blockRotationMatrix.length < 1)
1106                        return "";
1107
1108
1109                for ( int blockNr = 0 ; blockNr < blockNum  ; blockNr++){
1110                        Matrix m = blockRotationMatrix[blockNr];
1111                        Atom shift   = blockShiftVector[blockNr];
1112                        if ( blockNum > 1) {
1113                                txt.append("Operations for block " );
1114                                txt.append(blockNr);
1115                                txt.append(newline);
1116                        }
1117
1118                        String origString = "orig";
1119                        if ( blockNr > 0)
1120                                origString = String.valueOf(blockNr);
1121
1122
1123                        txt.append(String.format(Locale.US, "     X"+(blockNr+1)+" = (%9.6f)*X"+ origString +" + (%9.6f)*Y"+ origString +" + (%9.6f)*Z"+ origString +" + (%12.6f)",m.get(0,0),m.get(1,0), m.get(2,0), shift.getX()));
1124                        txt.append( newline);
1125                        txt.append(String.format(Locale.US, "     Y"+(blockNr+1)+" = (%9.6f)*X"+ origString +" + (%9.6f)*Y"+ origString +" + (%9.6f)*Z"+ origString +" + (%12.6f)",m.get(0,1),m.get(1,1), m.get(2,1), shift.getY()));
1126                        txt.append( newline);
1127                        txt.append(String.format(Locale.US, "     Z"+(blockNr+1)+" = (%9.6f)*X"+ origString +" + (%9.6f)*Y"+ origString +" + (%9.6f)*Z"+ origString +" + (%12.6f)",m.get(0,2),m.get(1,2), m.get(2,2), shift.getZ()));
1128                        txt.append(newline);
1129                }
1130                return txt.toString();
1131        }
1132
1133        public static String toCE(AFPChain afpChain, Atom[] ca1, Atom[] ca2)
1134        {
1135
1136                String name1 = afpChain.getName1();
1137                String name2 = afpChain.getName2();
1138
1139                int optLength = afpChain.getOptLength();
1140                double totalRmsdOpt = afpChain.getTotalRmsdOpt();
1141
1142                int alnLength = afpChain.getAlnLength();
1143                int gapLen = afpChain.getGapLen();
1144
1145                double similarity = afpChain.getSimilarity();
1146                double identity = afpChain.getIdentity();
1147                if (similarity <0 || identity <0  ){
1148                        afpChain.calcSimilarity();
1149                        similarity = afpChain.getSimilarity();
1150                        identity = afpChain.getIdentity();
1151                }
1152
1153                double probability = afpChain.getProbability();
1154
1155
1156                int alnbeg1 = afpChain.getAlnbeg1();
1157                int alnbeg2 = afpChain.getAlnbeg2();
1158
1159                char[] alnseq1 = afpChain.getAlnseq1();
1160                char[] alnseq2 = afpChain.getAlnseq2();
1161
1162
1163                long calculationTime = afpChain.getCalculationTime();
1164
1165                // == end of extractation of data values from afpChain
1166
1167                StringBuffer txt = new StringBuffer();
1168
1169                txt.append("Chain 1: ");
1170                txt.append(name1);
1171                txt.append(" (Size=");
1172                txt.append(ca1.length);
1173                txt.append(")");
1174                txt.append(newline);
1175                txt.append("Chain 2: ");
1176                txt.append(name2);
1177                txt.append(" (Size=");
1178                txt.append(ca2.length);
1179                txt.append(")");
1180                txt.append(newline);
1181                txt.append(newline);
1182                txt.append(String.format(Locale.US, "Alignment length = %d Rmsd = %.2fA Z-Score = %.1f",optLength,totalRmsdOpt,probability));
1183                txt.append(String.format(Locale.US, " Gaps = %d(%.1f%%) CPU = %d ms. Sequence identities = %.1f%%",gapLen,( gapLen*100.0/optLength),calculationTime,identity*100));
1184
1185                int     linelen = 70;
1186                String a;
1187                String b;
1188
1189                int     t = 0;
1190                int     ap = alnbeg1;
1191                int     bp = alnbeg2;
1192                int     k, len;
1193
1194                while((alnLength - t) > 0)      {
1195                        if(alnLength - t > linelen)     len = linelen;
1196                        else    len = alnLength - t;
1197
1198
1199                        //System.err.println("t,len:"+t+":"+len);
1200                        a = new String(alnseq1).substring(t,t+len);
1201                        b = new String(alnseq2).substring(t,t+len);
1202
1203                        //System.err.println("B:" + b);
1204
1205                        /*
1206                        txt.append(newline);
1207                        txt.append(String.format("%14s", " "));
1208
1209                        for(k = 10; k <= len; k += 10)
1210                                txt.append("    .    :");
1211                        if(k <= len + 5) txt.append("    .");
1212                         */
1213
1214                        //String pdb1 = ca1[ap].getParent().getPDBCode();
1215                        //String pdb2 = ca2[bp].getParent().getPDBCode();
1216                        txt.append(newline);
1217                        txt.append(String.format("Chain 1:%5s %s"+newline+"Chain 2:%5s %s",
1218                                        (ap+1), a, (bp+1), b));
1219                        txt.append(newline);
1220                        for(k = 0; k < len; k ++)       {
1221                                if(a.charAt(k) != '-') ap ++;
1222                                if(b.charAt(k) != '-') bp ++;
1223                        }
1224                        t += len;
1225
1226                }
1227                txt.append(newline);
1228
1229                txt.append(toRotMat(afpChain));
1230
1231                return txt.toString();
1232        }
1233
1234}