All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.izforge.izpack.panels.packs.PacksModel Maven / Gradle / Ivy

There is a newer version: 5.2.3
Show newest version
/*
 * IzPack - Copyright 2001-2008 Julien Ponge, All Rights Reserved.
 *
 * http://izpack.org/
 * http://izpack.codehaus.org/
 *
 * Copyright 2002 Marcus Wolschon
 * Copyright 2002 Jan Blok
 * Copyright 2004 Gaganis Giorgos
 * Copyright 2006,2007 Dennis Reil
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.izforge.izpack.panels.packs;

import com.izforge.izpack.api.data.InstallData;
import com.izforge.izpack.api.data.Pack;
import com.izforge.izpack.api.data.PackColor;
import com.izforge.izpack.api.data.Variables;
import com.izforge.izpack.api.exception.ResourceNotFoundException;
import com.izforge.izpack.api.resource.Messages;
import com.izforge.izpack.api.resource.Resources;
import com.izforge.izpack.api.rules.RulesEngine;
import com.izforge.izpack.installer.util.PackHelper;

import javax.swing.table.AbstractTableModel;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.*;
import java.util.logging.Logger;

/**
 * User: Gaganis Giorgos Date: Sep 17, 2004 Time: 8:33:21 AM
 */
public class PacksModel extends AbstractTableModel
{
    private static final long serialVersionUID = 3258128076746733110L;
    private static final transient Logger logger = Logger.getLogger(PacksModel.class.getName());

    protected List packs;
    protected final List hiddenPacks;
    private final List packsToInstall;

    protected final transient RulesEngine rules;
    protected final transient Variables variables;
    private final transient InstallData installData;
    private transient Messages messages;

    private final Map installedPacks;

    List checkValues;

    private final Map nameToPack;
    private final Map nameToRow;

    private final boolean modifyInstallation;

    public PacksModel(InstallData idata)
    {
        this.installData = idata;
        this.rules = idata.getRules();
        try{
          this.messages = idata.getMessages().newMessages(Resources.PACK_TRANSLATIONS_RESOURCE_NAME);
        } catch(ResourceNotFoundException ex){
          this.messages=idata.getMessages();
        }
        this.variables = idata.getVariables();
        this.packsToInstall = idata.getSelectedPacks();

        this.modifyInstallation = Boolean.valueOf(idata.getVariable(InstallData.MODIFY_INSTALLATION));
        this.installedPacks = loadInstallationInformation(modifyInstallation);

        this.packs = getVisiblePacks();
        this.hiddenPacks = getHiddenPacks();
        this.nameToRow = getNametoRowMapping(packs);
        this.nameToPack = getNametoPackMapping(idata.getAvailablePacks());

        this.packs = setPackProperties(packs, nameToPack);
        this.checkValues = initCheckValues(packs, packsToInstall);

        updateConditions(true);
        updatePacksToInstall();
    }


    /**
     * @return a list of hidden packs
     */
    private List getHiddenPacks()
    {
        List hiddenPacks = new ArrayList();
        for (Pack availablePack : installData.getAvailablePacks())
        {
            if (availablePack.isHidden())
            {
                hiddenPacks.add(availablePack);
            }
        }
        return hiddenPacks;
    }

    /**
     * @return a list of visible packs
     */
    public List getVisiblePacks()
    {
        List visiblePacks = new ArrayList();
        for (Pack availablePack : installData.getAvailablePacks())
        {
            if (!availablePack.isHidden())
            {
                visiblePacks.add(availablePack);
            }
        }
        return visiblePacks;
    }

    /**
     * Generate a map from a pack's name to its pack object.
     *
     * @param packs list of pack objects
     * @return map from a pack's name to its pack object.
     */
    private Map getNametoPackMapping(List packs)
    {
        Map  nameToPack = new HashMap();
        for (Pack pack : packs)
        {
            nameToPack.put(pack.getName(), pack);
        }
        return nameToPack;
    }

    /**
     * Generate a map from a pack's name to its row number visible on the UI.
     *
     * @param packs list of pack objects
     * @return map from a pack's name to its row number visible on the UI
     */
    private Map getNametoRowMapping(List packs)
    {
        Map nameToPos = new HashMap();
        for (int i = 0; i < packs.size(); i++)
        {
            Pack pack = packs.get(i);
            nameToPos.put(pack.getName(), i);
        }
        return nameToPos;
    }

    /**
     * Ensure that parent packs know which packs are their children.
     * Ensure that packs who have dependants know which packs depend on them
     *
     * @param packs packs visible to the user
     * @param nameToPack mapping from pack names to pack objects
     * @return packs
     */
    private List setPackProperties(List packs, Map nameToPack)
    {
        Pack parent;
        for (Pack pack : packs)
        {
            if (pack.hasParent())
            {
                String parentName = pack.getParent();
                parent = nameToPack.get(parentName);
                parent.addChild(pack.getName());
            }

            if (pack.hasDependencies())
            {
                for (String name : pack.getDependencies())
                {
                    parent = nameToPack.get(name);
                    parent.addDependant(pack.getName());
                }
            }
        }
        return packs;
    }

    /**
     * Helper function to retrieve a pack object based on which row it is on.
     *
     * @param row
     * @return pack on the given row
     */
    public Pack getPackAtRow(int row)
    {
        return this.packs.get(row);
    }

    private void updateConditions()
    {
        this.updateConditions(false);
    }

    /**
     * Update the conditions for dependent packages.
     * Update the conditions for optional packages.
     *
     * @param initial indicates if its the first time updating conditions.
     */
    private void updateConditions(boolean initial)
    {
        boolean changes = true;

        while (changes)
        {
            changes = false;
            for (Pack pack : packs)
            {
                String packName = pack.getName();
                int pos = getPos(packName);
                if (!rules.canInstallPack(packName, variables))
                {
                    logger.fine("Conditions for pack '" + packName + "' are not complied with");
                    if (rules.canInstallPackOptional(packName, variables))
                    {
                        logger.fine("Pack '" + packName + "' can be installed optionally.");
                        if (initial)
                        {
                            if (checkValues.get(pos) != CbSelectionState.DESELECTED)
                            {
                                checkValues.set(pos, CbSelectionState.DESELECTED);
                                changes = true;
                            }
                        }
                    }
                    else
                    {
                        if (checkValues.get(pos) != CbSelectionState.DEPENDENT_DESELECTED)
                        {
                            logger.fine("Pack '" + packName + "' cannot be installed");
                            checkValues.set(pos, CbSelectionState.DEPENDENT_DESELECTED);
                            changes = true;
                        }
                    }
                }
            }
        }
    }

    /**
     * Ensure that the table is up to date.
     * Order does matter
     */
    public void updateTable()
    {
        updateDeps();
        updateConditions();
        updatePacksToInstall();
        fireTableDataChanged();
    }

    /**
     * Initialize the data that represented the checkbox states.
     *
     * @param packs
     * @param packsToInstall
     * @return
     */
    private List initCheckValues(List packs, List packsToInstall)
    {
        CbSelectionState[] checkValues = new CbSelectionState[packs.size()];

        // If a pack is indicated to be installed checkbox value should be SELECTED
        for (int i = 0; i < packs.size(); i++)
        {
            Pack pack = packs.get(i);
            if (packsToInstall.contains(pack))
            {
                checkValues[i] = CbSelectionState.SELECTED;
            }
            else
            {
                checkValues[i] = CbSelectionState.DESELECTED;
            }
        }

        // If a packs dependency cannot be resolved checkboc value should be DEPENDENT_DESELECTED
        for (int i = 0; i < packs.size(); i++)
        {
            Pack pack = packs.get(i);
            if (checkValues[i] == CbSelectionState.DESELECTED)
            {
                List deps = pack.getDependants();
                for (int j = 0; deps != null && j < deps.size(); j++)
                {
                    String name = deps.get(j);
                    int pos = getPos(name);
                    checkValues[pos] = CbSelectionState.DEPENDENT_DESELECTED;
                }
            }

            // for mutual exclusion, uncheck uncompatible packs too
            // (if available in the current installGroup)
            CbSelectionState checkState = checkValues[i];
            if (checkState != null && checkState.isFullyOrPartiallySelected() && pack.getExcludeGroup() != null)
            {
                for (int q = 0; q < packs.size(); q++)
                {
                    if (q != i)
                    {
                        Pack otherPack = packs.get(q);
                        if (pack.getExcludeGroup().equals(otherPack.getExcludeGroup()))
                        {
                            if (checkValues[q] == CbSelectionState.SELECTED)
                            {
                                checkValues[q] = CbSelectionState.DESELECTED;
                            }
                        }
                    }
                }
            }
        }

        // Configure required packs
        for (Pack pack : packs)
        {
            if (pack.isRequired())
            {
                checkValues = propRequirement(pack.getName(), Arrays.asList(checkValues)).toArray(new CbSelectionState[checkValues.length]);
            }
        }

        return Arrays.asList(checkValues);
    }

    /**
     * Configure required packs.
     * @param name
     * @param checkValues
     * @return
     */
    private List propRequirement(String name, List checkValues)
    {

        final int pos = getPos(name);
        checkValues.set(pos, CbSelectionState.REQUIRED_SELECTED);
        List deps = packs.get(pos).getDependencies();
        if (deps != null)
        {
            for (String s : deps)
            {
                return propRequirement(s, checkValues);
            }
        }
        return checkValues;

    }

    /**
     * Given a map of names and Integer for position and a name it return the position of this name
     * as an int
     *
     * @return position of the name
     */
    private int getPos(String name)
    {
        return nameToRow.get(name);
    }

    /*
     * @see TableModel#getRowCount()
     */
    @Override
    public int getRowCount()
    {
        return packs.size();
    }

    /*
     * @see TableModel#getColumnCount()
     */
    @Override
    public int getColumnCount()
    {
        return 3;
    }

    /*
     * @see TableModel#getColumnClass(int)
     */
    @Override
    public Class getColumnClass(int columnIndex)
    {
        switch (columnIndex)
        {
            case 0:
                return Integer.class;

            default:
                return String.class;
        }
    }

    /*
     * @see TableModel#isCellEditable(int, int)
     */
    @Override
    public boolean isCellEditable(int rowIndex, int columnIndex)
    {
        CbSelectionState state = checkValues.get(rowIndex);
        return state != null && state.isSelectable() && columnIndex == 0;
    }

    /*
     * @see TableModel#getValueAt(int, int)
     */
    @Override
    public Object getValueAt(int rowIndex, int columnIndex)
    {
        Pack pack = packs.get(rowIndex);
        switch (columnIndex)
        {
            case 0:
                CbSelectionState state = checkValues.get(rowIndex);
                return state != null ? checkValues.get(rowIndex) : null;

            case 1:
                return PackHelper.getPackName(pack, messages);

            case 2:
                return Pack.toByteUnitsString(pack.getSize());

            default:
                return null;
        }
    }


    /**
     * Toggle checkbox value from selected to deselected and vice-versa.
     * @param rowIndex
     */
    public void toggleValueAt(int rowIndex)
    {
        CbSelectionState state = checkValues.get(rowIndex);
        if  (state != null && checkValues.get(rowIndex) == CbSelectionState.SELECTED)
        {
            setValueAt(CbSelectionState.DESELECTED, rowIndex, 0);
        }
        else
        {
            setValueAt(CbSelectionState.SELECTED, rowIndex, 0);
        }

    }

    /*
     * @see TableModel#setValueAt(Object, int, int)
     * Update the value of some checkbox
     */
    @Override
    public void setValueAt(Object checkValue, int rowIndex, int columnIndex)
    {
        if (!(columnIndex != 0 || !(checkValue instanceof CbSelectionState)))
        {
            Pack pack = packs.get(rowIndex);

            boolean added;
            if ((CbSelectionState) checkValue == CbSelectionState.SELECTED)
            {
                added = true;
                String name = pack.getName();
                if (rules.canInstallPack(name, variables) || rules.canInstallPackOptional(name, variables))
                {
                    if (pack.isRequired())
                    {
                        checkValues.set(rowIndex, CbSelectionState.REQUIRED_SELECTED);
                    }
                    else
                    {
                        checkValues.set(rowIndex, CbSelectionState.SELECTED);
                    }
                }
            }
            else
            {
                added = false;
                checkValues.set(rowIndex, CbSelectionState.DESELECTED);
            }

            updateExcludes(rowIndex);
            updateDeps();

            if (added)
            {
                onSelectionUpdate(rowIndex);
                this.packsToInstall.add(pack);    //Temporarily add
                updateConditions();
                this.packsToInstall.remove(pack); //Redo
            }
            else
            {
                onDeselectionUpdate(rowIndex);
                this.packsToInstall.remove(pack); //Temporarily remove
                updateConditions();
                this.packsToInstall.add(pack); //Redo
            }

            updatePacksToInstall();

            if (pack.hasParent())
            {
                updateParent(pack);
            }
            else if (pack.hasChildren())
            {
                updateChildren(pack);
            }

            fireTableDataChanged();
        }
    }

    /**
     * Set the value of the parent pack of the given pack to SELECTED, PARTIAL_SELECT, or DESELECTED.
     * Value of the pack is dependent of its children values.
     *
     * @param childPack
     */
    private void updateParent(Pack childPack)
    {
        String parentName = childPack.getParent();
        Pack parentPack = nameToPack.get(parentName);
        int parentPosition = nameToRow.get(parentName);

        int childrenSelected = 0;
        for (String childName : parentPack.getChildren())
        {
            int childPosition = nameToRow.get(childName);
            if (isChecked(childPosition))
            {
                childrenSelected += 1;
            }
        }

        if (parentPack.getChildren().size() == childrenSelected)
        {
            if (!checkValues.get(parentPosition).isSelectable())
            {
                checkValues.set(parentPosition, CbSelectionState.REQUIRED_SELECTED);
            }
            else
            {
                checkValues.set(parentPosition, CbSelectionState.SELECTED);
            }
        }
        else if (childrenSelected > 0)
        {

            if (!checkValues.get(parentPosition).isSelectable())
            {
                checkValues.set(parentPosition, CbSelectionState.REQUIRED_PARTIAL_SELECTED);
            }
            else
            {
                checkValues.set(parentPosition, CbSelectionState.PARTIAL_SELECTED);
            }
        }
        else
        {
            if (!checkValues.get(parentPosition).isSelectable())
            {
                checkValues.set(parentPosition, CbSelectionState.REQUIRED_DESELECTED);
            }
            else
            {
                checkValues.set(parentPosition, CbSelectionState.DESELECTED);
            }
        }
    }


    /**
     * Set the value of children packs to the same value as the parent pack.
     *
     * @param parentPack
     */
    private void updateChildren(Pack parentPack)
    {
        String parentName = parentPack.getName();
        int parentPosition = nameToRow.get(parentName);
        CbSelectionState parentValue = checkValues.get(parentPosition);

        for (String childName : parentPack.getChildren())
        {
            int childPosition = nameToRow.get(childName);
            checkValues.set(childPosition, parentValue);
        }
    }

    /**
     * Select/Deselect pack(s) based on packsData mapping.
     * This is related to the onSelect and onDeselect attributes for packs.
     * User is not allowed to has a required pack for onSelect and onDeselect.
     *
     * @param packsData
     */
    private void selectionUpdate(Map packsData)
    {
        RulesEngine rules = installData.getRules();
        for (Map.Entry packData : packsData.entrySet())
        {
            CbSelectionState value;
            int packPos;
            String packName = packData.getKey();
            String condition = packData.getValue();

            if(condition != null && !rules.isConditionTrue(condition))
            {
                return; //Do nothing if condition is false
            }

            Pack pack;

            if (packName.startsWith("!"))
            {
                packName = packName.substring(1);
                pack  = nameToPack.get(packName);
                packPos = getPos(packName);
                value = CbSelectionState.DESELECTED;
            }
            else
            {
                pack  = nameToPack.get(packName);
                packPos = getPos(packName);
                value = CbSelectionState.SELECTED;
            }
            if (!pack.isRequired() && dependenciesResolved(pack))
            {
                checkValues.set(packPos, value);
            }
        }
    }

    /**
     * Update checkboxes based on the onSelect attribute
     * @param index
     */
    private void onSelectionUpdate(int index)
    {
        Pack pack = packs.get(index);
        Map packsData = pack.getOnSelect();
        selectionUpdate(packsData);
    }

    /**
     * Update checkboxes based on the onDeselect attribute
     * @param index
     */
    private void onDeselectionUpdate(int index)
    {
        Pack pack = packs.get(index);
        Map packsData = pack.getOnDeselect();
        selectionUpdate(packsData);
    }

    /**
     * Update packs to installed.
     * A pack to be installed is:
     * 1. A visible pack that has its checkbox checked
     * 2. A hidden pack that condition
     * @return
     */
    public List updatePacksToInstall()
    {
        packsToInstall.clear();
        for (int i = 0; i < packs.size(); i++)
        {
            Pack pack = packs.get(i);
            if (isChecked(i) && !installedPacks.containsKey(pack.getName()))
            {
                packsToInstall.add(pack);
            }
            else if (installedPacks.containsKey(pack.getName()))
            {
                checkValues.set(i, CbSelectionState.REQUIRED_PARTIAL_SELECTED);
            }
        }

        for (Pack hiddenPack : this.hiddenPacks)
        {
            if (this.rules.canInstallPack(hiddenPack.getName(), variables))
            {
                packsToInstall.add(hiddenPack);
            }
        }

        installData.setSelectedPacks(packsToInstall);
        return packsToInstall;
    }


    /**
     * This function updates the checkboxes after a change by disabling packs that cannot be
     * installed anymore and enabling those that can after the change. This is accomplished by
     * running a search that pinpoints the packs that must be disabled by a non-fullfiled
     * dependency.
     * TODO: Look into "+2" and "-2", doesn't look safe
     */
    private void updateDeps()
    {
        int[] statusArray = new int[packs.size()];
        for (int i = 0; i < statusArray.length; i++)
        {
            statusArray[i] = 0;
        }
        dfs(statusArray);
        for (int i = 0; i < statusArray.length; i++)
        {
            if (statusArray[i] == 0 && !checkValues.get(i).isSelectable())
            {
                checkValues.set(i, CbSelectionState.PARTIAL_SELECTED);
            }
            if (statusArray[i] == 1 && checkValues.get(i).isSelectable())
            {
                checkValues.set(i, CbSelectionState.DEPENDENT_DESELECTED);
            }

        }
        // The required ones must propagate their required status to all the ones that they depend on
        for (Pack pack : packs)
        {
            if (pack.isRequired())
            {
                String name = pack.getName();
                if (!(!rules.canInstallPack(name, variables) && rules.canInstallPackOptional(name, variables)))
                {
                    checkValues = propRequirement(name, checkValues);
                }
            }
        }

    }

    /*
     * Sees which packs (if any) should be unchecked and updates checkValues
     */
    private void updateExcludes(int rowindex)
    {
        CbSelectionState value = checkValues.get(rowindex);
        Pack pack = packs.get(rowindex);
        if (value != null && value.isFullyOrPartiallySelected() && pack.getExcludeGroup() != null)
        {
            for (int q = 0; q < packs.size(); q++)
            {
                if (rowindex != q)
                {
                    Pack otherPack = packs.get(q);
                    String name1 = otherPack.getExcludeGroup();
                    String name2 = pack.getExcludeGroup();
                    if (name2.equals(name1))
                    {
                        if (checkValues.get(q) == CbSelectionState.SELECTED)
                        {
                            checkValues.set(q, CbSelectionState.DESELECTED);
                        }
                    }
                }
            }
        }
    }


    /**
     * We use a modified dfs graph search algorithm as described in: Thomas H. Cormen, Charles
     * Leiserson, Ronald Rivest and Clifford Stein. Introduction to algorithms 2nd Edition
     * 540-549,MIT Press, 2001
     */
    private void dfs(int[] status)
    {
        Map colours = new HashMap();
        for (int i = 0; i < packs.size(); i++)
        {
            for (Pack pack : packs)
            {
                colours.put(pack.getName(), PackColor.WHITE);
            }
            Pack pack = packs.get(i);
            boolean wipe = false;

            if (dfsVisit(pack, status, wipe, colours) != 0)
            {
                return;
            }

        }
    }

    private int dfsVisit(Pack u, int[] status, boolean wipe, Map colours)
    {
        colours.put(u.getName(), PackColor.GREY);
        CbSelectionState check = checkValues.get(getPos(u.getName()));

        if (!check.isSelectedOrRequiredSelected())
        {
            wipe = true;
        }
        List deps = u.getDependants();
        if (deps != null)
        {
            for (String name : deps)
            {
                Pack v = nameToPack.get(name);
                if (wipe)
                {
                    status[getPos(v.getName())] = 1;
                }
                if (colours.get(v.getName()) == PackColor.WHITE)
                {
                    final int result = dfsVisit(v, status, wipe, colours);
                    if (result != 0)
                    {
                        return result;
                    }
                }
            }
        }
        colours.put(u.getName(), PackColor.BLACK);
        return 0;
    }

    /**
     * Get previously installed packs on modifying a pre-installed application
     * @return the installedPacks
     */
    public Map getInstalledPacks()
    {
        return this.installedPacks;
    }

    /**
     * @return the modifyInstallation
     */
    public boolean isModifyInstallation()
    {
        return this.modifyInstallation;
    }

    /**
     * Remove pack that are already installed
     * @param selectedPacks
     * @param installedPacks the packs found in an existing .installationinformation file
     */
    private void removeAlreadyInstalledPacks(List selectedPacks, Map installedPacks)
    {
        List removePacks = new ArrayList();

        for (Pack selectedPack : selectedPacks)
        {
            if (installedPacks.containsKey(selectedPack.getName()))
            {
                // pack is already installed, remove it
                removePacks.add(selectedPack);
            }
        }
        for (Pack removePack : removePacks)
        {
            selectedPacks.remove(removePack);
        }
    }

    private Map loadInstallationInformation(boolean modifyInstallation)
    {
        Map readPacks = new HashMap();
        if (!modifyInstallation)
        {
            return readPacks;
        }

        // installation shall be modified
        // load installation information
        ObjectInputStream oin = null;
        File installInfo = new File(installData.getInstallPath(), InstallData.INSTALLATION_INFORMATION);
        try
        {
            if (installInfo.exists())
            {
                FileInputStream fin = new FileInputStream(installInfo);
                oin = new ObjectInputStream(fin);
                //noinspection unchecked
                List packsinstalled = (List) oin.readObject();
                for (Pack installedpack : packsinstalled)
                {
                    readPacks.put(installedpack.getName(), installedpack);
                }
                removeAlreadyInstalledPacks(installData.getSelectedPacks(), readPacks);
                logger.fine("Found " + packsinstalled.size() + " installed packs");

                Properties variables = (Properties) oin.readObject();
                for (Object key : variables.keySet())
                {
                    installData.setVariable((String) key, (String) variables.get(key));
                }
            }
        }
        catch (Exception e)
        {
            logger.warning("Could not read installation information: " + e.getMessage());
        }
        finally
        {
            if (oin != null)
            {
                try
                {
                    oin.close();
                }
                catch (IOException ignored) {}
            }
        }
        return readPacks;
    }

    /**
     * Check if a pack's dependencies are resolved.
     * @param pack
     * @return
     */
    private boolean dependenciesResolved(Pack pack)
    {
        if(!pack.hasDependencies())
        {
            return true;
        }
        for (String dependentPackName : pack.getDependencies())
        {
            if (!isChecked(nameToRow.get(dependentPackName)))
            {
                return false;
            }
        }
        return true;
    }
    /**
     * @return mapping from pack name to pack
     */
    public Map getNameToPack()
    {
        return nameToPack;
    }

    /**
     * @return mapping from pack to row position
     */
    public Map getPacksToRowNumbers()
    {
        Map packsToRowNumbers = new HashMap();
        for (Map.Entry entry : nameToRow.entrySet())
        {
            packsToRowNumbers.put(nameToPack.get(entry.getKey()), entry.getValue());
        }
        return packsToRowNumbers;
    }

    /**
     * @return mapping from pack name to row position
     */
    public Map getNameToRow()
    {
        return nameToRow;
    }

    /**
     * @return the number of bytes that the installation requires based on selected packs
     */
    public int getTotalByteSize()
    {
        Map packToRow = getPacksToRowNumbers();
        int row;
        int bytes = 0;
        for (Pack pack : packs)
        {
            row = packToRow.get(pack);
            if(isChecked(row))
            {
                bytes += pack.getSize();
            }
        }
        return bytes;
    }

    /**
     * Check if the checkbox is selected given its row.
     *
     * @param row
     * @return {@code true} if checkbox is selected else {@code false}
     */
    public boolean isChecked(int row)
    {
        CbSelectionState state = checkValues.get(row);
        return state != null ? state.isChecked() : false;
    }

    /**
     * @param row
     * @return {@code true} if checkbox is partially selected else {@code false}
     */
    public boolean isPartiallyChecked(int row)
    {
        CbSelectionState state = checkValues.get(row);
        return state != null ? state.isPartiallyChecked() : false;
    }

    /**
     * @param row
     * @return {@code true} if the checkbox is selected else {@code false}
     */
    public boolean isCheckBoxSelectable(int row)
    {
        CbSelectionState state = checkValues.get(row);
        return state != null ? state.isSelectable() : false;
    }

    /**
     * @return {@code true} if any dependencies for the visible packs exists else {@code false}
     */
    public boolean dependenciesExist()
    {
        for (Pack pack : getVisiblePacks())
        {
            if (pack.hasDependencies())
            {
                return true;
            }
        }
        return false;
    }

    /**
     * @param packName
     * @return helper method to get a pack object from the pack's name
     */
    public Pack getPack(String packName)
    {
        return nameToPack.get(packName);
    }

    /**
     * Enumeration of possible internal model states of a pack checkbox
     */
    public enum CbSelectionState
    {
        PARTIAL_SELECTED(2), SELECTED(1), DESELECTED(0),
        REQUIRED_SELECTED(-1), DEPENDENT_DESELECTED(-2), REQUIRED_PARTIAL_SELECTED(-3), REQUIRED_DESELECTED(-4);

        private final int value;

        CbSelectionState(int value)
        {
            this.value = value;
        }

        /**
         * Check whether the checkbox state is one of {@code SELECTED}, {@code PARTIAL_SELECTED}.
         * @return {@code true} if the above condition is met
         */
        public boolean isFullyOrPartiallySelected()
        {
            return this.value > 0;
        }

        /**
         * Check whether the checkbox state is one of {@code DESELECTED}, {@code SELECTED}, {@code PARTIAL_SELECTED}.
         * @return {@code true} if the above condition is met
         */
        public boolean isSelectable()
        {
            return this.value >= 0;
        }

        /**
         * Check whether the checkbox state is one of {@code REQUIRED_SELECTED}, {@code SELECTED}.
         * @return {@code true} if the above condition is met
         */
        public boolean isSelectedOrRequiredSelected()
        {
            final int ordinal = this.ordinal();
            return SELECTED.ordinal() == ordinal || REQUIRED_SELECTED.ordinal() == ordinal;
        }

        /**
         * Check if the checkbox state means selected or required to be selected (even partially).
         * @return {@code true} if checkbox is selected else {@code false}
         */
        public boolean isChecked()
        {
            final int ordinal = this.ordinal();
            return SELECTED.ordinal() == ordinal || REQUIRED_SELECTED.ordinal() == ordinal
                    || PARTIAL_SELECTED.ordinal() == ordinal || REQUIRED_PARTIAL_SELECTED.ordinal() == ordinal;
        }

        /**
         * Check if the checkbox state means partially selected or required to be partially selected.
         * @return {@code true} if checkbox is selected else {@code false}
         */
        public boolean isPartiallyChecked()
        {
            final int ordinal = this.ordinal();
            return PARTIAL_SELECTED.ordinal() == ordinal || REQUIRED_PARTIAL_SELECTED.ordinal() == ordinal;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy