new ajax api methods for linking existing jmol viewers with sequences in an alignframe
[jalview.git] / src / jalview / bin / JalviewLite.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.5)
3  * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
10  * 
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 package jalview.bin;
19
20 import java.applet.*;
21
22 import java.awt.*;
23 import java.awt.event.*;
24 import java.io.BufferedReader;
25 import java.io.InputStreamReader;
26 import java.util.*;
27
28 import jalview.api.SequenceStructureBinding;
29 import jalview.appletgui.*;
30 import jalview.datamodel.*;
31 import jalview.io.*;
32
33 /**
34  * Jalview Applet. Runs in Java 1.18 runtime
35  * 
36  * @author $author$
37  * @version $Revision$
38  */
39 public class JalviewLite extends Applet
40 {
41
42   // /////////////////////////////////////////
43   // The following public methods maybe called
44   // externally, eg via javascript in HTML page
45   /**
46    * @return String list of selected sequence IDs, each terminated by "¬"
47    *         (&#172;)
48    */
49   public String getSelectedSequences()
50   {
51     return getSelectedSequencesFrom(getDefaultTargetFrame());
52   }
53
54   /**
55    * @param sep
56    *          separator string or null for default
57    * @return String list of selected sequence IDs, each terminated by sep or
58    *         ("¬" as default)
59    */
60   public String getSelectedSequences(String sep)
61   {
62     return getSelectedSequencesFrom(getDefaultTargetFrame(), sep);
63   }
64
65   /**
66    * @param alf
67    *          alignframe containing selection
68    * @return String list of selected sequence IDs, each terminated by "¬"
69    * 
70    */
71   public String getSelectedSequencesFrom(AlignFrame alf)
72   {
73     return getSelectedSequencesFrom(alf, "¬");
74   }
75
76   /**
77    * get list of selected sequence IDs separated by given separator
78    * 
79    * @param alf
80    *          window containing selection
81    * @param sep
82    *          separator string to use - default is "¬"
83    * @return String list of selected sequence IDs, each terminated by the given
84    *         separator
85    */
86   public String getSelectedSequencesFrom(AlignFrame alf, String sep)
87   {
88     StringBuffer result = new StringBuffer("");
89     if (sep == null || sep.length() == 0)
90     {
91       sep = "¬";
92     }
93     if (alf.viewport.getSelectionGroup() != null)
94     {
95       SequenceI[] seqs = alf.viewport.getSelectionGroup()
96               .getSequencesInOrder(alf.viewport.getAlignment());
97
98       for (int i = 0; i < seqs.length; i++)
99       {
100         result.append(seqs[i].getName());
101         result.append(sep);
102       }
103     }
104
105     return result.toString();
106   }
107
108   /**
109    * get sequences selected in current alignFrame and return their alignment in
110    * format 'format' either with or without suffix
111    * 
112    * @param alf
113    *          - where selection is
114    * @param format
115    *          - format of alignment file
116    * @param suffix
117    *          - "true" to append /start-end string to each sequence ID
118    * @return selected sequences as flat file or empty string if there was no
119    *         current selection
120    */
121   public String getSelectedSequencesAsAlignment(String format, String suffix)
122   {
123     return getSelectedSequencesAsAlignmentFrom(currentAlignFrame, format,
124             suffix);
125   }
126
127   /**
128    * get sequences selected in alf and return their alignment in format 'format'
129    * either with or without suffix
130    * 
131    * @param alf
132    *          - where selection is
133    * @param format
134    *          - format of alignment file
135    * @param suffix
136    *          - "true" to append /start-end string to each sequence ID
137    * @return selected sequences as flat file or empty string if there was no
138    *         current selection
139    */
140   public String getSelectedSequencesAsAlignmentFrom(AlignFrame alf,
141           String format, String suffix)
142   {
143     try
144     {
145       boolean seqlimits = suffix.equalsIgnoreCase("true");
146       if (alf.viewport.getSelectionGroup() != null)
147       {
148         String reply = new AppletFormatAdapter().formatSequences(format,
149                 new Alignment(alf.viewport.getSelectionAsNewSequence()),
150                 seqlimits);
151         return reply;
152       }
153     } catch (Exception ex)
154     {
155       ex.printStackTrace();
156       return "Error retrieving alignment in " + format + " format. ";
157     }
158     return "";
159   }
160
161   public String getAlignment(String format)
162   {
163     return getAlignmentFrom(getDefaultTargetFrame(), format, "true");
164   }
165
166   public String getAlignmentFrom(AlignFrame alf, String format)
167   {
168     return getAlignmentFrom(alf, format, "true");
169   }
170
171   public String getAlignment(String format, String suffix)
172   {
173     return getAlignmentFrom(getDefaultTargetFrame(), format, suffix);
174   }
175
176   public String getAlignmentFrom(AlignFrame alf, String format,
177           String suffix)
178   {
179     try
180     {
181       boolean seqlimits = suffix.equalsIgnoreCase("true");
182
183       String reply = new AppletFormatAdapter().formatSequences(format,
184               alf.viewport.getAlignment(), seqlimits);
185       return reply;
186     } catch (Exception ex)
187     {
188       ex.printStackTrace();
189       return "Error retrieving alignment in " + format + " format. ";
190     }
191   }
192
193   public void loadAnnotation(String annotation)
194   {
195     loadAnnotationFrom(getDefaultTargetFrame(), annotation);
196   }
197
198   public void loadAnnotationFrom(AlignFrame alf, String annotation)
199   {
200     if (new AnnotationFile().readAnnotationFile(alf.getAlignViewport()
201             .getAlignment(), annotation, AppletFormatAdapter.PASTE))
202     {
203       alf.alignPanel.fontChanged();
204       alf.alignPanel.setScrollValues(0, 0);
205     }
206     else
207     {
208       alf.parseFeaturesFile(annotation, AppletFormatAdapter.PASTE);
209     }
210   }
211
212   public String getFeatures(String format)
213   {
214     return getFeaturesFrom(getDefaultTargetFrame(), format);
215   }
216
217   public String getFeaturesFrom(AlignFrame alf, String format)
218   {
219     return alf.outputFeatures(false, format);
220   }
221
222   public String getAnnotation()
223   {
224     return getAnnotationFrom(getDefaultTargetFrame());
225   }
226
227   public String getAnnotationFrom(AlignFrame alf)
228   {
229     return alf.outputAnnotations(false);
230   }
231
232   public AlignFrame newView()
233   {
234     return newViewFrom(getDefaultTargetFrame());
235   }
236
237   public AlignFrame newView(String name)
238   {
239     return newViewFrom(getDefaultTargetFrame(), name);
240   }
241
242   public AlignFrame newViewFrom(AlignFrame alf)
243   {
244     return alf.newView(null);
245   }
246
247   public AlignFrame newViewFrom(AlignFrame alf, String name)
248   {
249     return alf.newView(name);
250   }
251
252   /**
253    * 
254    * @param text
255    *          alignment file as a string
256    * @param title
257    *          window title
258    * @return null or new alignment frame
259    */
260   public AlignFrame loadAlignment(String text, String title)
261   {
262     Alignment al = null;
263
264     String format = new IdentifyFile().Identify(text,
265             AppletFormatAdapter.PASTE);
266     try
267     {
268       al = new AppletFormatAdapter().readFile(text,
269               AppletFormatAdapter.PASTE, format);
270       if (al.getHeight() > 0)
271       {
272         return new AlignFrame(al, this, title, false);
273       }
274     } catch (java.io.IOException ex)
275     {
276       ex.printStackTrace();
277     }
278     return null;
279   }
280
281   // //////////////////////////////////////////////
282   // //////////////////////////////////////////////
283
284   static int lastFrameX = 200;
285
286   static int lastFrameY = 200;
287
288   boolean fileFound = true;
289
290   String file = "No file";
291
292   Button launcher = new Button("Start Jalview");
293
294   /**
295    * The currentAlignFrame is static, it will change if and when the user
296    * selects a new window. Note that it will *never* point back to the embedded
297    * AlignFrame if the applet is started as embedded on the page and then
298    * afterwards a new view is created.
299    */
300   public static AlignFrame currentAlignFrame = null;
301
302   /**
303    * This is the first frame to be displayed, and does not change. API calls
304    * will default to this instance if currentAlignFrame is null.
305    */
306   AlignFrame initialAlignFrame = null;
307
308   boolean embedded = false;
309
310   private boolean checkForJmol = true;
311
312   private boolean checkedForJmol = false; // ensure we don't check for jmol
313
314   // every time the app is re-inited
315
316   public boolean jmolAvailable = false;
317
318   public static boolean debug = false;
319
320   static String builddate = null, version = null;
321
322   private static void initBuildDetails()
323   {
324     if (builddate == null)
325     {
326       builddate = "unknown";
327       version = "test";
328       java.net.URL url = JalviewLite.class
329               .getResource("/.build_properties");
330       if (url != null)
331       {
332         try
333         {
334           BufferedReader reader = new BufferedReader(new InputStreamReader(
335                   url.openStream()));
336           String line;
337           while ((line = reader.readLine()) != null)
338           {
339             if (line.indexOf("VERSION") > -1)
340             {
341               version = line.substring(line.indexOf("=") + 1);
342             }
343             if (line.indexOf("BUILD_DATE") > -1)
344             {
345               builddate = line.substring(line.indexOf("=") + 1);
346             }
347           }
348         } catch (Exception ex)
349         {
350           ex.printStackTrace();
351         }
352       }
353     }
354   }
355
356   public static String getBuildDate()
357   {
358     initBuildDetails();
359     return builddate;
360   }
361
362   public static String getVersion()
363   {
364     initBuildDetails();
365     return version;
366   }
367
368   /**
369    * init method for Jalview Applet
370    */
371   public void init()
372   {
373
374     /**
375      * turn on extra applet debugging
376      */
377     String dbg = getParameter("debug");
378     if (dbg != null)
379     {
380       debug = dbg.toLowerCase().equals("true");
381     }
382     if (debug)
383     {
384
385       System.err.println("JalviewLite Version " + getVersion());
386       System.err.println("Build Date : " + getBuildDate());
387
388     }
389     /**
390      * if true disable the check for jmol
391      */
392     String chkforJmol = getParameter("nojmol");
393     if (chkforJmol != null)
394     {
395       checkForJmol = !chkforJmol.equals("true");
396     }
397     /**
398      * get the separator parameter if present
399      */
400     String sep = getParameter("separator");
401     if (sep != null)
402     {
403       if (sep.length() > 0)
404       {
405         separator = sep;
406         if (debug)
407         {
408           System.err.println("Separator set to '" + separator + "'");
409         }
410       }
411       else
412       {
413         throw new Error(
414                 "Invalid separator parameter - must be non-zero length");
415       }
416     }
417     int r = 255;
418     int g = 255;
419     int b = 255;
420     String param = getParameter("RGB");
421
422     if (param != null)
423     {
424       try
425       {
426         r = Integer.parseInt(param.substring(0, 2), 16);
427         g = Integer.parseInt(param.substring(2, 4), 16);
428         b = Integer.parseInt(param.substring(4, 6), 16);
429       } catch (Exception ex)
430       {
431         r = 255;
432         g = 255;
433         b = 255;
434       }
435     }
436
437     param = getParameter("label");
438     if (param != null)
439     {
440       launcher.setLabel(param);
441     }
442
443     this.setBackground(new Color(r, g, b));
444
445     file = getParameter("file");
446
447     if (file == null)
448     {
449       // Maybe the sequences are added as parameters
450       StringBuffer data = new StringBuffer("PASTE");
451       int i = 1;
452       while ((file = getParameter("sequence" + i)) != null)
453       {
454         data.append(file.toString() + "\n");
455         i++;
456       }
457       if (data.length() > 5)
458       {
459         file = data.toString();
460       }
461     }
462
463     final JalviewLite applet = this;
464     if (getParameter("embedded") != null
465             && getParameter("embedded").equalsIgnoreCase("true"))
466     {
467       // Launch as embedded applet in page
468       embedded = true;
469       LoadingThread loader = new LoadingThread(file, applet);
470       loader.start();
471     }
472     else if (file != null)
473     {
474       if (getParameter("showbutton") == null
475               || !getParameter("showbutton").equalsIgnoreCase("false"))
476       {
477         // Add the JalviewLite 'Button' to the page
478         add(launcher);
479         launcher.addActionListener(new java.awt.event.ActionListener()
480         {
481           public void actionPerformed(ActionEvent e)
482           {
483             LoadingThread loader = new LoadingThread(file, applet);
484             loader.start();
485           }
486         });
487       }
488       else
489       {
490         // Open jalviewLite immediately.
491         LoadingThread loader = new LoadingThread(file, applet);
492         loader.start();
493       }
494     }
495     else
496     {
497       // jalview initialisation with no alignment. loadAlignment() method can
498       // still be called to open new alignments.
499       file = "NO FILE";
500       fileFound = false;
501     }
502   }
503
504   /**
505    * Initialises and displays a new java.awt.Frame
506    * 
507    * @param frame
508    *          java.awt.Frame to be displayed
509    * @param title
510    *          title of new frame
511    * @param width
512    *          width if new frame
513    * @param height
514    *          height of new frame
515    */
516   public static void addFrame(final Frame frame, String title, int width,
517           int height)
518   {
519     frame.setLocation(lastFrameX, lastFrameY);
520     lastFrameX += 40;
521     lastFrameY += 40;
522     frame.setSize(width, height);
523     frame.setTitle(title);
524     frame.addWindowListener(new WindowAdapter()
525     {
526       public void windowClosing(WindowEvent e)
527       {
528         if (frame instanceof AlignFrame)
529         {
530           ((AlignFrame) frame).closeMenuItem_actionPerformed();
531         }
532         if (currentAlignFrame == frame)
533         {
534           currentAlignFrame = null;
535         }
536         lastFrameX -= 40;
537         lastFrameY -= 40;
538         if (frame instanceof EmbmenuFrame)
539         {
540           ((EmbmenuFrame) frame).destroyMenus();
541         }
542         frame.setMenuBar(null);
543         frame.dispose();
544       }
545
546       public void windowActivated(WindowEvent e)
547       {
548         if (frame instanceof AlignFrame)
549         {
550           currentAlignFrame = (AlignFrame) frame;
551           if (debug)
552           {
553             System.err.println("Activated window " + frame);
554           }
555         }
556         // be good.
557         super.windowActivated(e);
558       }
559       /*
560        * Probably not necessary to do this - see TODO above. (non-Javadoc)
561        * 
562        * @see
563        * java.awt.event.WindowAdapter#windowDeactivated(java.awt.event.WindowEvent
564        * )
565        * 
566        * public void windowDeactivated(WindowEvent e) { if (currentAlignFrame ==
567        * frame) { currentAlignFrame = null; if (debug) {
568        * System.err.println("Deactivated window "+frame); } }
569        * super.windowDeactivated(e); }
570        */
571     });
572     frame.setVisible(true);
573   }
574
575   /**
576    * This paints the background surrounding the "Launch Jalview button" <br>
577    * <br>
578    * If file given in parameter not found, displays error message
579    * 
580    * @param g
581    *          graphics context
582    */
583   public void paint(Graphics g)
584   {
585     if (!fileFound)
586     {
587       g.setColor(new Color(200, 200, 200));
588       g.setColor(Color.cyan);
589       g.fillRect(0, 0, getSize().width, getSize().height);
590       g.setColor(Color.red);
591       g.drawString("Jalview can't open file", 5, 15);
592       g.drawString("\"" + file + "\"", 5, 30);
593     }
594     else if (embedded)
595     {
596       g.setColor(Color.black);
597       g.setFont(new Font("Arial", Font.BOLD, 24));
598       g.drawString("Jalview Applet", 50, this.getSize().height / 2 - 30);
599       g.drawString("Loading Data...", 50, this.getSize().height / 2);
600     }
601   }
602
603   class LoadJmolThread extends Thread
604   {
605     private boolean running = false;
606
607     public void run()
608     {
609       if (running || checkedForJmol)
610       {
611         return;
612       }
613       running = true;
614       if (checkForJmol)
615       {
616         try
617         {
618           if (!System.getProperty("java.version").startsWith("1.1"))
619           {
620             Class.forName("org.jmol.adapter.smarter.SmarterJmolAdapter");
621             jmolAvailable = true;
622           }
623           if (!jmolAvailable)
624           {
625             System.out
626                     .println("Jmol not available - Using MCview for structures");
627           }
628         } catch (java.lang.ClassNotFoundException ex)
629         {
630         }
631       }
632       else
633       {
634         jmolAvailable = false;
635         if (debug)
636         {
637           System.err
638                   .println("Skipping Jmol check. Will use MCView (probably)");
639         }
640       }
641       checkedForJmol = true;
642       running = false;
643     }
644
645     public boolean notFinished()
646     {
647       return running || !checkedForJmol;
648     }
649   }
650
651   class LoadingThread extends Thread
652   {
653     /**
654      * State variable: File source
655      */
656     String file;
657
658     /**
659      * State variable: protocol for access to file source
660      */
661     String protocol;
662
663     /**
664      * State variable: format of file source
665      */
666     String format;
667
668     String _file;
669
670     JalviewLite applet;
671
672     private void dbgMsg(String msg)
673     {
674       if (applet.debug)
675       {
676         System.err.println(msg);
677       }
678     }
679
680     /**
681      * update the protocol state variable for accessing the datasource located
682      * by file.
683      * 
684      * @param file
685      * @return possibly updated datasource string
686      */
687     public String setProtocolState(String file)
688     {
689       if (file.startsWith("PASTE"))
690       {
691         file = file.substring(5);
692         protocol = AppletFormatAdapter.PASTE;
693       }
694       else if (inArchive(file))
695       {
696         protocol = AppletFormatAdapter.CLASSLOADER;
697       }
698       else
699       {
700         file = addProtocol(file);
701         protocol = AppletFormatAdapter.URL;
702       }
703       dbgMsg("Protocol identified as '" + protocol + "'");
704       return file;
705     }
706
707     public LoadingThread(String _file, JalviewLite _applet)
708     {
709       this._file = _file;
710       applet = _applet;
711     }
712
713     public void run()
714     {
715       LoadJmolThread jmolchecker = new LoadJmolThread();
716       jmolchecker.start();
717       while (jmolchecker.notFinished())
718       {
719         // wait around until the Jmol check is complete.
720         try
721         {
722           Thread.sleep(2);
723         } catch (Exception e)
724         {
725         }
726         ;
727       }
728       startLoading();
729     }
730
731     private void startLoading()
732     {
733       AlignFrame newAlignFrame;
734       dbgMsg("Loading thread started with:\n>>file\n" + _file + ">>endfile");
735       file = setProtocolState(_file);
736
737       format = new jalview.io.IdentifyFile().Identify(file, protocol);
738       dbgMsg("File identified as '" + format + "'");
739       dbgMsg("Loading started.");
740       Alignment al = null;
741       try
742       {
743         al = new AppletFormatAdapter().readFile(file, protocol, format);
744       } catch (java.io.IOException ex)
745       {
746         dbgMsg("File load exception.");
747         ex.printStackTrace();
748         if (debug)
749         {
750           try
751           {
752             FileParse fp = new FileParse(file, protocol);
753             String ln = null;
754             dbgMsg(">>>Dumping contents of '" + file + "' " + "("
755                     + protocol + ")");
756             while ((ln = fp.nextLine()) != null)
757             {
758               dbgMsg(ln);
759             }
760             dbgMsg(">>>Dump finished.");
761           } catch (Exception e)
762           {
763             System.err
764                     .println("Exception when trying to dump the content of the file parameter.");
765             e.printStackTrace();
766           }
767         }
768       }
769       if ((al != null) && (al.getHeight() > 0))
770       {
771         dbgMsg("Successfully loaded file.");
772         newAlignFrame = new AlignFrame(al, applet, file, embedded);
773         if (initialAlignFrame == null)
774         {
775           initialAlignFrame = newAlignFrame;
776         }
777         // update the focus.
778         currentAlignFrame = newAlignFrame;
779
780         if (protocol == jalview.io.AppletFormatAdapter.PASTE)
781         {
782           newAlignFrame.setTitle("Sequences from " + getDocumentBase());
783         }
784
785         newAlignFrame.statusBar.setText("Successfully loaded file " + file);
786
787         String treeFile = applet.getParameter("tree");
788         if (treeFile == null)
789         {
790           treeFile = applet.getParameter("treeFile");
791         }
792
793         if (treeFile != null)
794         {
795           try
796           {
797             treeFile = setProtocolState(treeFile);
798             /*
799              * if (inArchive(treeFile)) { protocol =
800              * AppletFormatAdapter.CLASSLOADER; } else { protocol =
801              * AppletFormatAdapter.URL; treeFile = addProtocol(treeFile); }
802              */
803             jalview.io.NewickFile fin = new jalview.io.NewickFile(treeFile,
804                     protocol);
805
806             fin.parse();
807
808             if (fin.getTree() != null)
809             {
810               newAlignFrame.loadTree(fin, treeFile);
811               dbgMsg("Successfuly imported tree.");
812             }
813             else
814             {
815               dbgMsg("Tree parameter did not resolve to a valid tree.");
816             }
817           } catch (Exception ex)
818           {
819             ex.printStackTrace();
820           }
821         }
822
823         String param = getParameter("features");
824         if (param != null)
825         {
826           param = setProtocolState(param);
827
828           newAlignFrame.parseFeaturesFile(param, protocol);
829         }
830
831         param = getParameter("showFeatureSettings");
832         if (param != null && param.equalsIgnoreCase("true"))
833         {
834           newAlignFrame.viewport.showSequenceFeatures(true);
835           new FeatureSettings(newAlignFrame.alignPanel);
836         }
837
838         param = getParameter("annotations");
839         if (param != null)
840         {
841           param = setProtocolState(param);
842
843           if (new AnnotationFile().readAnnotationFile(
844                   newAlignFrame.viewport.getAlignment(), param, protocol))
845           {
846             newAlignFrame.alignPanel.fontChanged();
847             newAlignFrame.alignPanel.setScrollValues(0, 0);
848           }
849           else
850           {
851             System.err
852                     .println("Annotations were not added from annotation file '"
853                             + param + "'");
854           }
855
856         }
857
858         param = getParameter("jnetfile");
859         if (param != null)
860         {
861           try
862           {
863             param = setProtocolState(param);
864             jalview.io.JPredFile predictions = new jalview.io.JPredFile(
865                     param, protocol);
866             JnetAnnotationMaker.add_annotation(predictions,
867                     newAlignFrame.viewport.getAlignment(), 0, false); // false==do
868             // not
869             // add
870             // sequence
871             // profile
872             // from
873             // concise
874             // output
875             newAlignFrame.alignPanel.fontChanged();
876             newAlignFrame.alignPanel.setScrollValues(0, 0);
877           } catch (Exception ex)
878           {
879             ex.printStackTrace();
880           }
881         }
882
883         /*
884          * <param name="PDBfile" value="1gaq.txt PDB|1GAQ|1GAQ|A PDB|1GAQ|1GAQ|B
885          * PDB|1GAQ|1GAQ|C">
886          * 
887          * <param name="PDBfile2" value="1gaq.txt A=SEQA B=SEQB C=SEQB">
888          * 
889          * <param name="PDBfile3" value="1q0o Q45135_9MICO">
890          */
891
892         int pdbFileCount = 0;
893         do
894         {
895           if (pdbFileCount > 0)
896             param = getParameter("PDBFILE" + pdbFileCount);
897           else
898             param = getParameter("PDBFILE");
899
900           if (param != null)
901           {
902             PDBEntry pdb = new PDBEntry();
903
904             String seqstring;
905             SequenceI[] seqs = null;
906             String[] chains = null;
907
908             StringTokenizer st = new StringTokenizer(param, " ");
909
910             if (st.countTokens() < 2)
911             {
912               String sequence = applet.getParameter("PDBSEQ");
913               if (sequence != null)
914                 seqs = new SequenceI[]
915                 { (Sequence) newAlignFrame.getAlignViewport()
916                         .getAlignment().findName(sequence) };
917
918             }
919             else
920             {
921               param = st.nextToken();
922               Vector tmp = new Vector();
923               Vector tmp2 = new Vector();
924
925               while (st.hasMoreTokens())
926               {
927                 seqstring = st.nextToken();
928                 StringTokenizer st2 = new StringTokenizer(seqstring, "=");
929                 if (st2.countTokens() > 1)
930                 {
931                   // This is the chain
932                   tmp2.addElement(st2.nextToken());
933                   seqstring = st2.nextToken();
934                 }
935                 tmp.addElement((Sequence) newAlignFrame.getAlignViewport()
936                         .getAlignment().findName(seqstring));
937               }
938
939               seqs = new SequenceI[tmp.size()];
940               tmp.copyInto(seqs);
941               if (tmp2.size() == tmp.size())
942               {
943                 chains = new String[tmp2.size()];
944                 tmp2.copyInto(chains);
945               }
946             }
947             param = setProtocolState(param);
948
949             if (// !jmolAvailable
950             // &&
951             protocol == AppletFormatAdapter.CLASSLOADER)
952             {
953               // TODO: verify this Re:
954               // https://mantis.lifesci.dundee.ac.uk/view.php?id=36605
955               // This exception preserves the current behaviour where, even if
956               // the local pdb file was identified in the class loader
957               protocol = AppletFormatAdapter.URL; // this is probably NOT
958               // CORRECT!
959               param = addProtocol(param); // 
960             }
961
962             pdb.setFile(param);
963
964             if (seqs != null)
965             {
966               for (int i = 0; i < seqs.length; i++)
967               {
968                 if (seqs[i] != null)
969                 {
970                   ((Sequence) seqs[i]).addPDBId(pdb);
971                 }
972                 else
973                 {
974                   if (JalviewLite.debug)
975                   {
976                     // this may not really be a problem but we give a warning
977                     // anyway
978                     System.err
979                             .println("Warning: Possible input parsing error: Null sequence for attachment of PDB (sequence "
980                                     + i + ")");
981                   }
982                 }
983               }
984
985               if (jmolAvailable)
986               {
987                 new jalview.appletgui.AppletJmol(pdb, seqs, chains,
988                         newAlignFrame.alignPanel, protocol);
989                 lastFrameX += 40;
990                 lastFrameY += 40;
991               }
992               else
993                 new MCview.AppletPDBViewer(pdb, seqs, chains,
994                         newAlignFrame.alignPanel, protocol);
995             }
996           }
997
998           pdbFileCount++;
999         } while (pdbFileCount < 10);
1000
1001         // ///////////////////////////
1002         // modify display of features
1003         //
1004         // hide specific groups
1005         param = getParameter("hidefeaturegroups");
1006         if (param != null)
1007         {
1008           applet.setFeatureGroupStateOn(newAlignFrame, param, false);
1009         }
1010         // show specific groups
1011         param = getParameter("showfeaturegroups");
1012         if (param != null)
1013         {
1014           applet.setFeatureGroupStateOn(newAlignFrame, param, true);
1015         }
1016       }
1017       else
1018       {
1019         fileFound = false;
1020         remove(launcher);
1021         repaint();
1022       }
1023     }
1024
1025     /**
1026      * Discovers whether the given file is in the Applet Archive
1027      * 
1028      * @param file
1029      *          String
1030      * @return boolean
1031      */
1032     boolean inArchive(String file)
1033     {
1034       // This might throw a security exception in certain browsers
1035       // Netscape Communicator for instance.
1036       try
1037       {
1038         boolean rtn = (getClass().getResourceAsStream("/" + file) != null);
1039         if (debug)
1040         {
1041           System.err.println("Resource '" + file + "' was "
1042                   + (rtn ? "" : "not") + " located by classloader.");
1043         }
1044         return rtn;
1045       } catch (Exception ex)
1046       {
1047         System.out.println("Exception checking resources: " + file + " "
1048                 + ex);
1049         return false;
1050       }
1051     }
1052
1053     String addProtocol(String file)
1054     {
1055       if (file.indexOf("://") == -1)
1056       {
1057         file = getCodeBase() + file;
1058         if (debug)
1059         {
1060           System.err.println("Prepended codebase for resource: '" + file
1061                   + "'");
1062         }
1063       }
1064
1065       return file;
1066     }
1067   }
1068
1069   /**
1070    * @return the default alignFrame acted on by the public applet methods. May
1071    *         return null with an error message on System.err indicating the
1072    *         fact.
1073    */
1074   protected AlignFrame getDefaultTargetFrame()
1075   {
1076     if (currentAlignFrame != null)
1077     {
1078       return currentAlignFrame;
1079     }
1080     if (initialAlignFrame != null)
1081     {
1082       return initialAlignFrame;
1083     }
1084     System.err
1085             .println("Implementation error: Jalview Applet API cannot work out which AlignFrame to use.");
1086     return null;
1087   }
1088
1089   /**
1090    * separator used for separatorList
1091    */
1092   protected String separator = "|"; // this is a safe(ish) separator - tabs
1093
1094   // don't work for firefox
1095
1096   /**
1097    * parse the string into a list
1098    * 
1099    * @param list
1100    * @return elements separated by separator
1101    */
1102   public String[] separatorListToArray(String list)
1103   {
1104     int seplen = separator.length();
1105     if (list == null || list.equals(""))
1106       return null;
1107     java.util.Vector jv = new Vector();
1108     int cp = 0, pos;
1109     while ((pos = list.indexOf(separator, cp)) > cp)
1110     {
1111       jv.addElement(list.substring(cp, pos));
1112       cp = pos + seplen;
1113     }
1114     if (cp < list.length())
1115     {
1116       jv.addElement(list.substring(cp));
1117     }
1118     if (jv.size() > 0)
1119     {
1120       String[] v = new String[jv.size()];
1121       for (int i = 0; i < v.length; i++)
1122       {
1123         v[i] = (String) jv.elementAt(i);
1124       }
1125       jv.removeAllElements();
1126       if (debug)
1127       {
1128         System.err.println("Array from '" + separator
1129                 + "' separated List:\n" + v.length);
1130         for (int i = 0; i < v.length; i++)
1131         {
1132           System.err.println("item " + i + " '" + v[i] + "'");
1133         }
1134       }
1135       return v;
1136     }
1137     if (debug)
1138     {
1139       System.err.println("Empty Array from '" + separator
1140               + "' separated List");
1141     }
1142     return null;
1143   }
1144
1145   /**
1146    * concatenate the list with separator
1147    * 
1148    * @param list
1149    * @return concatenated string
1150    */
1151   public String arrayToSeparatorList(String[] list)
1152   {
1153     StringBuffer v = new StringBuffer();
1154     if (list != null)
1155     {
1156       for (int i = 0, iSize = list.length - 1; i < iSize; i++)
1157       {
1158         if (list[i] != null)
1159         {
1160           v.append(list[i]);
1161         }
1162         v.append(separator);
1163       }
1164       if (list[list.length - 1] != null)
1165       {
1166         v.append(list[list.length - 1]);
1167       }
1168       if (debug)
1169       {
1170         System.err.println("Returning '" + separator
1171                 + "' separated List:\n");
1172         System.err.println(v);
1173       }
1174       return v.toString();
1175     }
1176     if (debug)
1177     {
1178       System.err.println("Returning empty '" + separator
1179               + "' separated List\n");
1180     }
1181     return "";
1182   }
1183
1184   /**
1185    * @return
1186    * @see jalview.appletgui.AlignFrame#getFeatureGroups()
1187    */
1188   public String getFeatureGroups()
1189   {
1190     String lst = arrayToSeparatorList(getDefaultTargetFrame()
1191             .getFeatureGroups());
1192     return lst;
1193   }
1194
1195   /**
1196    * @param alf
1197    *          alignframe to get feature groups on
1198    * @return
1199    * @see jalview.appletgui.AlignFrame#getFeatureGroups()
1200    */
1201   public String getFeatureGroupsOn(AlignFrame alf)
1202   {
1203     String lst = arrayToSeparatorList(alf.getFeatureGroups());
1204     return lst;
1205   }
1206
1207   /**
1208    * @param visible
1209    * @return
1210    * @see jalview.appletgui.AlignFrame#getFeatureGroupsOfState(boolean)
1211    */
1212   public String getFeatureGroupsOfState(boolean visible)
1213   {
1214     return arrayToSeparatorList(getDefaultTargetFrame()
1215             .getFeatureGroupsOfState(visible));
1216   }
1217
1218   /**
1219    * @param alf
1220    *          align frame to get groups of state visible
1221    * @param visible
1222    * @return
1223    * @see jalview.appletgui.AlignFrame#getFeatureGroupsOfState(boolean)
1224    */
1225   public String getFeatureGroupsOfStateOn(AlignFrame alf, boolean visible)
1226   {
1227     return arrayToSeparatorList(alf.getFeatureGroupsOfState(visible));
1228   }
1229
1230   /**
1231    * @param groups
1232    *          tab separated list of group names
1233    * @param state
1234    *          true or false
1235    * @see jalview.appletgui.AlignFrame#setFeatureGroupState(java.lang.String[],
1236    *      boolean)
1237    */
1238   public void setFeatureGroupStateOn(AlignFrame alf, String groups,
1239           boolean state)
1240   {
1241     boolean st = state;// !(state==null || state.equals("") ||
1242     // state.toLowerCase().equals("false"));
1243     alf.setFeatureGroupState(separatorListToArray(groups), st);
1244   }
1245
1246   public void setFeatureGroupState(String groups, boolean state)
1247   {
1248     setFeatureGroupStateOn(getDefaultTargetFrame(), groups, state);
1249   }
1250
1251   /**
1252    * List separator string
1253    * 
1254    * @return the separator
1255    */
1256   public String getSeparator()
1257   {
1258     return separator;
1259   }
1260
1261   /**
1262    * List separator string
1263    * 
1264    * @param separator
1265    *          the separator to set
1266    */
1267   public void setSeparator(String separator)
1268   {
1269     this.separator = separator;
1270   }
1271
1272   /**
1273    * get boolean value of applet parameter 'name' and return default if
1274    * parameter is not set
1275    * 
1276    * @param name
1277    *          name of paremeter
1278    * @param def
1279    *          the value to return otherwise
1280    * @return true or false
1281    */
1282   public boolean getDefaultParameter(String name, boolean def)
1283   {
1284     String stn;
1285     if ((stn = getParameter(name)) == null)
1286     {
1287       return def;
1288     }
1289     if (stn.toLowerCase().equals("true"))
1290     {
1291       return true;
1292     }
1293     return false;
1294   }
1295   /**
1296    * bind a pdb file to a sequence in the given alignFrame.  
1297    * @param alFrame - null or specific alignFrame. This specifies the dataset that will be searched for a seuqence called sequenceId
1298    * @param sequenceId - sequenceId within the dataset.
1299    * @param pdbEntryString - the short name for the PDB file
1300    * @param pdbFile - pdb file - either a URL or a valid PDB file.
1301    * @return true if binding was as success
1302    * TODO: consider making an exception structure for indicating when PDB parsing or seqeunceId location fails.
1303    */
1304   public boolean addPdbFile(AlignFrame alFrame, String sequenceId, String pdbEntryString, String pdbFile)
1305   {
1306     System.err.println("addPdbFile not yet implemented.");
1307       return true;
1308   }
1309   /**
1310    * bind the viewer instance to the pdbFile associated with sequences in the given alFrame.
1311    * @param alFrame
1312    * @param pdbFile - pdbFile URI as given via applet's parameters or by addPdb 
1313    * @param viewer
1314    * @return binding for viewer 
1315    * TODO: consider making an exception structure for indicating when binding fails
1316    */
1317   public SequenceStructureBinding addJmolInstance(AlignFrame alFrame, String pdbFile, org.jmol.api.JmolViewer viewer) 
1318   {
1319     System.err.println("addJmolInstance not yet implemented.");
1320     /**
1321      */
1322     return null;
1323   }
1324   /**
1325    * bind structures in a viewer to any matching sequences in an alignFrame (use seuqenceIds to limit scope of search to specific sequences)
1326    * @param alFrame
1327    * @param viewer
1328    * @param sequenceIds
1329    * @return
1330    */
1331   public SequenceStructureBinding addJmolInstance(AlignFrame alFrame, org.jmol.api.JmolViewer viewer, String sequenceIds) 
1332   {
1333     if (viewer!=null)
1334     {
1335       if (sequenceIds!=null && sequenceIds.length()>0)
1336       {
1337         return alFrame.addJmolInstance(viewer, separatorListToArray(sequenceIds));
1338       } else {
1339         return alFrame.addJmolInstance(viewer, null);
1340       }
1341     }
1342     return null;
1343   }
1344 }