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.lang.reflect.Proxy; 025import java.nio.file.Files; 026import java.nio.file.Path; 027import java.time.Instant; 028import java.util.Objects; 029import org.jdrupes.builder.api.BuildException; 030import org.jdrupes.builder.api.FileResource; 031import org.jdrupes.builder.api.Proxyable; 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 protected DefaultFileResource(ResourceType<? extends FileResource> type, 047 Path path) { 048 super(type); 049 if (!path.isAbsolute()) { 050 throw new BuildException("Path must be absolute, is " + path); 051 } 052 this.path = path; 053 } 054 055 /// Creates a new file resource. 056 /// 057 /// @param <T> the resource type 058 /// @param type the type 059 /// @param path the path 060 /// @return the t 061 /// 062 @SuppressWarnings({ "unchecked" }) 063 public static <T extends FileResource> T createFileResource( 064 ResourceType<T> type, Path path) { 065 return (T) Proxy.newProxyInstance(type.rawType().getClassLoader(), 066 new Class<?>[] { type.rawType(), Proxyable.class }, 067 new ForwardingHandler(new DefaultFileResource(type, path))); 068 } 069 070 /// Path. 071 /// 072 /// @return the path 073 /// 074 @Override 075 public Path path() { 076 return path; 077 } 078 079 @Override 080 public Instant asOf() { 081 if (!path.toFile().exists()) { 082 return Instant.MIN; 083 } 084 return Instant.ofEpochMilli(path.toFile().lastModified()); 085 } 086 087 @Override 088 public InputStream inputStream() throws IOException { 089 return Files.newInputStream(path); 090 } 091 092 @Override 093 public OutputStream outputStream() throws IOException { 094 return Files.newOutputStream(path); 095 } 096 097 @Override 098 public int hashCode() { 099 final int prime = 31; 100 int result = super.hashCode(); 101 result = prime * result + Objects.hash(path); 102 return result; 103 } 104 105 @Override 106 public boolean equals(Object obj) { 107 if (this == obj) { 108 return true; 109 } 110 if (!super.equals(obj)) { 111 return false; 112 } 113 return (obj instanceof FileResource other) 114 && Objects.equals(path, other.path()); 115 } 116 117 @Override 118 public String toString() { 119 var relPath = Path.of("").toAbsolutePath().relativize(path); 120 return type() + ": " + (relPath.equals(Path.of("")) ? "." 121 : relPath.toString()) + " (" + asOfLocalized() + ")"; 122 } 123}