<commit_before>package name.abuchen.portfolio.model;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.crypto.SecretKey;

import name.abuchen.portfolio.Messages;
import name.abuchen.portfolio.model.Classification.Assignment;
import name.abuchen.portfolio.money.CurrencyUnit;

public class Client
{
    /* package */static final int MAJOR_VERSION = 1;

    public static final int CURRENT_VERSION = 37;
    public static final int VERSION_WITH_CURRENCY_SUPPORT = 29;

    private transient PropertyChangeSupport propertyChangeSupport;

    /**
     * The (minor) version of the file format. If it is lower than the current
     * version, then {@link ClientFactory#upgradeModel} will upgrade the model
     * and set the version number to the current version.
     */
    private int version = CURRENT_VERSION;

    /**
     * The (minor) version of the file format as it has been read from file.
     */
    private transient int fileVersionAfterRead = CURRENT_VERSION;

    private String baseCurrency = CurrencyUnit.EUR;

    private List<Security> securities = new ArrayList<>();
    private List<Watchlist> watchlists;

    // keep typo -> xstream deserialization
    private List<ConsumerPriceIndex> consumerPriceIndeces;

    private List<Account> accounts = new ArrayList<>();
    private List<Portfolio> portfolios = new ArrayList<>();
    private List<InvestmentPlan> plans;
    private List<Taxonomy> taxonomies;
    private List<Dashboard> dashboards;

    private Map<String, String> properties;
    private ClientSettings settings;

    @Deprecated
    private String industryTaxonomyId;

    @Deprecated
    private Category rootCategory;

    private transient SecretKey secret;

    public Client()
    {
        doPostLoadInitialization();
    }

    /* package */final void doPostLoadInitialization()
    {
        // when loading the Client from XML, attributes that are not (yet)
        // persisted in that version are not initialized

        if (watchlists == null)
            watchlists = new ArrayList<>();

        if (consumerPriceIndeces == null)
            consumerPriceIndeces = new ArrayList<>();

        if (properties == null)
            properties = new HashMap<>();

        if (propertyChangeSupport == null)
            propertyChangeSupport = new PropertyChangeSupport(this);

        if (plans == null)
            plans = new ArrayList<>();

        if (taxonomies == null)
            taxonomies = new ArrayList<>();

        if (dashboards == null)
            dashboards = new ArrayList<>();

        if (settings == null)
            settings = new ClientSettings();
        else
            settings.doPostLoadInitialization();
    }

    /* package */int getVersion()
    {
        return version;
    }

    /* package */void setVersion(int version)
    {
        this.version = version;
    }

    public int getFileVersionAfterRead()
    {
        return fileVersionAfterRead;
    }

    /* package */
    void setFileVersionAfterRead(int fileVersionAfterRead)
    {
        this.fileVersionAfterRead = fileVersionAfterRead;
    }

    public String getBaseCurrency()
    {
        return baseCurrency;
    }

    public void setBaseCurrency(String baseCurrency)
    {
        propertyChangeSupport.firePropertyChange("baseCurrency", this.baseCurrency, this.baseCurrency = baseCurrency); //$NON-NLS-1$
    }

    public List<InvestmentPlan> getPlans()
    {
        return Collections.unmodifiableList(plans);
    }

    public void addPlan(InvestmentPlan plan)
    {
        plans.add(plan);
    }

    public void removePlan(InvestmentPlan plan)
    {
        plans.remove(plan);
    }

    public List<Security> getSecurities()
    {
        return Collections.unmodifiableList(securities);
    }

    /**
     * Returns a sorted list of active securities, i.e. securities that are not
     * marked as retired.
     */
    public List<Security> getActiveSecurities()
    {
        return securities.stream() //
                        .filter(s -> s.getCurrencyCode() != null) //
                        .filter(s -> !s.isRetired()) //
                        .sorted(new Security.ByName()) //
                        .collect(Collectors.toList());
    }

    public void addSecurity(Security security)
    {
        Objects.requireNonNull(security);

        securities.add(security);

        propertyChangeSupport.firePropertyChange("securities", null, security); //$NON-NLS-1$
    }

    public void removeSecurity(final Security security)
    {
        for (Watchlist w : watchlists)
            w.getSecurities().remove(security);
        deleteInvestmentPlans(security);
        deleteTaxonomyAssignments(security);
        deleteAccountTransactions(security);
        deletePortfolioTransactions(security);

        securities.remove(security);

        propertyChangeSupport.firePropertyChange("securities", security, null); //$NON-NLS-1$
    }
    
    /**
     * Gets a list of used {@link CurrencyUnit}s.
     * 
     * @return list
     */
    public List<CurrencyUnit> getUsedCurrencies()
    {
        // collect all used currency codes
        HashSet<String> hsUsedCodes = new HashSet<String>();
        // first client and all accounts
        hsUsedCodes.add(baseCurrency);
        for (Account account : accounts)
        {
            hsUsedCodes.add(account.getCurrencyCode());
            for (AccountTransaction t : account.getTransactions())
            {
                hsUsedCodes.add(t.getCurrencyCode());
            }
        }
        // then portfolios
        for (Portfolio portfolio : portfolios)
        {
            for (PortfolioTransaction t : portfolio.getTransactions())
            {
                hsUsedCodes.add(t.getCurrencyCode());
            }
        }
        // then from all securities
        for (Security security : securities)<commit_msg>account transactions should always have the same currency as the account, so skip them.<commit_after>package name.abuchen.portfolio.model;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.crypto.SecretKey;

import name.abuchen.portfolio.Messages;
import name.abuchen.portfolio.model.Classification.Assignment;
import name.abuchen.portfolio.money.CurrencyUnit;

public class Client
{
    /* package */static final int MAJOR_VERSION = 1;

    public static final int CURRENT_VERSION = 37;
    public static final int VERSION_WITH_CURRENCY_SUPPORT = 29;

    private transient PropertyChangeSupport propertyChangeSupport;

    /**
     * The (minor) version of the file format. If it is lower than the current
     * version, then {@link ClientFactory#upgradeModel} will upgrade the model
     * and set the version number to the current version.
     */
    private int version = CURRENT_VERSION;

    /**
     * The (minor) version of the file format as it has been read from file.
     */
    private transient int fileVersionAfterRead = CURRENT_VERSION;

    private String baseCurrency = CurrencyUnit.EUR;

    private List<Security> securities = new ArrayList<>();
    private List<Watchlist> watchlists;

    // keep typo -> xstream deserialization
    private List<ConsumerPriceIndex> consumerPriceIndeces;

    private List<Account> accounts = new ArrayList<>();
    private List<Portfolio> portfolios = new ArrayList<>();
    private List<InvestmentPlan> plans;
    private List<Taxonomy> taxonomies;
    private List<Dashboard> dashboards;

    private Map<String, String> properties;
    private ClientSettings settings;

    @Deprecated
    private String industryTaxonomyId;

    @Deprecated
    private Category rootCategory;

    private transient SecretKey secret;

    public Client()
    {
        doPostLoadInitialization();
    }

    /* package */final void doPostLoadInitialization()
    {
        // when loading the Client from XML, attributes that are not (yet)
        // persisted in that version are not initialized

        if (watchlists == null)
            watchlists = new ArrayList<>();

        if (consumerPriceIndeces == null)
            consumerPriceIndeces = new ArrayList<>();

        if (properties == null)
            properties = new HashMap<>();

        if (propertyChangeSupport == null)
            propertyChangeSupport = new PropertyChangeSupport(this);

        if (plans == null)
            plans = new ArrayList<>();

        if (taxonomies == null)
            taxonomies = new ArrayList<>();

        if (dashboards == null)
            dashboards = new ArrayList<>();

        if (settings == null)
            settings = new ClientSettings();
        else
            settings.doPostLoadInitialization();
    }

    /* package */int getVersion()
    {
        return version;
    }

    /* package */void setVersion(int version)
    {
        this.version = version;
    }

    public int getFileVersionAfterRead()
    {
        return fileVersionAfterRead;
    }

    /* package */
    void setFileVersionAfterRead(int fileVersionAfterRead)
    {
        this.fileVersionAfterRead = fileVersionAfterRead;
    }

    public String getBaseCurrency()
    {
        return baseCurrency;
    }

    public void setBaseCurrency(String baseCurrency)
    {
        propertyChangeSupport.firePropertyChange("baseCurrency", this.baseCurrency, this.baseCurrency = baseCurrency); //$NON-NLS-1$
    }

    public List<InvestmentPlan> getPlans()
    {
        return Collections.unmodifiableList(plans);
    }

    public void addPlan(InvestmentPlan plan)
    {
        plans.add(plan);
    }

    public void removePlan(InvestmentPlan plan)
    {
        plans.remove(plan);
    }

    public List<Security> getSecurities()
    {
        return Collections.unmodifiableList(securities);
    }

    /**
     * Returns a sorted list of active securities, i.e. securities that are not
     * marked as retired.
     */
    public List<Security> getActiveSecurities()
    {
        return securities.stream() //
                        .filter(s -> s.getCurrencyCode() != null) //
                        .filter(s -> !s.isRetired()) //
                        .sorted(new Security.ByName()) //
                        .collect(Collectors.toList());
    }

    public void addSecurity(Security security)
    {
        Objects.requireNonNull(security);

        securities.add(security);

        propertyChangeSupport.firePropertyChange("securities", null, security); //$NON-NLS-1$
    }

    public void removeSecurity(final Security security)
    {
        for (Watchlist w : watchlists)
            w.getSecurities().remove(security);
        deleteInvestmentPlans(security);
        deleteTaxonomyAssignments(security);
        deleteAccountTransactions(security);
        deletePortfolioTransactions(security);

        securities.remove(security);

        propertyChangeSupport.firePropertyChange("securities", security, null); //$NON-NLS-1$
    }
    
    /**
     * Gets a list of used {@link CurrencyUnit}s.
     * 
     * @return list
     */
    public List<CurrencyUnit> getUsedCurrencies()
    {
        // collect all used currency codes
        HashSet<String> hsUsedCodes = new HashSet<String>();
        // first client and all accounts
        hsUsedCodes.add(baseCurrency);
        for (Account account : accounts)
        {
            hsUsedCodes.add(account.getCurrencyCode());
        }
        // then portfolios
        for (Portfolio portfolio : portfolios)
        {
            for (PortfolioTransaction t : portfolio.getTransactions())
            {
                hsUsedCodes.add(t.getCurrencyCode());
            }
        }
        // then from all securities
        for (Security security : securities)
{'fraction_non_alphanumeric': 0.0931272875152501, 'fraction_numerical': 0.0013013420089467262, 'mean_word_length': 2.4991462720546385, 'pattern_counts': {'":': 0, '<': 51, '<?xml version=': 0, '>': 57, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1088816', 'n_tokens_mistral': 3313, 'n_tokens_neox': 3191, 'n_words': 835}