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