001// Copyright 2007 by Basil Vandegriend.  All rights reserved.
002
003package com.basilv.envgen;
004
005import com.basilv.core.Assert;
006import com.basilv.core.StringUtilities;
007import com.csvreader.CsvReader;
008
009/**
010 * Loads environment properties from the specified CSV file and populates an instance of 
011 * EnvironmentProperties. 
012 * 
013 * The file format is as follows: 
014 * <ul>
015 * <li>Each row corresponds to a single property or to a comment.</li>
016 * <li>For a property, the first column contains the name of the property.<br> 
017 * Each subsequent column contains the value of the property for a single environment.</li>
018 * <li>For a comment, the first column must start with the symbol '#' or '//', or be completely empty. 
019 * The remainder of the row is ignored. </li>
020 * </ul>
021 */
022public class EnvironmentPropertiesLoader
023{
024
025        /**
026         * Load the CSV file into an EnvironmentProperties instance.
027         * @param filename  Cannot be null or empty. Must correspond to a CSV file of the correct format.
028         * @return the EnvironmentProperties instance.
029         */
030        public static EnvironmentProperties load(String filename) {
031                Assert.notNullOrEmpty("filename", filename);
032                
033                try {
034                        CsvReader reader = new CsvReader(filename);
035                        try {
036                                EnvironmentProperties envProps = null;
037
038                                final int FIRST_ROW = 1; 
039                                int rowNumber = FIRST_ROW; // Indexed from one.
040                                while (reader.readRecord()) {
041                                        String propertyName = reader.get(0);
042                                        if (isComment(propertyName)) {
043                                                continue;
044                                        }
045                                        
046                                        int columnCount = reader.getColumnCount();
047                                        Assert.isTrue("Must have at least two columns - one for the property name " +
048                                                "and one row per environment for the property value.", columnCount > 1);
049                                        if (rowNumber == FIRST_ROW) {
050                                                int envCount = columnCount - 1;
051                                                envProps = new EnvironmentProperties(envCount);
052                                        }
053                                        
054                                        for (int column = 1; column < columnCount; column++) {
055                                                int environment = column - 1;
056                                                String propertyValue = reader.get(column);
057                                                envProps.addProperty(environment, propertyName, propertyValue);
058                                        }
059                                        
060                                        rowNumber++;
061                                }
062                                if (envProps == null) {
063                                        Assert.shouldNotReach("File [" + filename + "] does not contain any rows.");
064                                }
065                                
066                                return envProps;
067
068                        } finally {
069                                reader.close();
070                        }
071                } catch (Exception e) {
072                        throw new RuntimeException("Error reading cvs file [" + 
073                                filename + "] due to " + e.getMessage() + ".", e);
074                }
075                
076        }
077
078        // Non-private for testing
079        static boolean isComment(String propertyName) {
080                if (StringUtilities.isNullOrEmpty(propertyName)) {
081                        return true;
082                }
083                
084                if (propertyName.startsWith("#")) {
085                        return true;
086                }
087
088                if (propertyName.startsWith("//")) {
089                        return true;
090                }
091
092                return false;
093        }
094        
095}