001/*
002 * JDrupes Builder
003 * Copyright (C) 2025 Michael N. Lipp
004 * 
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013 * GNU Affero General Public License for more details.
014 *
015 * You should have received a copy of the GNU Affero General Public License
016 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
017 */
018
019package org.jdrupes.builder.core;
020
021import java.io.IOException;
022import java.io.InputStream;
023import java.io.OutputStream;
024import java.nio.file.Files;
025import java.nio.file.Path;
026import java.time.Instant;
027import java.util.Objects;
028import java.util.Optional;
029import org.jdrupes.builder.api.ConfigurationException;
030import org.jdrupes.builder.api.FileResource;
031import org.jdrupes.builder.api.ResourceType;
032
033/// A resource that represents a file.
034///
035public class DefaultFileResource extends ResourceObject
036        implements FileResource {
037
038    private final Path path;
039
040    /// Instantiates a new file resource.
041    ///
042    /// @param type the type
043    /// @param path the path
044    ///
045    @SuppressWarnings("PMD.ConstructorCallsOverridableMethod")
046    protected DefaultFileResource(ResourceType<? extends FileResource> type,
047            Path path) {
048        super(type);
049        if (!path.isAbsolute()) {
050            throw new ConfigurationException().message(
051                "Path must be absolute, is %s", path);
052        }
053        var relPath = Path.of("").toAbsolutePath().relativize(path);
054        name(relPath.equals(Path.of("")) ? "." : relPath.toString());
055        this.path = path;
056    }
057
058    /// Path.
059    ///
060    /// @return the path
061    ///
062    @Override
063    public Path path() {
064        return path;
065    }
066
067    @Override
068    public Optional<Instant> asOf() {
069        if (!path.toFile().exists()) {
070            return Optional.empty();
071        }
072        return Optional.of(Instant.ofEpochMilli(path.toFile().lastModified()));
073    }
074
075    @Override
076    public InputStream inputStream() throws IOException {
077        return Files.newInputStream(path);
078    }
079
080    @Override
081    public OutputStream outputStream() throws IOException {
082        return Files.newOutputStream(path);
083    }
084
085    @Override
086    public int hashCode() {
087        final int prime = 31;
088        int result = super.hashCode();
089        result = prime * result + Objects.hash(path);
090        return result;
091    }
092
093    @Override
094    public boolean equals(Object obj) {
095        if (this == obj) {
096            return true;
097        }
098        if (!super.equals(obj)) {
099            return false;
100        }
101        return (obj instanceof FileResource other)
102            && Objects.equals(path, other.path());
103    }
104}