1 // ******************************************************************************
2 //
3 // Title: Force Field X.
4 // Description: Force Field X - Software for Molecular Biophysics.
5 // Copyright: Copyright (c) Michael J. Schnieders 2001-2025.
6 //
7 // This file is part of Force Field X.
8 //
9 // Force Field X is free software; you can redistribute it and/or modify it
10 // under the terms of the GNU General Public License version 3 as published by
11 // the Free Software Foundation.
12 //
13 // Force Field X is distributed in the hope that it will be useful, but WITHOUT
14 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16 // details.
17 //
18 // You should have received a copy of the GNU General Public License along with
19 // Force Field X; if not, write to the Free Software Foundation, Inc., 59 Temple
20 // Place, Suite 330, Boston, MA 02111-1307 USA
21 //
22 // Linking this library statically or dynamically with other modules is making a
23 // combined work based on this library. Thus, the terms and conditions of the
24 // GNU General Public License cover the whole combination.
25 //
26 // As a special exception, the copyright holders of this library give you
27 // permission to link this library with independent modules to produce an
28 // executable, regardless of the license terms of these independent modules, and
29 // to copy and distribute the resulting executable under terms of your choice,
30 // provided that you also meet, for each linked independent module, the terms
31 // and conditions of the license of that module. An independent module is a
32 // module which is not derived from or based on this library. If you modify this
33 // library, you may extend this exception to your version of the library, but
34 // you are not obligated to do so. If you do not wish to do so, delete this
35 // exception statement from your version.
36 //
37 // ******************************************************************************
38 package ffx.potential.parsers;
39
40 import ffx.utilities.Keyword;
41
42 import java.io.BufferedReader;
43 import java.io.File;
44 import java.io.FileReader;
45 import java.io.IOException;
46 import java.util.Hashtable;
47 import java.util.logging.Level;
48 import java.util.logging.Logger;
49
50 import static java.lang.Integer.parseInt;
51
52 /**
53 * The KeyFilter class parses Force Field X Keyword (*.KEY) and Property (*.PROPERTIES) files.
54 *
55 * @author Michael J. Schnieders
56 * @since 1.0
57 */
58 public class KeyFilter {
59
60 private static final Logger logger = Logger.getLogger(KeyFilter.class.getName());
61
62 /** Constructor for KeyFilter. */
63 public KeyFilter() {
64 }
65
66 /**
67 * open
68 *
69 * @param keyFile a {@link java.io.File} object.
70 * @return a {@link java.util.Hashtable} object.
71 */
72 public static Hashtable<String, Keyword> open(File keyFile) {
73 if (keyFile == null || !keyFile.exists() || !keyFile.canRead()) {
74 return null;
75 }
76 Hashtable<String, Keyword> keywordHash = loadSystemKeywords();
77 return open(keyFile, keywordHash);
78 }
79
80 /**
81 * open
82 *
83 * @param keyFile a {@link java.io.File} object.
84 * @param keywordHash a {@link java.util.Hashtable} object.
85 * @return a {@link java.util.Hashtable} object.
86 */
87 public static Hashtable<String, Keyword> open(
88 File keyFile, Hashtable<String, Keyword> keywordHash) {
89 if (keyFile == null || !keyFile.exists() || !keyFile.canRead()) {
90 return null;
91 }
92 if (keywordHash == null) {
93 keywordHash = new Hashtable<>();
94 }
95 try (BufferedReader br = new BufferedReader(new FileReader(keyFile))) {
96 Keyword comments = new Keyword("COMMENTS");
97 keywordHash.put("COMMENTS", comments);
98 while (br.ready()) {
99 String s = br.readLine();
100 if (s == null) {
101 continue;
102 }
103 s = s.trim();
104 if (s.isEmpty()) {
105 continue; // Skip blank lines
106 }
107 // Store comments together
108 if (s.startsWith("#") || s.toUpperCase().startsWith("ECHO")) {
109 comments.append(s);
110 } else {
111 int firstSpace = s.indexOf(" ");
112 String keyword, data;
113 if (firstSpace == -1) { // no parameters
114 keyword = s.trim().toUpperCase();
115 // Rattle is special case, because it can be active
116 // without being checked
117 // Valid Key files can have: RATTLE
118 // or RATTLE BONDS
119 // or RATTLE & RATTLE BONDS as separate lines
120 // Each of these valid cases mean different things...
121 if (keyword.equalsIgnoreCase("rattle")) {
122 data = "RATTLE";
123 } else {
124 data = null;
125 }
126 } else {
127 keyword = s.substring(0, firstSpace).toUpperCase();
128 data = s.substring(firstSpace).trim();
129 }
130 Keyword kd = keywordHash.get(keyword);
131 if (kd == null) {
132 kd = new Keyword(keyword);
133 keywordHash.put(keyword, kd);
134 }
135 if (data != null) {
136 kd.append(data);
137 }
138 /*
139 Multipoles and TORTORS are the only keywords that span
140 multiple lines. Editing these from within Force Field X
141 seems unlikely, so they are treated as comments.
142 */
143 if (keyword.equalsIgnoreCase("MULTIPOLE")) {
144 int[] mnum = {3, 1, 2, 3};
145 for (int i = 0; i < 4; i++) {
146 if (!br.ready()) {
147 System.out.println("Check for an invalid MULTIPOLE keyword.");
148 return null;
149 }
150 s = br.readLine();
151 if (s == null) {
152 logger.warning("Multipole format error.");
153 return null;
154 }
155 s = s.trim();
156 if (s.split(" +").length != mnum[i]) {
157 logger.warning("Multipole format error.");
158 return null;
159 }
160 kd.append(s);
161 }
162 } else if (keyword.equalsIgnoreCase("TORTORS")) {
163 String[] res = data.split(" +");
164 if (res.length < 7) {
165 logger.warning("TORTOR format error.");
166 return null;
167 }
168 int xres = parseInt(res[5]);
169 int yres = parseInt(res[6]);
170 for (int i = 0; i < xres * yres; i++) {
171 if (!br.ready()) {
172 System.out.println("Check for an invalid TORTOR keyword.");
173 return null;
174 }
175 s = br.readLine();
176 if (s == null) {
177 logger.warning("TORTOR format error.");
178 return null;
179 }
180 s = s.trim();
181 if (s.split(" +").length != 3) {
182 logger.warning("TORTOR format error.");
183 return null;
184 }
185 kd.append(s);
186 }
187 }
188 }
189 }
190 return keywordHash;
191 } catch (IOException e) {
192 System.err.println("Error reading Key File: " + e);
193 return null;
194 }
195 }
196
197 /**
198 * loadSystemKeywords
199 *
200 * @return a {@link java.util.Hashtable} object.
201 */
202 private static Hashtable<String, Keyword> loadSystemKeywords() {
203 File f = new File("/etc/ffx.conf");
204 Hashtable<String, Keyword> systemKeywords = new Hashtable<>();
205 if (f.exists() && f.canRead()) {
206 logger.info("Reading /etc/ffx.conf");
207 systemKeywords = KeyFilter.open(f, systemKeywords);
208 }
209 String path = System.getProperty("user.home") + File.separator + ".ffx/ffx.conf";
210 f = new File(path);
211 if (f.exists() && f.canRead()) {
212 logger.log(Level.INFO, "Reading {0}", path);
213 systemKeywords = KeyFilter.open(f, systemKeywords);
214 }
215 return systemKeywords;
216 }
217 }