1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 package ffx.utilities;
39
40 import groovy.lang.Binding;
41 import groovy.lang.Script;
42 import picocli.CommandLine;
43 import picocli.CommandLine.Help.Ansi;
44 import picocli.CommandLine.Option;
45 import picocli.CommandLine.ParseResult;
46
47 import java.awt.GraphicsEnvironment;
48 import java.io.ByteArrayOutputStream;
49 import java.io.UnsupportedEncodingException;
50 import java.net.URL;
51 import java.net.URLDecoder;
52 import java.nio.charset.StandardCharsets;
53 import java.util.ArrayList;
54 import java.util.Arrays;
55 import java.util.Enumeration;
56 import java.util.List;
57 import java.util.jar.JarEntry;
58 import java.util.jar.JarFile;
59 import java.util.logging.Level;
60 import java.util.logging.Logger;
61 import java.util.zip.ZipEntry;
62
63 import static java.lang.String.format;
64 import static java.util.Collections.sort;
65 import static picocli.CommandLine.usage;
66
67
68
69
70
71
72 public abstract class FFXScript extends Script {
73
74
75
76
77 public static final Logger logger = Logger.getLogger(FFXScript.class.getName());
78
79
80
81
82
83
84
85 public final Ansi color;
86
87
88
89
90 public String[] args;
91
92
93
94
95 public ParseResult parseResult = null;
96
97
98
99
100 @Option(
101 names = {"-V", "--version"},
102 versionHelp = true,
103 defaultValue = "false",
104 description = "Print the Force Field X version and exit.")
105 public boolean version;
106
107
108
109
110 @Option(
111 names = {"-h", "--help"},
112 usageHelp = true,
113 defaultValue = "false",
114 description = "Print command help and exit.")
115 public boolean help;
116
117
118
119
120 public FFXScript() {
121 this(new Binding());
122 }
123
124
125
126
127
128
129 public FFXScript(String[] args) {
130 this(new Binding());
131 Binding binding = getBinding();
132 binding.setVariable("args", Arrays.asList(args));
133 }
134
135
136
137
138
139
140 public FFXScript(Binding binding) {
141 super(binding);
142 if (GraphicsEnvironment.isHeadless()) {
143 color = Ansi.ON;
144 } else {
145 color = Ansi.OFF;
146 }
147 }
148
149
150
151
152
153
154
155 public static Class<? extends FFXScript> getScript(String name) {
156 ClassLoader loader = FFXScript.class.getClassLoader();
157 String pathName = name;
158 Class<?> script;
159 try {
160
161 script = loader.loadClass(pathName);
162 } catch (ClassNotFoundException e) {
163
164 pathName = "ffx.potential.commands." + name;
165 try {
166 script = loader.loadClass(pathName);
167 } catch (ClassNotFoundException e2) {
168
169 pathName = "ffx.algorithms.commands." + name;
170 try {
171 script = loader.loadClass(pathName);
172 } catch (ClassNotFoundException e2b) {
173 if (name.startsWith("xray.")) {
174
175 pathName = "ffx.xray.commands." + name.replaceAll("xray.", "");
176 } else if (name.startsWith("realspace.")) {
177 pathName = "ffx.realspace.commands." + name.replaceAll("realspace.", "");
178 } else {
179 pathName = "ffx." + name;
180 }
181 try {
182 script = loader.loadClass(pathName);
183 } catch (ClassNotFoundException e4) {
184 logger.warning(format(" %s was not found.", name));
185 return null;
186 }
187 }
188 }
189 }
190 return script.asSubclass(FFXScript.class);
191 }
192
193
194
195
196
197
198
199 public static void listGroovyScripts(boolean logScripts, boolean logTestScripts) {
200 ClassLoader classLoader = ClassLoader.getSystemClassLoader();
201 try {
202 logger.info("\n Potential Package Commands:");
203 URL url = classLoader.getResource("ffx/potential");
204 listScriptsForPackage(url, logScripts, logTestScripts);
205 logger.info("\n Algorithms Package Commands:");
206 url = classLoader.getResource("ffx/algorithms");
207 listScriptsForPackage(url, logScripts, logTestScripts);
208 logger.info("\n Refinement Package Commands:");
209 url = classLoader.getResource("ffx/xray");
210 listScriptsForPackage(url, logScripts, logTestScripts);
211 } catch (Exception e) {
212 logger.info(" The ffx resource could not be found by the classloader.");
213 }
214 }
215
216
217
218
219
220
221
222
223 private static void listScriptsForPackage(URL scriptURL, boolean logScripts, boolean logTestScripts) {
224 String scriptPath = scriptURL.getPath();
225 String ffx = scriptPath.substring(5, scriptURL.getPath().indexOf("!"));
226 List<String> scripts = new ArrayList<>();
227 List<String> testScripts = new ArrayList<>();
228
229 try (JarFile jar = new JarFile(URLDecoder.decode(ffx, StandardCharsets.UTF_8))) {
230
231 Enumeration<JarEntry> enumeration = jar.entries();
232 while (enumeration.hasMoreElements()) {
233 ZipEntry zipEntry = enumeration.nextElement();
234 String className = zipEntry.getName();
235 if (className.startsWith("ffx")
236 && className.endsWith(".class")
237 && !className.contains("$")
238 && (className.contains("groovy") || className.contains("commands"))) {
239 className = className.replace("/", ".");
240 className = className.replace(".class", "");
241
242 className = className.replace("ffx.potential.commands.", "");
243 className = className.replace("ffx.algorithms.commands.", "");
244 className = className.replace("ffx.realspace.commands", "realspace");
245 className = className.replace("ffx.xray.commands", "xray");
246 if (className.toUpperCase().contains("TEST")) {
247 testScripts.add(className);
248 } else {
249 scripts.add(className);
250 }
251 }
252 }
253 } catch (Exception e) {
254 logger.info(format(" The %s resource could not be decoded.", scriptPath));
255 return;
256 }
257
258
259 sort(scripts);
260 sort(testScripts);
261
262
263 if (logTestScripts) {
264 for (String script : testScripts) {
265 logger.info(" " + script);
266 }
267 }
268 if (logScripts) {
269 for (String script : scripts) {
270 logger.info(" " + script);
271 }
272 }
273 }
274
275
276
277
278
279
280 public String helpString() {
281 try {
282 StringOutputStream sos = new StringOutputStream(new ByteArrayOutputStream());
283 usage(this, sos, color);
284 return " " + sos;
285 } catch (UnsupportedEncodingException e) {
286 logger.log(Level.WARNING, e.toString());
287 return null;
288 }
289 }
290
291
292
293
294
295
296 public boolean init() {
297 Binding binding = getBinding();
298
299
300 Object arguments = binding.getProperty("args");
301 if (arguments instanceof List<?> list) {
302 int numArgs = list.size();
303 args = new String[numArgs];
304 for (int i = 0; i < numArgs; i++) {
305 args[i] = (String) list.get(i);
306 }
307 } else if (arguments instanceof String[]) {
308 args = (String[]) arguments;
309 } else if (arguments instanceof String) {
310 args = new String[]{(String) arguments};
311 } else {
312 args = new String[0];
313 }
314
315 CommandLine commandLine = new CommandLine(this);
316 try {
317 parseResult = commandLine.parseArgs(args);
318 } catch (CommandLine.UnmatchedArgumentException uae) {
319 logger.warning(
320 " The usual source of this exception is when long-form arguments (such as --uaA) are only preceded by one dash (such as -uaA, which is an error).");
321 throw uae;
322 }
323
324
325 if (help) {
326 logger.info(helpString());
327 return false;
328 }
329
330
331
332
333 return !version;
334 }
335
336
337
338
339
340
341 @Override
342 public FFXScript run() {
343 logger.info(helpString());
344 return this;
345 }
346 }