<commit_before>package com.molina.cvmfs.repository;

import com.molina.cvmfs.catalog.Catalog;
import com.molina.cvmfs.catalog.CatalogReference;
import com.molina.cvmfs.catalog.exception.CatalogInitializationException;
import com.molina.cvmfs.certificate.Certificate;
import com.molina.cvmfs.common.Common;
import com.molina.cvmfs.directoryentry.DirectoryEntry;
import com.molina.cvmfs.directoryentry.DirectoryEntryWrapper;
import com.molina.cvmfs.manifest.Manifest;
import com.molina.cvmfs.manifest.exception.ManifestException;
import com.molina.cvmfs.manifest.exception.ManifestValidityError;
import com.molina.cvmfs.manifest.exception.UnknownManifestField;
import com.molina.cvmfs.repository.exception.CacheDirectoryNotFound;
import com.molina.cvmfs.repository.exception.FailedToLoadSourceException;
import com.molina.cvmfs.repository.exception.FileNotFoundInRepositoryException;
import com.molina.cvmfs.repository.fetcher.Fetcher;
import com.molina.cvmfs.rootfile.exception.IncompleteRootFileSignature;
import com.molina.cvmfs.rootfile.exception.InvalidRootFileSignature;
import com.molina.cvmfs.rootfile.exception.RootFileException;
import com.molina.cvmfs.whitelist.Whitelist;

import java.io.*;
import java.nio.file.Files;
import java.security.cert.CertificateException;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Wrapper around a CVMFS repository representation
 */
public class Repository implements Iterable<DirectoryEntryWrapper> {
    protected Map<String, Catalog> openedCatalogs;
    protected Manifest manifest;
    protected String fqrn;
    protected String storageLocation;
    protected String type = "unknown";
    protected Date replicatingSince;
    protected Date lastReplication;
    protected boolean replicating;
    protected Fetcher fetcher;

    public Repository(String source, String cacheDirectory)
            throws FailedToLoadSourceException,
            IOException, CacheDirectoryNotFound, RootFileException {
        if (source == null || source.isEmpty())
            throw new FailedToLoadSourceException("The source cannot be empty");
        openedCatalogs = new HashMap<String, Catalog>();
        storageLocation = cacheDirectory;
        determineSource(source, cacheDirectory);
        readManifest();
        try {
            tryToGetLastReplicationTimestamp();
            tryToGetReplicationState();
        } catch (IOException e) {
            System.err.println("Couldn't retrieve all replication data");
        }
        // load the root catalog to be able to perform operations
        retrieveRootCatalog();
    }

    public Repository(String source) throws IOException, RootFileException,
            FailedToLoadSourceException, CacheDirectoryNotFound {
        this(source, Files.createTempDirectory("cache.").toFile().getAbsolutePath());
    }

    public boolean unloadCatalogs() {
        boolean closed = true;
        for (Catalog c : openedCatalogs.values()) {
            if (!c.close())
                closed = false;
        }
        openedCatalogs.clear();
        return closed;
    }

    /**
     * Returns the catalog that has the corresponding path, or the closest
     *
     * @param path the path to search for
     * @return the currently best fit for the given path, but NOT
     * necessarily the catalog that contains the given path
     */
    private Catalog getOpenedCatalogForPath(String path) {
        Catalog bestCatalog = retrieveRootCatalog();
        int maxLength = 0;
        for (Catalog catalog : openedCatalogs.values()) {
            if (path.indexOf(catalog.getRootPrefix()) == 0 &&
                    catalog.getRootPrefix().length() > maxLength) {
                bestCatalog = catalog;
                maxLength = catalog.getRootPrefix().length();
            }
        }
        return bestCatalog;
    }

    private CatalogReference findBestFit(CatalogReference[] catalogReferences,
                                         String path) {
        CatalogReference bestFit = null;
        for (CatalogReference cr : catalogReferences) {
            if (cr.getRootPath().contains(path)) {
                bestFit = cr;
            }
        }
        return bestFit;
    }

    private DirectoryEntry lookupPath(String currentPath) {
        if (currentPath.equals("/"))
            currentPath = "";
        Catalog bestFit = getOpenedCatalogForPath(currentPath);
        DirectoryEntry result = bestFit.findDirectoryEntry(currentPath);
        while (result == null) {
            CatalogReference bestNested = findBestFit(bestFit.listNested(), currentPath);
            if (bestNested == null)
                break;
            bestFit = bestNested.retrieveFrom(this);
            result = bestFit.findDirectoryEntry(currentPath);
        }
        return result;
    }

    /**
     * Retrieves the DirectoryEntry that corresponds to the given path, if exists
     *
     * @param path          the path of the file or directory
     * @param followSymlink flag to indicate if symlinks should be followed
     * @return the DirectoryEntry for the given path, or null if the path is not correct
     */
    public DirectoryEntry lookup(String path, boolean followSymlink) {
        DirectoryEntry result = null;
        int index = -1;
        if (path == null || path.equals(""))
            path = "/";
        while (index < path.length() - 1) {
            index = path.indexOf('/', index + 1);
            String currentPath;
            if (index < 0) {
                index = path.length();
                currentPath = path;
            } else {
                currentPath = path.substring(0, index);
            }
            result = lookupPath(currentPath);
            if (result == null)
                return null;
            if (result.isSymplink()) {
                int nextIndex = path.indexOf('/', index + 1);
                if (nextIndex != -1 || followSymlink) {
                    nextIndex = nextIndex == -1 ? path.length() : nextIndex;
                    path = path.substring(0, index + 1) +
                            result.getSymlink() + path.substring(nextIndex);
                }
            }
        }
        return result;
    }

    /**
     * Retrieves the DirectoryEntry that corresponds to the given path, if exists.
     * Symlinks will be followed
     *
     * @param path the path of the file or directory
     * @return the DirectoryEntry for the given path, or null if the path is not correct
     */
    public DirectoryEntry lookup(String path) {
        return lookup(path, true);
    }

    /**
     * List a directory
     *
     * @param path path of the directory<commit_msg>FIX: symlinks resolved in Repository<commit_after>package com.molina.cvmfs.repository;

import com.molina.cvmfs.catalog.Catalog;
import com.molina.cvmfs.catalog.CatalogReference;
import com.molina.cvmfs.catalog.exception.CatalogInitializationException;
import com.molina.cvmfs.certificate.Certificate;
import com.molina.cvmfs.common.Common;
import com.molina.cvmfs.directoryentry.DirectoryEntry;
import com.molina.cvmfs.directoryentry.DirectoryEntryWrapper;
import com.molina.cvmfs.manifest.Manifest;
import com.molina.cvmfs.manifest.exception.ManifestException;
import com.molina.cvmfs.manifest.exception.ManifestValidityError;
import com.molina.cvmfs.manifest.exception.UnknownManifestField;
import com.molina.cvmfs.repository.exception.CacheDirectoryNotFound;
import com.molina.cvmfs.repository.exception.FailedToLoadSourceException;
import com.molina.cvmfs.repository.exception.FileNotFoundInRepositoryException;
import com.molina.cvmfs.repository.fetcher.Fetcher;
import com.molina.cvmfs.rootfile.exception.IncompleteRootFileSignature;
import com.molina.cvmfs.rootfile.exception.InvalidRootFileSignature;
import com.molina.cvmfs.rootfile.exception.RootFileException;
import com.molina.cvmfs.whitelist.Whitelist;

import java.io.*;
import java.nio.file.Files;
import java.security.cert.CertificateException;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Wrapper around a CVMFS repository representation
 */
public class Repository implements Iterable<DirectoryEntryWrapper> {
    protected Map<String, Catalog> openedCatalogs;
    protected Manifest manifest;
    protected String fqrn;
    protected String storageLocation;
    protected String type = "unknown";
    protected Date replicatingSince;
    protected Date lastReplication;
    protected boolean replicating;
    protected Fetcher fetcher;

    public Repository(String source, String cacheDirectory)
            throws FailedToLoadSourceException,
            IOException, CacheDirectoryNotFound, RootFileException {
        if (source == null || source.isEmpty())
            throw new FailedToLoadSourceException("The source cannot be empty");
        openedCatalogs = new HashMap<String, Catalog>();
        storageLocation = cacheDirectory;
        determineSource(source, cacheDirectory);
        readManifest();
        try {
            tryToGetLastReplicationTimestamp();
            tryToGetReplicationState();
        } catch (IOException e) {
            System.err.println("Couldn't retrieve all replication data");
        }
        // load the root catalog to be able to perform operations
        retrieveRootCatalog();
    }

    public Repository(String source) throws IOException, RootFileException,
            FailedToLoadSourceException, CacheDirectoryNotFound {
        this(source, Files.createTempDirectory("cache.").toFile().getAbsolutePath());
    }

    public boolean unloadCatalogs() {
        boolean closed = true;
        for (Catalog c : openedCatalogs.values()) {
            if (!c.close())
                closed = false;
        }
        openedCatalogs.clear();
        return closed;
    }

    /**
     * Returns the catalog that has the corresponding path, or the closest
     *
     * @param path the path to search for
     * @return the currently best fit for the given path, but NOT
     * necessarily the catalog that contains the given path
     */
    private Catalog getOpenedCatalogForPath(String path) {
        Catalog bestCatalog = retrieveRootCatalog();
        int maxLength = 0;
        for (Catalog catalog : openedCatalogs.values()) {
            if (path.indexOf(catalog.getRootPrefix()) == 0 &&
                    catalog.getRootPrefix().length() > maxLength) {
                bestCatalog = catalog;
                maxLength = catalog.getRootPrefix().length();
            }
        }
        return bestCatalog;
    }

    private CatalogReference findBestFit(CatalogReference[] catalogReferences,
                                         String path) {
        CatalogReference bestFit = null;
        for (CatalogReference cr : catalogReferences) {
            if (cr.getRootPath().contains(path)) {
                bestFit = cr;
            }
        }
        return bestFit;
    }

    private DirectoryEntry lookupPath(String currentPath) {
        if (currentPath.equals("/"))
            currentPath = "";
        Catalog bestFit = getOpenedCatalogForPath(currentPath);
        DirectoryEntry result = bestFit.findDirectoryEntry(currentPath);
        while (result == null) {
            CatalogReference bestNested = findBestFit(bestFit.listNested(), currentPath);
            if (bestNested == null)
                break;
            bestFit = bestNested.retrieveFrom(this);
            result = bestFit.findDirectoryEntry(currentPath);
        }
        return result;
    }

    /**
     * Retrieves the DirectoryEntry that corresponds to the given path, if exists
     *
     * @param path          the path of the file or directory
     * @return the DirectoryEntry for the given path, or null if the path is not correct
     */
    public DirectoryEntry lookup(String path) {
        DirectoryEntry result = null;
        int index = -1;
        if (path == null || path.equals(""))
            path = "/";
        while (index < path.length() - 1) {
            index = path.indexOf('/', index + 1);
            String currentPath;
            if (index < 0) {
                index = path.length();
                currentPath = path;
            } else {
                currentPath = path.substring(0, index);
            }
            result = lookupPath(currentPath);
            if (result == null)
                return null;
        }
        return result;
    }

    /**
     * List a directory
     *
     * @param path path of the directory
{'fraction_non_alphanumeric': 0.08801141769743102, 'fraction_numerical': 0.0015065017443704409, 'mean_word_length': 2.5439730261309355, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '253879', 'n_tokens_mistral': 3333, 'n_tokens_neox': 3211, 'n_words': 831}