com.swordlord.jalapeno.datacontainer.DataContainer.java Source code

Java tutorial

Introduction

Here is the source code for com.swordlord.jalapeno.datacontainer.DataContainer.java

Source

/*-----------------------------------------------------------------------------
**
** -Gozer is not Zuul-
**
** Copyright 2017 by SwordLord - the coding crew - https://www.swordlord.com/
**
** This program is free software; you can redistribute it and/or modify it
** under the terms of the GNU Affero General Public License as published by the Free
** Software Foundation, either version 3 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 Affero General Public License for
** more details.
**
** You should have received a copy of the GNU Affero General Public License along
** with this program. If not, see <http://www.gnu.org/licenses/>.
**
**-----------------------------------------------------------------------------
**
** $Id: DataContainer.java 1314 2011-12-19 17:27:08Z LordEidi $
**
-----------------------------------------------------------------------------*/

package com.swordlord.jalapeno.datacontainer;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.cayenne.CayenneRuntimeException;
import org.apache.cayenne.DeleteDenyException;
import org.apache.cayenne.FaultFailureException;
import org.apache.cayenne.ObjectContext;
import org.apache.cayenne.PersistenceState;
import org.apache.cayenne.access.DataContext;
import org.apache.cayenne.access.ObjectStore;
import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.ExpressionFactory;
import org.apache.cayenne.validation.BeanValidationFailure;
import org.apache.cayenne.validation.ValidationException;
import org.apache.cayenne.validation.ValidationFailure;
import org.apache.cayenne.validation.ValidationResult;
import com.swordlord.gozer.databinding.DataBindingContext;
import com.swordlord.gozer.databinding.DataBindingManager;
import com.swordlord.gozer.databinding.DataBindingMember;
import com.swordlord.gozer.session.IGozerSessionInfo;
import com.swordlord.jalapeno.DBConnection;
import com.swordlord.jalapeno.OrderingEx;
import com.swordlord.jalapeno.datarow.DataRowBase;
import com.swordlord.jalapeno.datatable.DataTableBase;
import com.swordlord.jalapeno.dataview.OrderingParam;
import com.swordlord.jalapeno.dataview.ViewFilter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * The Class DataContainer.
 */
@SuppressWarnings("serial")
public class DataContainer implements Serializable {
    /** The Constant LOG. */
    protected static final Log LOG = LogFactory.getLog(DataContainer.class);

    /** The _data tables. */
    private Hashtable<String, DataTableBase> _dataTables;

    /** The _oc. */
    private ObjectContext _oc;

    /** The _session info. */
    private IGozerSessionInfo _sessionInfo;

    private DataBindingContext _dbContext;

    /** The _errors. */
    private LinkedList<String> _errors;

    // Disables the validation mechanism during the persist process if set to true
    private Boolean _bDisableValidation;

    /**
     * Instantiates a new data container.
     */
    public DataContainer() {
        _dataTables = new Hashtable<String, DataTableBase>();

        clearErrors();
    }

    /**
     * Instantiates a new data container.
     * 
     * @param sessionInfo the session info
     */
    public DataContainer(IGozerSessionInfo sessionInfo, DataBindingContext dbContext) {
        _dataTables = new Hashtable<String, DataTableBase>();

        _sessionInfo = sessionInfo;
        _dbContext = dbContext;

        clearErrors();
    }

    public DataContainer(IGozerSessionInfo sessionInfo) {
        _dataTables = new Hashtable<String, DataTableBase>();

        _sessionInfo = sessionInfo;

        clearErrors();
    }

    /**
     * Adds the error.
     * 
     * @param strError the str error
     */
    public void addError(String strError) {
        _errors.add(strError);
    }

    /**
     * Adds the error.
     * 
     * @param strField the str field
     * @param strError the str error
     */
    public void addError(String strField, String strError) {
        // Field is not yet supported, but interface has it for compatibility reasons
        _errors.add(strError);
    }

    /**
     * Adds the table.
     * 
     * @param dt the dt
     */
    public void addTable(DataTableBase dt) {
        // TODO WARNING: This is an ugly hack and should be changed!
        if (!_dataTables.containsKey(dt.getAbsoluteTableName())) {
            _dataTables.put(dt.getAbsoluteTableName(), dt);
        }
    }

    /**
     * Delete a DataRow.
     * 
     * @param row the DataRow
     * 
     * @return true, if successful
     */
    public boolean deleteDataRow(DataRowBase row) {
        if (row == null)
            return false;

        ObjectContext oc = getObjectContext();

        try {
            oc.deleteObject(row);
        } catch (DeleteDenyException e) {
            LOG.error(e.getMessage());
            return false;
        } catch (Exception e) {
            LOG.error(e.getMessage());
            return false;
        }

        return true;
    }

    /**
     * Clear errors.
     */
    public void clearErrors() {
        _errors = new LinkedList<String>();
    }

    /**
     * Dump content to file.
     */
    public void dumpContentToFile() {
        try {
            FileWriter fileWriter = new FileWriter("dump.txt");

            BufferedWriter buffWriter = new BufferedWriter(fileWriter);

            Enumeration<DataTableBase> list = _dataTables.elements();

            while (list.hasMoreElements()) {
                DataTableBase dt = list.nextElement();
                buffWriter.write(dt.toHTML());
            }

            buffWriter.close();
        } catch (IOException e) {
            System.out.println("Exception " + e);
        }
    }

    /**
     * Dump content to string.
     * 
     * @return the string
     */
    public String dumpContentToString() {
        StringBuilder sb = new StringBuilder();

        Enumeration<DataTableBase> list = _dataTables.elements();

        while (list.hasMoreElements()) {
            DataTableBase dt = list.nextElement();
            sb.append(dt.toHTML() + "<br/>");
        }

        return sb.toString();
    }

    /**
     * Gets the data rows.
     * 
     * @param dataRow the data row
     * 
     * @return the data rows
     */
    public List<DataRowBase> getDataRows(Class<DataRowBase> dataRow) {
        ArrayList<DataRowBase> rows = new ArrayList<DataRowBase>();

        Iterator<DataRowBase> it = getObjectStore().getObjectIterator();
        while (it.hasNext()) {
            DataRowBase row = it.next();

            if (row.getPersistenceState() != PersistenceState.DELETED) {
                if (dataRow == row.getClass()) {
                    rows.add(row);
                }
            }
        }

        return rows;
    }

    /**
     * Gets the data rows.
     * 
     * @param dt the dt
     * 
     * @return the data rows
     */
    public List<DataRowBase> getDataRows(DataTableBase dt) {
        return getDataRows(dt, null, (OrderingParam) null);
    }

    /**
     * Gets the data rows.
     * 
     * @param dt the dt
     * @param clazz the clazz
     * 
     * @return the data rows
     */
    public <T> List<T> getDataRows(DataTableBase dt, Class<T> clazz) {
        return getDataRows(dt, null, null, clazz);
    }

    /**
     * Gets the data rows.
     * 
     * @param dt the dt
     * @param ordering the ordering
     * @param clazz the clazz
     * 
     * @return the data rows
     */
    public <T> List<T> getDataRows(DataTableBase dt, OrderingParam ordering, Class<T> clazz) {
        return getDataRows(dt, null, ordering, clazz);
    }

    /**
     * Gets the data rows.
     * 
     * @param dt the dt
     * @param ordering the ordering
     * 
     * @return the data rows
     */
    public List<DataRowBase> getDataRows(DataTableBase dt, OrderingParam ordering) {
        return getDataRows(dt, null, ordering);
    }

    /**
     * Gets the data rows.
     * 
     * @param dt the dt
     * @param filter the str filter
     * 
     * @return the data rows
     */
    public List<DataRowBase> getDataRows(DataTableBase dt, ViewFilter filter) {
        return getDataRows(dt, filter, null);
    }

    /**
     * Gets the data rows.
     * 
     * @param dt the dt
     * @param filter the str filter
     * @param orderingParam the ordering param
     * 
     * @return the data rows
     */
    public List<DataRowBase> getDataRows(DataTableBase dt, ViewFilter filter, OrderingParam orderingParam) {
        return getDataRows(dt, filter, orderingParam, DataRowBase.class);
    }

    /**
     * Gets the data rows.
     * 
     * @param dt the dt
     * @param filter the filter
     * @param orderingParam the ordering param
     * @param clazz the clazz
     * 
     * @return the data rows
     */
    public <T> List<T> getDataRows(DataTableBase dt, ViewFilter filter, OrderingParam orderingParam,
            Class<T> clazz) {
        String strDataRowClassName = dt.getAbsoluteDataRowName();

        ArrayList<T> rows = new ArrayList<T>();

        Iterator<DataRowBase> it = getObjectStore().getObjectIterator();
        while (it.hasNext()) {
            DataRowBase row = it.next();

            // is this enough or do we have to filter TRANSIENT as well?
            // && row.getPersistenceState() != PersistenceState.TRANSIENT 
            if (row.getPersistenceState() != PersistenceState.DELETED) {
                if (strDataRowClassName.compareTo(row.getClass().getName()) == 0) {
                    rows.add((T) row);
                }
            }
        }

        return DataContainer.filterAndOrderDataRows(rows, filter, orderingParam, getDataBindingContext(), dt);
    }

    /**
     * Filters and orders the given rows according to the passed arguments.
     * 
     * @param <T>
     *            The row type
     * @param rows
     *            The rows to process
     * @param filter
     *            The filter or null
     * @param orderingParam
     *            The ordering or null
     * @param context
     *            The binding context or null
     * @param dt
     *            The table to which the rows belong
     * @return The processed rows
     */
    public static <T> List<T> filterAndOrderDataRows(List<T> rows, ViewFilter filter, OrderingParam orderingParam,
            DataBindingContext context, DataTableBase dt) {
        final List<T> filtered = filter(rows, filter, context, dt);
        if (orderingParam != null) {
            OrderingEx ordering = new OrderingEx(dt, orderingParam.getField(), orderingParam.getSortOrder());
            ordering.orderList(filtered);
        }
        return filtered;
    }

    /**
     * Filters the given rows.
     * 
     * @param <T>
     *            The row type
     * @param rows
     *            The rows to process
     * @param filter
     *            The filter or null
     * @param context
     *            The binding context or null
     * @param dt
     *            The table to which the rows belong
     * @return The filtered rows
     */
    private static <T> List<T> filter(List<T> rows, ViewFilter filter, DataBindingContext context,
            DataTableBase dt) {
        if (rows.isEmpty() || filter == null) {
            return rows;
        }
        final String strFilter = filter.getFilter();
        // Complex filters need to be created "manually"
        if (strFilter.charAt(0) == '?') {
            return complexFilter(rows, context, dt, strFilter);
        }
        // Trivial filters are done directly by Cayenne
        final Expression exp = Expression.fromString(strFilter);
        // expression without parameters?
        final Map<String, Object> args = filter.getArguments();
        if (args.isEmpty()) {
            return new ArrayList<T>(exp.filterObjects(rows));
        }
        return new ArrayList<T>(exp.expWithParameters(args, false).filterObjects(rows));
    }

    /**
     * Filters the given rows using an complex filter.
     * 
     * @param <T>
     *            The row type
     * @param rows
     *            The rows to process
     * @param filter
     *            The complex filter
     * @param context
     *            The binding context or null
     * @param dt
     *            The table to which the rows belong
     * @return The filtered rows
     */
    private static <T> List<T> complexFilter(List<T> rows, DataBindingContext context, DataTableBase dt,
            String filter) {
        // Example of a filter: ?NotIn(threatId,@ActiveThreat[0]:threatId)
        final Pattern pattern = Pattern.compile(
                "^(\\?)?([a-z]*)(\\(){1,1}([a-z]*)?(,){1,1}([(\\?)?a-z_0-9,:='%\\s@\\[\\]\\(\\)]*)?(\\)){1,1}$",
                Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
        final Matcher matcher = pattern.matcher(filter);

        /*
         * The groups are as follows 1 - ! 2 - NotIn 3 - ( 4 - threatId 5 -
         * , 6 - @ActiveThreat[0]:threatId 7 - )
         */

        if (!matcher.find()) {
            LOG.info("Parser can't parse filter: " + filter);
            return rows;
        }

        if (matcher.groupCount() != 7) {
            LOG.error("Wrong group count during parsing of filter: " + filter);
            return rows;
        }

        final String strCommand = matcher.group(2);
        final String strBoundField = matcher.group(4).replace(":", ".");
        final String strBindingMember = matcher.group(6).replace(":", ".");

        final DataBindingMember bm = new DataBindingMember(strBindingMember);

        // re-use the DataBindingContext when there is one
        // this is needed so that all currentRow informations are correct
        // within a filter
        final DataBindingManager dbm;
        if (context != null) {
            dbm = context.getDataBindingManager(bm);
        } else {
            dbm = new DataBindingManager(dt.getDataContainer(), bm);
        }

        // get all to-be-filtered records as field because the expression
        // filters on one single field and does no lookup
        final List<String> fieldsFilter = new ArrayList<String>();
        for (DataRowBase row : dbm.getRows(bm)) {
            if (row.getPersistenceState() != PersistenceState.DELETED) {
                final String strFieldName = bm.getDataBindingFieldName();
                if (strFieldName == null || strFieldName.length() == 0) {
                    LOG.error("There must be something wrong with your filter. Field is empty: " + filter);
                } else {
                    fieldsFilter.add(row.getPropertyAsStringForce(strFieldName));
                }
            }
        }

        // Create the expression according to the binding information
        if (strCommand.equalsIgnoreCase("in")) {
            final Expression exp = ExpressionFactory.inExp(strBoundField, fieldsFilter);
            return new ArrayList<T>(exp.filterObjects(rows));
        } else if (strCommand.equalsIgnoreCase("notin")) {
            final Expression exp = ExpressionFactory.notInExp(strBoundField, fieldsFilter);
            return new ArrayList<T>(exp.filterObjects(rows));
        } else {
            LOG.warn("Unknown filter command: " + strCommand);
            return rows;
        }
    }

    /**
     * Gets the errors.
     * 
     * @return the errors
     */
    public List<String> getErrors() {
        return _errors;
    }

    /**
     * Gets the object context.
     * 
     * @return the object context
     */
    public ObjectContext getObjectContext() {
        if (_oc == null) {
            _oc = DBConnection.instance().getObjectContext(this);
        }
        return _oc;
    }

    /**
     * Gets the object store.
     * 
     * @return the object store
     */
    public ObjectStore getObjectStore() {
        DataContext dataContext = (DataContext) getObjectContext();
        if (dataContext == null) {
            return null;
        }

        return dataContext.getObjectStore();
    }

    /**
     * Gets the session info.
     * 
     * @return the session info
     */
    public IGozerSessionInfo getSessionInfo() {
        return _sessionInfo;
    }

    public DataBindingContext getDataBindingContext() {
        return _dbContext;
    }

    /**
     * Gets the table.
     * 
     * @param clazz the clazz
     * 
     * @return the table
     */
    public DataTableBase getTable(Class<? extends DataTableBase> clazz) {
        if (clazz == null) {
            throw new InternalError("Table is null (clazz)");
        }

        String strTableId = null;

        try {
            Constructor<? extends DataTableBase> ct = clazz.getConstructor(DataContainer.class);
            DataTableBase tab = ct.newInstance(this);
            strTableId = tab.getAbsoluteTableName();
        } catch (InstantiationException ex) {
            LOG.error(ex);
        } catch (IllegalAccessException ex) {
            LOG.error(ex);
        } catch (NoSuchMethodException ex) {
            LOG.error(ex);
        } catch (InvocationTargetException ex) {
            LOG.error(ex);
        }

        return getTableByAbsoluteName(strTableId);
    }

    /**
     * Gets the table by absolute name.
     * 
     * @param strId the str id
     * 
     * @return the table by absolute name
     */
    @SuppressWarnings("unchecked")
    public DataTableBase getTableByAbsoluteName(String strId) {
        if (strId == null) {
            throw new InternalError("Tablename is null (strId)");
        }

        if (!_dataTables.contains(strId)) {
            // if not exists, create!
            try {
                Class<DataTableBase> clazz = (Class<DataTableBase>) Class.forName(strId);
                Constructor<DataTableBase> ct = clazz.getConstructor(DataContainer.class);
                DataTableBase tab = ct.newInstance(this);

                if (tab != null) {
                    addTable(tab);
                }
            } catch (ClassNotFoundException ex) {
                LOG.error(ex);
            } catch (InstantiationException ex) {
                LOG.error(ex);
            } catch (IllegalAccessException ex) {
                LOG.error(ex);
            } catch (NoSuchMethodException ex) {
                LOG.error(ex);
            } catch (InvocationTargetException ex) {
                LOG.error(ex);
            }
        }

        return _dataTables.get(strId);
    }

    /**
     * Gets the table from row.
     * 
     * @param clazz the clazz
     * 
     * @return the table from row
     */
    public DataTableBase getTableFromRow(Class<? extends DataRowBase> clazz) {
        if (clazz == null) {
            throw new InternalError("DataRow is null (clazz)");
        }

        String strClassName = clazz.getName();

        strClassName = strClassName.replaceAll("DataRow", "DataTable");
        strClassName = strClassName.replaceAll("datarow", "datatable");

        return getTableByAbsoluteName(strClassName);
    }

    /**
     * Checks for errors.
     * 
     * @return true, if successful
     */
    public boolean hasErrors() {
        return _errors.size() > 0;
    }

    /**
     * Checks for session info.
     * 
     * @return true, if successful
     */
    public boolean hasSessionInfo() {
        return _sessionInfo != null;
    }

    public boolean hasDataBindingContext() {
        return _dbContext != null;
    }

    public void setDisableValidation(boolean bDisabled) {
        _bDisableValidation = bDisabled;
    }

    public boolean getDisableValidation() {
        if (_bDisableValidation == null) {
            setDisableValidation(false);
        }

        return _bDisableValidation;
    }

    /**
     * Persist.
     * 
     * @return true, if successful
     */
    public boolean persist() {
        // iterate through all rows and clear errors
        clearErrors();
        return persistObjectContext(getObjectContext());
    }

    /**
     * Persist object context.
     * 
     * @param oc the oc
     * 
     * @return true, if successful
     */
    private boolean persistObjectContext(ObjectContext oc) {
        try {
            // disabling the context validation should only be used when you know what you do!
            if (getDisableValidation()) {
                DataContext context = (DataContext) oc;
                context.setValidatingObjectsOnCommit(false);
            }
            oc.commitChanges();
        } catch (ValidationException vex) {
            String strMessage = "";

            ValidationResult vr = vex.getValidationResult();
            if (vr.hasFailures()) {
                List<ValidationFailure> failures = vr.getFailures();
                if (failures.size() > 0) {
                    Iterator<ValidationFailure> it = failures.iterator();
                    while (it.hasNext()) {
                        ValidationFailure failure = it.next();
                        if (failure != null) {
                            addError(failure.getSource().toString(), failure.getDescription());

                            LOG.info(failure.getSource() + " - " + failure.getDescription());
                        }

                        if (failure instanceof BeanValidationFailure) {
                            BeanValidationFailure bvf = (BeanValidationFailure) failure;
                            strMessage += bvf.getSource() + " - " + bvf.getProperty() + " - " + bvf.getDescription()
                                    + ".\n\r";
                            LOG.error(bvf.getSource() + " - " + bvf.getProperty() + " - " + bvf.getDescription());
                        }
                    }
                }
            }

            String strError = "Persist crashed: " + vex.getMessage() + ": " + vex.getCause() + "\n\r" + strMessage;
            LOG.info(strError);

            strMessage = vex.getLocalizedMessage() + " - " + strMessage;

            Throwable cause = vex.getCause();
            if (cause != null) {
                strMessage += " - " + cause.getMessage();
            }

            addError("", strMessage);

            //ErrorDialog.reportError("validation: " + strMessage);
            return false;
        } catch (DeleteDenyException dde) {
            String strError = "Persist crashed: " + dde.getLocalizedMessage() + ": " + dde.getCause();
            LOG.error(strError);

            String strMessage = dde.getLocalizedMessage();

            addError("", strMessage);

            return false;
        } catch (FaultFailureException ffe) {
            String strError = "Persist crashed: " + ffe.getLocalizedMessage() + ": " + ffe.getCause();
            LOG.error(strError);

            String strMessage = ffe.getLocalizedMessage();

            addError("", strMessage);

            return false;
        } catch (CayenneRuntimeException cex) {
            String strError = "Persist crashed: " + cex.getMessage() + ": " + cex.getCause();
            LOG.error(strError);

            String strMessage = cex.getLocalizedMessage();

            addError("", strMessage);

            return false;
        } catch (NullPointerException e) {
            String strError = "Persist crashed: NullPointerException";
            LOG.error(strError);

            addError("", strError);
            LOG.debug(e.getMessage());

            return false;
        } catch (Exception e) {
            String strError = "Persist crashed: " + e.getLocalizedMessage() + ": " + e.getCause();
            LOG.error(strError);

            String strMessage = e.getLocalizedMessage();

            addError("", strMessage);

            return false;
        }

        return true;
    }

    /**
     * Rollback.
     */
    public void rollback() {
        rollbackObjectContext((DataContext) getObjectContext());
    }

    /**
     * Rollback object context.
     * 
     * @param context the context
     */
    private void rollbackObjectContext(DataContext context) {
        if (context.hasChanges()) {
            context.rollbackChanges();
        }
    }

    /**
     * Sets the session info.
     * 
     * @param sessionInfo the new session info
     */
    public void setSessionInfo(IGozerSessionInfo sessionInfo) {
        _sessionInfo = sessionInfo;
    }
}