View Javadoc
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 }