JAL-4090 JAL-1551 source license
[jalview.git] / src / jalview / bin / argparser / ArgValuesMap.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.bin.argparser;
22
23 import java.io.File;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Locale;
29 import java.util.Map;
30 import java.util.Set;
31
32 import jalview.bin.Cache;
33 import jalview.bin.Console;
34 import jalview.bin.argparser.Arg.Opt;
35 import jalview.bin.argparser.Arg.Type;
36 import jalview.util.FileUtils;
37
38 /**
39  * Helper class to allow easy extraction of information about specific argument
40  * values (without having to check for null etc all the time)
41  */
42 public class ArgValuesMap
43 {
44   private List<ArgInfo> argInfoList = new ArrayList<>();
45
46   protected Map<Arg, ArgValues> m;
47
48   private String linkedId;
49
50   protected ArgValuesMap(String linkedId)
51   {
52     this.linkedId = linkedId;
53     this.newMap();
54   }
55
56   protected ArgValuesMap(String linkedId, Map<Arg, ArgValues> map)
57   {
58     this.linkedId = linkedId;
59     this.m = map;
60   }
61
62   public String getLinkedId()
63   {
64     return linkedId;
65   }
66
67   private Map<Arg, ArgValues> getMap()
68   {
69     return m;
70   }
71
72   private void newMap()
73   {
74     m = new HashMap<Arg, ArgValues>();
75   }
76
77   private void newArg(Arg a)
78   {
79     if (m == null)
80       newMap();
81     if (!containsArg(a))
82       m.put(a, new ArgValues(a));
83   }
84
85   public ArgValues getArgValues(Arg a)
86   {
87     return m == null ? null : m.get(a);
88   }
89
90   public ArgValues getOrCreateArgValues(Arg a)
91   {
92     ArgValues avs = m.get(a);
93     if (avs == null)
94       newArg(a);
95     return getArgValues(a);
96   }
97
98   public List<ArgValue> getArgValueList(Arg a)
99   {
100     ArgValues avs = getArgValues(a);
101     return avs == null ? new ArrayList<>() : avs.getArgValueList();
102   }
103
104   public ArgValue getArgValue(Arg a)
105   {
106     List<ArgValue> vals = getArgValueList(a);
107     return (vals == null || vals.size() == 0) ? null : vals.get(0);
108   }
109
110   public String getValue(Arg a)
111   {
112     ArgValue av = getArgValue(a);
113     return av == null ? null : av.getValue();
114   }
115
116   public boolean containsArg(Arg a)
117   {
118     if (m == null || !m.containsKey(a))
119       return false;
120     return a.hasOption(Opt.STRING) ? getArgValue(a) != null : true;
121   }
122
123   public boolean hasValue(Arg a, String val)
124   {
125     if (m == null || !m.containsKey(a))
126       return false;
127     for (ArgValue av : getArgValueList(a))
128     {
129       String avVal = av.getValue();
130       if ((val == null && avVal == null)
131               || (val != null && val.equals(avVal)))
132       {
133         return true;
134       }
135     }
136     return false;
137   }
138
139   public boolean getBoolean(Arg a)
140   {
141     ArgValues av = getArgValues(a);
142     return av == null ? false : av.getBoolean();
143   }
144
145   public Set<Arg> getArgKeys()
146   {
147     return m.keySet();
148   }
149
150   public ArgValue getArgValueOfArgWithSubValKey(Arg a, String svKey)
151   {
152     return getArgValueOfArgWithSubValKey(a, svKey, false);
153   }
154
155   public ArgValue getArgValueOfArgWithSubValKey(Arg a, String svKey,
156           boolean last)
157   {
158     ArgValues avs = this.getArgValues(a);
159     if (avs == null)
160     {
161       return null;
162     }
163     List<ArgValue> compareAvs = avs.getArgValueList();
164     for (int i = 0; i < compareAvs.size(); i++)
165     {
166       int index = last ? compareAvs.size() - 1 - i : i;
167       ArgValue av = compareAvs.get(index);
168       SubVals sv = av.getSubVals();
169       if (sv.has(svKey) && !sv.get(svKey).equals("false"))
170       {
171         return av;
172       }
173     }
174     return null;
175   }
176
177   public ArgValue getClosestPreviousArgValueOfArg(ArgValue thisAv, Arg a)
178   {
179     ArgValue closestAv = null;
180     int thisArgIndex = thisAv.getArgIndex();
181     ArgValues compareAvs = this.getArgValues(a);
182     int closestPreviousIndex = -1;
183     for (ArgValue av : compareAvs.getArgValueList())
184     {
185       int argIndex = av.getArgIndex();
186       if (argIndex < thisArgIndex && argIndex > closestPreviousIndex)
187       {
188         closestPreviousIndex = argIndex;
189         closestAv = av;
190       }
191     }
192     return closestAv;
193   }
194
195   public ArgValue getClosestNextArgValueOfArg(ArgValue thisAv, Arg a,
196           boolean withinType)
197   {
198     // this looks for the *next* arg that *might* be referring back to
199     // a thisAv. Such an arg would have no subValues (if it does it should
200     // specify an id in the subValues so wouldn't need to be guessed).
201     ArgValue closestAv = null;
202     int thisArgIndex = thisAv.getArgIndex();
203     if (!containsArg(a))
204       return null;
205     ArgValues compareAvs = this.getArgValues(a);
206     int closestNextIndex = Integer.MAX_VALUE;
207     for (ArgValue av : compareAvs.getArgValueList())
208     {
209       int argIndex = av.getArgIndex();
210       if (argIndex > thisArgIndex && argIndex < closestNextIndex)
211       {
212         closestNextIndex = argIndex;
213         closestAv = av;
214       }
215     }
216
217     // check if withinType this closestAv doesn't belong to the next primary arg
218     // of this type
219     if (withinType && closestAv != null)
220     {
221       int nextPrimaryArgOfSameTypeIndex = Integer.MAX_VALUE;
222       for (Arg tmpA : this.getArgKeys())
223       {
224         // interested in Opt.PRIMARY args of the same type
225         if (tmpA.getType() == a.getType() && tmpA.hasOption(Opt.PRIMARY))
226         {
227           for (ArgValue tmpAv : getArgValueList(tmpA))
228           {
229             int tmpArgIndex = tmpAv.getArgIndex();
230             if (tmpArgIndex > thisArgIndex
231                     && tmpArgIndex < nextPrimaryArgOfSameTypeIndex)
232             {
233               nextPrimaryArgOfSameTypeIndex = tmpArgIndex;
234             }
235           }
236         }
237       }
238       if (nextPrimaryArgOfSameTypeIndex < closestAv.getArgIndex())
239       {
240         // looks licke closestAv actually belongs to a different primary Arg
241         return null;
242       }
243     }
244
245     return closestAv;
246   }
247
248   // TODO this is incomplete and currently unused (fortunately)
249   public ArgValue[] getArgValuesReferringTo(String key, String value, Arg a)
250   {
251     // this looks for the *next* arg that *might* be referring back to
252     // a thisAv. Such an arg would have no subValues (if it does it should
253     // specify an id in the subValues so wouldn't need to be guessed).
254     List<ArgValue> avList = new ArrayList<>();
255     Arg[] args = a == null ? (Arg[]) this.getMap().keySet().toArray()
256             : new Arg[]
257             { a };
258     for (Arg keyArg : args)
259     {
260       for (ArgValue av : this.getArgValueList(keyArg))
261       {
262
263       }
264     }
265     return (ArgValue[]) avList.toArray();
266   }
267
268   public boolean hasId(Arg a, String id)
269   {
270     ArgValues avs = this.getArgValues(a);
271     return avs == null ? false : avs.hasId(id);
272   }
273
274   public ArgValue getId(Arg a, String id)
275   {
276     ArgValues avs = this.getArgValues(a);
277     return avs == null ? null : avs.getId(id);
278   }
279
280   /*
281    * This method returns the basename of the first --append or --open value. 
282    * Used primarily for substitutions in output filenames.
283    */
284   public String getBasename()
285   {
286     return getDirBasenameOrExtension(false, false, false);
287   }
288
289   /*
290    * This method returns the basename of the first --append or --open value. 
291    * Used primarily for substitutions in output filenames.
292    */
293   public String getExtension()
294   {
295     return getDirBasenameOrExtension(false, true, false);
296   }
297
298   /*
299    * This method returns the dirname of the first --append or --open value. 
300    * Used primarily for substitutions in output filenames.
301    */
302   public String getDirname()
303   {
304     return getDirBasenameOrExtension(true, false, false);
305   }
306
307   public String getDirBasenameOrExtension(boolean dirname,
308           boolean extension, boolean absoluteDirname)
309   {
310     String filename = null;
311     String appendVal = getValue(Arg.APPEND);
312     String openVal = getValue(Arg.OPEN);
313     if (appendVal != null)
314       filename = appendVal;
315     if (filename == null && openVal != null)
316       filename = openVal;
317     if (filename == null)
318       return null;
319
320     File file = new File(filename);
321     if (dirname)
322     {
323       return FileUtils.getDirname(file);
324     }
325     return extension ? FileUtils.getExtension(file)
326             : FileUtils.getBasename(file);
327   }
328
329   /*
330    * Checks if there is an Arg with Opt
331    */
332   public boolean hasArgWithOption(Opt o)
333   {
334     for (Arg a : getArgKeys())
335     {
336       if (a.hasOption(o))
337         return true;
338     }
339     return false;
340   }
341
342   /*
343    * ArgInfo is a more straightforward list of arguments and their info
344    */
345
346   public void addArgInfo(Arg arg, String value, SubVals subVals,
347           int argIndex)
348   {
349     argInfoList.add(new ArgInfo(arg, value, subVals, argIndex));
350   }
351
352   public List<ArgInfo> getArgInfoList()
353   {
354     Collections.sort(argInfoList);
355     return argInfoList;
356   }
357
358   /**
359    * get from following Arg of type a or subval of same name (lowercase)
360    */
361   public String getValueFromSubValOrArg(ArgValue av, Arg a, SubVals sv)
362   {
363     return getFromSubValArgOrPref(av, a, sv, null, null, null);
364   }
365
366   /**
367    * get from following Arg of type a or subval key or preference pref or
368    * default def
369    */
370   public String getFromSubValArgOrPref(ArgValue av, Arg a, SubVals sv,
371           String key, String pref, String def)
372   {
373     return getFromSubValArgOrPref(a, ArgValuesMap.Position.AFTER, av, sv,
374             key, pref, def);
375   }
376
377   /**
378    * get from following(AFTER), first occurence of (FIRST) or previous (BEFORE)
379    * Arg of type a or subval key or preference pref or default def
380    */
381   public String getFromSubValArgOrPref(Arg a, ArgValuesMap.Position pos,
382           ArgValue av, SubVals sv, String key, String pref, String def)
383   {
384     return getFromSubValArgOrPrefWithSubstitutions(null, a, pos, av, sv,
385             key, pref, def);
386   }
387
388   public String getFromSubValArgOrPrefWithSubstitutions(ArgParser ap, Arg a,
389           ArgValuesMap.Position pos, ArgValue av, SubVals sv, String key,
390           String pref, String def)
391   {
392     return getFromSubValArgOrPrefWithSubstitutionsWithinType(ap, a, pos, av,
393             sv, key, pref, def, true);
394   }
395
396   public String getFromSubValArgOrPrefWithSubstitutionsWithinType(
397           ArgParser ap, Arg a, ArgValuesMap.Position pos, ArgValue av,
398           SubVals sv, String key, String pref, String def,
399           boolean withinType)
400   {
401     if (key == null)
402       key = a.getName();
403     String value = null;
404     if (sv != null && sv.has(key) && sv.get(key) != null)
405     {
406       value = ap == null ? sv.get(key)
407               : sv.getWithSubstitutions(ap, getLinkedId(), key);
408     }
409     else if (containsArg(a))
410     {
411       if (pos == ArgValuesMap.Position.FIRST && getValue(a) != null)
412         value = getValue(a);
413       else if (pos == ArgValuesMap.Position.BEFORE
414               && getClosestPreviousArgValueOfArg(av, a) != null)
415         value = getClosestPreviousArgValueOfArg(av, a).getValue();
416       else if (pos == ArgValuesMap.Position.AFTER
417               && getClosestNextArgValueOfArg(av, a, withinType) != null)
418         value = getClosestNextArgValueOfArg(av, a, withinType).getValue();
419
420       // look for allstructures subval for Type.STRUCTURE
421       Arg arg = av.getArg();
422       if (value == null && arg.hasOption(Opt.PRIMARY)
423               && arg.getType() == Type.STRUCTURE
424               && !a.hasOption(Opt.PRIMARY) && (a.getType() == Type.STRUCTURE
425               // || a.getType() == Type.STRUCTUREIMAGE))
426               ))
427       {
428         ArgValue av2 = getArgValueOfArgWithSubValKey(a,
429                 Arg.ALLSTRUCTURES.getName());
430         if (av2 != null)
431         {
432           value = av2.getValue();
433         }
434       }
435     }
436     if (value == null)
437     {
438       value = pref != null ? Cache.getDefault(pref, def) : def;
439     }
440     return value;
441   }
442
443   public boolean getBoolFromSubValOrArg(Arg a, SubVals sv)
444   {
445     return getFromSubValArgOrPref(a, sv, null, null, false);
446   }
447
448   public boolean getFromSubValArgOrPref(Arg a, SubVals sv, String key,
449           String pref, boolean def)
450   {
451     return getFromSubValArgOrPref(a, sv, key, pref, def, false);
452   }
453
454   public boolean getFromSubValArgOrPref(Arg a, SubVals sv, String key,
455           String pref, boolean def, boolean invertPref)
456   {
457     if ((key == null && a == null) || (sv == null && a == null))
458       return false;
459
460     boolean usingArgKey = false;
461     if (key == null)
462     {
463       key = a.getName();
464       usingArgKey = true;
465     }
466
467     String nokey = ArgParser.NEGATESTRING + key;
468
469     // look for key or nokey in subvals first (if using Arg check options)
470     if (sv != null)
471     {
472       // check for true boolean
473       if (sv.has(key) && sv.get(key) != null)
474       {
475         if (usingArgKey)
476         {
477           if (!(a.hasOption(Opt.BOOLEAN) || a.hasOption(Opt.UNARY)))
478           {
479             Console.debug(
480                     "Looking for boolean in subval from non-boolean/non-unary Arg "
481                             + a.getName());
482             return false;
483           }
484         }
485         return sv.get(key).toLowerCase(Locale.ROOT).equals("true");
486       }
487
488       // check for negative boolean (subval "no..." will be "true")
489       if (sv.has(nokey) && sv.get(nokey) != null)
490       {
491         if (usingArgKey)
492         {
493           if (!(a.hasOption(Opt.BOOLEAN)))
494           {
495             Console.debug(
496                     "Looking for negative boolean in subval from non-boolean Arg "
497                             + a.getName());
498             return false;
499           }
500         }
501         return !sv.get(nokey).toLowerCase(Locale.ROOT).equals("true");
502       }
503     }
504
505     // check argvalues
506     if (containsArg(a))
507       return getBoolean(a);
508
509     // return preference or default
510     boolean prefVal = pref != null ? Cache.getDefault(pref, def) : false;
511     return pref != null ? (invertPref ? !prefVal : prefVal) : def;
512   }
513
514   public class ArgInfo implements Comparable<ArgInfo>
515   {
516     private Arg arg;
517
518     private String value;
519
520     private SubVals subVals;
521
522     private int argIndex;
523
524     public ArgInfo(Arg arg, String value, SubVals subVals, int argIndex)
525     {
526       this.arg = arg;
527       this.value = value;
528       this.subVals = subVals;
529       this.argIndex = argIndex;
530     }
531
532     public Arg arg()
533     {
534       return arg;
535     }
536
537     public String value()
538     {
539       return value;
540     }
541
542     public SubVals subVals()
543     {
544       return subVals;
545     }
546
547     public int argIndex()
548     {
549       return argIndex;
550     }
551
552     @Override
553     public int compareTo(ArgInfo ai2)
554     {
555       return Integer.compare(this.argIndex(), ai2.argIndex());
556     }
557   }
558
559   public static enum Position
560   {
561     FIRST, BEFORE, AFTER
562   }
563 }