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