====== How to use the FileLoader to read complex text files ====== //Upgraded fc-11.12.2017 Removed fileLoader report, replaced by exceptions// The FileLoader was built to facilitate the reading of parameters, measurements or inventory text files. These files may contain keyword parameters and rows with values in columns separated by tabs (e.g. 2 columns below): # Simeo-Principes command file parameterFileName = PhoenixRomana.txt plantAge = 200 plantSeed = 1 archimed1ConfigFileName = archimed1.config # Pattern coordinates in m pattern = ((0,10);(10,0)) patternBounds = ((-5,-5);(15,15)) # simName simulationParameters sim1 PinnaeDistance_1(2D;1;(0:2.3);(50:10);(100:2)) sim2 PinnaeDistance_1(2D;1;(0:6);(50:15);(100:4)) To read such a file, it is possible to write a subclass of FileLoader where **the public fields are expected to be automatically loaded** from the file. FileLoader proposes methods to interpret the lines and turn them into objects, by using Java introspection, based on the order and types of the fields. * Each **public field** will be searched in the file for a keyword parameter with same name and its type will be checked (e.g. error if an int is expected and a double is found in the file). * Each **public List** will be fed with the lines in the file matching the SomeType pattern. SomeType must extend jeeb.lib.util.Record, have its **fields public** and provide a constructor with a String parameter relying on the constructor of Record, so that an automatic type-matching method is run. The lines of the file starting with a '#' comment mark and blank lines are ignored. Possible types for the **public fields** in the loader: * byte, short, int, long * float, double * boolean * char * String * a subclass of Record Possible types for the **public lists**: * List (only List is possible, no other collection like Set...) * SomeType must extend Record, have its fields public and chain to its constructor (String) (see the example below) Here is an example of loader for the file upper: package jeeb.simeo.module.edito.scripts; import java.util.List; import jeeb.lib.util.Record; import jeeb.lib.util.fileloader.FileLoader; import jeeb.lib.util.fileloader.FileLoaderCheck; /** * Testing the new FileLoader class. * *
 * // To test this FileLoader
 * java -cp bin jeeb.simeo.module.edito.scripts.ScriptRaphael2013FileLoader
 * 
* * @author F. de Coligny - December 2013 */ public class ScriptRaphael2013FileLoader extends FileLoader { public String parameterFileName; public int plantAge; public int plantSeed; public String archimed1ConfigFileName; public String pattern; public String patternBounds; public List simRecords; /** * Constructor. */ public ScriptRaphael2013FileLoader () throws Exception { super (); } protected void checks() throws Exception { checkAssertion(plantAge > 0, "plantAge must be > 0"); checkAssertion(plantSeed >= 0, "plantSeed must be >= 0"); checkAssertion(!simRecords.isEmpty (), "The simRecords list should not be empty"); } // A line in the command file static public class SimRecord extends Record { // e.g. sim1 public String simName; // e.g. PinnaeDistance_1(2D;1;(0:2.3);(50:10);(100:2)) ! // FrondNervureLength_1(2D;1;(1:100);(175:400)) public String simulationParameters; public SimRecord(String line) throws Exception { super(line); } } }
This loader may be used like below: (...) // Interpret command file ScriptRaphael2013FileLoader loader = new ScriptRaphael2013FileLoader(); loader.load(commandFileName); // commandFileName = reader.getCommandFileName(); parameterFileName = loader.parameterFileName; plantAge = loader.plantAge; plantSeed = loader.plantSeed; pattern = loader.pattern; patternBounds = loader.patternBounds; archimed1ConfigFileName = loader.archimed1ConfigFileName; List simRecords = loader.simRecords; int numberOfSimulations = simRecords.size(); (...) The constructor and load () method may throw an exception if a problem arises during the file interpretation. Possible errors are: * a field expected by the loader could not be found in the file (missing in file) * a field in the file is unknown by the loader (missing in loader) * a field has a wrong type (e.g. expected int, found double) * a check failed (e.g. plantAge is supposed to be > 0) * ... It is possible to add checks by overriding the **checks ()** method. It is possible to check simple assertions like in the example above. It is also possible to make more complicated checks like checking if the number of rows in the list is greater than 0. In case of trouble in checks (), throw an exception to stop the process. After loading the file, all fields can be retrieved directly from the loader, they are all public fields. It is also possible to add and call a method in the fileLoader to change all these parameters into another data structure, e.g. a list of trees in a forest stand or a predefined parameter object. Finally, it is possible to call the FileLoader **toString ()** method after the process to check what happened. Here are two examples after a successful and after a failed loading : FileLoader : ScriptRaphael2013FileLoader loaded file : /home/coligny/workspace/amapstudio/data/simeo/edito/principesRaphael/wd1/principesCmd1.txt loading succeeded: true -- Loaded data -- parameterFileName = PhoenixRomana.txt plantAge = 200 plantSeed = 1 archimed1ConfigFileName = archimed1.config pattern = ((0,10);(10,0)) patternBounds = ((-5,-5);(15,15)) simRecords: sim1 PinnaeDistance_1(2D;1;(0:2.3);(50:10);(100:2)) sim2 PinnaeDistance_1(2D;1;(0:6);(50:15);(100:4)) -- end-of-loaded data -- FileLoader : ScriptRaphael2013FileLoader loaded file : /home/coligny/workspace/amapstudio/data/simeo/edito/principesRaphael/wd1/principesCmd1.txt loader report : The simRecords list should not be empty loading succeeded: false ===== Additional features ===== ==== Change the separator ==== In a record format (subclass of Record) like SimRecord upper, the default separator between the fields of a line is a tabulation. It is possible to change this by overriding a method in SimRecord, e.g. // Change the default \t separator by ; public String getSeparator() { return ";"; } ==== Manage comment marks ==== The file reader ignores all lines starting by a 'commentMark', by default '#' It is possible to add comment marks, lines starting with all comment marks will be ignored, e.g. // Ignore the lines starting with "//" fileReader.addAdditionalCommentMark("//");