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 static java.lang.String.format;
41 import static java.util.Collections.sort;
42 import static picocli.CommandLine.usage;
43
44 import groovy.lang.Binding;
45 import groovy.lang.Script;
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.Enumeration;
55 import java.util.List;
56 import java.util.jar.JarEntry;
57 import java.util.jar.JarFile;
58 import java.util.logging.Level;
59 import java.util.logging.Logger;
60 import java.util.zip.ZipEntry;
61
62 import picocli.CommandLine;
63 import picocli.CommandLine.Help.Ansi;
64 import picocli.CommandLine.Option;
65 import picocli.CommandLine.ParseResult;
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
121
122 public FFXScript(Binding binding) {
123 super(binding);
124 if (GraphicsEnvironment.isHeadless()) {
125 color = Ansi.ON;
126 } else {
127 color = Ansi.OFF;
128 }
129 }
130
131
132
133
134
135
136
137 public static Class<? extends FFXScript> getScript(String name) {
138 ClassLoader loader = FFXScript.class.getClassLoader();
139 String pathName = name;
140 Class<?> script;
141 try {
142
143 script = loader.loadClass(pathName);
144 } catch (ClassNotFoundException e) {
145
146 pathName = "ffx.potential.groovy." + name;
147 try {
148 script = loader.loadClass(pathName);
149 } catch (ClassNotFoundException e2) {
150
151 pathName = "ffx.algorithms.groovy." + name;
152 try {
153 script = loader.loadClass(pathName);
154 } catch (ClassNotFoundException e3) {
155 if (name.startsWith("xray.")) {
156
157 pathName = "ffx.xray.groovy." + name.replaceAll("xray.", "");
158 } else if (name.startsWith("realspace.")) {
159 pathName = "ffx.realspace.groovy." + name.replaceAll("realspace.", "");
160 } else {
161 pathName = "ffx." + name;
162 }
163 try {
164 script = loader.loadClass(pathName);
165 } catch (ClassNotFoundException e4) {
166 logger.warning(format(" %s was not found.", name));
167 return null;
168 }
169 }
170 }
171 }
172 return script.asSubclass(FFXScript.class);
173 }
174
175
176
177
178
179
180
181 public static void listGroovyScripts(boolean logScripts, boolean logTestScripts) {
182 ClassLoader classLoader = ClassLoader.getSystemClassLoader();
183 try {
184 logger.info("\n Potential Package Commands:");
185 URL url = classLoader.getResource("ffx/potential");
186 listScriptsForPackage(url, logScripts, logTestScripts);
187 logger.info("\n Algorithms Package Commands:");
188 url = classLoader.getResource("ffx/algorithms");
189 listScriptsForPackage(url, logScripts, logTestScripts);
190 logger.info("\n Refinement Package Commands:");
191 url = classLoader.getResource("ffx/xray");
192 listScriptsForPackage(url, logScripts, logTestScripts);
193 } catch (Exception e) {
194 logger.info(" The ffx resource could not be found by the classloader.");
195 }
196 }
197
198
199
200
201
202
203
204
205 private static void listScriptsForPackage(URL scriptURL, boolean logScripts, boolean logTestScripts) {
206 String scriptPath = scriptURL.getPath();
207 String ffx = scriptPath.substring(5, scriptURL.getPath().indexOf("!"));
208 List<String> scripts = new ArrayList<>();
209 List<String> testScripts = new ArrayList<>();
210
211 try (JarFile jar = new JarFile(URLDecoder.decode(ffx, StandardCharsets.UTF_8))) {
212
213 Enumeration<JarEntry> enumeration = jar.entries();
214 while (enumeration.hasMoreElements()) {
215 ZipEntry zipEntry = enumeration.nextElement();
216 String className = zipEntry.getName();
217 if (className.startsWith("ffx")
218 && className.contains("groovy")
219 && className.endsWith(".class")
220 && !className.contains("$")) {
221 className = className.replace("/", ".");
222 className = className.replace(".class", "");
223
224 className = className.replace("ffx.potential.groovy.", "");
225 className = className.replace("ffx.algorithms.groovy.", "");
226 className = className.replace("ffx.realspace.groovy", "realspace");
227 className = className.replace("ffx.xray.groovy", "xray");
228 if (className.toUpperCase().contains("TEST")) {
229 testScripts.add(className);
230 } else {
231 scripts.add(className);
232 }
233 }
234 }
235 } catch (Exception e) {
236 logger.info(format(" The %s resource could not be decoded.", scriptPath));
237 return;
238 }
239
240
241 sort(scripts);
242 sort(testScripts);
243
244
245 if (logTestScripts) {
246 for (String script : testScripts) {
247 logger.info(" " + script);
248 }
249 }
250 if (logScripts) {
251 for (String script : scripts) {
252 logger.info(" " + script);
253 }
254 }
255 }
256
257
258
259
260
261
262 public String helpString() {
263 try {
264 StringOutputStream sos = new StringOutputStream(new ByteArrayOutputStream());
265 usage(this, sos, color);
266 return " " + sos;
267 } catch (UnsupportedEncodingException e) {
268 logger.log(Level.WARNING, e.toString());
269 return null;
270 }
271 }
272
273
274
275
276
277
278 public boolean init() {
279 Binding binding = getBinding();
280
281
282 Object arguments = binding.getProperty("args");
283 if (arguments instanceof List<?> list) {
284 int numArgs = list.size();
285 args = new String[numArgs];
286 for (int i = 0; i < numArgs; i++) {
287 args[i] = (String) list.get(i);
288 }
289 } else {
290 args = (String[]) binding.getProperty("args");
291 }
292
293 CommandLine commandLine = new CommandLine(this);
294 try {
295 parseResult = commandLine.parseArgs(args);
296 } catch (CommandLine.UnmatchedArgumentException uae) {
297 logger.warning(
298 " 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).");
299 throw uae;
300 }
301
302
303 if (help) {
304 logger.info(helpString());
305 return false;
306 }
307
308
309
310
311 return !version;
312 }
313
314
315
316
317
318
319 @Override
320 public FFXScript run() {
321 logger.info(helpString());
322 return this;
323 }
324 }