DriveFile.java

/*
 * onedrive-java-sdk - A Java SDK to access OneDrive drives and files.
 * Copyright © 2023-2025 Andy Miles (andy.miles@amilesend.com)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package com.amilesend.onedrive.resource;

import com.amilesend.client.connection.file.LogProgressCallback;
import com.amilesend.client.connection.file.TransferProgressCallback;
import com.amilesend.onedrive.resource.item.DriveItem;
import com.amilesend.onedrive.resource.item.type.Audio;
import com.amilesend.onedrive.resource.item.type.GeoCoordinates;
import com.amilesend.onedrive.resource.item.type.Image;
import com.amilesend.onedrive.resource.item.type.Photo;
import com.amilesend.onedrive.resource.item.type.Preview;
import com.amilesend.onedrive.resource.item.type.Video;
import com.amilesend.onedrive.resource.request.CreateSharingLinkRequest;
import com.amilesend.onedrive.resource.request.PreviewRequest;
import lombok.NonNull;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.IOException;
import java.nio.file.Path;

import static com.amilesend.client.connection.file.LogProgressCallback.formatPrefix;

/** A wrapper around a {@link DriveItem} that represents a file. */
public class DriveFile extends DriveItemType {

    /**
     * Creates a new {@code DriveFile} that wraps the given {@code getDelegate()} {@link DriveItem}.
     *
     * @param delegate the drive item to wrap
     */
    public DriveFile(final DriveItem delegate) {
        super(delegate);
    }

    //////////////////////
    // Accessors
    //////////////////////

    /**
     * Describes if this is an audio file.
     *
     * @return {@code true} if this file is an audio file; else, {@code false}
     */
    public boolean isAudio() {
        return getAudioAttributes() != null;
    }

    /**
     * Gets the audio file attributes. Can be {@code null}.
     *
     * @return the audio file attributes
     * @see Audio
     */
    public Audio getAudioAttributes() {
        return getDelegate().getAudio();
    }

    /**
     * Describes if this is an image file.
     *
     * @return {@code true} if this file is an image file; else, {@code false}
     */
    public boolean isImage() {
        return getImageAttributes() != null;
    }

    /**
     * Gets the image file attributes. Can be {@code null}.
     *
     * @return the image file attributes
     * @see Image
     */
    public Image getImageAttributes() {
        return getDelegate().getImage();
    }

    /**
     * Determines if there is geolocation attributes for this file.
     *
     * @return {@code true} if this file has location attributes; else, {@code false}
     */
    public boolean isLocationAvailable() {
        return getLocationAttributes() != null;
    }

    /**
     * Gets the location attributes (e.g., photo taken from a cell phone). Can be {@code null}.
     *
     * @return the location file attributes
     * @see GeoCoordinates
     */
    public GeoCoordinates getLocationAttributes() {
        return getDelegate().getLocation();
    }

    /**
     * Describes if this has photo attributes. Can be {@code null}.
     *
     * @return {@code true} if this file contains photo attributes; else, {@code false}
     */
    public boolean isPhoto() {
        return getPhotoAttributes() != null;
    }

    /**
     * Gets the photo attributes (e.g., photo taken from a cell phone). Can be {@code null}.
     *
     * @return the photo attributes
     * @see Photo
     */
    public Photo getPhotoAttributes() {
        return getDelegate().getPhoto();
    }

    /**
     * Describes if this is a video file.
     *
     * @return {@code true} if this file is a video file; else, {@code false}
     */
    public boolean isVideo() {
        return getVideoAttributes() != null;
    }

    /**
     * Gets the vidoe file attributes. Can be {@code null}.
     *
     * @return the video file attributes
     * @see Video
     */
    public Video getVideoAttributes() {
        return getDelegate().getVideo();
    }

    @Override
    public String toString() {
        return new StringBuilder("DriveFile(name=")
                .append(getName())
                .append(", id=")
                .append(getId())
                .append(", isDeleted=")
                .append(isDeleted())
                .append(", isRemote=")
                .append(isRemote())
                .append(", isAudio=")
                .append(isAudio())
                .append(", isImage=")
                .append(isImage())
                .append(", isVideo=")
                .append(isVideo())
                .append(")")
                .toString();
    }

    //////////////////////
    // Download
    //////////////////////

    /**
     * Downloads this file to the given {@code folderPath}.
     *
     * @param folderPath the folder to download the file to
     */
    public void download(final Path folderPath) {
        getDelegate().download(
                folderPath,
                LogProgressCallback.builder()
                        .prefix(formatPrefix("OneDrive", folderPath.toFile().getName()))
                        .transferType(LogProgressCallback.TransferType.DOWNLOAD)
                        .build());
    }

    /**
     * Downloads this file to the given {@code folderPath} and reports transfer progress to the
     * specified {@link TransferProgressCallback}.
     *
     * @param folderPath the folder to download the file to
     * @param callback the callback be notified of transfer progress
     * @see TransferProgressCallback
     */
    public void download(final Path folderPath, final TransferProgressCallback callback) {
        getDelegate().download(folderPath, callback);
    }

    /**
     * Downloads this file asynchronously to the given {@code folderPath}. Consumers can block on transfer completion
     * by invoking {@link DriveFileDownloadExecution#get()} ()}.
     *
     * @param folderPath the folder to download the file to
     * @return the asynchronous execution that contains the number of bytes downloaded
     * @see DriveFileDownloadExecution
     */
    public DriveFileDownloadExecution downloadAsync(final Path folderPath) {
        return downloadAsync(
                folderPath,
                LogProgressCallback.builder()
                        .prefix(formatPrefix("OneDrive", folderPath.toFile().getName()))
                        .transferType(LogProgressCallback.TransferType.DOWNLOAD)
                        .build());
    }

    /**
     * Downloads this file asynchronously to the given {@code folderPath} and reports transfer progress to the specified
     * {@link TransferProgressCallback}. Consumers can block on transfer completion by invoking
     * {@link DriveFileDownloadExecution#get()}.
     *
     * @param folderPath the folder to download the file to
     * @param callback the callback be notified of transfer progress
     * @return the asynchronous execution that contains the number of bytes downloaded
     * @see TransferProgressCallback
     * @see DriveFileDownloadExecution
     */
    public DriveFileDownloadExecution downloadAsync(
            final Path folderPath,
            final TransferProgressCallback callback) {
        return new DriveFileDownloadExecution(getDelegate().downloadAsync(folderPath, callback));
    }

    //////////////////////
    // Upload
    //////////////////////

    /**
     * Uploads and replaces this drive file's contents with the given {@code file}.
     *
     * @param file the updated file contents
     * @return a new drive file that represents this updated file
     * @throws IOException if unable to read the file.
     * @see DriveFile
     * @deprecated use {@link #upload(Path)} instead
     */
    @Deprecated
    public DriveFile upload(final File file) throws IOException {
        return upload(file.toPath());
    }

    /**
     * Uploads and replaces this drive file's contents with the given {@code file}.
     *
     * @param filePath the updated file contents
     * @return a new drive file that represents this updated file
     * @throws IOException if unable to read the file.
     * @see DriveFile
     */
    public DriveFile upload(final Path filePath) throws IOException {
        return upload(
                filePath,
                LogProgressCallback.builder()
                        .prefix(formatPrefix(filePath.getFileName().toString(), "OneDrive"))
                        .transferType(LogProgressCallback.TransferType.UPLOAD)
                        .build());
    }

    /**
     * Uploads and replaces this drive file's contents with the given {@code file} and reports transfer
     * progress to the specified {@link TransferProgressCallback}.
     *
     * @param file the updated file contents
     * @param callback the callback be notified of transfer progress
     * @return a new drive file that represents this updated file
     * @throws IOException if unable to read the file.
     * @see DriveFile
     * @deprecated use {@link #upload(Path, TransferProgressCallback)} instead
     */
    @Deprecated
    public DriveFile upload(final File file, final TransferProgressCallback callback) throws IOException {
        return upload(file.toPath(), callback);
    }

    /**
     * Uploads and replaces this drive file's contents with the given {@code file} and reports transfer
     * progress to the specified {@link TransferProgressCallback}.
     *
     * @param filePath the updated file contents
     * @param callback the callback be notified of transfer progress
     * @return a new drive file that represents this updated file
     * @throws IOException if unable to read the file.
     * @see DriveFile
     */
    public DriveFile upload(final Path filePath, final TransferProgressCallback callback) throws IOException {
        return new DriveFile(getDelegate().upload(filePath, callback));
    }

    /**
     * Uploads and replaces this drive file's contents asynchronously with the given {@code file}.
     * Consumers can block on transfer completion by invoking {@link DriveFileUploadExecution#get()}.
     *
     * @param file the updated file contents
     * @return a new drive file that represents this updated file
     * @throws IOException if unable to read the file.
     * @see DriveFileUploadExecution
     * @deprecated use {@link #uploadAsync(Path)} instead
     */
    @Deprecated
    public DriveFileUploadExecution uploadAsync(final File file) throws IOException {
        return uploadAsync(
                file,
                LogProgressCallback.builder()
                        .prefix(formatPrefix(file.getName(), "OneDrive"))
                        .transferType(LogProgressCallback.TransferType.UPLOAD)
                        .build());
    }

    /**
     * Uploads and replaces this drive file's contents asynchronously with the given {@code file}.
     * Consumers can block on transfer completion by invoking {@link DriveFileUploadExecution#get()}.
     *
     * @param filePath the updated file contents
     * @return a new drive file that represents this updated file
     * @throws IOException if unable to read the file.
     * @see DriveFileUploadExecution
     */
    public DriveFileUploadExecution uploadAsync(final Path filePath) throws IOException {
        return uploadAsync(
                filePath,
                LogProgressCallback.builder()
                        .prefix(formatPrefix(filePath.getFileName().toString(), "OneDrive"))
                        .transferType(LogProgressCallback.TransferType.UPLOAD)
                        .build());
    }

    /**
     * Uploads and replaces this drive file's contents asynchronously with the given {@code file} and reports transfer
     * progress to the specified {@link TransferProgressCallback}.
     *
     * @param file the updated file contents
     * @param callback the callback be notified of transfer progress
     * @return the async execution used to obtain the drive file once it has completed
     * @throws IOException if unable to read the file.
     * @see TransferProgressCallback
     * @deprecated use {@link #uploadAsync(Path, TransferProgressCallback)} instead
     */
    @Deprecated
    public DriveFileUploadExecution uploadAsync(
            final File file,
            final TransferProgressCallback callback) throws IOException {
        return uploadAsync(file.toPath(), callback);
    }

    /**
     * Uploads and replaces this drive file's contents asynchronously with the given {@code file} and reports transfer
     * progress to the specified {@link TransferProgressCallback}.
     *
     * @param filePath the updated file contents
     * @param callback the callback be notified of transfer progress
     * @return the async execution used to obtain the drive file once it has completed
     * @throws IOException if unable to read the file.
     * @see TransferProgressCallback
     */
    public DriveFileUploadExecution uploadAsync(
            final Path filePath,
            final TransferProgressCallback callback) throws IOException {
        return new DriveFileUploadExecution(getDelegate().uploadAsync(filePath, callback));
    }

    //////////////////////
    // Operations
    //////////////////////

    /**
     * Updates the attributes for this file.
     *
     * @return the updated drive file
     */
    public DriveFile update() {
        return new DriveFile(getDelegate().update());
    }

    /**
     * Gets the embeddable file preview URLs for inclusion in a web-based UI. Note: For long-lived embeddable links,
     * use {@link #createSharingLink(CreateSharingLinkRequest)} instead.
     *
     * @param request the preview item request
     * @return the preview URLs
     */
    public Preview preview(final PreviewRequest request) {
        return getDelegate().previewItem(request);
    }

    /**
     * Moves this file to the specified {@link DriveFolder}.
     *
     * @param destinationFolder the destination drive folder
     * @return the updated drive file that represents this moved file
     */
    public DriveFile move(final DriveFolder destinationFolder) {
        return move(destinationFolder, StringUtils.EMPTY);
    }

    /**
     * Moves this file to the specified {@link DriveFolder} along with the new file name.
     *
     * @param destinationFolder the destination drive folder
     * @param newName the new name of the file
     * @return the updated drive file that represents this moved file
     */
    public DriveFile move(@NonNull final DriveFolder destinationFolder, final String newName) {
        return move(destinationFolder.getId(), newName);
    }

    /**
     * Moves this file to the specified {@code newParentId} (i.e., represents a new DriveFolder's ID)
     * along with the new file name.
     *
     * @param destinationParentId the new parent destination ID
     * @param newName the new name of the file
     * @return the updated drive file that represents this moved file.
     */
    public DriveFile move(final String destinationParentId, final String newName) {
        return new DriveFile(getDelegate().move(destinationParentId, newName));
    }
}