added separator parameter with default of '|' to control the list separator to use...
[jalview.git] / src / jalview / bin / JalviewLite.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer
3  * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18  */
19 package jalview.bin;
20
21 import java.applet.*;
22
23 import java.awt.*;
24 import java.awt.event.*;
25 import java.util.*;
26
27 import jalview.appletgui.*;
28 import jalview.datamodel.*;
29 import jalview.io.*;
30
31 /**
32  * Jalview Applet. Runs in Java 1.18 runtime
33  *
34  * @author $author$
35  * @version $Revision$
36  */
37 public class JalviewLite
38     extends Applet
39 {
40
41
42
43   ///////////////////////////////////////////
44   //The following public methods maybe called
45   //externally, eg via javascript in HTML page
46   /**
47    * @return list of selected sequences separated by "¬"
48    */
49   public String getSelectedSequences()
50   {
51     return getSelectedSequencesFrom(currentAlignFrame);
52   }
53   public String getSelectedSequencesFrom(AlignFrame alf)
54   {
55     StringBuffer result = new StringBuffer("");
56
57     if (alf.viewport.getSelectionGroup() != null)
58     {
59       SequenceI[] seqs = alf.viewport.getSelectionGroup().
60           getSequencesInOrder(
61                   alf.viewport.getAlignment());
62
63       for (int i = 0; i < seqs.length; i++)
64       {
65         result.append(seqs[i].getName() + "¬");
66       }
67     }
68
69     return result.toString();
70   }
71   
72   public String getAlignment(String format)
73   {
74     return getAlignmentFrom(currentAlignFrame, format, "true");
75   }
76   public String getAlignmentFrom(AlignFrame alf, String format)
77   {
78     return getAlignmentFrom(alf, format, "true");
79   }
80   public String getAlignment(String format, String suffix)
81   {
82     return getAlignmentFrom(currentAlignFrame, format, suffix);
83   }
84   public String getAlignmentFrom(AlignFrame alf, String format, String suffix)
85   {
86     try
87     {
88       boolean seqlimits = suffix.equalsIgnoreCase("true");
89
90       String reply = new AppletFormatAdapter().formatSequences(format,
91           alf.viewport.getAlignment(), seqlimits);
92       return reply;
93     }
94     catch (Exception ex)
95     {
96       ex.printStackTrace();
97       return "Error retrieving alignment in " + format + " format. ";
98     }
99   }
100
101   public void loadAnnotation(String annotation)
102   {
103     loadAnnotationFrom(currentAlignFrame, annotation);
104   }
105   public void loadAnnotationFrom(AlignFrame alf, String annotation)
106   {
107     if (new AnnotationFile().readAnnotationFile(
108         alf.getAlignViewport().getAlignment(), annotation,
109         AppletFormatAdapter.PASTE))
110     {
111       alf.alignPanel.fontChanged();
112       alf.alignPanel.setScrollValues(0, 0);
113     }
114     else
115     {
116       alf.parseFeaturesFile(annotation, AppletFormatAdapter.PASTE);
117     }
118   }
119
120   public String getFeatures(String format)
121   {
122     return getFeaturesFrom(currentAlignFrame, format);
123   }
124   public String getFeaturesFrom(AlignFrame alf, String format)
125   {
126     return alf.outputFeatures(false, format);
127   }
128   public String getAnnotation()
129   {
130     return getAnnotationFrom(currentAlignFrame);
131   }
132   public String getAnnotationFrom(AlignFrame alf)
133   {
134     return alf.outputAnnotations(false);
135   }
136   public AlignFrame newView()
137   {
138     return newViewFrom(currentAlignFrame);
139   }
140   public AlignFrame newView(String name)
141   {
142     return newViewFrom(currentAlignFrame, name);
143   }
144
145   public AlignFrame newViewFrom(AlignFrame alf)
146   {
147     return alf.newView(null);
148   }
149   public AlignFrame newViewFrom(AlignFrame alf, String name)
150   {
151     return alf.newView(name);
152   }
153   /**
154    * 
155    * @param text alignment file as a string
156    * @param title window title 
157    * @return null or new alignment frame
158    */
159   public AlignFrame loadAlignment(String text, String title)
160   {
161     Alignment al = null;
162     String format = new IdentifyFile().Identify(text, AppletFormatAdapter.PASTE);
163     try
164     {
165       al = new AppletFormatAdapter().readFile(text,
166                                               AppletFormatAdapter.PASTE,
167                                               format);
168       if (al.getHeight() > 0)
169       {
170         return new AlignFrame(al, this, title, false);
171       }
172     }
173     catch (java.io.IOException ex)
174     {
175       ex.printStackTrace();
176     }
177     return null;
178   }
179
180   ////////////////////////////////////////////////
181   ////////////////////////////////////////////////
182
183
184
185   static int lastFrameX = 200;
186   static int lastFrameY = 200;
187   boolean fileFound = true;
188   String file = "No file";
189   Button launcher = new Button("Start Jalview");
190
191   //The currentAlignFrame is static, it will change
192   //if and when the user selects a new window
193   public static AlignFrame currentAlignFrame;
194
195   //This is the first frame to be displayed, and does not change
196   AlignFrame initialAlignFrame;
197
198   boolean embedded = false;
199
200   public boolean jmolAvailable = false;
201   public static boolean debug;
202
203   /**
204    * init method for Jalview Applet
205    */
206   public void init()
207   {
208     String dbg = getParameter("debug");
209     if (dbg!=null)
210     {
211       debug = dbg.toLowerCase().equals("true");
212     }
213     /**
214      * get the separator parameter if present
215      */
216     String sep = getParameter("separator");
217     if (sep!=null)
218     {
219       if (sep.length()>0)
220       {      separator = sep;
221         if (debug)
222         {
223           System.err.println("Separator set to '"+separator+"'");
224         }
225       } else {
226         throw new Error("Invalid separator parameter - must be non-zero length");
227       }
228     }
229     int r = 255;
230     int g = 255;
231     int b = 255;
232     String param = getParameter("RGB");
233
234     if (param != null)
235     {
236       try
237       {
238         r = Integer.parseInt(param.substring(0, 2), 16);
239         g = Integer.parseInt(param.substring(2, 4), 16);
240         b = Integer.parseInt(param.substring(4, 6), 16);
241       }
242       catch (Exception ex)
243       {
244         r = 255;
245         g = 255;
246         b = 255;
247       }
248     }
249
250     param = getParameter("label");
251     if (param != null)
252     {
253       launcher.setLabel(param);
254     }
255
256     this.setBackground(new Color(r, g, b));
257
258     file = getParameter("file");
259
260     if (file == null)
261     {
262       //Maybe the sequences are added as parameters
263       StringBuffer data = new StringBuffer("PASTE");
264       int i = 1;
265       while ( (file = getParameter("sequence" + i)) != null)
266       {
267         data.append(file.toString() + "\n");
268         i++;
269       }
270       if (data.length() > 5)
271       {
272         file = data.toString();
273       }
274     }
275
276     LoadJmolThread jmolAvailable = new LoadJmolThread();
277     jmolAvailable.start();
278
279     final JalviewLite applet = this;
280     if (getParameter("embedded") != null
281         && getParameter("embedded").equalsIgnoreCase("true"))
282     {
283       embedded = true;
284       LoadingThread loader = new LoadingThread(file, applet);
285       loader.start();
286     }
287     else if (file != null)
288     {
289       add(launcher);
290
291       launcher.addActionListener(new java.awt.event.ActionListener()
292       {
293         public void actionPerformed(ActionEvent e)
294         {
295           LoadingThread loader = new LoadingThread(file,
296               applet);
297           loader.start();
298         }
299       });
300     }
301     else
302     {
303       file = "NO FILE";
304       fileFound = false;
305     }
306   }
307
308
309   /**
310    * Initialises and displays a new java.awt.Frame
311    *
312    * @param frame java.awt.Frame to be displayed
313    * @param title title of new frame
314    * @param width width if new frame
315    * @param height height of new frame
316    */
317   public static void addFrame(final Frame frame, String title, int width,
318                               int height)
319   {
320     frame.setLocation(lastFrameX, lastFrameY);
321     lastFrameX += 40;
322     lastFrameY += 40;
323     frame.setSize(width, height);
324     frame.setTitle(title);
325     frame.addWindowListener(new WindowAdapter()
326     {
327       public void windowClosing(WindowEvent e)
328       {
329         if (frame instanceof AlignFrame)
330         {
331           ( (AlignFrame) frame).closeMenuItem_actionPerformed();
332         }
333         if (currentAlignFrame == frame)
334         {
335           currentAlignFrame = null;
336         }
337         lastFrameX -= 40;
338         lastFrameY -= 40;
339         frame.setMenuBar(null);
340         frame.dispose();
341       }
342
343       public void windowActivated(WindowEvent e)
344       {
345         if (frame instanceof AlignFrame)
346         {
347           currentAlignFrame = (AlignFrame) frame;
348           if (debug)
349           {
350             System.err.println("Activated window "+frame);
351           }
352         }
353       }
354
355     });
356     frame.setVisible(true);
357   }
358
359   /**
360    * This paints the background surrounding the "Launch Jalview button"
361    * <br>
362    * <br>If file given in parameter not found, displays error message
363    *
364    * @param g graphics context
365    */
366   public void paint(Graphics g)
367   {
368     if (!fileFound)
369     {
370       g.setColor(new Color(200, 200, 200));
371       g.setColor(Color.cyan);
372       g.fillRect(0, 0, getSize().width, getSize().height);
373       g.setColor(Color.red);
374       g.drawString("Jalview can't open file", 5, 15);
375       g.drawString("\"" + file + "\"", 5, 30);
376     }
377     else if (embedded)
378     {
379       g.setColor(Color.black);
380       g.setFont(new Font("Arial", Font.BOLD, 24));
381       g.drawString("Jalview Applet", 50, this.getSize().height / 2 - 30);
382       g.drawString("Loading Data...", 50, this.getSize().height / 2);
383     }
384   }
385
386
387   class LoadJmolThread extends Thread
388   {
389     public void run()
390     {
391       try
392       {
393         if (!System.getProperty("java.version").startsWith("1.1"))
394         {
395           Class.forName("org.jmol.adapter.smarter.SmarterJmolAdapter");
396           jmolAvailable = true;
397         }
398       }
399       catch (java.lang.ClassNotFoundException ex)
400       {
401         System.out.println("Jmol not available - Using MCview for structures");
402       }
403     }
404   }
405
406
407   class LoadingThread
408       extends Thread
409   {
410     String file;
411     String protocol;
412     String format;
413     JalviewLite applet;
414
415     public LoadingThread(String _file,
416                          JalviewLite _applet)
417     {
418       if (applet.debug)
419       {
420         System.err.println("Loading thread started with:\n>>file\n"+_file+">>endfile");
421       }
422       file = _file;
423       if (file.startsWith("PASTE"))
424       {
425         file = file.substring(5);
426         protocol = AppletFormatAdapter.PASTE;
427       }
428       else if (inArchive(file))
429       {
430         protocol = AppletFormatAdapter.CLASSLOADER;
431       }
432       else
433       {
434         file = addProtocol(file);
435         protocol = AppletFormatAdapter.URL;
436       }
437       if (applet.debug)
438       {
439         System.err.println("Protocol identified as '"+protocol+"'");
440       }
441       format = new jalview.io.IdentifyFile().Identify(file, protocol);
442       if (applet.debug)
443       {
444         System.err.println("File identified as '"+format+"'");
445       }
446       applet = _applet;
447     }
448
449     public void run()
450     {
451       startLoading();
452     }
453
454     private void startLoading()
455     {
456       Alignment al = null;
457       try
458       {
459         al = new AppletFormatAdapter().readFile(file, protocol,
460                                                 format);
461       }
462       catch (java.io.IOException ex)
463       {
464         ex.printStackTrace();
465       }
466       if ( (al != null) && (al.getHeight() > 0))
467       {
468         currentAlignFrame = new AlignFrame(al,
469                                            applet,
470                                            file,
471                                            embedded);
472
473         if (protocol == jalview.io.AppletFormatAdapter.PASTE)
474         {
475           currentAlignFrame.setTitle("Sequences from " + getDocumentBase());
476         }
477
478         initialAlignFrame = currentAlignFrame;
479
480         currentAlignFrame.statusBar.setText("Successfully loaded file " + file);
481
482         String treeFile = applet.getParameter("tree");
483         if (treeFile == null)
484         {
485           treeFile = applet.getParameter("treeFile");
486         }
487
488         if (treeFile != null)
489         {
490           try
491           {
492             if (inArchive(treeFile))
493             {
494               protocol = AppletFormatAdapter.CLASSLOADER;
495             }
496             else
497             {
498               protocol = AppletFormatAdapter.URL;
499               treeFile = addProtocol(treeFile);
500             }
501
502             jalview.io.NewickFile fin = new jalview.io.NewickFile(treeFile,
503                 protocol);
504
505             fin.parse();
506
507             if (fin.getTree() != null)
508             {
509               currentAlignFrame.loadTree(fin, treeFile);
510             }
511           }
512           catch (Exception ex)
513           {
514             ex.printStackTrace();
515           }
516         }
517
518         String param = getParameter("features");
519         if (param != null)
520         {
521           if (!inArchive(param))
522           {
523             param = addProtocol(param);
524           }
525
526           currentAlignFrame.parseFeaturesFile(param, protocol);
527         }
528
529         param = getParameter("showFeatureSettings");
530         if (param != null && param.equalsIgnoreCase("true"))
531         {
532           currentAlignFrame.viewport.showSequenceFeatures(true);
533           new FeatureSettings(currentAlignFrame.alignPanel);
534         }
535
536         param = getParameter("annotations");
537         if (param != null)
538         {
539           if (!inArchive(param))
540           {
541             param = addProtocol(param);
542           }
543
544           new AnnotationFile().readAnnotationFile(
545               currentAlignFrame.viewport.getAlignment(),
546               param,
547               protocol);
548
549           currentAlignFrame.alignPanel.fontChanged();
550           currentAlignFrame.alignPanel.setScrollValues(0, 0);
551
552         }
553
554         param = getParameter("jnetfile");
555         if (param != null)
556         {
557           try
558           {
559             if (inArchive(param))
560             {
561               protocol = AppletFormatAdapter.CLASSLOADER;
562             }
563             else
564             {
565               protocol = AppletFormatAdapter.URL;
566               param = addProtocol(param);
567             }
568
569             jalview.io.JPredFile predictions = new jalview.io.JPredFile(
570                 param, protocol);
571             new JnetAnnotationMaker().add_annotation(predictions,
572                 currentAlignFrame.viewport.getAlignment(),
573                 0, false); // do not add sequence profile from concise output
574             currentAlignFrame.alignPanel.fontChanged();
575             currentAlignFrame.alignPanel.setScrollValues(0, 0);
576           }
577           catch (Exception ex)
578           {
579             ex.printStackTrace();
580           }
581         }
582
583         /*
584          <param name="PDBfile" value="1gaq.txt PDB|1GAQ|1GAQ|A PDB|1GAQ|1GAQ|B PDB|1GAQ|1GAQ|C">
585
586          <param name="PDBfile2" value="1gaq.txt A=SEQA B=SEQB C=SEQB">
587
588          <param name="PDBfile3" value="1q0o Q45135_9MICO">
589         */
590
591
592         int pdbFileCount = 0;
593         do{
594           if (pdbFileCount > 0)
595             param = getParameter("PDBFILE" + pdbFileCount);
596           else
597             param = getParameter("PDBFILE");
598
599           if (param != null)
600           {
601             PDBEntry pdb = new PDBEntry();
602
603             String seqstring;
604             SequenceI[] seqs = null;
605             String [] chains = null;
606
607             StringTokenizer st = new StringTokenizer(param, " ");
608
609             if (st.countTokens() < 2)
610             {
611               String sequence = applet.getParameter("PDBSEQ");
612               if (sequence != null)
613                 seqs = new SequenceI[]
614                     {
615                     (Sequence) currentAlignFrame.
616                     getAlignViewport().getAlignment().
617                     findName(sequence)};
618
619             }
620             else
621             {
622               param = st.nextToken();
623               Vector tmp = new Vector();
624               Vector tmp2 = new Vector();
625
626               while (st.hasMoreTokens())
627               {
628                 seqstring = st.nextToken();
629                 StringTokenizer st2 = new StringTokenizer(seqstring,"=");
630                 if(st2.countTokens()>1)
631                 {
632                   //This is the chain
633                   tmp2.addElement(st2.nextToken());
634                   seqstring = st2.nextToken();
635                 }
636                 tmp.addElement( (Sequence) currentAlignFrame.
637                                  getAlignViewport().getAlignment().
638                                  findName(seqstring));
639               }
640
641               seqs = new SequenceI[tmp.size()];
642               tmp.copyInto(seqs);
643               if(tmp2.size()==tmp.size())
644               {
645                 chains = new String[tmp2.size()];
646                 tmp2.copyInto(chains);
647               }
648             }
649
650             if (inArchive(param) && !jmolAvailable)
651             {
652               protocol = AppletFormatAdapter.CLASSLOADER;
653             }
654             else
655             {
656               protocol = AppletFormatAdapter.URL;
657               param = addProtocol(param);
658             }
659
660             pdb.setFile(param);
661
662             if(seqs!=null)
663             {
664               for (int i = 0; i < seqs.length; i++)
665               {
666                 ( (Sequence) seqs[i]).addPDBId(pdb);
667               }
668
669               if (jmolAvailable)
670               {
671                 new jalview.appletgui.AppletJmol(pdb,
672                                                  seqs,
673                                                  chains,
674                                                  currentAlignFrame.alignPanel,
675                                                  protocol);
676                 lastFrameX += 40;
677                 lastFrameY+=40;
678               }
679               else
680                     new MCview.AppletPDBViewer(pdb,
681                                            seqs,
682                                            chains,
683                                            currentAlignFrame.alignPanel,
684                                            protocol);
685             }
686           }
687
688           pdbFileCount++;
689         }
690         while(pdbFileCount < 10);
691         
692         /////////////////////////////
693         // modify display of features
694         //
695         // hide specific groups
696         param = getParameter("hidefeaturegroups");
697         if (param != null)
698         {
699           applet.setFeatureGroupState(param, false);
700         }
701         // show specific groups
702         param = getParameter("showfeaturegroups");
703         if (param != null)
704         {
705           applet.setFeatureGroupState(param, true);
706         }
707       }
708       else
709       {
710         fileFound = false;
711         remove(launcher);
712         repaint();
713       }
714     }
715
716     /**
717      * Discovers whether the given file is in the Applet Archive
718      * @param file String
719      * @return boolean
720      */
721     boolean inArchive(String file)
722     {
723       //This might throw a security exception in certain browsers
724       //Netscape Communicator for instance.
725       try
726       {
727         return (getClass().getResourceAsStream("/" + file) != null);
728       }
729       catch (Exception ex)
730       {
731         System.out.println("Exception checking resources: " + file + " " + ex);
732         return false;
733       }
734     }
735
736     String addProtocol(String file)
737     {
738       if (file.indexOf("://") == -1)
739       {
740         file = getCodeBase() + file;
741       }
742
743       return file;
744     }
745   }
746   /**
747    * separator used for separatorList
748    */
749   protected String separator = "|"; // this is a safe(ish) separator - tabs don't work for firefox
750   /**
751    * parse the string into a list
752    * @param list
753    * @return elements separated by separator
754    */
755   public String[] separatorListToArray(String list)
756   {
757     int seplen = separator.length();
758     if (list==null || list.equals(""))
759       return null;
760     java.util.Vector jv = new Vector();
761     int cp=0,pos;
762     while ((pos=list.indexOf(separator,cp))>cp)
763     {
764       jv.addElement(list.substring(cp,pos));
765       cp = pos+seplen;
766     }
767     if (cp<list.length())
768     {
769       jv.addElement(list.substring(cp));
770     }
771     if (jv.size()>0)
772     { String[] v = new String[jv.size()];
773       for (int i=0; i<v.length; i++)
774       {
775         v[i] = (String) jv.elementAt(i);
776       }
777       jv.removeAllElements();
778       if (debug)
779       {
780         System.err.println("Array from '"+separator+"' separated List:\n"+v.length);
781         for (int i=0; i<v.length;i++)
782         {
783           System.err.println("item "+i+" '"+v[i]+"'");
784         }
785       }
786       return v;
787     }
788     if (debug)
789     {
790       System.err.println("Empty Array from '"+separator+"' separated List");
791     }
792     return null;
793   }
794   /**
795    * concatenate the list with separator
796    * @param list
797    * @return concatenated string
798    */
799   public String arrayToSeparatorList(String[] list)
800   {
801     StringBuffer v = new StringBuffer();
802     if (list!=null)
803     {
804       for (int i=0,iSize=list.length-1;i<iSize;i++)
805       { 
806         if (list[i]!=null)
807         {  
808           v.append(list[i]); 
809         }
810         v.append(separator);
811       }
812       if (list[list.length-1]!=null)
813       { v.append(list[list.length-1]);
814       }
815       if (debug)
816       {
817         System.err.println("Returning '"+separator+"' separated List:\n");
818         System.err.println(v);
819       }
820       return v.toString();
821     }
822     if (debug)
823     {
824       System.err.println("Returning empty '"+separator+"' separated List\n");
825     }
826     return "";
827   }
828   /**
829    * @return
830    * @see jalview.appletgui.AlignFrame#getFeatureGroups()
831    */
832   public String getFeatureGroups()
833   {
834     String lst = arrayToSeparatorList(currentAlignFrame.getFeatureGroups());
835     return lst;
836   }
837   /**
838    * @param alf alignframe to get feature groups on
839    * @return
840    * @see jalview.appletgui.AlignFrame#getFeatureGroups()
841    */
842   public String getFeatureGroupsOn(AlignFrame alf)
843   {
844     String lst = arrayToSeparatorList(alf.getFeatureGroups());
845     return lst;
846   }
847   /**
848    * @param visible
849    * @return
850    * @see jalview.appletgui.AlignFrame#getFeatureGroupsOfState(boolean)
851    */
852   public String getFeatureGroupsOfState(boolean visible)
853   {
854     return arrayToSeparatorList(currentAlignFrame.getFeatureGroupsOfState(visible));
855   }
856   /**
857    * @param alf align frame to get groups of state visible
858    * @param visible
859    * @return
860    * @see jalview.appletgui.AlignFrame#getFeatureGroupsOfState(boolean)
861    */
862   public String getFeatureGroupsOfStateOn(AlignFrame alf, boolean visible)
863   {
864     return arrayToSeparatorList(alf.getFeatureGroupsOfState(visible));
865   }  /**
866    * @param groups tab separated list of group names 
867    * @param state true or false
868    * @see jalview.appletgui.AlignFrame#setFeatureGroupState(java.lang.String[], boolean)
869    */
870   public void setFeatureGroupStateOn(AlignFrame alf, String groups, boolean state)
871   {
872     boolean st = state;//!(state==null || state.equals("") || state.toLowerCase().equals("false"));
873     alf.setFeatureGroupState(separatorListToArray(groups), st);
874   }
875   public void setFeatureGroupState(String groups, boolean state)
876   {
877     setFeatureGroupStateOn(currentAlignFrame, groups, state);
878   }
879   /**
880    * List separator string
881    * @return the separator
882    */
883   public String getSeparator()
884   {
885     return separator;
886   }
887   /**
888    * List separator string
889    * @param separator the separator to set
890    */
891   public void setSeparator(String separator)
892   {
893     this.separator = separator;
894   }
895 }