FileEntry.java

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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 org.apache.doris.fs;

import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * Immutable record describing a file or directory in a storage system.
 * <p>
 * Replaces {@link org.apache.doris.fs.remote.RemoteFile} with zero Hadoop dependency.
 * Block location information uses {@link BlockInfo} instead of {@code BlockLocation[]}.
 */
public final class FileEntry {

    private final Location location;
    private final boolean directory;
    private final long length;
    private final long blockSize;
    private final long modificationTime;
    private final List<BlockInfo> blocks;

    private FileEntry(Builder builder) {
        this.location = Objects.requireNonNull(builder.location, "location");
        this.directory = builder.directory;
        this.length = builder.length;
        this.blockSize = builder.blockSize;
        this.modificationTime = builder.modificationTime;
        this.blocks = builder.blocks == null
                ? Collections.emptyList()
                : Collections.unmodifiableList(builder.blocks);
    }

    /** Full location (URI) of this file or directory. */
    public Location location() {
        return location;
    }

    /** Returns the last component of the path (file or directory name). */
    public String name() {
        return location.name();
    }

    public boolean isDirectory() {
        return directory;
    }

    public boolean isFile() {
        return !directory;
    }

    /** File size in bytes. -1 if unknown. */
    public long length() {
        return length;
    }

    /** Underlying block size of the storage (e.g., HDFS 128 MB). 0 for object storage. */
    public long blockSize() {
        return blockSize;
    }

    /** Last-modified time in milliseconds since epoch. */
    public long modificationTime() {
        return modificationTime;
    }

    /** Block location hints for split planning. Empty list for object storage. */
    public List<BlockInfo> blocks() {
        return blocks;
    }

    @Override
    public String toString() {
        return "FileEntry{location=" + location
                + ", directory=" + directory
                + ", length=" + length
                + ", modificationTime=" + modificationTime + "}";
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof FileEntry)) {
            return false;
        }
        FileEntry that = (FileEntry) o;
        return directory == that.directory
                && length == that.length
                && modificationTime == that.modificationTime
                && location.equals(that.location);
    }

    @Override
    public int hashCode() {
        return Objects.hash(location, directory, length, modificationTime);
    }

    public static Builder builder(Location location) {
        return new Builder(location);
    }

    /** Convenience factory for simple file entries (no block info). */
    public static FileEntry file(Location location, long length, long modificationTime) {
        return builder(location).length(length).modificationTime(modificationTime).build();
    }

    /** Convenience factory for directory entries. */
    public static FileEntry directory(Location location, long modificationTime) {
        return builder(location).directory(true).modificationTime(modificationTime).build();
    }

    public static final class Builder {
        private final Location location;
        private boolean directory = false;
        private long length = -1L;
        private long blockSize = 0L;
        private long modificationTime = 0L;
        private List<BlockInfo> blocks;

        private Builder(Location location) {
            this.location = Objects.requireNonNull(location);
        }

        public Builder directory(boolean isDirectory) {
            this.directory = isDirectory;
            return this;
        }

        public Builder length(long fileLength) {
            this.length = fileLength;
            return this;
        }

        public Builder blockSize(long bSize) {
            this.blockSize = bSize;
            return this;
        }

        public Builder modificationTime(long modTime) {
            this.modificationTime = modTime;
            return this;
        }

        public Builder blocks(List<BlockInfo> blockList) {
            this.blocks = blockList;
            return this;
        }

        public Builder addBlock(BlockInfo block) {
            if (this.blocks == null) {
                this.blocks = new java.util.ArrayList<>();
            }
            this.blocks.add(block);
            return this;
        }

        public FileEntry build() {
            return new FileEntry(this);
        }
    }

    /**
     * Lightweight replacement for {@code org.apache.hadoop.fs.BlockLocation}.
     * Contains only the data needed by Doris split planning.
     */
    public static final class BlockInfo {
        private final long offset;
        private final long length;
        private final List<String> hosts;

        public BlockInfo(long offset, long length, List<String> hosts) {
            this.offset = offset;
            this.length = length;
            this.hosts = hosts == null ? Collections.emptyList() : Collections.unmodifiableList(hosts);
        }

        public long offset() {
            return offset;
        }

        public long length() {
            return length;
        }

        /** Data-local host names for this block (for scheduling affinity). */
        public List<String> hosts() {
            return hosts;
        }

        @Override
        public String toString() {
            return "BlockInfo{offset=" + offset + ", length=" + length + ", hosts=" + hosts + "}";
        }
    }
}