View Javadoc

1   /*
2    * Copyright 2006 - 2012 Christina Bohk and Roland Ewald
3    *  
4    * Licensed under the Apache License, Version 2.0 (the "License"); 
5    * you may not use this file except in compliance with the License. 
6    * You may obtain a copy of the License at 
7    *  
8    *  http://www.apache.org/licenses/LICENSE-2.0
9    *  
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
13   * See the License for the specific language governing permissions and 
14   * limitations under the License. 
15   */
16  package p3j.experiment.results;
17  
18  import james.SimSystem;
19  import james.core.util.misc.Strings;
20  import james.core.util.misc.Triple;
21  
22  import java.io.BufferedInputStream;
23  import java.io.BufferedOutputStream;
24  import java.io.File;
25  import java.io.FileOutputStream;
26  import java.io.FileWriter;
27  import java.io.IOException;
28  import java.io.Writer;
29  import java.text.SimpleDateFormat;
30  import java.util.ArrayList;
31  import java.util.Calendar;
32  import java.util.Collections;
33  import java.util.Comparator;
34  import java.util.HashMap;
35  import java.util.List;
36  import java.util.Map;
37  import java.util.logging.Level;
38  
39  import p3j.database.DatabaseFactory;
40  import p3j.database.IProjectionResultsIterator;
41  import p3j.experiment.results.filters.IResultFilter;
42  import p3j.gui.P3J;
43  import p3j.misc.gui.GUI;
44  import p3j.misc.math.Matrix2D;
45  import p3j.pppm.ProjectionModel;
46  import freemarker.template.Configuration;
47  import freemarker.template.DefaultObjectWrapper;
48  import freemarker.template.Template;
49  import freemarker.template.TemplateException;
50  
51  /**
52   * Class responsible for exporting results. There are three modes:
53   * 
54   * Create report: aggregates all data to be displayed in the result report.
55   * 
56   * Export aggregated data: exports various data aggregations, does not copy or
57   * generate report-related files.
58   * 
59   * Export data: exports all raw data, to be analyzed with other tools.
60   * 
61   * TODO: Remove hard-coded density-plot years here and in plotting.R!
62   * 
63   * @author Christina Bohk
64   * @author Roland Ewald
65   */
66  public class ResultExport {
67  
68    /** The name of the R library required for report generation. */
69    public static final String R_LIBRARY = "plotting.R";
70  
71    /** The template directory. */
72    public static final String TEMPLATE_DIRECTORY = "report_template";
73  
74    /** The buffer size for copying the plotting library. */
75    private static final int COPY_BUFFER_SIZE = 1024;
76  
77    /** The result filter to be used. */
78    private final IResultFilter resultFilter;
79  
80    /** The year offsets to consider for plotting probability densities. */
81    private final int[] yearOffsetToConsider;
82  
83    /** The number of years to be projected. */
84    private final int numOfYears;
85  
86    /** The number of trials. */
87    private final int numOfTrials;
88  
89    /** The target directory. */
90    private final File targetDir;
91  
92    /** Reference to the projection of which the results shall be exported. */
93    private final ProjectionModel projection;
94  
95    /**
96     * The current quantiles to be used. TODO: This should be parameterisable via
97     * the UI.
98     */
99    static final double[] QUANTILES = new double[] { 0, .025, .05, .1, .5, .9,
100       0.95, 0.975, 1 };
101 
102   /** The number of sub-plots to be put in a density plot. */
103   static final int DENSITY_PLOT_NUM_SUBPLOTS = 9;
104 
105   /** The offset between jump-off year and first year to be plotted. */
106   static final int DENSITY_PLOT_FIRST_OFFSET = 3;
107 
108   /** The regular offset between years to be plotted. */
109   static final int DENSITY_PLOT_REGULAR_OFFSET = 5;
110 
111   /**
112    * Instantiates a new result export.
113    * 
114    * @param projectionModel
115    *          the projection model
116    * @param targetDirectory
117    *          the target directory
118    * @param resultFilter
119    *          the result filter to be used
120    */
121   public ResultExport(ProjectionModel projectionModel, File targetDirectory,
122       IResultFilter resultFilter) {
123 
124     projection = projectionModel;
125     targetDir = targetDirectory;
126     this.resultFilter = resultFilter;
127 
128     // Check whether target directory exists
129     if (!targetDir.exists() && !targetDir.mkdir()) {
130       throw new IllegalArgumentException("Could not create directory: "
131           + targetDir.getAbsolutePath());
132     }
133 
134     // Consider each year for aggregation
135     yearOffsetToConsider = new int[projectionModel.getYears() - 1];
136     for (int i = 0; i < yearOffsetToConsider.length; i++) {
137       yearOffsetToConsider[i] = 1;
138     }
139 
140     // Store the number overall number of years and trials
141     numOfYears = projectionModel.getYears();
142     numOfTrials = DatabaseFactory.getDatabaseSingleton()
143         .getAllResults(projection).size();
144   }
145 
146   /**
147    * Exports all results.
148    * 
149    * @throws IOException
150    *           if export fails
151    */
152   public void exportAllResults() throws IOException {
153     File dataDirectory = initializeSubDirectory("data");
154     exportData(dataDirectory);
155     GUI.printMessage(P3J.getInstance(), "Results Export Finished",
156         "Results export is finished.");
157   }
158 
159   /**
160    * Exports aggregated data.
161    * 
162    * @param selectedDir
163    *          the selected directory for export
164    * @throws IOException
165    *           if data storage failed
166    */
167   public void exportAggregatedResults() throws IOException {
168     File aggregatedDirectory = initializeSubDirectory("aggregated_data");
169     aggregateData(aggregatedDirectory,
170         (new ResultAggregation(projection.getGenerations(), numOfYears))
171             .getSelectorsForAggregatedDataExport());
172     GUI.printMessage(P3J.getInstance(), "Aggregated Results Export Finished",
173         "Aggregated results export is finished.");
174   }
175 
176   /**
177    * Creates result report displaying aggregated results.
178    * 
179    * @throws IOException
180    *           if aggregation of data fails
181    * @throws TemplateException
182    *           if processing of report template fails
183    */
184   public void createResultReport() throws IOException, TemplateException {
185     File aggregatedDirectory = initializeSubDirectory("aggregated");
186     IAggregationSelector[] selectors = (new ResultAggregation(
187         projection.getGenerations(), numOfYears)).getSelectorsForReport();
188     Map<String, Object> aggregationInfoMap = aggregateData(aggregatedDirectory,
189         selectors);
190     copyPlottingLib();
191     createSweaveFile(aggregationInfoMap);
192     GUI.printMessage(
193         P3J.getInstance(),
194         "Report Generation Finished",
195         "Result report generation is finished. Now run \"Sweave('report.Rtex')\" in R, then use LaTeX to process the 'report.tex' file.'");
196   }
197 
198   /**
199    * Stores assumption encoding.
200    * 
201    * @param projection
202    *          the projection
203    * @param targetDir
204    *          the target directory
205    * 
206    * @return the corresponding parameter assumption encoder
207    * 
208    * @throws IOException
209    *           Signals that an I/O exception has occurred.
210    */
211   private ParameterAssumptionEncoder storeAssumptionEncoding(
212       ProjectionModel projection, File targetDir) throws IOException {
213     ParameterAssumptionEncoder assumptionEncoder = new ParameterAssumptionEncoder(
214         projection);
215     assumptionEncoder.writeMappingSummary(targetDir);
216     return assumptionEncoder;
217   }
218 
219   /**
220    * Aggregate data.
221    * 
222    * @param destinationDir
223    *          the destination directory
224    * @param projection
225    *          the projection
226    * 
227    * @return a map containing the data to parse into the report template
228    * @throws IOException
229    *           if data storage failed
230    */
231   private Map<String, Object> aggregateData(File destinationDir,
232       IAggregationSelector[] selectors) throws IOException {
233 
234     // Create assumption encoder
235     ParameterAssumptionEncoder assumptionEncoder = storeAssumptionEncoding(
236         projection, destinationDir);
237     Map<String, Object> results = new HashMap<String, Object>();
238 
239     // TODO: Implement multi-threaded report-generation.
240     // Each selector should have its own thread.
241 
242     initializeSelectors(projection, selectors);
243     List<Triple<Integer, Double, int[]>> trialAssumptions = analyzeResults(
244         projection, selectors, assumptionEncoder);
245     storeData(destinationDir, selectors, trialAssumptions);
246 
247     results.put(
248         "densityYears",
249         createDensityYearList(projection.getJumpOffYear(),
250             DENSITY_PLOT_FIRST_OFFSET, DENSITY_PLOT_REGULAR_OFFSET));
251     results.put("finalYearIndex", numOfYears - 1);
252     results.put("allYears",
253         createFullYearList(projection.getJumpOffYear(), numOfYears - 1));
254 
255     return results;
256   }
257 
258   /**
259    * Store data.
260    * 
261    * @param destinationDir
262    *          the destination dir
263    * @param selectors
264    *          the selectors
265    * @param trialAssumptions
266    *          the trial assumptions
267    * 
268    * @throws IOException
269    *           Signals that an I/O exception has occurred.
270    */
271   private void storeData(File destinationDir, IAggregationSelector[] selectors,
272       List<Triple<Integer, Double, int[]>> trialAssumptions) throws IOException {
273     List<Integer> indexOrder = sortAndStore(destinationDir, trialAssumptions);
274     for (IAggregationSelector selector : selectors) {
275       selector.finish(destinationDir, indexOrder, this);
276     }
277   }
278 
279   /**
280    * Sorts assumption sets of trial by probability (descending order) and stores
281    * them into file.
282    * 
283    * @param destinationDir
284    *          the destination directory
285    * @param trialAssumptions
286    *          the trial assumptions
287    * 
288    * @return the list of trial indices, which defines the reordering
289    * 
290    * @throws IOException
291    *           Signals that an I/O exception has occurred.
292    */
293   private List<Integer> sortAndStore(File destinationDir,
294       List<Triple<Integer, Double, int[]>> trialAssumptions) throws IOException {
295 
296     // Sort in *descending* order
297     Collections.sort(trialAssumptions,
298         new Comparator<Triple<Integer, Double, int[]>>() {
299           @Override
300           public int compare(Triple<Integer, Double, int[]> o1,
301               Triple<Integer, Double, int[]> o2) {
302             return -1 * o1.getB().compareTo(o2.getB());
303           }
304         });
305 
306     // Write file
307     FileWriter fw = new FileWriter(destinationDir.getAbsolutePath()
308         + File.separatorChar + "assumptions_sorted.txt");
309     List<Integer> resultingOrder = new ArrayList<Integer>();
310     for (Triple<Integer, Double, int[]> trialAssumption : trialAssumptions) {
311       resultingOrder.add(trialAssumption.getA());
312       fw.append(trialAssumption.getA() + "\t" + trialAssumption.getB() + "\t");
313       for (int elem : trialAssumption.getC()) {
314         fw.append(elem + "\t");
315       }
316       fw.append("\n");
317     }
318     fw.close();
319 
320     return resultingOrder;
321   }
322 
323   /**
324    * Analyzes and filters results. Applies all selectors to the results that are
325    * not filtered.
326    * 
327    * @param projection
328    *          the projection
329    * @param selectors
330    *          the selectors
331    * @param assumptionEncoder
332    *          the assumption encoder
333    * 
334    * @return the assumptions of the trials - triples of (#trial, probability,
335    *         encoded assumptions) - in correct order
336    */
337   private List<Triple<Integer, Double, int[]>> analyzeResults(
338       ProjectionModel projection, IAggregationSelector[] selectors,
339       ParameterAssumptionEncoder assumptionEncoder) {
340     IProjectionResultsIterator resultsIterator = DatabaseFactory
341         .getDatabaseSingleton().getResultIterator(projection);
342     ResultsOfTrial result = resultsIterator.getNextResult();
343     int trialCount = 0;
344     List<Triple<Integer, Double, int[]>> trialAssumptions = new ArrayList<Triple<Integer, Double, int[]>>();
345 
346     while (result != null) {
347       if (!resultFilter.considerResult(result)) {
348         SimSystem.report(
349             Level.INFO,
350             "Results with ID " + result.getID()
351                 + " are dismissed by the result filter '"
352                 + Strings.dispClassName(resultFilter.getClass()));
353         result = resultsIterator.getNextResult();
354         continue;
355       }
356       SimSystem.report(Level.INFO, "Analyzing trial #" + (trialCount + 1));
357       try {
358         trialAssumptions.add(new Triple<Integer, Double, int[]>(trialCount,
359             result.getAssignmentProbability(), assumptionEncoder.encode(result
360                 .getAssignment())));
361       } catch (RuntimeException ex) {
362         SimSystem.report(Level.WARNING, "Encoding trial failed.", ex);
363         result = resultsIterator.getNextResult();
364         continue;
365       }
366 
367       for (IAggregationSelector selector : selectors) {
368         selector.consider(trialCount, result);
369       }
370       trialCount++;
371 
372       result = resultsIterator.getNextResult();
373     }
374     return trialAssumptions;
375   }
376 
377   /**
378    * Initialize selectors.
379    * 
380    * @param projection
381    *          the projection
382    * @param selectors
383    *          the selectors
384    */
385   private void initializeSelectors(ProjectionModel projection,
386       IAggregationSelector[] selectors) {
387     for (IAggregationSelector selector : selectors) {
388       selector
389           .init(numOfTrials, numOfYears, projection.getNumberOfAgeClasses());
390     }
391   }
392 
393   /**
394    * Calculates quantiles per column.
395    * 
396    * @param inputMatrix
397    *          the input matrix
398    * 
399    * @return the double[][]
400    */
401   protected double[][] calcQuantiles(double[][] inputMatrix) {
402     if (inputMatrix.length == 0) {
403       return new double[0][0];
404     }
405     int maxRowIndex = inputMatrix.length - 1;
406     int cols = inputMatrix[0].length;
407     double[][] result = new double[QUANTILES.length][cols];
408     for (int col = 0; col < cols; col++) {
409       List<Double> currentValues = new ArrayList<Double>();
410       for (int i = 0; i < inputMatrix.length; i++) {
411         currentValues.add(inputMatrix[i][col]);
412       }
413       Collections.sort(currentValues);
414       for (int i = 0; i < QUANTILES.length; i++) {
415         result[i][col] = currentValues.get((int) (QUANTILES[i] * maxRowIndex));
416       }
417     }
418     return result;
419   }
420 
421   /**
422    * An alternative way to create the list of years for the density plot.
423    * 
424    * TODO: Should be replaced as soon as possible by
425    * {@link ResultExport#createEquidistantDensityYearList()}, this will also
426    * require a change to the densityPlot function in plotting.R.
427    * 
428    * 
429    * @param jumpOffYear
430    *          the jump off year
431    * @param firstOffset
432    *          the first offset
433    * @param regularOffset
434    *          the regular offset between years
435    * @return the string containing the density year list
436    * 
437    */
438   private String createDensityYearList(int jumpOffYear, int firstOffset,
439       int regularOffset) {
440     StringBuffer yearList = new StringBuffer();
441     for (int i = 0; i < DENSITY_PLOT_NUM_SUBPLOTS; i++) {
442       yearList.append(jumpOffYear + firstOffset + i * regularOffset
443           + (i == DENSITY_PLOT_NUM_SUBPLOTS - 1 ? "" : ","));
444     }
445     return yearList.toString();
446   }
447 
448   /**
449    * Creates the density year list. The density years are equidistant among
450    * jump-off year and the last year of the projection.
451    * 
452    * @return the string containing the density year list
453    */
454   protected String createEquidistantDensityYearList(int jumpOffYear) {
455     StringBuffer yearList = new StringBuffer();
456     for (int i = 0; i < DENSITY_PLOT_NUM_SUBPLOTS; i++) {
457       yearList.append((jumpOffYear + (int) Math.round(i * numOfYears
458           / (DENSITY_PLOT_NUM_SUBPLOTS - 1.0)))
459           + (i == DENSITY_PLOT_NUM_SUBPLOTS - 1 ? "" : ","));
460     }
461     return yearList.toString();
462   }
463 
464   /**
465    * Creates the full year list.
466    * 
467    * @param jumpOffYear
468    *          the jump off year
469    * @param projectionYears
470    *          the projection years
471    * @return the string
472    */
473   private String createFullYearList(int jumpOffYear, int projectionYears) {
474     StringBuffer yearList = new StringBuffer(Integer.toString(jumpOffYear));
475     yearList.append(',');
476     int lastYear = jumpOffYear;
477     for (int i = 0; i < projectionYears - 1; i++) {
478       lastYear++;
479       yearList.append(lastYear);
480       yearList.append(',');
481     }
482     yearList.append("" + (lastYear + 1));
483     return yearList.toString();
484   }
485 
486   /**
487    * Filter out all unnecessary data.
488    * 
489    * @param original
490    *          the original
491    * 
492    * @return the double[][]
493    */
494   protected double[][] filter(double[][] original) {
495     double[][] filteredResult = new double[original.length][yearOffsetToConsider.length + 1];
496     for (int i = 0; i < original.length; i++) {
497       filteredResult[i][0] = original[i][0];
498       int currentOffset = 0;
499       for (int j = 0; j < yearOffsetToConsider.length; j++) { // NOSONAR
500         currentOffset += yearOffsetToConsider[j];
501         filteredResult[i][j + 1] = original[i][currentOffset];
502       }
503     }
504     return filteredResult;
505   }
506 
507   /**
508    * Write result.
509    * 
510    * @param destinationDir
511    *          the destination dir
512    * @param result
513    *          the result
514    * @param fileName
515    *          the file name
516    * 
517    * @throws IOException
518    *           Signals that an I/O exception has occurred.
519    */
520   protected void writeResult(File destinationDir, double[][] result,
521       String fileName) throws IOException {
522     FileWriter fw = new FileWriter(destinationDir.getAbsolutePath()
523         + File.separatorChar + fileName);
524     fw.append(toCSV(result, ','));
525     fw.close();
526   }
527 
528   /**
529    * Export data.
530    * 
531    * @param dataDirectory
532    *          the data directory
533    * 
534    * @throws IOException
535    *           Signals that an I/O exception has occurred.
536    */
537   private void exportData(File dataDirectory) throws IOException {
538     SimSystem.report(Level.INFO, "Exporting CSV data...");
539     IProjectionResultsIterator resultsSet = DatabaseFactory
540         .getDatabaseSingleton().getResultIterator(projection);
541 
542     ParameterAssumptionEncoder assumptionEncoder = storeAssumptionEncoding(
543         projection, dataDirectory);
544 
545     int resultCount = 1;
546     for (ResultsOfTrial results : resultsSet) {
547       SimSystem.report(Level.INFO, "Export Trial #" + resultCount + " (ID: "
548           + results.getID() + ")");
549       if (!resultFilter.considerResult(results)) {
550         SimSystem.report(
551             Level.INFO,
552             "Results with ID " + results.getID()
553                 + " are dismissed by the result filter '"
554                 + Strings.dispClassName(resultFilter.getClass()));
555         continue;
556       }
557 
558       File trialDirectory = new File(dataDirectory.getAbsolutePath()
559           + File.separatorChar + "trial_" + resultCount);
560       if (!trialDirectory.mkdir()) {
561         throw new IllegalStateException("Could not create directory: "
562             + trialDirectory.getAbsolutePath());
563       }
564       exportBasicResults(trialDirectory, results.getNativesResults(), "natives");
565       writeGenerationResults(trialDirectory, results.getImmigrantResults(),
566           "immigrants");
567       writeGenerationResults(trialDirectory, results.getEmigrantResults(),
568           "emigrants");
569       writeAssumptionsUsed(trialDirectory, results, assumptionEncoder);
570       resultCount++;
571     }
572   }
573 
574   /**
575    * Writes a file that specifies which assumptions are used for the parameters
576    * in a given trial.
577    * 
578    * @param trialDirectory
579    *          the trial directory
580    * @param results
581    *          the results of the trial
582    * @param assumptionEncoder
583    *          the encoder for the assumptions that are used
584    * 
585    * @throws IOException
586    *           Signals that an I/O exception has occurred.
587    */
588   private void writeAssumptionsUsed(File trialDirectory,
589       ResultsOfTrial results, ParameterAssumptionEncoder assumptionEncoder)
590       throws IOException {
591 
592     FileWriter fw = new FileWriter(trialDirectory.getAbsolutePath()
593         + File.separatorChar + "assumptions.txt");
594     fw.append("Assumption probability: \t" + results.getAssignmentProbability()
595         + "\n");
596     fw.append("Numerical encoding: \t"
597         + Strings.dispArray(assumptionEncoder.encode(results.getAssignment()))
598         + "\n");
599     fw.append("\n\nAssumptions: \n"
600         + assumptionEncoder.verboseEncoding(results.getAssignment()) + "\n");
601     fw.close();
602   }
603 
604   /**
605    * Write results for a list of basic results that correspond to different
606    * generations of the same population.
607    * 
608    * @param trialDirectory
609    *          the trial directory
610    * @param results
611    *          the results to be written to file
612    * @param populationName
613    *          the name of the population
614    * 
615    * @throws IOException
616    *           Signals that an I/O exception has occurred.
617    */
618   private void writeGenerationResults(File trialDirectory,
619       List<BasicResults> results, String populationName) throws IOException {
620     for (int i = 0; i < results.size(); i++) {
621       exportBasicResults(trialDirectory, results.get(i), populationName
622           + "_gen_" + i);
623     }
624   }
625 
626   /**
627    * Export basic results.
628    * 
629    * @param trialDirectory
630    *          the trial directory
631    * @param basicResults
632    *          the basic results
633    * @param exportPrefix
634    *          the export prefix
635    * 
636    * @throws IOException
637    *           Signals that an I/O exception has occurred.
638    */
639   private void exportBasicResults(File trialDirectory,
640       BasicResults basicResults, String exportPrefix) throws IOException {
641     exportMatrix(trialDirectory, basicResults.getEndXm(), exportPrefix
642         + "_end_x_m.csv");
643     exportMatrix(trialDirectory, basicResults.getEndXf(), exportPrefix
644         + "_end_x_f.csv");
645     exportMatrix(trialDirectory, basicResults.getMeanXm(), exportPrefix
646         + "_mean_x_m.csv");
647     exportMatrix(trialDirectory, basicResults.getMeanXf(), exportPrefix
648         + "_mean_x_f.csv");
649   }
650 
651   /**
652    * Export matrix.
653    * 
654    * @param trialDirectory
655    *          the trial directory
656    * @param data
657    *          the data
658    * @param fileName
659    *          the file name
660    * 
661    * @throws IOException
662    *           Signals that an I/O exception has occurred.
663    */
664   private static void exportMatrix(File trialDirectory, Matrix2D data,
665       String fileName) throws IOException {
666     if (data == null) {
667       SimSystem.report(Level.INFO, "WARNING: Cannot write '" + fileName
668           + "' - data is null.");
669       return;
670     }
671     FileWriter fw = new FileWriter(trialDirectory.getAbsolutePath()
672         + File.separatorChar + fileName);
673     fw.append(toCSV(data.toArray(), ','));
674     fw.close();
675   }
676 
677   /**
678    * Initialize sub directory.
679    * 
680    * @param subDirName
681    *          the name of the sub-directory
682    * 
683    * @return the file
684    */
685   private File initializeSubDirectory(String subDirName) {
686     File subDirectory = new File(targetDir.getAbsolutePath()
687         + File.separatorChar + subDirName);
688 
689     if (subDirectory.exists()
690         && (!deleteAllDirFiles(subDirectory) || !subDirectory.delete())) {
691       throw new IllegalArgumentException("Could not delete directory: "
692           + subDirectory.getAbsolutePath());
693     }
694 
695     if (!subDirectory.mkdir()) {
696       throw new IllegalArgumentException("Could not create directory: "
697           + subDirectory.getAbsolutePath());
698     }
699     return subDirectory;
700   }
701 
702   /**
703    * Delete all files in a directory.
704    * 
705    * @param dir
706    *          the directory
707    * 
708    * @return true, if successful
709    */
710   private boolean deleteAllDirFiles(File dir) {
711     for (File subFile : dir.listFiles()) {
712       if (!subFile.delete()) {
713         return false;
714       }
715     }
716     return true;
717   }
718 
719   /**
720    * Copies plotting library to target directory.
721    * 
722    * @throws IOException
723    *           if copying fails
724    */
725   private void copyPlottingLib() throws IOException {
726 
727     //TODO: User JAMESII result reporting instead
728     
729     String sourcePath = '/' + TEMPLATE_DIRECTORY + '/' + R_LIBRARY;
730     String targetPath = targetDir.getAbsolutePath() + File.separatorChar
731         + R_LIBRARY;
732 
733     BufferedInputStream source = null;
734     BufferedOutputStream target = null;
735     final String errorMessage = "Could not copy library file from '"
736         + sourcePath + "' to '" + targetPath
737         + "' (required to execute R code).";
738     try {
739       source = new BufferedInputStream(getClass().getResourceAsStream(
740           sourcePath));
741       target = new BufferedOutputStream(new FileOutputStream(targetPath));
742       byte[] buffer = new byte[COPY_BUFFER_SIZE];
743       int len = source.read(buffer);
744       while (len != -1) {
745         target.write(buffer, 0, len);
746         len = source.read(buffer);
747       }
748     } catch (IOException e) {
749       GUI.printErrorMessage(errorMessage, e);
750     } finally {
751       try {
752         if (target != null) {
753           target.close();
754         }
755       } catch (IOException e) {
756         GUI.printErrorMessage(errorMessage, e);
757       }
758       try {
759         if (source != null) {
760           source.close();
761         }
762       } catch (IOException e) {
763         GUI.printErrorMessage(errorMessage, e);
764       }
765     }
766   }
767 
768   /**
769    * Export to CSV.
770    * 
771    * @param matrix
772    *          the matrix
773    * @param delim
774    *          the delimiter
775    * 
776    * @return the string builder
777    */
778   public static StringBuilder toCSV(double[][] matrix, char delim) {
779     if (matrix == null) {
780       return new StringBuilder("null");
781     }
782     StringBuilder matrixString = new StringBuilder();
783     for (int i = 0; i < matrix.length; i++) {
784       for (int j = 0; j < matrix[i].length; j++) {
785         matrixString.append(Double.toString(matrix[i][j]));
786         if (j < matrix[i].length - 1) {
787           matrixString.append(delim);
788         }
789       }
790       matrixString.append('\n');
791     }
792     return matrixString;
793   }
794 
795   /**
796    * Creates the Sweave file, inserts all data contianed in map.
797    * 
798    * @param dataToBeInserted
799    *          map containing the pointers to the aggregated data
800    * @throws IOException
801    *           if file creation fails
802    * @throws TemplateException
803    *           if template processing fails
804    */
805   private void createSweaveFile(Map<String, Object> dataToBeInserted)
806       throws IOException, TemplateException {
807 
808     SimSystem.report(Level.INFO, "Creating report template.");
809 
810     Configuration cfg = new Configuration();
811     cfg.setClassForTemplateLoading(ResultExport.class, "/" + TEMPLATE_DIRECTORY);
812     cfg.setObjectWrapper(new DefaultObjectWrapper());
813 
814     dataToBeInserted.put("projection", projection);
815     dataToBeInserted.put("ages", projection.getMaximumAge() + 1);
816     dataToBeInserted.put("years", projection.getYears() + 1);
817     dataToBeInserted.put("trials", numOfTrials);
818     dataToBeInserted.put("date", (new SimpleDateFormat(
819         "dd. MM. yyyy (HH:mm:ss)")).format(Calendar.getInstance().getTime()));
820     dataToBeInserted.put("numsettypes", projection.getAllSetTypes().size());
821 
822     // Add index list of generations
823     List<Integer> generations = new ArrayList<Integer>();
824     for (int i = 0; i < projection.getGenerations(); i++) {
825       generations.add(i);
826     }
827     dataToBeInserted.put("generations", generations);
828 
829     // Add index list of descendant generations
830     List<Integer> descendantGenerations = new ArrayList<Integer>();
831     for (int i = 1; i < projection.getGenerations(); i++) {
832       descendantGenerations.add(i);
833     }
834     dataToBeInserted.put("desc_generations", descendantGenerations);
835 
836     Template basicTemplate = cfg.getTemplate("template.Rtfm");
837     Writer out = new FileWriter(targetDir.getAbsolutePath()
838         + File.separatorChar + "report.Rtex");
839     basicTemplate.process(dataToBeInserted, out);
840     out.close();
841   }
842 
843   /**
844    * Gets the result filter.
845    * 
846    * @return the result filter
847    */
848   public IResultFilter getResultFilter() {
849     return resultFilter;
850   }
851 
852 }