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 ffx.utilities.TinkerUtils.parseTinkerAtomList;
41 import static java.lang.Integer.parseInt;
42 import static java.lang.Integer.parseUnsignedInt;
43 import static java.lang.String.format;
44 import static java.util.Arrays.asList;
45 import static org.apache.commons.math3.util.FastMath.max;
46
47 import java.io.BufferedReader;
48 import java.io.BufferedWriter;
49 import java.io.File;
50 import java.io.FileInputStream;
51 import java.io.FileOutputStream;
52 import java.io.IOException;
53 import java.io.InputStreamReader;
54 import java.io.OutputStreamWriter;
55 import java.io.Reader;
56 import java.io.Writer;
57 import java.nio.charset.Charset;
58 import java.util.ArrayList;
59 import java.util.Arrays;
60 import java.util.Collections;
61 import java.util.HashMap;
62 import java.util.List;
63 import java.util.Map;
64 import java.util.Set;
65 import java.util.logging.Level;
66 import java.util.logging.Logger;
67 import java.util.regex.Matcher;
68 import java.util.regex.Pattern;
69 import java.util.zip.GZIPInputStream;
70 import java.util.zip.GZIPOutputStream;
71 import org.apache.commons.math3.util.FastMath;
72
73
74
75
76
77
78
79 public class StringUtils {
80
81
82 public static final String STANDARD_WATER_NAME = "HOH";
83
84 private static final Logger logger = Logger.getLogger(StringUtils.class.getName());
85 private static final Set<String> waterNames = Set.of("HOH", "DOD", "WAT", "TIP", "TIP3", "TIP4", "MOL");
86 private static final Map<String, String> ionNames;
87 private static final Pattern intRangePattern = Pattern.compile("(\\d+)-(\\d+)");
88
89 static {
90 Map<String, String> ions = new HashMap<>();
91
92 List<String> monoCats = asList("NA", "K", "LI", "RB", "CS", "FR", "AG", "AU");
93 for (String mCat : monoCats) {
94 ions.put(mCat, mCat);
95 ions.put(mCat + "+", mCat);
96 ions.put(mCat + "1", mCat);
97 ions.put(mCat + "1+", mCat);
98 ions.put(mCat + "+1", mCat);
99 }
100
101
102
103 List<String> diCats = asList("BE", "MG", "CA", "SR", "BA", "RA", "MN", "ZN");
104 for (String diCat : diCats) {
105 ions.put(diCat, diCat);
106 ions.put(diCat + "+", diCat);
107 ions.put(diCat + "2", diCat);
108 ions.put(diCat + "2+", diCat);
109 ions.put(diCat + "+2", diCat);
110 ions.put(diCat + "++", diCat);
111 }
112
113 List<String> monoAns = asList("F", "CL", "BR", "I", "AT");
114 for (String monoAn : monoAns) {
115 ions.put(monoAn, monoAn);
116 ions.put(monoAn + "-", monoAn);
117 ions.put(monoAn + "1", monoAn);
118 ions.put(monoAn + "1-", monoAn);
119 ions.put(monoAn + "-1", monoAn);
120 }
121
122 ionNames = Collections.unmodifiableMap(ions);
123 }
124
125
126
127
128 private StringUtils() {
129
130 }
131
132
133
134
135
136
137
138 public static String cifForID(String id) {
139 if (id.length() != 4) {
140 return null;
141 }
142 return "http://www.rcsb.org/pdb/files/" + id.toLowerCase() + ".cif";
143 }
144
145
146
147
148
149
150
151
152
153
154 public static List<int[]> consecutiveInts(int[] set) {
155 if (set == null || set.length == 0) {
156 return Collections.emptyList();
157 }
158 List<int[]> allRanges = new ArrayList<>();
159
160 int rangeStart = set[0];
161 int rangeEnd = rangeStart;
162 for (int i = 1; i < set.length; i++) {
163 if (set[i] == rangeEnd + 1) {
164 rangeEnd = set[i];
165 } else {
166 allRanges.add(new int[] {rangeStart, rangeEnd});
167 rangeStart = set[i];
168 rangeEnd = rangeStart;
169 }
170 }
171 allRanges.add(new int[] {rangeStart, rangeEnd});
172 return allRanges;
173 }
174
175
176
177
178
179
180
181
182 public static Reader createGzipReader(File file) throws IOException {
183 return createGzipReader(file, Charset.defaultCharset());
184 }
185
186
187
188
189
190
191
192
193
194 public static Reader createGzipReader(File file, Charset cs) throws IOException {
195
196
197
198
199
200
201 return new BufferedReader(
202 new InputStreamReader(new GZIPInputStream(new FileInputStream(file)), cs));
203 }
204
205
206
207
208
209
210
211
212 public static Writer createGzipWriter(File file) throws IOException {
213 return createGzipWriter(file, Charset.defaultCharset());
214 }
215
216
217
218
219
220
221
222
223
224 public static Writer createGzipWriter(File file, Charset cs) throws IOException {
225
226
227
228
229
230
231 return new BufferedWriter(
232 new OutputStreamWriter(new GZIPOutputStream(new FileOutputStream(file)), cs));
233 }
234
235
236
237
238
239
240
241
242
243
244
245
246
247 public static String fwDec(double val, int width, int prec) throws IllegalArgumentException {
248 if (width < 1 || prec < 0) {
249 throw new IllegalArgumentException(" Must have width >= 1 and precision >= 0");
250 }
251 int w1 = width - 1;
252 double maxVal = FastMath.pow(10.0, width);
253 double minVal = maxVal / -10.0;
254
255 if (val >= maxVal) {
256 throw new IllegalArgumentException(
257 String.format(
258 " Value %f exceeded the maximum of %f enforced by width %d", val, maxVal, width));
259 } else if (val <= minVal) {
260 throw new IllegalArgumentException(
261 String.format(
262 " Value %f is less than the minimum of %f enforced by width %d", val, minVal, width));
263 }
264
265 String str = String.format("%" + width + "." + prec + "f", val);
266 if (str.charAt(w1) == '.') {
267 return " " + str.substring(0, w1);
268 } else {
269 return str.substring(0, width);
270 }
271 }
272
273
274
275
276
277
278
279
280
281
282
283
284 public static String fwFpDec(double val, int width, int prec) throws IllegalArgumentException {
285 String str = String.format("%" + width + "." + prec + "f", val);
286 if (str.length() > width) {
287 throw new IllegalArgumentException(
288 String.format(" Value %f cannot fit in width %d with precision %d", val, width, prec));
289 } else {
290 return str;
291 }
292 }
293
294
295
296
297
298
299
300
301
302
303 public static String fwFpTrunc(double val, int width, int prec) {
304 String str = String.format("%" + width + "." + prec + "f", val);
305 if (str.length() > width) {
306 StringBuilder sb;
307 if (val < 0) {
308 sb = new StringBuilder("-");
309 } else {
310 sb = new StringBuilder("9");
311 }
312
313 sb.append(org.apache.commons.lang3.StringUtils.repeat("9", max(0, (width - prec - 2))));
314 sb.append(".");
315 sb.append(org.apache.commons.lang3.StringUtils.repeat("9", max(0, prec)));
316 str = sb.toString();
317 }
318 return str;
319 }
320
321
322
323
324
325
326 public static Map<String, String> getIonNames() {
327 return new HashMap<>(ionNames);
328 }
329
330
331
332
333
334
335 public static List<String> getWaterNames() {
336 return new ArrayList<>(waterNames);
337 }
338
339
340
341
342
343
344
345 public static boolean looksLikeIon(String name) {
346 return ionNames.containsKey(name.toUpperCase());
347 }
348
349
350
351
352
353
354
355 public static boolean looksLikeWater(String name) {
356 return waterNames.contains(name.toUpperCase());
357 }
358
359
360
361
362
363
364
365
366 public static String padLeft(String s, int n) {
367 return String.format("%" + n + "s", s);
368 }
369
370
371
372
373
374
375
376
377 public static String padRight(String s, int n) {
378 return String.format("%-" + n + "s", s);
379 }
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394 public static List<Integer> parseAtomRange(String keyType, String atomRange, int nAtoms)
395 throws IllegalArgumentException {
396 Matcher m = intRangePattern.matcher(atomRange);
397 if (m.matches()) {
398 int start = parseInt(m.group(1)) - 1;
399 int end = parseInt(m.group(2)) - 1;
400 if (start > end) {
401 throw new IllegalArgumentException(format(" %s input %s not valid: start > end.", keyType, atomRange));
402 } else if (start < 0) {
403 throw new IllegalArgumentException(
404 format(" %s input %s not valid: atoms should be indexed starting from 1.", keyType, atomRange));
405 } else if (start >= nAtoms) {
406 throw new IllegalArgumentException(
407 format(" %s input %s not valid: atom range is out of bounds for assembly of length %d.",
408 keyType, atomRange, nAtoms));
409 } else {
410 if (end >= nAtoms) {
411 logger.log(
412 Level.INFO,
413 format(" Truncating range %s to end of valid range %d.", atomRange, nAtoms));
414 end = nAtoms - 1;
415 }
416 List<Integer> selectedAtoms = new ArrayList<>();
417 for (int i = start; i <= end; i++) {
418 selectedAtoms.add(i);
419 }
420 return selectedAtoms;
421 }
422 } else {
423 try {
424 int atNum = parseUnsignedInt(atomRange) - 1;
425 if (atNum < 0 || atNum >= nAtoms) {
426 throw new IllegalArgumentException(
427 format(
428 " %s numerical argument %s out-of-bounds for range 1 to %d",
429 keyType, atomRange, nAtoms));
430 }
431 List<Integer> selectedAtoms = new ArrayList<>();
432 selectedAtoms.add(atNum);
433 return selectedAtoms;
434 } catch (NumberFormatException ex) {
435
436 List<String> tokens = asList(atomRange.split("\\s+"));
437 return parseTinkerAtomList(tokens, -1, -1);
438 }
439 }
440 }
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455 public static List<Integer> parseAtomRanges(String keyType, String atomRanges, int nAtoms)
456 throws IllegalArgumentException {
457 List<Integer> atomList = new ArrayList<>();
458
459 String n = Integer.toString(nAtoms);
460 atomRanges = atomRanges.toUpperCase().replace("N", n);
461
462
463 String[] ranges =
464 Arrays.stream(atomRanges.split("\\.|,|;")).map(String::trim).toArray(String[]::new);
465
466 for (String range : ranges) {
467 List<Integer> list = parseAtomRange(keyType, range, nAtoms);
468
469 for (int i : list) {
470 if (!atomList.contains(i)) {
471 atomList.add(i);
472 }
473 }
474 }
475 return atomList;
476 }
477
478
479
480
481
482
483
484 public static String pdbForID(String id) {
485 if (id.length() != 4) {
486 return null;
487 }
488 return "http://www.rcsb.org/pdb/files/" + id.toLowerCase() + ".pdb";
489 }
490
491
492
493
494
495
496
497
498 public static String tryParseIon(String name) {
499 return ionNames.getOrDefault(name.toUpperCase(), null);
500 }
501
502
503
504
505
506
507
508
509 public static String tryParseWater(String name) {
510 return waterNames.contains(name.toUpperCase()) ? STANDARD_WATER_NAME : null;
511 }
512
513
514
515
516
517
518 public static String writeAtomRanges(int[] atoms){
519 Arrays.sort(atoms);
520 int nAtoms = atoms.length;
521 StringBuilder output = new StringBuilder();
522 for(int i = 0; i < nAtoms; i++){
523 int index = 0;
524 int current = atoms[i] + 1;
525 output.append(current);
526
527 while(i + index + 1 < nAtoms && atoms[i + index + 1] + 1 == current + index + 1){
528 index++;
529 }
530 if(index >= 2){
531 output.append("-").append(atoms[i + index] + 1);
532 }else if(index == 1){
533 output.append(",").append(atoms[i + index] + 1);
534 }
535 i += index;
536 if(i + 1 < nAtoms){
537 output.append(",");
538 }
539 }
540 String string = output.toString();
541 if(string.endsWith(",")){
542 return string.substring(0,string.length() - 1);
543 }else {
544 return output.toString();
545 }
546 }
547 }