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-2024.
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.utilities;
39  
40  import static java.lang.String.format;
41  import static org.apache.commons.io.FilenameUtils.removeExtension;
42  
43  import java.io.File;
44  import java.io.IOException;
45  import java.util.Arrays;
46  import java.util.Iterator;
47  import java.util.Vector;
48  import java.util.logging.Level;
49  import java.util.logging.Logger;
50  import org.apache.commons.configuration2.CompositeConfiguration;
51  import org.apache.commons.configuration2.PropertiesConfiguration;
52  import org.apache.commons.configuration2.SystemConfiguration;
53  import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
54  import org.apache.commons.configuration2.builder.fluent.Parameters;
55  import org.apache.commons.configuration2.ex.ConfigurationException;
56  
57  /**
58   * The Keyword class holds a single Force Field X keyword entry.
59   *
60   * @author Michael J. Schnieders
61   * @since 1.0
62   */
63  public class Keyword {
64  
65    private static final Logger logger = Logger.getLogger(Keyword.class.getName());
66    private final String keyword;
67    private final Vector<String> data;
68  
69    /**
70     * Constructor for Keyword.
71     *
72     * @param k a {@link java.lang.String} object.
73     */
74    public Keyword(String k) {
75      keyword = k;
76      data = new Vector<>();
77    }
78  
79    /**
80     * Constructor for Keyword.
81     *
82     * @param k a {@link java.lang.String} object.
83     * @param entry a {@link java.lang.String} object.
84     */
85    public Keyword(String k, String entry) {
86      this(k);
87      data.add(entry);
88    }
89  
90    /**
91     * Constructor for Keyword.
92     *
93     * @param k a {@link java.lang.String} object.
94     * @param entry an array of {@link java.lang.String} objects.
95     */
96    public Keyword(String k, String[] entry) {
97      this(k);
98      data.addAll(Arrays.asList(entry));
99    }
100 
101   /**
102    * This method sets up configuration properties in the following precedence * order:
103    *
104    * <p>1.) Structure specific properties (for example pdbname.properties)
105    *
106    * <p>2.) Java system properties a.) -Dkey=value from the Java command line b.)
107    * System.setProperty("key","value") within Java code.
108    *
109    * <p>3.) User specific properties (~/.ffx/ffx.properties)
110    *
111    * <p>4.) System wide properties (file defined by environment variable FFX_PROPERTIES)
112    *
113    * <p>5.) Internal force field definition.
114    *
115    * @param file a {@link java.io.File} object.
116    * @return a {@link org.apache.commons.configuration2.CompositeConfiguration} object.
117    * @since 1.0
118    */
119   public static CompositeConfiguration loadProperties(File file) {
120 
121     // Command line options take precedence.
122     CompositeConfiguration properties = new CompositeConfiguration();
123 
124     /*
125      JVM system properties are read first.
126      a.) -Dkey=value from the Java command line
127      b.) System.setProperty("key","value") within Java code.
128     */
129     PropertiesConfiguration systemConfiguration = new PropertiesConfiguration();
130     systemConfiguration.append(new SystemConfiguration());
131     systemConfiguration.setHeader("JVM system properties (i.e. command line -Dkey=value pairs).");
132     properties.addConfiguration(systemConfiguration);
133 
134     // Structure specific options are 2md.
135     // -Dkey=file.key takes precedence over using the structure file name.
136     if (properties.containsKey("key")) {
137       String keyString = properties.getString("key");
138       File keyFile = new File(keyString);
139       if (keyFile.exists() && keyFile.canRead()) {
140         try {
141           FileBasedConfigurationBuilder<PropertiesConfiguration> builder =
142               new FileBasedConfigurationBuilder<>(PropertiesConfiguration.class)
143                   .configure(
144                       new Parameters()
145                           .properties()
146                           .setFile(keyFile)
147                           .setThrowExceptionOnMissing(true)
148                           .setIncludesAllowed(false));
149           PropertiesConfiguration propertyConfiguration = builder.getConfiguration();
150           propertyConfiguration.setHeader(
151               "Structure properties from (" + keyString + ").");
152           properties.addConfiguration(propertyConfiguration);
153           properties.addProperty("propertyFile", keyFile.getCanonicalPath());
154         } catch (ConfigurationException | IOException e) {
155           logger.log(Level.INFO, " Error loading {0}.", keyFile);
156         }
157       } else {
158         logger.severe(
159             format(" Keyword file supplied at the command line does not exist: %s.", keyString));
160       }
161     } else if (file != null) {
162       String structureBasename = removeExtension(file.getAbsolutePath());
163       String propertyFilename =
164           (new File(structureBasename + ".properties").exists())
165               ? structureBasename + ".properties"
166               : (new File(structureBasename + ".prop").exists())
167                   ? structureBasename + ".prop"
168                   : (new File(structureBasename + ".key").exists())
169                       ? structureBasename + ".key"
170                       : null;
171       if (propertyFilename != null) {
172         File structurePropFile = new File(propertyFilename);
173         if (structurePropFile.canRead()) {
174           try {
175             FileBasedConfigurationBuilder<PropertiesConfiguration> builder =
176                 new FileBasedConfigurationBuilder<>(PropertiesConfiguration.class)
177                     .configure(
178                         new Parameters()
179                             .properties()
180                             .setFile(structurePropFile)
181                             .setThrowExceptionOnMissing(true)
182                             .setIncludesAllowed(false));
183             PropertiesConfiguration propertyConfiguration = builder.getConfiguration();
184             propertyConfiguration.setHeader(
185                 "Structure properties from (" + propertyFilename + ").");
186             properties.addConfiguration(propertyConfiguration);
187             properties.addProperty("propertyFile", structurePropFile.getCanonicalPath());
188           } catch (ConfigurationException | IOException e) {
189             logger.log(Level.INFO, " Error loading {0}.", structureBasename);
190           }
191         }
192       }
193     }
194 
195     // User specific options are 3rd.
196     String filename = System.getProperty("user.home") + File.separator + ".ffx/ffx.properties";
197     File userPropFile = new File(filename);
198     if (userPropFile.exists() && userPropFile.canRead()) {
199       try {
200         FileBasedConfigurationBuilder<PropertiesConfiguration> builder =
201             new FileBasedConfigurationBuilder<>(PropertiesConfiguration.class)
202                 .configure(
203                     new Parameters()
204                         .properties()
205                         .setFile(userPropFile)
206                         .setThrowExceptionOnMissing(true)
207                         // .setListDelimiterHandler(new DefaultListDelimiterHandler(','))
208                         .setIncludesAllowed(false));
209         PropertiesConfiguration ffxConfiguration = builder.getConfiguration();
210         ffxConfiguration.setHeader("FFX user property file (" + filename + ").");
211         properties.addConfiguration(ffxConfiguration);
212       } catch (ConfigurationException e) {
213         logger.log(Level.INFO, " Error loading {0}.", filename);
214       }
215     }
216 
217     // System-wide options are 2nd to last.
218     filename = System.getenv("FFX_PROPERTIES");
219     if (filename != null) {
220       File systemPropFile = new File(filename);
221       if (systemPropFile.exists() && systemPropFile.canRead()) {
222         try {
223           FileBasedConfigurationBuilder<PropertiesConfiguration> builder =
224               new FileBasedConfigurationBuilder<>(PropertiesConfiguration.class)
225                   .configure(
226                       new Parameters()
227                           .properties()
228                           .setFile(systemPropFile)
229                           .setThrowExceptionOnMissing(true)
230                           // .setListDelimiterHandler(new DefaultListDelimiterHandler(','))
231                           .setIncludesAllowed(false));
232           PropertiesConfiguration envConfiguration = builder.getConfiguration();
233           envConfiguration.setHeader("Environment variable FFX_PROPERTIES (" + filename + ").");
234           properties.addConfiguration(envConfiguration);
235         } catch (ConfigurationException e) {
236           logger.log(Level.INFO, " Error loading {0}.", filename);
237         }
238       }
239     }
240 
241     // Echo the interpolated configuration.
242     if (logger.isLoggable(Level.FINE)) {
243       Iterator<String> i = properties.getKeys();
244       StringBuilder sb = new StringBuilder();
245       sb.append(format("\n %-30s %s\n", "Property", "Value"));
246       while (i.hasNext()) {
247         String s = i.next();
248         sb.append(
249             format(" %-30s %s\n", s, Arrays.toString(properties.getList(s).toArray())));
250       }
251       logger.fine(sb.toString());
252     }
253 
254     return properties;
255   }
256 
257   /**
258    * append
259    *
260    * @param entry a {@link java.lang.String} object.
261    */
262   public void append(String entry) {
263     data.add(entry);
264   }
265 
266   /**
267    * append
268    *
269    * @param entry an array of {@link java.lang.String} objects.
270    */
271   public void append(String[] entry) {
272     data.addAll(Arrays.asList(entry));
273   }
274 
275   /**
276    * clear
277    */
278   public void clear() {
279     data.clear();
280   }
281 
282   /**
283    * getEntries
284    *
285    * @return a {@link java.util.Vector} object.
286    */
287   public Vector<String> getEntries() {
288     return data;
289   }
290 
291   /**
292    * getEntry
293    *
294    * @param i The index of the entry to return.
295    * @return a {@link java.lang.String} object.
296    */
297   public String getEntry(int i) {
298     return data.get(i);
299   }
300 
301   /**
302    * Getter for the field <code>keyword</code>.
303    *
304    * @return a {@link java.lang.String} object.
305    */
306   public String getKeyword() {
307     return keyword;
308   }
309 
310   /**
311    * print
312    */
313   public void print() {
314     logger.info(this.toString());
315   }
316 
317   /** {@inheritDoc} */
318   @Override
319   public String toString() {
320     StringBuilder sb = new StringBuilder(keyword + " ");
321     for (String s : data) {
322       sb.append(s);
323     }
324     return sb.toString();
325   }
326 }