JAL-3030 allow uncaught exceptions (for easier JS debuggin)
[jalview.git] / src / jalview / bin / JalviewJS.java
1 package jalview.bin;
2
3 import jalview.analysis.AlignmentUtils;
4 import jalview.datamodel.AlignmentI;
5 import jalview.gui.AlignFrame;
6 import jalview.gui.SplitFrame;
7 import jalview.io.AppletFormatAdapter;
8 import jalview.io.DataSourceType;
9 import jalview.io.FileFormatException;
10 import jalview.io.FileFormatI;
11 import jalview.io.FileLoader;
12 import jalview.io.IdentifyFile;
13 import jalview.structure.StructureSelectionManager;
14
15 import java.util.ArrayList;
16 import java.util.HashMap;
17 import java.util.List;
18 import java.util.Map;
19
20 import javax.swing.JFrame;
21 import javax.swing.JInternalFrame;
22
23 /**
24  * Entry point for Jalview as Javascript. Expects parameter names as for the
25  * JalviewLite applet, formatted as for the Jalview application, for example
26  * 
27  * <pre>
28  *   JalviewJS file /examples/uniref50.fa features /examples/exampleFeatures.txt \
29  *       PDBFile "/examples/pdb1.txt seq1"
30  * </pre>
31  * 
32  * Note that (unlike the applet) parameter names are case sensitive
33  */
34 // TODO or format as file=/examples/uniref50.fa (etc)?
35 public class JalviewJS
36 {
37   private static final String PARAM_FILE = "file";
38
39   private static final String PARAM_FILE2 = "file2";
40
41   private static final String PARAM_TREE = "tree";
42
43   private static final String PARAM_FEATURES = "features";
44
45   private static final String PARAM_ANNOTATIONS = "annotations";
46
47   private static final String PARAM_SHOW_ANNOTATION = "showAnnotation";
48
49   private static final String PARAM_PDBFILE = "PDBFile";
50
51   private static final String PARAM_PROPS = "props";
52
53   private Map<String, String> params;
54
55   private List<String> pdbFileParams;
56
57   public static void main(String[] args) throws Exception
58   {
59     new JalviewJS().doMain(args);
60   }
61
62   /**
63    * Parses parameters and shows the frame and any loaded panels
64    * 
65    * @throws FileFormatException
66    */
67   void doMain(String[] args) throws FileFormatException
68   {
69     loadParameters(args);
70     if (getParameter(PARAM_FILE) == null)
71     {
72       usage();
73     }
74     else
75     {
76       showFrame();
77     }
78   }
79
80   /**
81    * Answers the value of the given runtime parameter, or null if not provided
82    * 
83    * @param paramName
84    * @return
85    */
86   private String getParameter(String paramName)
87   {
88     return params.get(paramName);
89   }
90
91   /**
92    * Prints a chastising, yet helpful, error message on syserr
93    */
94   private void usage()
95   {
96     System.err.println(
97             "Usage: JalviewJS file <alignmentFile> [features <featuresFile>]");
98     System.err.println("See documentation for full parameter list");
99   }
100
101   /**
102    * Parses any supplied parameters. Note that (unlike for the applet),
103    * parameter names are case sensitive.
104    * 
105    * @param args
106    * 
107    * @see http://www.jalview.org/examples/index.html#appletParameters
108    */
109   void loadParameters(String[] args)
110   {
111     ArgsParser parser = new ArgsParser(args);
112     params = new HashMap<>();
113
114     // TODO javascript-friendly source of properties
115     Cache.loadProperties(parser.getValue(PARAM_PROPS));
116     loadParameter(parser, PARAM_FILE);
117     loadParameter(parser, PARAM_FILE2);
118     loadParameter(parser, PARAM_TREE);
119     loadParameter(parser, PARAM_FEATURES);
120     loadParameter(parser, PARAM_ANNOTATIONS);
121     loadParameter(parser, PARAM_SHOW_ANNOTATION);
122     pdbFileParams = loadPdbParameters(parser);
123   }
124
125   /**
126    * Reads one command line parameter value and saves it against the parameter
127    * name. Note the saved value is null if the parameter is not present.
128    * 
129    * @param parser
130    * @param param
131    */
132   protected void loadParameter(ArgsParser parser, String param)
133   {
134     params.put(param, parser.getValue(param));
135   }
136
137   /**
138    * Reads parameter PDBFile, PDBFile1, PDFile2, ... and saves the value(s) (if
139    * any)
140    * 
141    * @param parser
142    * @return
143    */
144   List<String> loadPdbParameters(ArgsParser parser)
145   {
146     List<String> values = new ArrayList<>();
147     String value = parser.getValue(PARAM_PDBFILE);
148     if (value != null)
149     {
150       values.add(value);
151     }
152     int i = 1;
153     while (true)
154     {
155       value = parser.getValue(PARAM_PDBFILE + String.valueOf(i));
156       if (value != null)
157       {
158         values.add(value);
159       }
160       else
161       {
162         break;
163       }
164     }
165     return values;
166   }
167
168   /**
169    * Constructs and displays a JFrame containing an alignment panel (and any
170    * additional panels depending on parameters supplied)
171    * 
172    * @throws FileFormatException
173    */
174   void showFrame() throws FileFormatException
175   {
176     JFrame frame = new JFrame(getParameter(PARAM_FILE));
177     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
178
179     /*
180      * construct an AlignFrame (optionally with features)
181      */
182     AlignFrame alignFrame = createAlignFrame(PARAM_FILE);
183     loadFeatures(alignFrame, getParameter(PARAM_FEATURES));
184
185     JInternalFrame internalFrame = alignFrame;
186
187     /*
188      * convert to SplitFrame if a valid file2 is supplied
189      */
190     AlignFrame alignFrame2 = createAlignFrame(PARAM_FILE2);
191     if (alignFrame2 != null)
192     {
193       SplitFrame splitFrame = loadSplitFrame(alignFrame, alignFrame2);
194       if (splitFrame != null)
195       {
196         internalFrame = splitFrame;
197       }
198     }
199
200     /*
201      * move AlignFrame (or SplitFrame) menu bar and content pane to our frame
202      * TODO there may be a less obscure way to do this
203      */
204     frame.setContentPane(internalFrame.getContentPane());
205     frame.setJMenuBar(internalFrame.getJMenuBar());
206
207     // fudge so that dialogs can be opened with this frame as parent
208     // todo JAL-3031 also override Desktop.addInternalFrame etc
209     // Desktop.parent = frame.getContentPane();
210
211     frame.pack();
212     frame.setSize(AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
213     frame.setVisible(true);
214   }
215
216   /**
217    * Constructs a SplitFrame if cdna-protein mappings can be made between the
218    * given alignment frames, else returns null. Any mappings made are registered
219    * with StructureSelectionManager to enable broadcast to listeners.
220    * 
221    * @param alignFrame
222    * @param alignFrame2
223    * @return
224    */
225   protected SplitFrame loadSplitFrame(AlignFrame alignFrame,
226           AlignFrame alignFrame2)
227   {
228     // code borrowed from AlignViewport.openLinkedAlignment
229     AlignmentI al1 = alignFrame.getViewport().getAlignment();
230     AlignmentI al2 = alignFrame2.getViewport().getAlignment();
231     boolean al1Nuc = al1.isNucleotide();
232     if (al1Nuc == al2.isNucleotide())
233     {
234       System.err.println("Can't make split frame as alignments are both "
235               + (al1Nuc ? "nucleotide" : "protein"));
236       return null;
237     }
238     AlignmentI protein = al1Nuc ? al2 : al1;
239     AlignmentI cdna = al1Nuc ? al1 : al2;
240     boolean mapped = AlignmentUtils.mapProteinAlignmentToCdna(protein,
241             cdna);
242     if (!mapped)
243     {
244       System.err.println("Can't make any mappings for split frame");
245       return null;
246     }
247
248     /*
249      * register sequence mappings
250      */
251     StructureSelectionManager ssm = StructureSelectionManager
252             .getStructureSelectionManager(null);
253     ssm.registerMappings(protein.getCodonFrames());
254
255     cdna.alignAs(protein);
256
257     SplitFrame splitFrame = new SplitFrame(
258             al1Nuc ? alignFrame : alignFrame2,
259             al1Nuc ? alignFrame2 : alignFrame);
260
261     return splitFrame;
262   }
263
264   /**
265    * Loads on a features file if one was specified as a parameter
266    * 
267    * @param alignFrame
268    * @param featureFile
269    */
270   protected void loadFeatures(AlignFrame alignFrame, String featureFile)
271   {
272     if (featureFile != null)
273     {
274       // todo extract helper for protocol resolution from JalviewLite
275       DataSourceType sourceType = featureFile.startsWith("http")
276               ? DataSourceType.URL
277               : DataSourceType.FILE;
278       alignFrame.parseFeaturesFile(featureFile, sourceType);
279     }
280   }
281
282   /**
283    * Constructs and returns the frame containing the alignment and its
284    * annotations. Returns null if the specified parameter value is not set.
285    * 
286    * @param fileParam
287    * 
288    * @return
289    * @throws FileFormatException
290    */
291   AlignFrame createAlignFrame(String fileParam) throws FileFormatException
292   {
293     AlignFrame af = null;
294     String file = getParameter(fileParam);
295     if (file != null)
296     {
297       DataSourceType protocol = AppletFormatAdapter.checkProtocol(file);
298       FileFormatI format = new IdentifyFile().identify(file, protocol);
299       FileLoader fileLoader = new FileLoader(false);
300       af = fileLoader.LoadFileWaitTillLoaded(file, protocol, format);
301     }
302
303     return af;
304   }
305
306 }