uk.co.platosys.boox.core.Boox.java Source code

Java tutorial

Introduction

Here is the source code for uk.co.platosys.boox.core.Boox.java

Source

/* 
 * Boox.java
  * 
Copyright (C) 2008  Edward Barrow
    
This class is free software; you can redistribute it and/or
modify it under the terms of the GNU  General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
    
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.
    
You should have received a copy of the GNU General Public
License along with this program; if not, write to the Free
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 * 
 * for more information contact edward.barrow@platosys.co.uk
     
 * Boox.java
 *
 * Created on 24 March 2007, 14:02
 *
 * 
 */

package uk.co.platosys.boox.core;

import java.io.File;
import java.io.FileInputStream;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.Namespace;
import org.postgresql.util.PSQLException;

import uk.co.platosys.boox.compliance.Role;
import uk.co.platosys.boox.compliance.Task;
//import uk.co.platosys.boox.compliance.BasicTask;
//import uk.co.platosys.boox.compliance.Role;
//import uk.co.platosys.boox.compliance.Task;
import uk.co.platosys.boox.core.exceptions.BooxException;
import uk.co.platosys.boox.core.exceptions.BooxXMLException;
import uk.co.platosys.boox.core.exceptions.CredentialsException;
import uk.co.platosys.boox.core.exceptions.PermissionsException;
import uk.co.platosys.boox.money.Currency;
import uk.co.platosys.boox.money.Money;
import uk.co.platosys.db.jdbc.ConnectionSource;
import uk.co.platosys.db.jdbc.JDBCSerialTable;
import uk.co.platosys.db.Table;
import uk.co.platosys.db.jdbc.JDBCTable;
import uk.co.platosys.util.DocMan;
import uk.co.platosys.util.HashPass;
import uk.co.platosys.util.Logger;
import uk.co.platosys.util.ShortHash;

/**
 * This class provides static methods for administration and setup.
 *
 * It needs a serious bit of tidying up!
 *
 *
 * 
 * @author edward
 */
public class Boox {

    public final static String APPLICATION_NAME = "boox";
    public final static String DEFAULT_CURRENCY = "XBX";
    public final static String APPLICATION_DATABASE = "platax";//OK this is a kluj for now
    public final static Namespace NAMESPACE = Namespace.getNamespace("http://www.platosys.co.uk/boox");
    private final static Namespace ns = NAMESPACE;

    public static int JOURNAL_SIZE = 100;//number of journal transactions held in memory
    static Logger debugLogger = Logger.getLogger("boox");
    static Logger logger = debugLogger;
    public static String GUEST_NAME = "visitor";
    //some text constants
    static final String LEDGERS_ELEMENT_NAME = "Ledgers";
    static final String LEDGER_ELEMENT_NAME = "Ledger";
    static final String TASKS_ELEMENT_NAME = "Tasks";
    static final String TASK_ELEMENT_NAME = "Task";
    static final String DESCRIPTION_ELEMENT_NAME = "Description";
    static final String ACCOUNT_ELEMENT_NAME = "account";
    static final String NAME_ATTRIBUTE_NAME = "name";
    static final String PERMISSION_ATTRIBUTE_NAME = "permission";
    static final String CASCADES_ATTRIBUTE_NAME = "cascades";
    static final String IS_PRIVATE_ATTRIBUTE_NAME = "isPrivate";
    static final String FREQUENCY_ATTRIBUTE_NAME = "frequency";
    static final String FIRSTDATE_ATTRIBUTE_NAME = "firstdate";
    static final String ROLE_ATTRIBUTE_NAME = "role";
    static final String CURRENCY_ATTRIBUTE_NAME = "currency";

    /**
    * this returns true if your accounting system has been set up.
    * @param databaseName
    * @param applicationName
    * @return true if the accounting system is set up.
    */
    public static boolean isSetup(String databaseName, String applicationName) {
        return (JDBCTable.tableExists(databaseName, applicationName));
    }

    public static boolean markSetup(String databaseName, String enterpriseName) {
        try {
            JDBCTable enterpriseTable = new JDBCTable(databaseName, Enterprise.TABLENAME, Enterprise.KEY_COLNAME);
            String[] cols = { Enterprise.KEY_COLNAME, Enterprise.VALUE_COLNAME };
            String[] vals = { "setup", "true" };
            enterpriseTable.addRow(cols, vals);
            return true;
        } catch (Exception ex) {
            debugLogger.log("problem marking setup", ex);
            return false;
        }
    }

    /**
     *@param databaseName the database holding the accounts
     TODO: refactor to avoid direct SQL work.
     *
     *This method is called internally if no journal has been set up and it creates the necessary tables on the database.
     */

    static void createNewJournal(Enterprise enterprise) {
        Connection connection = null;
        try {
            connection = ConnectionSource.getConnection(enterprise.getDatabaseName());
            Statement statement = connection.createStatement();
            //create clerks JDBCTable first
            try {
                statement.execute("CREATE TABLE " + Clerk.TABLENAME + " (" + Clerk.NAME_COLNAME
                        + " text PRIMARY KEY," + Clerk.PASSWORD_COLNAME + " text, " + Clerk.LEDGER_COLNAME
                        + " text," + Clerk.SUPERVISOR_COLNAME + " text, " + Clerk.EMAIL_COLNAME + " text,"
                        + Clerk.ROLE_COLNAME + " text," + Clerk.ACCOUNTS_COLNAME + "  boolean,"
                        + Clerk.LEDGERS_COLNAME + " boolean," + Clerk.CLERKS_COLNAME + " boolean)");
                debugLogger.log(5, "Bx-cNJ: clerks JDBCTable created OK");
            } catch (SQLException ex) {
                debugLogger.log("failed to create clerks JDBCTable", ex);
            }
            //then ledgers
            try {
                statement.execute("CREATE TABLE " + Ledger.TABLENAME + "(" + Ledger.NAME_COLNAME + " text ,"
                        + Ledger.FULLNAME_COLNAME + " text PRIMARY KEY, " + Ledger.PARENT_COLNAME + " text, "
                        + Ledger.CURRENCY_COLNAME + " text," + Ledger.OWNER_COLNAME + " text REFERENCES  "
                        + Clerk.TABLENAME + " (" + Clerk.NAME_COLNAME + ")," + Ledger.ISPRIVATE_COLNAME
                        + " boolean)");
                debugLogger.log(5, "Bx-cNJ: ledgers Table created OK");
            } catch (SQLException ex) {
                debugLogger.log("failed to create ledgers Table", ex);
            }
            //then permissions
            try {
                statement.execute("CREATE TABLE " + Permission.TABLENAME + " (" + Permission.CLERK_COLNAME
                        + " text REFERENCES  " + Clerk.TABLENAME + " (" + Clerk.NAME_COLNAME + "),"
                        + Permission.LEDGER_COLNAME + " text REFERENCES " + Ledger.TABLENAME + "("
                        + Ledger.FULLNAME_COLNAME + ")," + Permission.ACCOUNTS_COLNAME + " boolean,"
                        + Permission.CREDIT_COLNAME + " boolean," + Permission.DEBIT_COLNAME + " boolean,"
                        + Permission.POST_COLNAME + " boolean," + Permission.BALANCE_COLNAME + " boolean,"
                        + Permission.READ_COLNAME + " boolean," + Permission.AUDIT_COLNAME + " boolean,"
                        + Permission.GET_BUDGET_COLNAME + " boolean," + Permission.SET_BUDGET_COLNAME + " boolean,"
                        + Permission.ALL_COLNAME + " boolean," + Permission.CASCADES_COLNAME + " boolean)");
                debugLogger.log(5, "Bx-cNJ: permissions JDBCTable created OK");
            } catch (SQLException ex) {
                debugLogger.log("failed to create permissions JDBCTable", ex);
            }
            //then clerkgroups
            try {
                statement.execute("CREATE TABLE " + ClerkGroup.TABLENAME + "(" + ClerkGroup.GROUP_COLNAME
                        + " text REFERENCES  " + Clerk.TABLENAME + " (" + Clerk.NAME_COLNAME + "),"
                        + ClerkGroup.CLERK_COLNAME + " text REFERENCES  " + Clerk.TABLENAME + " ("
                        + Clerk.NAME_COLNAME + ")," + ClerkGroup.OWNER_COLNAME + " boolean)");
                debugLogger.log(5, "Bx-cNJ: clerkgroupss Table created OK");
            } catch (SQLException ex) {
                debugLogger.log("failed to create clerkgroups Table", ex);
            }

            //then the chart of accounts      
            try {
                statement.execute("CREATE TABLE " + Chart.TABLENAME + "(" + Chart.SYSNAME_COLNAME
                        + " text PRIMARY KEY," + Chart.NAME_COLNAME + " text," + Chart.FULLNAME_COLNAME + " text,"
                        + Chart.OWNER_COLNAME + " text REFERENCES  " + Clerk.TABLENAME + " (" + Clerk.NAME_COLNAME
                        + ") ," + Chart.LEDGER_COLNAME + " text REFERENCES " + Ledger.TABLENAME + "("
                        + Ledger.FULLNAME_COLNAME + ")," + Chart.CURRENCY_COLNAME + " text," + Chart.TYPE_COLNAME
                        + " text," + Chart.DESCRIPTION_COLNAME + " text)");
                debugLogger.log(5, "Bx-cNJ: chart Table created OK");
            } catch (SQLException ex) {
                debugLogger.log("failed to create chart Table", ex);
            }
            /*This is the wrong place to create the tasks table. we have given it the wrong schema anyway
            //now the tasks JDBCTable
            try {
                 
            //statement.execute("CREATE TABLE journal (transactionID integer PRIMARY KEY, debit text, credit text, amount numeric(20,2), currency text, date timestamp, clerk text REFERENCES clerks (name), note text)");
            //
            JDBCTable taskTable = JDBCTable.createTable(enterprise.getDatabaseName(), Task.TABLENAME, Task.TASKNAME_COLNAME, Table.TEXT_COLUMN);
            taskTable.addColumn(Task.TASKDESCRIPTION_COLNAME, Table.TEXT_COLUMN);
            taskTable.addColumn(Task.FREQUENCY_COLNAME, Table.INTEGER_COLUMN);
            taskTable.addColumn(Task.LAST_DATE_COLNAME, Table.TIMESTAMP_COLUMN);
            taskTable.addColumn(Task.NEXT_DATE_COLNAME, Table.TIMESTAMP_COLUMN);
            taskTable.addColumn(Task.OWNER_COLNAME, Table.TEXT_COLUMN);
            taskTable.addColumn(Task.DELEGATE_COLNAME, Table.TEXT_COLUMN);
            taskTable.addColumn(Task.ROLE_COLNAME, Table.TEXT_COLUMN);
            //if((journalTable.addSerialRow(Task.TID_COLNAME, Task.NOTE_COLNAME, "Row Zero"))!=0){
               //throw new BooxException("Task Row Zero is not Zero!!!");
            //}
            debugLogger.log(5, "Bx-cNJ: tasks Table created OK");
                 
            } catch (Exception ex) {
            debugLogger.log("failed to create tasks Table", ex);
                   
            }*/
            //finally the journal JDBCTable
            try {

                //statement.execute("CREATE TABLE journal (transactionID integer PRIMARY KEY, debit text, credit text, amount numeric(20,2), currency text, date timestamp, clerk text REFERENCES clerks (name), note text)");
                //
                JDBCSerialTable journalTable = JDBCSerialTable.createTable(enterprise.getDatabaseName(),
                        Journal.TABLENAME, Journal.TID_COLNAME);
                if (journalTable == null) {
                    logger.log("journal table not created");
                }
                journalTable.addColumn(Journal.DEBIT_COLNAME, Table.TEXT_COLUMN);
                journalTable.addColumn(Journal.CREDIT_COLNAME, Table.TEXT_COLUMN);
                journalTable.addColumn(Journal.AMOUNT_COLNAME, Table.DECIMAL_COLUMN);
                journalTable.addColumn(Journal.CURRENCY_COLNAME, Table.TEXT_COLUMN);
                journalTable.addColumn(Journal.DATE_COLNAME, Table.TIMESTAMP_COLUMN);
                journalTable.addColumn(Journal.CLERK_COLNAME, Table.TEXT_COLUMN);
                journalTable.addColumn(Journal.NOTE_COLNAME, Table.TEXT_COLUMN);
                journalTable.addColumn(Journal.STATUS_COLNAME, Table.BOOLEAN_COLUMN);
                //if((journalTable.addSerialRow(Journal.TID_COLNAME, Journal.NOTE_COLNAME, "Row Zero"))!=0){
                //throw new BooxException("Journal Row Zero is not Zero!!!");
                //}
                debugLogger.log(5, "Bx-cNJ: journal Table created OK");

            } catch (Exception ex) {
                debugLogger.log("failed to create journalTable", ex);

            }
            try {
                //This line is created so that the account tables can have a balance line with transaction id = 0;
                statement.execute(
                        "INSERT INTO " + Journal.TABLENAME + " VALUES(0, null, null, 0, null, null, null)");
            } catch (SQLException ex) {
                debugLogger.log("failed to initialise journal Table", ex);

            }

            connection.close();
            debugLogger.log(5, "Bx-cnJ: journal created OK");
            //return new Journal(enterprise,0,false);
        } catch (Exception e) {
            try {
                connection.close();
            } catch (Exception p) {
            }

            debugLogger.log("Books failed to create new journal", e);
            // return null;
        }
    }

    /**
     * When first called for any existing set of accounts (i.e. on first running an application using Boox), this 
     * method creates both the supervisor - the Clerk with super-user powers - and the General Ledger, that is, the root 
     * ledger in the hierarchy of ledgers. It also creates a guest Clerk, who has no powers and whose password is the empty string.
     * 
     * If a supervisor has already been created, it will return a Clerk object,
     * provided the credentials are correct - that is, the name and password of the
     * supervisor first created.
     *
     * If the credentials don't match, it will throw a Credentials Exception
     * 
     * 
     * @param databaseName the name of the database.
     * @param name the supervisor username
     * @param password the supervisor's password
     * @return a clerk object, or null if there's an error.
     */
    public static Clerk createSupervisor(Enterprise enterprise, String name, String password)
            throws CredentialsException {
        String generalLedgerName = Ledger.ROOT_LEDGER_NAME;
        logger.log("boox creating new supervisor " + name + " for enterprise " + enterprise.getName());
        Connection connection = null;
        try {
            String databaseName = enterprise.getDatabaseName();
            logger.log("CreateSupervisor: database name is " + databaseName);
            connection = ConnectionSource.getConnection(databaseName);
            logger.log("got connection to " + databaseName);
            Statement statement = connection.createStatement();
            ResultSet rs = statement
                    .executeQuery("SELECT * from  " + Clerk.TABLENAME + "  WHERE role = 'supervisor'");
            if (!rs.next()) {
                logger.log("no supervisor created - creating one");
                statement.execute("INSERT INTO  " + Clerk.TABLENAME + "  (" + Clerk.NAME_COLNAME + ","
                        + Clerk.PASSWORD_COLNAME + "," + Clerk.LEDGER_COLNAME + "," + Clerk.SUPERVISOR_COLNAME + ","
                        + Clerk.ROLE_COLNAME + "," + Clerk.ACCOUNTS_COLNAME + "," + Clerk.LEDGERS_COLNAME + ","
                        + Clerk.CLERKS_COLNAME + ")" + " VALUES (\'" + name + "\',\'" + hash(password) + "\',\'"
                        + generalLedgerName + "\',\'" + name + "\',\'supervisor\',true,true,true)");
                debugLogger.log(5, "Boox - Supervisor added to db");

                statement.execute("INSERT INTO " + Ledger.TABLENAME + "(" + Ledger.NAME_COLNAME + ","
                        + Ledger.FULLNAME_COLNAME + "," + Ledger.PARENT_COLNAME + "," + Ledger.CURRENCY_COLNAME
                        + "," + Ledger.OWNER_COLNAME + ")" + " VALUES(\'" + generalLedgerName + "\',\'"
                        + generalLedgerName + "\',\'none\',\'all\',\'" + name + "\')");
                //statement.execute("ALTER JDBCTable clerks SET ledger REFERENCES ledgers(name)");
                statement.execute("INSERT INTO " + Permission.TABLENAME
                        + "(clerk, ledger, createAccounts, credit, debit, balance, read, audit) VALUES(\'" + name
                        + "\',\'" + generalLedgerName + "\', true, true, true, true, true, true)");
                connection.close();
                Ledger generalLedger = new Ledger(enterprise, generalLedgerName);
                Clerk supervisor = new Clerk(enterprise, name, password);
                ClerkGroup.createClerkGroup(enterprise, ClerkGroup.ALL_GROUPNAME, supervisor);
                Clerk.createClerk(enterprise, GUEST_NAME, "", supervisor, generalLedger, false, false, false, false,
                        false, false, false, false, false);

                //all.addMember(supervisor, guest);
                return supervisor;
            } else {
                connection.close();
                debugLogger.log(5, "Boox - didn't create supervisor, returning the existing one");
                return new Clerk(enterprise, name, password);//this bit throws the Credentials Exception!
            }
        } catch (CredentialsException ce) {
            logger.log("create supervisor ", ce);
            try {
                connection.close();
            } catch (Exception p) {
            }

            throw ce;
        } catch (Exception e) {
            try {
                connection.close();
            } catch (Exception p) {
            }

            debugLogger.log("error creating supervisor", e);
            return null;
        }
    }

    /**
     * Opens a ledger.
     * @param name
     * @param clerk
     * @return
     * @throws BooxException
     */
    public static Ledger openLedger(Enterprise enterprise, String name, Clerk clerk)
            throws BooxException, PermissionsException {
        return new Ledger(enterprise, name);
    }

    /**
     *     runs a check to ensure that the system is in balance.
     *     note that this is currency-agnostic. 
     * 
     */
    public static final boolean balance(String databaseName) {
        BigDecimal balance = BigDecimal.ZERO;
        Connection connection1 = null;
        Connection connection2 = null;
        try {
            connection1 = ConnectionSource.getConnection(databaseName);
            connection2 = ConnectionSource.getConnection(databaseName);

            Statement statement = connection1.createStatement();
            Statement statement2 = connection2.createStatement();

            ResultSet rs = statement.executeQuery("SELECT * FROM chart");
            while (rs.next()) {
                String name = rs.getString("name");
                ResultSet account = statement2
                        .executeQuery("SELECT amount FROM " + name + "WHERE contra = \'balance\'");
                if (account.next()) {
                    balance = balance.add(rs.getBigDecimal("amount"), Money.MC);

                } else {
                    connection1.close();
                    connection2.close();
                    return false;
                }
            }
            connection1.close();
            connection2.close();
        } catch (Exception e) {
            try {
                connection1.close();
                connection2.close();
            } catch (Exception p) {
            }

            return false;
        }
        if (!balance.equals(BigDecimal.ZERO)) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * Hashes the given password for security
     * @param password
     * @return a hash of the given password
     */
    public static String hash(String password) {
        return HashPass.hash(password);
    }

    /**
     * Tests for the existence of a Journal in the given database
     * @param databaseName
     * @return true if a Journal has been created.
     */

    public static boolean journalExists(String databaseName) {
        Connection connection = null;
        try {
            connection = ConnectionSource.getConnection(databaseName);
            Statement statement = connection.createStatement();
            try {
                ResultSet rs = statement.executeQuery(
                        "SELECT * FROM " + Journal.TABLENAME + " WHERE " + Journal.TID_COLNAME + "=0");
                if (rs.next()) {
                    debugLogger.log(5, "Boox-JournalExists: TRUE");
                    connection.close();
                    return true;
                } else {
                    debugLogger.log(5, "Boox-JournalExists: FALSE");

                    connection.close();
                    return false;
                }
            } catch (PSQLException psqe) {
                debugLogger.log(3, "Books-JournalExists? error:");

                connection.close();
                return false;

            }
        } catch (Exception e) {
            debugLogger.log("Books-JournalExists? error: ", e);
            return false;
        }

    }

    /**
     *returns the full name of an account given its system (short) name.
         
    public static String getFullName(String accountName){
        return getFullName(accountName, DATABASE_NAME);
    }*/
    public static String getFullName(String accountName, String databaseName) {
        Connection connection = null;
        try {
            connection = ConnectionSource.getConnection(databaseName);
            Statement statement = connection.createStatement();
            ResultSet rs = statement
                    .executeQuery("SELECT * FROM " + Chart.TABLENAME + " WHERE name = \'" + accountName + "\'");
            if (rs.next()) {
                String fullName = rs.getString(Chart.FULLNAME_COLNAME);
                connection.close();
                return fullName;
            } else {
                connection.close();
                debugLogger.log("Books-getFullName - couldn't find account named " + accountName);
                return null;
            }
        } catch (Exception e) {
            try {
                connection.close();
            } catch (Exception ex) {
            }
            debugLogger.log("Books-getFullName - error finding  account named " + accountName, e);
            return null;

        }
    }

    /**
     * 
     * @param databaseName
     * @param ledgerName
     * @return the String representing the currency of the ledger
     */
    public static String getLedgerCurrency(String databaseName, String ledgerName) {
        Connection connection = null;
        try {
            connection = ConnectionSource.getConnection(databaseName);
            Statement statement = connection.createStatement();
            ResultSet rs = statement.executeQuery("SELECT " + Ledger.CURRENCY_COLNAME + " FROM " + Ledger.TABLENAME
                    + " WHERE name = \'" + ledgerName + "\'");
            if (rs.next()) {
                String currency = rs.getString("currency");
                connection.close();
                return currency;
            } else {
                connection.close();
                debugLogger.log("Books-getLedgerCurrency - couldn't find ledger named " + ledgerName);
                return null;
            }
        } catch (Exception e) {
            try {
                connection.close();
            } catch (Exception ex) {
            }
            debugLogger.log("Books-getLedgerCurrency - error finding  ledger named " + ledgerName, e);
            return null;

        }
    }

    private static String readProperty(String propertyName) {
        String propertiesFileName = "/etc/platosys/boox.xml";

        try {
            File propertiesFile = new File(propertiesFileName);
            java.util.Properties properties = new java.util.Properties();
            properties.loadFromXML(new FileInputStream(propertiesFile));
            return properties.getProperty(propertyName);
        } catch (Exception e) {
            debugLogger.log("problem reading property", e);
            return null;
        }
    }

    /**
     * This returns a Map of the available Modules, indexed by the module name, reading from the boox.xml config
     * file which lists them all. It is deprecated in favour of the identical method in the Module class.
     * @return
     */
    @Deprecated
    public static Map<String, Module> getModules() {
        try {
            Map<String, Module> modules = new HashMap<String, Module>();
            Document moduleDoc = DocMan.build(Module.MODULE_FILE);
            Element rtel = moduleDoc.getRootElement();
            List<Element> modelements = rtel.getChildren(Module.ELEMENT_NAME, ns);
            Iterator<Element> it = modelements.iterator();
            while (it.hasNext()) {
                Module module = new Module(it.next());
                modules.put(module.getName(), module);
            }
            return modules;
        } catch (Exception e) {
            logger.log("Boox - problem parsing the modules file", e);
            return null;
        }
    }

    /**
     * This returns a Map of the available Segments, indexed by the segment name, reading from the boox.xml config
     * file which lists them all. A segment is a group of modules.
     * @return
     */
    public static Map<String, Segment> getSegments() {
        try {
            Map<String, Segment> segments = new HashMap<String, Segment>();
            Map<String, Module> modules = getModules();
            Document segmentDoc = DocMan.build(Module.MODULE_FILE);
            Element rtel = segmentDoc.getRootElement();
            List<Element> modelements = rtel.getChildren(Segment.ELEMENT_NAME, ns);
            Iterator<Element> it = modelements.iterator();
            while (it.hasNext()) {
                Segment segment = new Segment(it.next());
                for (Entry<String, Module> mit : modules.entrySet()) {
                    Module module = mit.getValue();
                    if (module.getSegment().equals(segment.getName())) {
                        segment.addModule(module);
                    }
                }
                ;

                segments.put(segment.getName(), segment);
            }
            return segments;
        } catch (Exception e) {
            logger.log("Boox - problem parsing the modules file", e);
            return null;
        }
    }

    /**
     * This is one of a group of setup/configuration methods.
     * @param enterprise
     * @param clerk
     * @param tasksElement
     */
    public static void createTasks(Enterprise enterprise, Clerk clerk, Element tasksElement)
            throws PermissionsException {
        List<Element> taskElements = tasksElement.getChildren(TASK_ELEMENT_NAME, ns);
        Map<Ledger, Permission> permissions = new HashMap<Ledger, Permission>();
        for (Element taskElement : taskElements) {
            String name = "";
            String description = "";
            String frequency = "";
            String rolename = "";
            String formClassName = "";

            try {
                name = taskElement.getAttributeValue(Task.NAME_ATTNAME);
                description = taskElement.getChildText(Task.TASKDESCRIPTION_ELNAME, ns);
                frequency = taskElement.getAttributeValue(Task.FREQ_ATTNAME);
                rolename = taskElement.getAttributeValue(Task.ROLE_ATTNAME);
                formClassName = taskElement.getAttributeValue(Task.FORM_ATTNAME);
            } catch (Throwable t) {

            }
            String sysname = ShortHash.hash(Task.SYSNAME_PREFIX + name + description);

            int frint = 20;

            switch (frequency) {
            case "once":
                frint = Task.ONCE;
                break;
            case "daily":
                frint = Task.DAILY;
                break;
            case "weekly":
                frint = Task.WEEKLY;
                break;
            case "fortnightly":
                frint = Task.FORTNIGHTLY;
                break;
            case "fourweekly":
                frint = Task.FOURWEEKLY;
                break;
            case "monthly":
                frint = Task.MONTHLY;
                break;
            case "periodend":
                frint = Task.PERIOD_END;
                break;
            case "quarterly":
                frint = Task.QUARTERLY;
                break;
            case "annual":
                frint = Task.ANNUAL;
                break;
            case "irregular":
                frint = Task.IRREGULAR;
                break;
            default:
                frint = Task.IRREGULAR;
            }
            Role role = Role.getRole(rolename);

            List<Element> ledgerElements = taskElement.getChildren(Boox.LEDGER_ELEMENT_NAME, ns);

            for (Element ledgerElement : ledgerElements) {
                String permissionName = "";
                String ledgerName = "";
                boolean cascades = false;
                try {
                    ledgerName = ledgerElement.getAttributeValue(NAME_ATTRIBUTE_NAME);
                    permissionName = ledgerElement.getAttributeValue(PERMISSION_ATTRIBUTE_NAME);
                    cascades = (ledgerElement.getAttributeValue(CASCADES_ATTRIBUTE_NAME).equals("true"));
                } catch (Throwable t) {

                }
                Ledger ledger = Ledger.getLedger(enterprise, ledgerName);
                Permission permission;
                if (cascades) {
                    permission = CascadingPermission.getPermission(permissionName);
                } else {
                    permission = Permission.getPermission(permissionName);
                }
                if (clerk.hasPermission(enterprise, ledger, permission)) {
                    permissions.put(ledger, permission);
                } else {
                    String error = "BX: creating task - permissions error: owning clerk " + clerk.getName()
                            + " does not have " + permission.getName() + " permission on ledger "
                            + ledger.getFullName();
                    logger.log(error);
                    throw new PermissionsException(error);
                }
            }
            Task.createTask(enterprise, clerk, name, description, frint, role, formClassName, permissions);
        }
    }

    ///
    /// These methods are used to set up or modify a Boox system given a Boox xml file, an
    /// org.jdom.Document, or an org.jdom.Element.
    ///

    /**
     * Boox provides a simple way to set up an accounting installation with a full
     * chart of accounts, using an XML file which maps the hierarchy of Ledgers.
     * The distribution jar contains a sample boox.xml file which can easily be
     * customised for your application.
     *
     * @param clerk the supervisor setting up the hierarchy of accounts
     * @param file the location of the boox.xml file
     * @throws BooxException
     * @throws PermissionsException
     */
    public static void createLedgersAndAccounts(Enterprise enterprise, Clerk clerk, File file)
            throws BooxException, PermissionsException {
        logger.log("creating ledgers and accounts for " + enterprise.getName() + " from template file "
                + file.getAbsolutePath());
        Document document;
        try {
            if (file.exists() && file.canRead()) {
                document = DocMan.build(file);
                if (document == null) {
                    throw new BooxException("Boox: DocMan returned a null document at " + file.getAbsolutePath());
                }
            } else {
                throw new BooxException(
                        "template file at " + file.getAbsolutePath() + " does not exist or is unreadable");
            }
        } catch (Exception e) {
            logger.log("failed to build xml accts doc from file", e);
            throw new BooxException("failed to build xml accts doc from file", e);
        }
        try {
            createLedgersAndAccounts(enterprise, clerk, document);
        } catch (BooxXMLException e) {
            // TODO Auto-generated catch block
            logger.log("Boox XML exception thrown - xml error in file " + file.getAbsolutePath(), e);
            //throw new BooxException("XML Exception", e);
        }
    }

    /**
     * This takes an org.jdom.Document argument instead of a java.io.File, but
     * does the same thing.
     *
     * @param clerk
     * @param document
     * @throws BooxException
     * @throws PermissionsException
    * @throws BooxXMLException 
     */
    public static void createLedgersAndAccounts(Enterprise enterprise, Clerk clerk, Document document)
            throws BooxException, PermissionsException, BooxXMLException {
        Element rootElement = document.getRootElement();
        Element ledgersElement = rootElement.getChild(LEDGERS_ELEMENT_NAME, ns);
        if (ledgersElement == null) {
            throw new BooxXMLException("ledgers schema docs faulty, no ledgers element");
        }
        if (ledgersElement.getNamespace() != ns) {
            throw new BooxXMLException("wrong namespace in schema documents");
        }

        logger.log("creating ledgers and accounts for " + enterprise.getName() + " from template document "
                + document.toString());
        createLedgersAndAccounts(enterprise, clerk, ledgersElement,
                openLedger(enterprise, Ledger.ROOT_LEDGER_NAME, clerk));

        Element tasksElement = rootElement.getChild(TASKS_ELEMENT_NAME, ns);
        if (tasksElement != null) {
            createTasks(enterprise, clerk, tasksElement);
        } //no requirement for a tasks element.
    }

    /**
     * This method takes an org.jdom.Element argument and creates a sub-hierarchy
     * of Ledgers in the given Ledger.
     * @param databaseName
     * @param clerk
     * @param element
     * @param ledger
     * @throws BooxException
     * @throws PermissionsException
     */
    public static void createLedgersAndAccounts(Enterprise enterprise, Clerk clerk, Element element, Ledger ledger)
            throws BooxException, PermissionsException {
        createAccounts(enterprise, clerk, element, ledger);
        debugLogger.log(5, "creating sub-ledgers in ledger " + ledger.getName());
        setLedgerPermissions(enterprise, clerk, element, ledger);
        List<Element> ledgerElements = element.getChildren("Ledger", ns);//use constant!
        for (Element ledgerElement : ledgerElements) {
            String name = ledgerElement.getAttributeValue("name");
            boolean isprivate = false;
            isprivate = Boolean.valueOf(ledgerElement.getAttributeValue("isPrivate"));//use constant
            Currency currency = ledger.getCurrency();
            try {
                if (ledgerElement.getAttributeValue("currency") != null) {
                    currency = Currency.getCurrency(ledgerElement.getAttributeValue("currency"));//use constant
                }
                logger.log("Bx-CL: ledger " + name + " currency is " + currency.getTLA());
            } catch (Exception e) {
                debugLogger.log("error reading currency attribute for ledger " + name, e);
            }
            Ledger newLedger = Ledger.createLedger(enterprise, name, ledger, currency, clerk, isprivate);
            debugLogger.log("BX_CL Created ledger:" + newLedger.getName() + " , owner "
                    + newLedger.getOwner().getName() + ", fullname " + newLedger.getFullName());
            createLedgersAndAccounts(enterprise, clerk, ledgerElement, newLedger);
        }
    }

    private static void createAccounts(Enterprise enterprise, Clerk clerk, Element element, Ledger ledger)
            throws BooxException, PermissionsException {
        List<Element> accountElements = element.getChildren("Account", ns);
        Iterator<Element> ait = accountElements.iterator();
        while (ait.hasNext()) {
            Element accountElement = (Element) ait.next();
            String name = accountElement.getAttributeValue("name");
            // String fullName=accountElement.getAttributeValue("fullName");
            String description = accountElement.getAttributeValue("description");
            debugLogger.log(5, "about to create " + name + " account");
            Account.createAccount(enterprise, name, clerk, ledger, ledger.getCurrency(), description);
        }
    }

    private static void setLedgerPermissions(Enterprise enterprise, Clerk clerk, Element element, Ledger ledger)
            throws BooxException, PermissionsException {
        List<Element> permissionElements = element.getChildren("Permission", ns);
        Iterator<Element> pit = permissionElements.iterator();
        while (pit.hasNext()) {
            Element permissionElement = (Element) pit.next();
            Permission permission = Permission.getPermission(permissionElement.getAttributeValue("value"));
            String pclerk = permissionElement.getAttributeValue("clerk");
            Permission.setPermission(enterprise, pclerk, ledger, permission, true);
        }
    }
    /** public static void setApplicationName(String applicationName){
         APPLICATION_NAME=applicationName;
     }*/

}