921ccdfecaabfe00aac6689ee2038f5a618e7770
[jalview.git] / src / jalview / bin / JalviewAppLoader.java
1 package jalview.bin;
2
3 import jalview.api.AlignFrameI;
4 import jalview.api.JalviewApp;
5 import jalview.api.StructureSelectionManagerProvider;
6 import jalview.datamodel.Alignment;
7 import jalview.datamodel.AlignmentI;
8 import jalview.datamodel.AlignmentOrder;
9 import jalview.datamodel.ColumnSelection;
10 import jalview.datamodel.PDBEntry;
11 import jalview.datamodel.Sequence;
12 import jalview.datamodel.SequenceGroup;
13 import jalview.datamodel.SequenceI;
14 import jalview.gui.AlignFrame;
15 import jalview.gui.AlignViewport;
16 import jalview.gui.Desktop;
17 import jalview.io.AnnotationFile;
18 import jalview.io.AppletFormatAdapter;
19 import jalview.io.DataSourceType;
20 import jalview.io.FileFormat;
21 import jalview.io.FileFormatI;
22 import jalview.io.FileFormats;
23 import jalview.io.IdentifyFile;
24 import jalview.io.JPredFile;
25 import jalview.io.JnetAnnotationMaker;
26 import jalview.io.NewickFile;
27 import jalview.structure.StructureSelectionManager;
28 import jalview.util.HttpUtils;
29 import jalview.util.MessageManager;
30
31 import java.awt.EventQueue;
32 import java.io.IOException;
33 import java.net.URL;
34 import java.util.ArrayList;
35 import java.util.List;
36 import java.util.StringTokenizer;
37 import java.util.Vector;
38
39 /**
40  * A class to load parameters for either JalviewLite or Jalview
41  * 
42  * @author hansonr
43  *
44  */
45 public class JalviewAppLoader
46 {
47
48   private JalviewApp app; // Jalview or JalviewJS or JalviewLite
49
50   private boolean debug;
51
52   String separator = "\u00AC"; // JalviewLite note: the default used to
53                                        // be '|', but many sequence IDS include
54                                        // pipes.
55
56   public String getSeparator()
57   {
58     return separator;
59   }
60
61   public void setSeparator(String separator)
62   {
63     this.separator = separator;
64   }
65
66   public JalviewAppLoader(boolean debug)
67   {
68     this.debug = debug;
69   }
70
71   public void load(JalviewApp app)
72   {
73
74     this.app = app;
75
76     String sep = app.getParameter("separator");
77     if (sep != null)
78     {
79       if (sep.length() > 0)
80       {
81         separator = sep;
82       }
83       else
84       {
85         throw new Error(MessageManager
86                 .getString("error.invalid_separator_parameter"));
87       }
88     }
89
90     loadTree();
91     loadScoreFile();
92     loadFeatures();
93     loadAnnotations();
94     loadJnetFile();
95     loadPdbFiles();
96     callInitCallback();
97   }
98
99   /**
100    * Load PDBFiles if any specified by parameter(s). Returns true if loaded,
101    * else false.
102    * 
103    * @param loaderFrame
104    * @return
105    */
106   protected boolean loadPdbFiles()
107   {
108     boolean result = false;
109     /*
110      * <param name="alignpdbfiles" value="false/true"/> Undocumented for 2.6 -
111      * related to JAL-434
112      */
113
114     boolean doAlign = app.getDefaultParameter("alignpdbfiles", false);
115     app.setAlignPdbStructures(doAlign);
116     /*
117      * <param name="PDBfile" value="1gaq.txt PDB|1GAQ|1GAQ|A PDB|1GAQ|1GAQ|B
118      * PDB|1GAQ|1GAQ|C">
119      * 
120      * <param name="PDBfile2" value="1gaq.txt A=SEQA B=SEQB C=SEQB">
121      * 
122      * <param name="PDBfile3" value="1q0o Q45135_9MICO">
123      */
124
125     // Accumulate pdbs here if they are heading for the same view (if
126     // alignPdbStructures is true)
127     Vector<Object[]> pdbs = new Vector<>();
128     // create a lazy matcher if we're asked to
129     jalview.analysis.SequenceIdMatcher matcher = (app
130             .getDefaultParameter("relaxedidmatch", false))
131                     ? new jalview.analysis.SequenceIdMatcher(
132                             app.getViewport().getAlignment()
133                                     .getSequencesArray())
134                     : null;
135
136     int pdbFileCount = 0;
137     String param;
138     do
139     {
140       if (pdbFileCount > 0)
141       {
142         param = app.getParameter("PDBFILE" + pdbFileCount);
143       }
144       else
145       {
146         param = app.getParameter("PDBFILE");
147       }
148
149       if (param != null)
150       {
151         PDBEntry pdb = new PDBEntry();
152
153         String seqstring;
154         SequenceI[] seqs = null;
155         String[] chains = null;
156
157         StringTokenizer st = new StringTokenizer(param, " ");
158
159         if (st.countTokens() < 2)
160         {
161           String sequence = app.getParameter("PDBSEQ");
162           if (sequence != null)
163           {
164             seqs = new SequenceI[] { matcher == null
165                     ? (Sequence) app.getViewport().getAlignment()
166                             .findName(sequence)
167                     : matcher.findIdMatch(sequence) };
168           }
169
170         }
171         else
172         {
173           param = st.nextToken();
174           List<SequenceI> tmp = new ArrayList<>();
175           List<String> tmp2 = new ArrayList<>();
176
177           while (st.hasMoreTokens())
178           {
179             seqstring = st.nextToken();
180             StringTokenizer st2 = new StringTokenizer(seqstring, "=");
181             if (st2.countTokens() > 1)
182             {
183               // This is the chain
184               tmp2.add(st2.nextToken());
185               seqstring = st2.nextToken();
186             }
187             tmp.add(matcher == null
188                     ? (Sequence) app.getViewport().getAlignment()
189                             .findName(seqstring)
190                     : matcher.findIdMatch(seqstring));
191           }
192
193           seqs = tmp.toArray(new SequenceI[tmp.size()]);
194           if (tmp2.size() == tmp.size())
195           {
196             chains = tmp2.toArray(new String[tmp2.size()]);
197           }
198         }
199         pdb.setId(param);
200         ret[0] = param;
201         DataSourceType protocol = resolveFileProtocol(app, ret);
202         // TODO check JAL-357 for files in a jar (CLASSLOADER)
203         pdb.setFile(ret[0]);
204
205         if (seqs != null)
206         {
207           for (int i = 0; i < seqs.length; i++)
208           {
209             if (seqs[i] != null)
210             {
211               ((Sequence) seqs[i]).addPDBId(pdb);
212               StructureSelectionManager
213                       .getStructureSelectionManager(
214                               (StructureSelectionManagerProvider) app)
215                       .registerPDBEntry(pdb);
216             }
217             else
218             {
219               if (debug)
220               {
221                 // this may not really be a problem but we give a warning
222                 // anyway
223                 System.err.println(
224                         "Warning: Possible input parsing error: Null sequence for attachment of PDB (sequence "
225                                 + i + ")");
226               }
227             }
228           }
229
230           if (doAlign)
231           {
232             pdbs.addElement(new Object[] { pdb, seqs, chains, protocol });
233           }
234           else
235           {
236             app.newStructureView(pdb, seqs, chains, protocol);
237           }
238         }
239       }
240
241       pdbFileCount++;
242     } while (param != null || pdbFileCount < 10);
243     if (pdbs.size() > 0)
244     {
245       SequenceI[][] seqs = new SequenceI[pdbs.size()][];
246       PDBEntry[] pdb = new PDBEntry[pdbs.size()];
247       String[][] chains = new String[pdbs.size()][];
248       String[] protocols = new String[pdbs.size()];
249       for (int pdbsi = 0, pdbsiSize = pdbs
250               .size(); pdbsi < pdbsiSize; pdbsi++)
251       {
252         Object[] o = pdbs.elementAt(pdbsi);
253         pdb[pdbsi] = (PDBEntry) o[0];
254         seqs[pdbsi] = (SequenceI[]) o[1];
255         chains[pdbsi] = (String[]) o[2];
256         protocols[pdbsi] = (String) o[3];
257       }
258       app.alignedStructureView(pdb, seqs, chains, protocols);
259       result = true;
260     }
261     return result;
262   }
263
264   /**
265    * Load in a Jnetfile if specified by parameter. Returns true if loaded, else
266    * false.
267    * 
268    * @param alignFrame
269    * @return
270    */
271   protected boolean loadJnetFile()
272   {
273     boolean result = false;
274     String param = app.getParameter("jnetfile");
275     if (param == null)
276     {
277       // jnet became jpred around 2016
278       param = app.getParameter("jpredfile");
279     }
280     if (param != null)
281     {
282       try
283       {
284         ret[0] = param;
285         DataSourceType protocol = resolveFileProtocol(app, ret);
286         JPredFile predictions = new JPredFile(ret[0], protocol);
287         JnetAnnotationMaker.add_annotation(predictions,
288                 app.getViewport().getAlignment(), 0, false);
289         // false == do not add sequence profile from concise output
290         app.getViewport().getAlignment().setupJPredAlignment();
291         app.updateForAnnotations();
292         result = true;
293       } catch (Exception ex)
294       {
295         ex.printStackTrace();
296       }
297     }
298     return result;
299   }
300
301   /**
302    * Load annotations if specified by parameter. Returns true if loaded, else
303    * false.
304    * 
305    * @param alignFrame
306    * @return
307    */
308   protected boolean loadAnnotations()
309   {
310     boolean result = false;
311     String param = app.getParameter("annotations");
312     if (param != null)
313     {
314       ret[0] = param;
315       DataSourceType protocol = resolveFileProtocol(app, ret);
316       param = ret[0];
317       if (new AnnotationFile().annotateAlignmentView(app.getViewport(),
318               param, protocol))
319       {
320         app.updateForAnnotations();
321         result = true;
322       }
323       else
324       {
325         System.err
326                 .println("Annotations were not added from annotation file '"
327                         + param + "'");
328       }
329     }
330     return result;
331   }
332
333   /**
334    * Load features file and view settings as specified by parameters. Returns
335    * true if features were loaded, else false.
336    * 
337    * @param alignFrame
338    * @return
339    */
340   protected boolean loadFeatures()
341   {
342     boolean result = false;
343     // ///////////////////////////
344     // modify display of features
345     // we do this before any features have been loaded, ensuring any hidden
346     // groups are hidden when features first displayed
347     //
348     // hide specific groups
349     //
350     String param = app.getParameter("hidefeaturegroups");
351     if (param != null)
352     {
353       app.setFeatureGroupState(separatorListToArray(param, separator),
354               false);
355       // app.setFeatureGroupStateOn(newAlignFrame, param, false);
356     }
357     // show specific groups
358     param = app.getParameter("showfeaturegroups");
359     if (param != null)
360     {
361       app.setFeatureGroupState(separatorListToArray(param, separator),
362               true);
363       // app.setFeatureGroupStateOn(newAlignFrame, param, true);
364     }
365     // and now load features
366     param = app.getParameter("features");
367     if (param != null)
368     {
369       ret[0] = param;
370       DataSourceType protocol = resolveFileProtocol(app, ret);
371
372       result = app.parseFeaturesFile(ret[0], protocol);
373     }
374
375     param = app.getParameter("showFeatureSettings");
376     if (param != null && param.equalsIgnoreCase("true"))
377     {
378       app.newFeatureSettings();
379     }
380     return result;
381   }
382
383   /**
384    * Load a score file if specified by parameter. Returns true if file was
385    * loaded, else false.
386    * 
387    * @param loaderFrame
388    */
389   protected boolean loadScoreFile()
390   {
391     boolean result = false;
392     String sScoreFile = app.getParameter("scoreFile");
393     if (sScoreFile != null && !"".equals(sScoreFile))
394     {
395       try
396       {
397         if (debug)
398         {
399           System.err.println(
400                   "Attempting to load T-COFFEE score file from the scoreFile parameter");
401         }
402         result = app.loadScoreFile(sScoreFile);
403         if (!result)
404         {
405           System.err.println(
406                   "Failed to parse T-COFFEE parameter as a valid score file ('"
407                           + sScoreFile + "')");
408         }
409       } catch (Exception e)
410       {
411         System.err.printf("Cannot read score file: '%s'. Cause: %s \n",
412                 sScoreFile, e.getMessage());
413       }
414     }
415     return result;
416   }
417
418   String[] ret = new String[1];
419
420   /**
421    * Load a tree for the alignment if specified by parameter. Returns true if a
422    * tree was loaded, else false.
423    * 
424    * @param loaderFrame
425    * @return
426    */
427   protected boolean loadTree()
428   {
429     boolean result = false;
430     String treeFile = app.getParameter("tree");
431     if (treeFile == null)
432     {
433       treeFile = app.getParameter("treeFile");
434     }
435
436     if (treeFile != null)
437     {
438       try
439       {
440         ret[0] = treeFile;
441         NewickFile fin = new NewickFile(treeFile,
442                 resolveFileProtocol(app, ret));
443         fin.parse();
444
445         if (fin.getTree() != null)
446         {
447           app.loadTree(fin, ret[0]);
448           result = true;
449           if (debug)
450           {
451             System.out.println("Successfully imported tree.");
452           }
453         }
454         else
455         {
456           if (debug)
457           {
458             System.out.println(
459                     "Tree parameter did not resolve to a valid tree.");
460           }
461         }
462       } catch (Exception ex)
463       {
464         ex.printStackTrace();
465       }
466     }
467     return result;
468   }
469
470   /**
471    * form a complete URL given a path to a resource and a reference location on
472    * the same server
473    * 
474    * @param targetPath
475    *          - an absolute path on the same server as localref or a document
476    *          located relative to localref
477    * @param localref
478    *          - a URL on the same server as url
479    * @return a complete URL for the resource located by url
480    */
481   public static String resolveUrlForLocalOrAbsolute(String targetPath,
482           URL localref)
483   {
484     String resolvedPath = "";
485     if (targetPath.startsWith("/"))
486     {
487       String codebase = localref.toString();
488       String localfile = localref.getFile();
489       resolvedPath = codebase.substring(0,
490               codebase.length() - localfile.length()) + targetPath;
491       return resolvedPath;
492     }
493
494     /*
495      * get URL path and strip off any trailing file e.g.
496      * www.jalview.org/examples/index.html#applets?a=b is trimmed to
497      * www.jalview.org/examples/
498      */
499     String urlPath = localref.toString();
500     String directoryPath = urlPath;
501     int lastSeparator = directoryPath.lastIndexOf("/");
502     if (lastSeparator > 0)
503     {
504       directoryPath = directoryPath.substring(0, lastSeparator + 1);
505     }
506
507     if (targetPath.startsWith("/"))
508     {
509       /*
510        * construct absolute URL to a file on the server - this is not allowed?
511        */
512       // String localfile = localref.getFile();
513       // resolvedPath = urlPath.substring(0,
514       // urlPath.length() - localfile.length())
515       // + targetPath;
516       resolvedPath = directoryPath + targetPath.substring(1);
517     }
518     else
519     {
520       resolvedPath = directoryPath + targetPath;
521     }
522     // if (debug)
523     // {
524     // System.err.println(
525     // "resolveUrlForLocalOrAbsolute returning " + resolvedPath);
526     // }
527     return resolvedPath;
528   }
529
530   /**
531    * parse the string into a list
532    * 
533    * @param list
534    * @param separator
535    * @return elements separated by separator
536    */
537   public static String[] separatorListToArray(String list, String separator)
538   {
539     // TODO use StringUtils version (slightly different...)
540     int seplen = separator.length();
541     if (list == null || list.equals("") || list.equals(separator))
542     {
543       return null;
544     }
545     Vector<String> jv = new Vector<>();
546     int cp = 0, pos;
547     while ((pos = list.indexOf(separator, cp)) > cp)
548     {
549       jv.addElement(list.substring(cp, pos));
550       cp = pos + seplen;
551     }
552     if (cp < list.length())
553     {
554       String c = list.substring(cp);
555       if (!c.equals(separator))
556       {
557         jv.addElement(c);
558       }
559     }
560     if (jv.size() > 0)
561     {
562       String[] v = new String[jv.size()];
563       for (int i = 0; i < v.length; i++)
564       {
565         v[i] = jv.elementAt(i);
566       }
567       jv.removeAllElements();
568       // if (debug)
569       // {
570       // System.err.println("Array from '" + separator
571       // + "' separated List:\n" + v.length);
572       // for (int i = 0; i < v.length; i++)
573       // {
574       // System.err.println("item " + i + " '" + v[i] + "'");
575       // }
576       // }
577       return v;
578     }
579     // if (debug)
580     // {
581     // System.err.println(
582     // "Empty Array from '" + separator + "' separated List");
583     // }
584     return null;
585   }
586
587   public static DataSourceType resolveFileProtocol(JalviewApp app,
588           String[] retPath)
589   {
590     String path = retPath[0];
591     /*
592      * is it paste data?
593      */
594     if (path.startsWith("PASTE"))
595     {
596       retPath[0] = path.substring(5);
597       return DataSourceType.PASTE;
598     }
599
600     /*
601      * is it a URL?
602      */
603     if (path.indexOf("://") >= 0)
604     {
605       return DataSourceType.URL;
606     }
607
608     /*
609      * try relative to document root
610      */
611     URL documentBase = app.getDocumentBase();
612     String withDocBase = resolveUrlForLocalOrAbsolute(path, documentBase);
613     if (HttpUtils.isValidUrl(withDocBase))
614     {
615       // if (debug)
616       // {
617       // System.err.println("Prepended document base '" + documentBase
618       // + "' to make: '" + withDocBase + "'");
619       // }
620       retPath[0] = withDocBase;
621       return DataSourceType.URL;
622     }
623
624     /*
625      * try relative to codebase (if different to document base)
626      */
627     URL codeBase = app.getCodeBase();
628     String withCodeBase = resolveUrlForLocalOrAbsolute(path, codeBase);
629     if (!withCodeBase.equals(withDocBase)
630             && HttpUtils.isValidUrl(withCodeBase))
631     {
632       // if (debug)
633       // {
634       // System.err.println("Prepended codebase '" + codeBase
635       // + "' to make: '" + withCodeBase + "'");
636       // }
637       retPath[0] = withCodeBase;
638       return DataSourceType.URL;
639     }
640
641     /*
642      * try locating by classloader; try this last so files in the directory
643      * are resolved using document base
644      */
645     if (inArchive(app.getClass(), path))
646     {
647       return DataSourceType.CLASSLOADER;
648     }
649     return null;
650   }
651
652   /**
653    * Discovers whether the given file is in the Applet Archive
654    * 
655    * @param f
656    *          String
657    * @return boolean
658    */
659   private static boolean inArchive(Class<?> c, String f)
660   {
661     // This might throw a security exception in certain browsers
662     // Netscape Communicator for instance.
663     try
664     {
665       boolean rtn = (c.getResourceAsStream("/" + f) != null);
666       // if (debug)
667       // {
668       // System.err.println("Resource '" + f + "' was "
669       // + (rtn ? "" : "not ") + "located by classloader.");
670       // }
671       return rtn;
672     } catch (Exception ex)
673     {
674       System.out.println("Exception checking resources: " + f + " " + ex);
675       return false;
676     }
677   }
678
679   public void callInitCallback()
680   {
681     String initjscallback = app.getParameter("oninit");
682     if (initjscallback == null)
683     {
684       return;
685     }
686     initjscallback = initjscallback.trim();
687     if (initjscallback.length() > 0)
688     {
689       // TODO
690     }
691   }
692
693   /**
694    * read sequence1...sequenceN as a raw alignment
695    * 
696    * @param jalviewApp
697    * @return
698    */
699   public String getPastedSequence(JalviewApp jalviewApp)
700   {
701     StringBuffer data = new StringBuffer("PASTE");
702     int i = 1;
703     String file = null;
704     while ((file = app.getParameter("sequence" + i)) != null)
705     {
706       data.append(file.toString() + "\n");
707       i++;
708     }
709     if (data.length() > 5)
710     {
711       file = data.toString();
712     }
713     return file;
714   }
715
716   /**
717    * concatenate the list with separator
718    * 
719    * @param list
720    * @param separator
721    * @return concatenated string
722    */
723   public static String arrayToSeparatorList(String[] list, String separator)
724   {
725     // TODO use StringUtils version
726     StringBuffer v = new StringBuffer();
727     if (list != null && list.length > 0)
728     {
729       for (int i = 0, iSize = list.length; i < iSize; i++)
730       {
731         if (list[i] != null)
732         {
733           if (i > 0)
734           {
735             v.append(separator);
736           }
737           v.append(list[i]);
738         }
739       }
740       // if (debug)
741       // {
742       // System.err
743       // .println("Returning '" + separator + "' separated List:\n");
744       // System.err.println(v);
745       // }
746       return v.toString();
747     }
748     // if (debug)
749     // {
750     // System.err.println(
751     // "Returning empty '" + separator + "' separated List\n");
752     // }
753     return "" + separator;
754   }
755
756   public String arrayToSeparatorList(String[] array)
757   {
758     return arrayToSeparatorList(array, separator);
759   }
760
761   public String getSelectedSequencesFrom(AlignFrameI alf, String sep)
762   {
763     StringBuffer result = new StringBuffer("");
764     if (sep == null || sep.length() == 0)
765     {
766       sep = separator; // "+0x00AC;
767     }
768     AlignViewport v = ((AlignFrame) alf).getViewport();
769     if (v.getSelectionGroup() != null)
770     {
771       SequenceI[] seqs = v.getSelectionGroup()
772               .getSequencesInOrder(v.getAlignment());
773
774       for (int i = 0; i < seqs.length; i++)
775       {
776         result.append(seqs[i].getName());
777         result.append(sep);
778       }
779     }
780
781     return result.toString();
782   }
783
784   public void setFeatureGroupStateOn(final AlignFrameI alf,
785           final String groups, boolean state)
786   {
787     java.awt.EventQueue.invokeLater(new Runnable()
788     {
789       @Override
790       public void run()
791       {
792         ((AlignFrame) alf).setFeatureGroupState(
793                 separatorListToArray(groups, separator), state);
794       }
795     });
796   }
797
798   public String getFeatureGroupsOfStateOn(AlignFrameI alf, boolean visible)
799   {
800     return arrayToSeparatorList(
801             ((AlignFrame) alf).getFeatureGroupsOfState(visible));
802   }
803
804   public void scrollViewToIn(final AlignFrameI alf, final String topRow,
805           final String leftHandColumn)
806   {
807     java.awt.EventQueue.invokeLater(new Runnable()
808     {
809       @Override
810       public void run()
811       {
812         try
813         {
814           ((AlignFrame) alf).scrollTo(new Integer(topRow).intValue(),
815                   new Integer(leftHandColumn).intValue());
816
817         } catch (Exception ex)
818         {
819           System.err.println("Couldn't parse integer arguments (topRow='"
820                   + topRow + "' and leftHandColumn='" + leftHandColumn
821                   + "')");
822           ex.printStackTrace();
823         }
824       }
825     });
826   }
827
828   public void scrollViewToRowIn(final AlignFrameI alf, final String topRow)
829   {
830
831     java.awt.EventQueue.invokeLater(new Runnable()
832     {
833       @Override
834       public void run()
835       {
836         try
837         {
838           ((AlignFrame) alf).scrollToRow(new Integer(topRow).intValue());
839
840         } catch (Exception ex)
841         {
842           System.err.println("Couldn't parse integer arguments (topRow='"
843                   + topRow + "')");
844           ex.printStackTrace();
845         }
846
847       }
848     });
849   }
850
851   public void scrollViewToColumnIn(final AlignFrameI alf,
852           final String leftHandColumn)
853   {
854     java.awt.EventQueue.invokeLater(new Runnable()
855     {
856
857       @Override
858       public void run()
859       {
860         try
861         {
862           ((AlignFrame) alf)
863                   .scrollToColumn(new Integer(leftHandColumn).intValue());
864
865         } catch (Exception ex)
866         {
867           System.err.println(
868                   "Couldn't parse integer arguments (leftHandColumn='"
869                           + leftHandColumn + "')");
870           ex.printStackTrace();
871         }
872       }
873     });
874
875   }
876
877   public boolean addPdbFile(AlignFrameI alf, String sequenceId,
878           String pdbEntryString, String pdbFile)
879   {
880     AlignFrame alFrame = (AlignFrame) alf;
881     SequenceI toaddpdb = alFrame.getViewport().getAlignment()
882             .findName(sequenceId);
883     boolean needtoadd = false;
884     if (toaddpdb != null)
885     {
886       Vector<PDBEntry> pdbe = toaddpdb.getAllPDBEntries();
887       PDBEntry pdbentry = null;
888       if (pdbe != null && pdbe.size() > 0)
889       {
890         for (int pe = 0, peSize = pdbe.size(); pe < peSize; pe++)
891         {
892           pdbentry = pdbe.elementAt(pe);
893           if (!pdbentry.getId().equals(pdbEntryString)
894                   && !pdbentry.getFile().equals(pdbFile))
895           {
896             pdbentry = null;
897           }
898           else
899           {
900             continue;
901           }
902         }
903       }
904       if (pdbentry == null)
905       {
906         pdbentry = new PDBEntry();
907         pdbentry.setId(pdbEntryString);
908         pdbentry.setFile(pdbFile);
909         needtoadd = true; // add this new entry to sequence.
910       }
911       // resolve data source
912       // TODO: this code should be a refactored to an io package
913       DataSourceType protocol = AppletFormatAdapter.resolveProtocol(pdbFile,
914               FileFormat.PDB);
915       if (protocol == null)
916       {
917         return false;
918       }
919       if (needtoadd)
920       {
921         pdbentry.setProperty("protocol", protocol);
922         toaddpdb.addPDBId(pdbentry);
923         alFrame.alignPanel.getStructureSelectionManager()
924                 .registerPDBEntry(pdbentry);
925       }
926     }
927     return true;
928   }
929
930   public AlignFrameI loadAlignment(String text, int width, int height,
931           String title)
932   {
933     AlignmentI al = null;
934
935     try
936     {
937       FileFormatI format = new IdentifyFile().identify(text,
938               DataSourceType.PASTE);
939       al = new AppletFormatAdapter().readFile(text, DataSourceType.PASTE,
940               format);
941       if (al.getHeight() > 0)
942       {
943         return new AlignFrame(al, width, height, title);
944       }
945     } catch (IOException ex)
946     {
947       ex.printStackTrace();
948     }
949     return null;
950   }
951
952   public String getFeatureGroupsOn(AlignFrameI alf)
953   {
954     return arrayToSeparatorList(
955             ((AlignFrame) alf).getFeatureGroups());
956   }
957
958   public void highlightIn(final AlignFrameI alf, final String sequenceId,
959           final String position, final String alignedPosition)
960   {
961     // TODO: could try to highlight in all alignments if alf==null
962     jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher(
963             ((AlignFrame) alf).getViewport().getAlignment()
964                     .getSequencesArray());
965     final SequenceI sq = matcher.findIdMatch(sequenceId);
966     if (sq != null)
967     {
968       int apos = -1;
969       try
970       {
971         apos = new Integer(position).intValue();
972         apos--;
973       } catch (NumberFormatException ex)
974       {
975         return;
976       }
977       final int pos = apos;
978       // use vamsas listener to broadcast to all listeners in scope
979       if (alignedPosition != null && (alignedPosition.trim().length() == 0
980               || alignedPosition.toLowerCase().indexOf("false") > -1))
981       {
982         java.awt.EventQueue.invokeLater(new Runnable()
983         {
984           @Override
985           public void run()
986           {
987             StructureSelectionManager
988                     .getStructureSelectionManager(Desktop.getInstance())
989                     .mouseOverVamsasSequence(sq, sq.findIndex(pos), null);
990           }
991         });
992       }
993       else
994       {
995         java.awt.EventQueue.invokeLater(new Runnable()
996         {
997           @Override
998           public void run()
999           {
1000             StructureSelectionManager
1001                     .getStructureSelectionManager(Desktop.getInstance())
1002                     .mouseOverVamsasSequence(sq, pos, null);
1003           }
1004         });
1005       }
1006     }
1007   }
1008
1009   public void selectIn(final AlignFrameI alf, String sequenceIds,
1010           String columns, String sep)
1011   {
1012     if (sep == null || sep.length() == 0)
1013     {
1014       sep = separator;
1015     }
1016     else
1017     {
1018       if (debug)
1019       {
1020         System.err.println("Selecting region using separator string '"
1021                 + separator + "'");
1022       }
1023     }
1024     // deparse fields
1025     String[] ids = JalviewAppLoader.separatorListToArray(sequenceIds, sep);
1026     String[] cols = JalviewAppLoader.separatorListToArray(columns, sep);
1027     final SequenceGroup sel = new SequenceGroup();
1028     final ColumnSelection csel = new ColumnSelection();
1029     AlignmentI al = ((AlignFrame) alf).getViewport().getAlignment();
1030     jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher(
1031             ((AlignFrame) alf).getViewport().getAlignment()
1032                     .getSequencesArray());
1033     int start = 0, end = al.getWidth(), alw = al.getWidth();
1034     boolean seqsfound = true;
1035     if (ids != null && ids.length > 0)
1036     {
1037       seqsfound = false;
1038       for (int i = 0; i < ids.length; i++)
1039       {
1040         if (ids[i].trim().length() == 0)
1041         {
1042           continue;
1043         }
1044         SequenceI sq = matcher.findIdMatch(ids[i]);
1045         if (sq != null)
1046         {
1047           seqsfound = true;
1048           sel.addSequence(sq, false);
1049         }
1050       }
1051     }
1052     boolean inseqpos = false;
1053     if (cols != null && cols.length > 0)
1054     {
1055       boolean seset = false;
1056       for (int i = 0; i < cols.length; i++)
1057       {
1058         String cl = cols[i].trim();
1059         if (cl.length() == 0)
1060         {
1061           continue;
1062         }
1063         int p;
1064         if ((p = cl.indexOf("-")) > -1)
1065         {
1066           int from = -1, to = -1;
1067           try
1068           {
1069             from = new Integer(cl.substring(0, p)).intValue();
1070             from--;
1071           } catch (NumberFormatException ex)
1072           {
1073             System.err.println(
1074                     "ERROR: Couldn't parse first integer in range element column selection string '"
1075                             + cl + "' - format is 'from-to'");
1076             return;
1077           }
1078           try
1079           {
1080             to = new Integer(cl.substring(p + 1)).intValue();
1081             to--;
1082           } catch (NumberFormatException ex)
1083           {
1084             System.err.println(
1085                     "ERROR: Couldn't parse second integer in range element column selection string '"
1086                             + cl + "' - format is 'from-to'");
1087             return;
1088           }
1089           if (from >= 0 && to >= 0)
1090           {
1091             // valid range
1092             if (from < to)
1093             {
1094               int t = to;
1095               to = from;
1096               to = t;
1097             }
1098             if (!seset)
1099             {
1100               start = from;
1101               end = to;
1102               seset = true;
1103             }
1104             else
1105             {
1106               // comment to prevent range extension
1107               if (start > from)
1108               {
1109                 start = from;
1110               }
1111               if (end < to)
1112               {
1113                 end = to;
1114               }
1115             }
1116             for (int r = from; r <= to; r++)
1117             {
1118               if (r >= 0 && r < alw)
1119               {
1120                 csel.addElement(r);
1121               }
1122             }
1123             if (debug)
1124             {
1125               System.err.println("Range '" + cl + "' deparsed as [" + from
1126                       + "," + to + "]");
1127             }
1128           }
1129           else
1130           {
1131             System.err.println("ERROR: Invalid Range '" + cl
1132                     + "' deparsed as [" + from + "," + to + "]");
1133           }
1134         }
1135         else
1136         {
1137           int r = -1;
1138           try
1139           {
1140             r = new Integer(cl).intValue();
1141             r--;
1142           } catch (NumberFormatException ex)
1143           {
1144             if (cl.toLowerCase().equals("sequence"))
1145             {
1146               // we are in the dataset sequence's coordinate frame.
1147               inseqpos = true;
1148             }
1149             else
1150             {
1151               System.err.println(
1152                       "ERROR: Couldn't parse integer from point selection element of column selection string '"
1153                               + cl + "'");
1154               return;
1155             }
1156           }
1157           if (r >= 0 && r <= alw)
1158           {
1159             if (!seset)
1160             {
1161               start = r;
1162               end = r;
1163               seset = true;
1164             }
1165             else
1166             {
1167               // comment to prevent range extension
1168               if (start > r)
1169               {
1170                 start = r;
1171               }
1172               if (end < r)
1173               {
1174                 end = r;
1175               }
1176             }
1177             csel.addElement(r);
1178             if (debug)
1179             {
1180               System.err.println("Point selection '" + cl
1181                       + "' deparsed as [" + r + "]");
1182             }
1183           }
1184           else
1185           {
1186             System.err.println("ERROR: Invalid Point selection '" + cl
1187                     + "' deparsed as [" + r + "]");
1188           }
1189         }
1190       }
1191     }
1192     if (seqsfound)
1193     {
1194       // we only propagate the selection when it was the null selection, or the
1195       // given sequences were found in the alignment.
1196       if (inseqpos && sel.getSize() > 0)
1197       {
1198         // assume first sequence provides reference frame ?
1199         SequenceI rs = sel.getSequenceAt(0);
1200         start = rs.findIndex(start);
1201         end = rs.findIndex(end);
1202         List<Integer> cs = new ArrayList<>(csel.getSelected());
1203         csel.clear();
1204         for (Integer selectedCol : cs)
1205         {
1206           csel.addElement(rs.findIndex(selectedCol));
1207         }
1208       }
1209       sel.setStartRes(start);
1210       sel.setEndRes(end);
1211       EventQueue.invokeLater(new Runnable()
1212       {
1213         @Override
1214         public void run()
1215         {
1216           ((AlignFrame) alf).select(sel, csel, ((AlignFrame) alf)
1217                   .getCurrentView().getAlignment().getHiddenColumns());
1218         }
1219       });
1220     }
1221   }
1222
1223   public String getAlignmentOrderFrom(AlignFrameI alf, String sep)
1224   {
1225     AlignmentI alorder = ((AlignFrame) alf).getViewport().getAlignment();
1226     String[] order = new String[alorder.getHeight()];
1227     for (int i = 0; i < order.length; i++)
1228     {
1229       order[i] = alorder.getSequenceAt(i).getName();
1230     }
1231     return arrayToSeparatorList(order, sep);
1232   }
1233
1234   public String getSelectedSequencesAsAlignmentFrom(AlignFrameI alf,
1235           String format, String suffix)
1236   {
1237     try
1238     {
1239       AlignViewport vp = ((AlignFrame) alf).getViewport();
1240       FileFormatI theFormat = FileFormats.getInstance().forName(format);
1241       boolean seqlimits = (suffix == null
1242               || suffix.equalsIgnoreCase("true"));
1243       if (vp.getSelectionGroup() != null)
1244       {
1245         // JBPNote: getSelectionAsNewSequence behaviour has changed - this
1246         // method now returns a full copy of sequence data
1247         // TODO consider using getSequenceSelection instead here
1248         String reply = new AppletFormatAdapter().formatSequences(theFormat,
1249                 new Alignment(vp.getSelectionAsNewSequence()),
1250                 seqlimits);
1251         return reply;
1252       }
1253     } catch (IllegalArgumentException ex)
1254     {
1255       ex.printStackTrace();
1256       return "Error retrieving alignment, possibly invalid format specifier: "
1257               + format;
1258     }
1259     return "";
1260   }
1261
1262   public String orderAlignmentBy(AlignFrameI alf, String order,
1263           String undoName, String sep)
1264   {
1265     if (sep == null || sep.length() == 0)
1266     {
1267       sep = separator;
1268     }
1269     String[] ids = JalviewAppLoader.separatorListToArray(order, sep);
1270     SequenceI[] sqs = null;
1271     if (ids != null && ids.length > 0)
1272     {
1273       jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher(
1274               ((AlignFrame) alf).getViewport().getAlignment()
1275                       .getSequencesArray());
1276       int s = 0;
1277       sqs = new SequenceI[ids.length];
1278       for (int i = 0; i < ids.length; i++)
1279       {
1280         if (ids[i].trim().length() == 0)
1281         {
1282           continue;
1283         }
1284         SequenceI sq = matcher.findIdMatch(ids[i]);
1285         if (sq != null)
1286         {
1287           sqs[s++] = sq;
1288         }
1289       }
1290       if (s > 0)
1291       {
1292         SequenceI[] sqq = new SequenceI[s];
1293         System.arraycopy(sqs, 0, sqq, 0, s);
1294         sqs = sqq;
1295       }
1296       else
1297       {
1298         sqs = null;
1299       }
1300     }
1301     if (sqs == null)
1302     {
1303       return "";
1304     }
1305     ;
1306     final AlignmentOrder aorder = new AlignmentOrder(sqs);
1307
1308     if (undoName != null && undoName.trim().length() == 0)
1309     {
1310       undoName = null;
1311     }
1312     final String _undoName = undoName;
1313     // TODO: deal with synchronization here: cannot raise any events until after
1314     // this has returned.
1315     return ((AlignFrame) alf).sortBy(aorder, _undoName) ? "true" : "";
1316   }
1317
1318   public String getAlignmentFrom(AlignFrameI alf, String format,
1319           String suffix)
1320   {
1321     try
1322     {
1323       boolean seqlimits = (suffix == null
1324               || suffix.equalsIgnoreCase("true"));
1325
1326       FileFormatI theFormat = FileFormats.getInstance().forName(format);
1327       String reply = new AppletFormatAdapter().formatSequences(theFormat,
1328               ((AlignFrame) alf).getViewport().getAlignment(), seqlimits);
1329       return reply;
1330     } catch (IllegalArgumentException ex)
1331     {
1332       ex.printStackTrace();
1333       return "Error retrieving alignment, possibly invalid format specifier: "
1334               + format;
1335     }
1336   }
1337
1338 }