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.BuildException;
030import org.jdrupes.builder.api.ConfigurationException;
031import org.jdrupes.builder.api.FileResource;
032import org.jdrupes.builder.api.ResourceType;
033
034/// A resource that represents a file.
035///
036public class DefaultFileResource extends ResourceObject
037        implements FileResource {
038
039    private final Path path;
040
041    /// Instantiates a new file resource.
042    ///
043    /// @param type the type
044    /// @param path the path
045    ///
046    @SuppressWarnings("PMD.ConstructorCallsOverridableMethod")
047    protected DefaultFileResource(ResourceType<? extends FileResource> type,
048            Path path) {
049        super(type);
050        if (!path.isAbsolute()) {
051            throw new ConfigurationException().message(
052                "Path must be absolute, is %s", path);
053        }
054        var relPath = Path.of("").toAbsolutePath().relativize(path);
055        name(relPath.equals(Path.of("")) ? "." : relPath.toString());
056        this.path = path;
057    }
058
059    /// Path.
060    ///
061    /// @return the path
062    ///
063    @Override
064    public Path path() {
065        return path;
066    }
067
068    @Override
069    public Optional<Instant> asOf() {
070        if (!path.toFile().exists()) {
071            return Optional.empty();
072        }
073        return Optional.of(Instant.ofEpochMilli(path.toFile().lastModified()));
074    }
075
076    @Override
077    public InputStream inputStream() {
078        try {
079            return Files.newInputStream(path);
080        } catch (IOException e) {
081            throw new BuildException().cause(e);
082        }
083    }
084
085    @Override
086    public OutputStream outputStream() {
087        try {
088            return Files.newOutputStream(path);
089        } catch (IOException e) {
090            throw new BuildException().cause(e);
091        }
092    }
093
094    @Override
095    public int hashCode() {
096        final int prime = 31;
097        int result = super.hashCode();
098        result = prime * result + Objects.hash(path);
099        return result;
100    }
101
102    @Override
103    public boolean equals(Object obj) {
104        if (this == obj) {
105            return true;
106        }
107        if (!super.equals(obj)) {
108            return false;
109        }
110        return (obj instanceof FileResource other)
111            && Objects.equals(path, other.path());
112    }
113}