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