JAL-629 null check, fixed tests!
[jalview.git] / src / jalview / bin / Commands.java
1 package jalview.bin;
2
3 import java.io.File;
4 import java.util.ArrayList;
5 import java.util.Arrays;
6 import java.util.Collections;
7 import java.util.HashMap;
8 import java.util.Iterator;
9 import java.util.List;
10 import java.util.Locale;
11 import java.util.Map;
12
13 import jalview.analysis.AlignmentUtils;
14 import jalview.api.AlignmentViewPanel;
15 import jalview.bin.ArgParser.Arg;
16 import jalview.bin.ArgParser.ArgValues;
17 import jalview.datamodel.AlignmentAnnotation;
18 import jalview.gui.AlignFrame;
19 import jalview.gui.Desktop;
20 import jalview.io.AppletFormatAdapter;
21 import jalview.io.DataSourceType;
22 import jalview.io.FileFormatException;
23 import jalview.io.FileFormatI;
24 import jalview.io.FileLoader;
25 import jalview.io.IdentifyFile;
26 import jalview.util.HttpUtils;
27 import jalview.util.MessageManager;
28 import jalview.util.Platform;
29 import jalview.ws.dbsources.EBIAlfaFold;
30
31 public class Commands
32 {
33   Desktop desktop;
34
35   private static boolean headless;
36
37   private static ArgParser argParser;
38
39   private Map<String, AlignFrame> afMap;
40
41   public static void processArgs(ArgParser ap, boolean h)
42   {
43     argParser = ap;
44     headless = h;
45     if (argParser != null && argParser.linkedIds() != null)
46     {
47       for (String id : argParser.linkedIds())
48       {
49         Commands cmds = new Commands();
50         if (id == null)
51         {
52           cmds.processUnlinked(id);
53         }
54         else
55         {
56           cmds.processLinked(id);
57         }
58       }
59     }
60   }
61
62   public Commands()
63   {
64     this(Desktop.instance);
65   }
66
67   public Commands(Desktop d)
68   {
69     this.desktop = d;
70     afMap = new HashMap<String, AlignFrame>();
71   }
72
73   protected void processUnlinked(String id)
74   {
75     processLinked(id);
76   }
77
78   protected void processLinked(String id)
79   {
80     Map<Arg, ArgValues> m = argParser.linkedArgs(id);
81
82     /*
83     // script to execute after all loading is completed one way or another
84     String groovyscript = m.get(Arg.GROOVY) == null ? null
85             : m.get(Arg.GROOVY).getValue();
86     String file = m.get(Arg.OPEN) == null ? null
87             : m.get(Arg.OPEN).getValue();
88     String data = null;
89     FileFormatI format = null;
90     DataSourceType protocol = null;
91     */
92
93     if (m.get(Arg.OPEN) != null)
94     {
95       long progress = -1;
96
97       boolean first = true;
98       AlignFrame af;
99       OPEN: for (String openFile : m.get(Arg.OPEN).getValues())
100       {
101         if (openFile == null)
102           continue OPEN;
103
104         if (first)
105         {
106           first = false;
107           if (!headless)
108           {
109             desktop.setProgressBar(
110                     MessageManager.getString(
111                             "status.processing_commandline_args"),
112                     progress = System.currentTimeMillis());
113           }
114         }
115
116         if (!Platform.isJS())
117         /**
118          * ignore in JavaScript -- can't just file existence - could load it?
119          * 
120          * @j2sIgnore
121          */
122         {
123           if (!HttpUtils.startsWithHttpOrHttps(openFile))
124           {
125             if (!(new File(openFile)).exists())
126             {
127               Console.warn("Can't find file '" + openFile + "'");
128               continue OPEN;
129             }
130           }
131         }
132
133         DataSourceType protocol = AppletFormatAdapter
134                 .checkProtocol(openFile);
135
136         FileFormatI format = null;
137         try
138         {
139           format = new IdentifyFile().identify(openFile, protocol);
140         } catch (FileFormatException e1)
141         {
142           Console.error("Unknown file format for '" + openFile + "'");
143         }
144
145         af = afMap.get(id);
146         if (af == null)
147         {
148           // get kind of temperature factor annotation
149           AlignmentAnnotation.TFType tempfacType = null;
150           if ((m.get(Arg.NOTEMPFAC) == null
151                   || !m.get(Arg.NOTEMPFAC).getBoolean())
152                   && m.get(Arg.TEMPFAC) != null)
153           {
154             try
155             {
156               tempfacType = AlignmentAnnotation.TFType
157                       .valueOf(m.get(Arg.TEMPFAC).getValue()
158                               .toUpperCase(Locale.ROOT));
159               Console.debug("Obtained Temperature Factor type of '"
160                       + tempfacType + "'");
161             } catch (IllegalArgumentException e)
162             {
163               StringBuilder sb = new StringBuilder().append("Cannot set --")
164                       .append(Arg.TEMPFAC.getName()).append(" to '")
165                       .append(tempfacType)
166                       .append("', ignoring.  Valid values are: ");
167               Iterator<AlignmentAnnotation.TFType> it = Arrays
168                       .stream(AlignmentAnnotation.TFType.values())
169                       .iterator();
170               while (it.hasNext())
171               {
172                 sb.append(it.next().toString().toLowerCase(Locale.ROOT));
173                 if (it.hasNext())
174                   sb.append(", ");
175               }
176               Console.warn(sb.toString());
177             }
178           }
179
180           Console.debug(
181                   "Opening '" + openFile + "' in new alignment frame");
182           FileLoader fileLoader = new FileLoader(!headless);
183           af = fileLoader.LoadFileWaitTillLoaded(openFile, protocol, format,
184                   tempfacType);
185
186           // wrap alignment?
187           if (m.get(Arg.WRAP) != null && m.get(Arg.WRAP).getBoolean())
188           {
189             af.getCurrentView().setWrapAlignment(true);
190           }
191
192           // change alignment frame title
193           if (m.get(Arg.TITLE) != null)
194             af.setTitle(m.get(Arg.TITLE).getValue());
195
196           // show secondary structure annotations?
197           if (m.get(Arg.SSANNOTATION) != null
198                   && !m.get(Arg.SSANNOTATION).getBoolean())
199           {
200             // do this better (annotation types?)
201             AlignmentUtils.showOrHideSequenceAnnotations(
202                     af.getCurrentView().getAlignment(),
203                     Collections.singleton("Secondary Structure"), null,
204                     false, false);
205           }
206
207           // show temperature factor annotations?
208           if (m.get(Arg.NOTEMPFAC) != null
209                   && m.get(Arg.NOTEMPFAC).getBoolean())
210           {
211             // do this better (annotation types?)
212             List<String> hideThese = new ArrayList<>();
213             hideThese.add("Temperature Factor");
214             hideThese.add(MessageManager
215                     .getString("label.alphafold_reliability"));
216             AlignmentUtils.showOrHideSequenceAnnotations(
217                     af.getCurrentView().getAlignment(), hideThese, null,
218                     false, false);
219           }
220           else
221           {
222             if (m.get(Arg.TEMPFAC_LABEL) != null)
223             {
224               AlignmentAnnotation aa = AlignmentUtils
225                       .getFirstSequenceAnnotationOfType(
226                               af.getCurrentView().getAlignment(),
227                               AlignmentAnnotation.LINE_GRAPH);
228               if (aa != null)
229               {
230                 aa.label = m.get(Arg.TEMPFAC_LABEL).getValue();
231               }
232             }
233           }
234
235           // store the AlignFrame for this id
236           afMap.put(id, af);
237         }
238         else
239         {
240           Console.debug(
241                   "Opening '" + openFile + "' in existing alignment frame");
242           af.getCurrentView().addFile(new File(openFile), format);
243         }
244
245         System.out
246                 .println("Command " + Arg.OPEN + " executed successfully!");
247
248       }
249       if (first) // first=true means nothing opened
250       {
251         if (headless)
252         {
253           Console.error("Could not open any files in headless mode");
254           System.exit(1);
255         }
256       }
257       else
258       {
259         Console.warn("No more files to open");
260         if (desktop != null)
261           desktop.setProgressBar(null, progress);
262       }
263
264     }
265
266     // load a pAE file if given
267     if (m.get(Arg.PAEMATRIX) != null)
268     {
269       AlignFrame af = afMap.get(id);
270       if (af != null)
271       {
272         for (String val : m.get(Arg.PAEMATRIX).getValues())
273         {
274           SubId subId = new SubId(val);
275           File paeFile = new File(subId.content);
276           EBIAlfaFold.addAlphaFoldPAE(af.getCurrentView().getAlignment(),
277                   paeFile, subId.index,
278                   "id".equals(subId.keyName) ? subId.keyValue : null);
279           // required to readjust the height and position of the pAE
280           // annotation
281           for (AlignmentViewPanel ap : af.getAlignPanels())
282           {
283             ap.adjustAnnotationHeight();
284           }
285         }
286       }
287     }
288   }
289
290   /**
291    * A helper class to parse a string of the possible forms "content"
292    * "[index]content", "[keyName=keyValue]content" and return the integer index,
293    * the strings keyName and keyValue, and the content after the square brackets
294    * (if present). Values not set will be -1 or null.
295    */
296   protected class SubId
297   {
298     protected int index = 0;
299
300     protected String keyName = null;
301
302     protected String keyValue = null;
303
304     protected String content = null;
305
306     protected SubId(String item)
307     {
308       if (item.indexOf('[') == 0 && item.indexOf(']') > 1)
309       {
310         int openBracket = item.indexOf('[');
311         int closeBracket = item.indexOf(']');
312         String indexString = item.substring(openBracket + 1, closeBracket);
313         this.content = item.substring(closeBracket + 1);
314         int equals = indexString.indexOf('=');
315         if (equals > -1)
316         {
317           this.keyName = indexString.substring(0, equals);
318           this.keyValue = indexString.substring(equals + 1);
319           this.index = -1;
320         }
321         else
322         {
323           try
324           {
325             this.index = Integer.parseInt(indexString);
326           } catch (NumberFormatException e)
327           {
328             Console.warn("Failed to obtain sequenced id or index from '"
329                     + item + "'. Setting index=0 and using content='"
330                     + content + "'.");
331           }
332         }
333       }
334       else
335       {
336         this.content = item;
337       }
338     }
339   }
340 }