Allow to select an existing security to replace a newly imported one
When reviewing the newly created items, the user can choose to replace a new security with an existing security. All transactions are then imported and added to the existing security.

* Extended ExtractedEntry with security dependency (= if an extracted entry depends on another extracted security) and security override (= if the user chose to replace the imported security with an existing security)
* Refactored the import logic into ImportController to allow sharing between PDF and CSV imports

https://github.com/portfolio-performance/portfolio/assets/587976/70d37082-d2f7-4d27-932a-2601c63f0a11


name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/wizards/datatransfer/ExtractedEntry.java
package name.abuchen.portfolio.ui.wizards.datatransfer;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

import name.abuchen.portfolio.datatransfer.Extractor;
import name.abuchen.portfolio.datatransfer.Extractor.Item;
import name.abuchen.portfolio.datatransfer.ImportAction;
import name.abuchen.portfolio.datatransfer.ImportAction.Status;

public class ExtractedEntry
{
    private final Extractor.Item item;

    private Boolean isImported = null;
    private Status.Code maxCode = Status.Code.OK;
    private List<Status> status = new ArrayList<>();

    public ExtractedEntry(Item item)
    {
        this.item = item;
    }

    public Extractor.Item getItem()
    {
        return item;
    }

    public void setImported(boolean isImported)
    {
        this.isImported = isImported;
    }
    
    public boolean isImported()
    {
        // do not import if explicitly excluded by the user
        if (isImported != null && !isImported.booleanValue())
            return false;

        // otherwise import if either the status is OK or the user explicitly
        // overrides warnings
        return maxCode == Status.Code.OK
                        || (maxCode == Status.Code.WARNING && isImported != null && isImported.booleanValue()
                        || (maxCode == Status.Code.WARNING && item.isInvestmentPlanItem()));
    }

    public void addStatus(ImportAction.Status status)
    {
        this.status.add(status);
        if (status.getCode().isHigherSeverityAs(maxCode))
            maxCode = status.getCode();
    }

    public Stream<Status> getStatus()
    {
        return this.status.stream();
    }

    public Status.Code getMaxCode()
    {
        return maxCode;
    }

    public void clearStatus()
    {
        this.status.clear();
        this.maxCode = Status.Code.OK;
    }
}
package name.abuchen.portfolio.ui.wizards.datatransfer;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

import name.abuchen.portfolio.datatransfer.Extractor;
import name.abuchen.portfolio.datatransfer.Extractor.Item;
import name.abuchen.portfolio.datatransfer.ImportAction;
import name.abuchen.portfolio.datatransfer.ImportAction.Status;
import name.abuchen.portfolio.model.Security;

public class ExtractedEntry
{
    private final Extractor.Item item;

    private Boolean isImported = null;
    private Status.Code maxCode = Status.Code.OK;
    private List<Status> status = new ArrayList<>();

    /**
     * If non null, the security dependency tells which other extracted items
     * represents the security which this extracted items (typically a
     * transactions) requires.
     */
    private ExtractedEntry securityDependency;

    /**
     * If non null, then the security dependency is overwritten by the given
     * security.
     */
    private Security securityOverride;

    public ExtractedEntry(Item item)
    {
        this.item = item;
    }

    public Extractor.Item getItem()
    {
        return item;
    }

    public void setImported(boolean isImported)
    {
        this.isImported = isImported;
    }

    public boolean isImported()
    {
        // do not import if explicitly excluded by the user
        if (isImported != null && !isImported.booleanValue())
            return false;

        // do not import if the entry has a dependency which is not imported
        // while there is no security override
        if (securityDependency != null && !securityDependency.isImported() && securityOverride == null)
            return false;

        // otherwise import if either the status is OK or the user explicitly
        // overrides warnings
        return maxCode == Status.Code.OK
                        || (maxCode == Status.Code.WARNING && isImported != null && isImported.booleanValue()
                                        || (maxCode == Status.Code.WARNING && item.isInvestmentPlanItem()));
    }

    public void addStatus(ImportAction.Status status)
    {
        this.status.add(status);
        if (status.getCode().isHigherSeverityAs(maxCode))
            maxCode = status.getCode();
    }

    public Stream<Status> getStatus()
    {
        return this.status.stream();
    }

    public Status.Code getMaxCode()
    {
        return maxCode;
    }

    public void clearStatus()
    {
        this.status.clear();
        this.maxCode = Status.Code.OK;
    }

    public ExtractedEntry getSecurityDependency()
    {
        return securityDependency;
    }

    public void setSecurityDependency(ExtractedEntry securityDependency)
    {
        this.securityDependency = securityDependency;
    }

    public Security getSecurityOverride()
    {
        return securityOverride;
    }

    public void setSecurityOverride(Security securityOverride)
    {
        this.securityOverride = securityOverride;
    }
}

It was only a formatting change - maybe I should remove even the formatting from the commit.

I am also not sure if the statements are identical due to the missing parenthesis.