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