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