refactored external structure viewer binding API calls so they do not reference Jmol...
[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   public static int lastFrameX = 200;
285
286   public 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               newAlignFrame.newStructureView(applet, pdb, seqs, chains, protocol);
986               
987             }
988           }
989
990           pdbFileCount++;
991         } while (pdbFileCount < 10);
992
993         // ///////////////////////////
994         // modify display of features
995         //
996         // hide specific groups
997         param = getParameter("hidefeaturegroups");
998         if (param != null)
999         {
1000           applet.setFeatureGroupStateOn(newAlignFrame, param, false);
1001         }
1002         // show specific groups
1003         param = getParameter("showfeaturegroups");
1004         if (param != null)
1005         {
1006           applet.setFeatureGroupStateOn(newAlignFrame, param, true);
1007         }
1008       }
1009       else
1010       {
1011         fileFound = false;
1012         remove(launcher);
1013         repaint();
1014       }
1015     }
1016
1017     /**
1018      * Discovers whether the given file is in the Applet Archive
1019      * 
1020      * @param file
1021      *          String
1022      * @return boolean
1023      */
1024     boolean inArchive(String file)
1025     {
1026       // This might throw a security exception in certain browsers
1027       // Netscape Communicator for instance.
1028       try
1029       {
1030         boolean rtn = (getClass().getResourceAsStream("/" + file) != null);
1031         if (debug)
1032         {
1033           System.err.println("Resource '" + file + "' was "
1034                   + (rtn ? "" : "not") + " located by classloader.");
1035         }
1036         return rtn;
1037       } catch (Exception ex)
1038       {
1039         System.out.println("Exception checking resources: " + file + " "
1040                 + ex);
1041         return false;
1042       }
1043     }
1044
1045     String addProtocol(String file)
1046     {
1047       if (file.indexOf("://") == -1)
1048       {
1049         file = getCodeBase() + file;
1050         if (debug)
1051         {
1052           System.err.println("Prepended codebase for resource: '" + file
1053                   + "'");
1054         }
1055       }
1056
1057       return file;
1058     }
1059   }
1060
1061   /**
1062    * @return the default alignFrame acted on by the public applet methods. May
1063    *         return null with an error message on System.err indicating the
1064    *         fact.
1065    */
1066   protected AlignFrame getDefaultTargetFrame()
1067   {
1068     if (currentAlignFrame != null)
1069     {
1070       return currentAlignFrame;
1071     }
1072     if (initialAlignFrame != null)
1073     {
1074       return initialAlignFrame;
1075     }
1076     System.err
1077             .println("Implementation error: Jalview Applet API cannot work out which AlignFrame to use.");
1078     return null;
1079   }
1080
1081   /**
1082    * separator used for separatorList
1083    */
1084   protected String separator = "|"; // this is a safe(ish) separator - tabs
1085
1086   // don't work for firefox
1087
1088   /**
1089    * parse the string into a list
1090    * 
1091    * @param list
1092    * @return elements separated by separator
1093    */
1094   public String[] separatorListToArray(String list)
1095   {
1096     int seplen = separator.length();
1097     if (list == null || list.equals(""))
1098       return null;
1099     java.util.Vector jv = new Vector();
1100     int cp = 0, pos;
1101     while ((pos = list.indexOf(separator, cp)) > cp)
1102     {
1103       jv.addElement(list.substring(cp, pos));
1104       cp = pos + seplen;
1105     }
1106     if (cp < list.length())
1107     {
1108       jv.addElement(list.substring(cp));
1109     }
1110     if (jv.size() > 0)
1111     {
1112       String[] v = new String[jv.size()];
1113       for (int i = 0; i < v.length; i++)
1114       {
1115         v[i] = (String) jv.elementAt(i);
1116       }
1117       jv.removeAllElements();
1118       if (debug)
1119       {
1120         System.err.println("Array from '" + separator
1121                 + "' separated List:\n" + v.length);
1122         for (int i = 0; i < v.length; i++)
1123         {
1124           System.err.println("item " + i + " '" + v[i] + "'");
1125         }
1126       }
1127       return v;
1128     }
1129     if (debug)
1130     {
1131       System.err.println("Empty Array from '" + separator
1132               + "' separated List");
1133     }
1134     return null;
1135   }
1136
1137   /**
1138    * concatenate the list with separator
1139    * 
1140    * @param list
1141    * @return concatenated string
1142    */
1143   public String arrayToSeparatorList(String[] list)
1144   {
1145     StringBuffer v = new StringBuffer();
1146     if (list != null)
1147     {
1148       for (int i = 0, iSize = list.length - 1; i < iSize; i++)
1149       {
1150         if (list[i] != null)
1151         {
1152           v.append(list[i]);
1153         }
1154         v.append(separator);
1155       }
1156       if (list[list.length - 1] != null)
1157       {
1158         v.append(list[list.length - 1]);
1159       }
1160       if (debug)
1161       {
1162         System.err.println("Returning '" + separator
1163                 + "' separated List:\n");
1164         System.err.println(v);
1165       }
1166       return v.toString();
1167     }
1168     if (debug)
1169     {
1170       System.err.println("Returning empty '" + separator
1171               + "' separated List\n");
1172     }
1173     return "";
1174   }
1175
1176   /**
1177    * @return
1178    * @see jalview.appletgui.AlignFrame#getFeatureGroups()
1179    */
1180   public String getFeatureGroups()
1181   {
1182     String lst = arrayToSeparatorList(getDefaultTargetFrame()
1183             .getFeatureGroups());
1184     return lst;
1185   }
1186
1187   /**
1188    * @param alf
1189    *          alignframe to get feature groups on
1190    * @return
1191    * @see jalview.appletgui.AlignFrame#getFeatureGroups()
1192    */
1193   public String getFeatureGroupsOn(AlignFrame alf)
1194   {
1195     String lst = arrayToSeparatorList(alf.getFeatureGroups());
1196     return lst;
1197   }
1198
1199   /**
1200    * @param visible
1201    * @return
1202    * @see jalview.appletgui.AlignFrame#getFeatureGroupsOfState(boolean)
1203    */
1204   public String getFeatureGroupsOfState(boolean visible)
1205   {
1206     return arrayToSeparatorList(getDefaultTargetFrame()
1207             .getFeatureGroupsOfState(visible));
1208   }
1209
1210   /**
1211    * @param alf
1212    *          align frame to get groups of state visible
1213    * @param visible
1214    * @return
1215    * @see jalview.appletgui.AlignFrame#getFeatureGroupsOfState(boolean)
1216    */
1217   public String getFeatureGroupsOfStateOn(AlignFrame alf, boolean visible)
1218   {
1219     return arrayToSeparatorList(alf.getFeatureGroupsOfState(visible));
1220   }
1221
1222   /**
1223    * @param groups
1224    *          tab separated list of group names
1225    * @param state
1226    *          true or false
1227    * @see jalview.appletgui.AlignFrame#setFeatureGroupState(java.lang.String[],
1228    *      boolean)
1229    */
1230   public void setFeatureGroupStateOn(AlignFrame alf, String groups,
1231           boolean state)
1232   {
1233     boolean st = state;// !(state==null || state.equals("") ||
1234     // state.toLowerCase().equals("false"));
1235     alf.setFeatureGroupState(separatorListToArray(groups), st);
1236   }
1237
1238   public void setFeatureGroupState(String groups, boolean state)
1239   {
1240     setFeatureGroupStateOn(getDefaultTargetFrame(), groups, state);
1241   }
1242
1243   /**
1244    * List separator string
1245    * 
1246    * @return the separator
1247    */
1248   public String getSeparator()
1249   {
1250     return separator;
1251   }
1252
1253   /**
1254    * List separator string
1255    * 
1256    * @param separator
1257    *          the separator to set
1258    */
1259   public void setSeparator(String separator)
1260   {
1261     this.separator = separator;
1262   }
1263
1264   /**
1265    * get boolean value of applet parameter 'name' and return default if
1266    * parameter is not set
1267    * 
1268    * @param name
1269    *          name of paremeter
1270    * @param def
1271    *          the value to return otherwise
1272    * @return true or false
1273    */
1274   public boolean getDefaultParameter(String name, boolean def)
1275   {
1276     String stn;
1277     if ((stn = getParameter(name)) == null)
1278     {
1279       return def;
1280     }
1281     if (stn.toLowerCase().equals("true"))
1282     {
1283       return true;
1284     }
1285     return false;
1286   }
1287   /**
1288    * bind a pdb file to a sequence in the given alignFrame.  
1289    * @param alFrame - null or specific alignFrame. This specifies the dataset that will be searched for a seuqence called sequenceId
1290    * @param sequenceId - sequenceId within the dataset.
1291    * @param pdbEntryString - the short name for the PDB file
1292    * @param pdbFile - pdb file - either a URL or a valid PDB file.
1293    * @return true if binding was as success
1294    * TODO: consider making an exception structure for indicating when PDB parsing or seqeunceId location fails.
1295    */
1296   public boolean addPdbFile(AlignFrame alFrame, String sequenceId, String pdbEntryString, String pdbFile)
1297   {
1298     return alFrame.addPdbFile(sequenceId, pdbEntryString, pdbFile);
1299   }
1300   /**
1301    * bind structures in a viewer to any matching sequences in an alignFrame (use sequenceIds to limit scope of search to specific sequences)
1302    * @param alFrame
1303    * @param viewer
1304    * @param sequenceIds
1305    * @return
1306    * TODO: consider making an exception structure for indicating when binding fails
1307    */
1308   public SequenceStructureBinding addStructureViewInstance(AlignFrame alFrame, Object viewer, String sequenceIds) 
1309   {
1310     if (viewer!=null)
1311     {
1312       if (sequenceIds!=null && sequenceIds.length()>0)
1313       {
1314         return alFrame.addStructureViewInstance(viewer, separatorListToArray(sequenceIds));
1315       } else {
1316         return alFrame.addStructureViewInstance(viewer, null);
1317       }
1318     }
1319     return null;
1320   }
1321 }