JAL-629 Added 'Type' to args and argvalues
[jalview.git] / src / jalview / bin / argparser / BootstrapArgs.java
1 package jalview.bin.argparser;
2
3 import java.io.File;
4 import java.util.AbstractMap;
5 import java.util.ArrayList;
6 import java.util.Arrays;
7 import java.util.HashMap;
8 import java.util.HashSet;
9 import java.util.List;
10 import java.util.Map;
11 import java.util.Set;
12 import java.util.stream.Collectors;
13
14 import jalview.bin.argparser.Arg.Opt;
15 import jalview.bin.argparser.Arg.Type;
16 import jalview.util.FileUtils;
17
18 public class BootstrapArgs
19 {
20   // only need one
21   private Map<Arg, List<Map.Entry<Type, String>>> bootstrapArgMap = new HashMap<>();
22
23   private Set<File> argFiles = new HashSet<>();
24
25   public static BootstrapArgs getBootstrapArgs(String[] args)
26   {
27     List<String> argList = new ArrayList<>(Arrays.asList(args));
28     return new BootstrapArgs(argList);
29   }
30
31   public static BootstrapArgs getBootstrapArgs(List<String> args)
32   {
33     return new BootstrapArgs(args);
34   }
35
36   private BootstrapArgs(List<String> args)
37   {
38     parse(args, null);
39   }
40
41   private void parse(List<String> args, File inArgFile)
42   {
43     if (args == null)
44       return;
45     // avoid looping argFiles
46     if (inArgFile != null)
47     {
48       if (argFiles.contains(inArgFile))
49       {
50         System.err.println(
51                 "Looped argfiles detected: '" + inArgFile.getPath() + "'");
52         return;
53       }
54       argFiles.add(inArgFile);
55     }
56
57     for (int i = 0; i < args.size(); i++)
58     {
59       String arg = args.get(i);
60       // look for double-dash, e.g. --arg
61       if (arg.startsWith(ArgParser.DOUBLEDASH))
62       {
63         String argName = null;
64         String val = null;
65         Type type = null;
66         // remove "--"
67         arg = arg.substring(ArgParser.DOUBLEDASH.length());
68
69         // look for equals e.g. --arg=value
70         int equalPos = arg.indexOf(ArgParser.EQUALS);
71         if (equalPos > -1
72                 && ArgParser.argMap.containsKey(arg.substring(0, equalPos)))
73         {
74           argName = arg.substring(0, equalPos);
75           val = arg.substring(equalPos + 1);
76         }
77         // check for boolean prepended by "no"
78         else if (arg.startsWith(ArgParser.NEGATESTRING)
79                 && ArgParser.argMap.containsKey(
80                         arg.substring(ArgParser.NEGATESTRING.length())))
81         {
82           argName = arg.substring(ArgParser.NEGATESTRING.length());
83           val = "false";
84         }
85         else if (ArgParser.argMap.containsKey(arg))
86         {
87           argName = arg;
88           val = "true";
89         }
90         else
91         {
92           // look for type modification e.g. --help-opening
93           int dashPos = arg.indexOf(ArgParser.SINGLEDASH);
94           if (dashPos > -1)
95           {
96             String potentialArgName = arg.substring(0, dashPos);
97             Arg potentialArg = ArgParser.argMap.get(potentialArgName);
98             if (potentialArg != null && potentialArg.hasOption(Opt.HASTYPE))
99             {
100               argName = arg.substring(0, dashPos);
101               String typeName = arg.substring(dashPos + 1);
102               type = Type.valueOf(typeName);
103             }
104           }
105         }
106
107         Arg a = ArgParser.argMap.get(argName);
108
109         if (a == null || !a.hasOption(Opt.BOOTSTRAP))
110         {
111           // not a valid bootstrap arg
112           continue;
113         }
114
115         if (a.hasOption(Opt.STRING))
116         {
117           List<String> vals = null;
118           if (equalPos == -1)
119           {
120             vals = ArgParser.getShellGlobbedFilenameValues(a, args, i + 1);
121           }
122           else
123           {
124             if (a.hasOption(Opt.GLOB))
125             {
126               vals = FileUtils.getFilenamesFromGlob(val);
127             }
128             else
129             {
130               vals = new ArrayList<>();
131               vals.add(val);
132             }
133           }
134           addAll(a, type, vals);
135
136           if (a == Arg.ARGFILE)
137           {
138             for (String filename : vals)
139             {
140               File argFile = new File(filename);
141               parse(ArgParser.readArgFile(argFile), argFile);
142             }
143           }
144         }
145         else
146         {
147           add(a, type, val);
148         }
149       }
150     }
151   }
152
153   public boolean contains(Arg a)
154   {
155     return bootstrapArgMap.containsKey(a);
156   }
157
158   public boolean containsType(Type t)
159   {
160     for (List<Map.Entry<Type, String>> l : bootstrapArgMap.values())
161     {
162       for (Map.Entry<Type, String> e : l)
163       {
164         if (e.getKey() == t)
165           return true;
166       }
167     }
168     return false;
169   }
170
171   public List<Arg> getArgsOfType(Type t)
172   {
173     return getArgsOfType(t, new Opt[] {});
174   }
175
176   public List<Arg> getArgsOfType(Type t, Opt... opts)
177   {
178     List<Arg> args = new ArrayList<>();
179     for (Arg a : bootstrapArgMap.keySet())
180     {
181       if (!a.hasAllOptions(opts))
182         continue;
183
184       List<Map.Entry<Type, String>> l = bootstrapArgMap.get(a);
185       if (l.stream().anyMatch(e -> e.getKey() == t))
186       {
187         args.add(a);
188       }
189     }
190     return args;
191   }
192
193   public List<Map.Entry<Type, String>> getList(Arg a)
194   {
195     return bootstrapArgMap.get(a);
196   }
197
198   public List<String> getValueList(Arg a)
199   {
200     return bootstrapArgMap.get(a).stream().map(e -> e.getValue())
201             .collect(Collectors.toList());
202   }
203
204   private List<Map.Entry<Type, String>> getOrCreateList(Arg a)
205   {
206     List<Map.Entry<Type, String>> l = getList(a);
207     if (l == null)
208     {
209       l = new ArrayList<>();
210       putList(a, l);
211     }
212     return l;
213   }
214
215   private void putList(Arg a, List<Map.Entry<Type, String>> l)
216   {
217     bootstrapArgMap.put(a, l);
218   }
219
220   /*
221    * Creates a new list if not used before,
222    * adds the value unless the existing list is non-empty
223    * and the arg is not MULTI (so first expressed value is
224    * retained).
225    */
226   private void add(Arg a, Type t, String s)
227   {
228     List<Map.Entry<Type, String>> l = getOrCreateList(a);
229     if (a.hasOption(Opt.MULTI) || l.size() == 0)
230     {
231       l.add(entry(t, s));
232     }
233   }
234
235   private void addAll(Arg a, Type t, List<String> al)
236   {
237     List<Map.Entry<Type, String>> l = getOrCreateList(a);
238     if (a.hasOption(Opt.MULTI))
239     {
240       for (String s : al)
241       {
242         l.add(entry(t, s));
243       }
244     }
245     else if (l.size() == 0 && al.size() > 0)
246     {
247       l.add(entry(t, al.get(0)));
248     }
249   }
250
251   private static Map.Entry<Type, String> entry(Type t, String s)
252   {
253     return new AbstractMap.SimpleEntry<Type, String>(t, s);
254   }
255
256   /*
257    * Retrieves the first value even if MULTI.
258    * A convenience for non-MULTI args.
259    */
260   public String get(Arg a)
261   {
262     if (!bootstrapArgMap.containsKey(a))
263       return null;
264     List<Map.Entry<Type, String>> aL = bootstrapArgMap.get(a);
265     return (aL == null || aL.size() == 0) ? null : aL.get(0).getValue();
266   }
267
268   public boolean getBoolean(Arg a, boolean d)
269   {
270     if (!bootstrapArgMap.containsKey(a))
271       return d;
272     return Boolean.parseBoolean(get(a));
273   }
274
275   public boolean getBoolean(Arg a)
276   {
277     if (!(a.hasOption(Opt.BOOLEAN) || a.hasOption(Opt.UNARY)))
278     {
279       return false;
280     }
281     if (bootstrapArgMap.containsKey(a))
282       return Boolean.parseBoolean(get(a));
283     else
284       return a.getDefaultBoolValue();
285   }
286 }