LionService.java
/*
* Copyright © 2024-2025 The CTAN Team and individual authors
*
* This file is distributed under the 3-clause BSD license.
* See file LICENSE for details.
*/
package org.ctan.site.services.content;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.ctan.site.CtanConfiguration.ContentConfig;
import org.ctan.site.services.DateUtils;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NonNull;
/**
* The class <code>LionService</code> contains the service to access the lion
* directory.
*
* @author <a href="mailto:gene@ctan.org">Gerd Neugebauer</a>
*/
public class LionService {
/**
* The class <code>FileListTo</code> contains a transport object for the
* list resource.
*/
@Getter
@Builder
@AllArgsConstructor
@SuppressFBWarnings(value = "EI_EXPOSE_REP")
public static class FileListTo {
/**
* The field <code>readme</code> contains the optional name of a README
* file.
*/
private String readme;
/**
* The field <code>files</code> contains the files contained.
*/
private List<FileTo> files;
}
/**
* The class <code>FileTo</code> contains the transport object for the list
* resource.
*/
@Getter
@Builder
@AllArgsConstructor
public static class FileTo {
/**
* The field <code>name</code> contains the file name.
*/
private String name;
/**
* The field <code>size</code> contains the file size.
*/
private long size;
/**
* The field <code>mtime</code> contains the modification time.
*/
private String mtime;
/**
* The field <code>type</code> contains the type.
*/
private char type;
}
/**
* The field <code>CTAN_LION</code> contains the name of the directory.
*/
private static final String CTAN_LION = "ctan-lion";
/**
* The field <code>dir</code> contains the directory of the lion scans.
*/
private File dir;
/**
* This is the constructor for the class <code>LionService</code>.
*
* @param config the content configuration
*/
@SuppressFBWarnings(value = "CT_CONSTRUCTOR_THROW")
public LionService(@NonNull ContentConfig config) {
var base = config.getDirectory();
if (base == null || "".equals(base) || !new File(base).isDirectory()) {
throw new IllegalArgumentException(
"ctan.content.directory is not a directory " + base);
}
this.dir = new File(base, CTAN_LION);
if (!this.dir.isDirectory()) {
throw new IllegalArgumentException(
"ctan.content has no directory '" + CTAN_LION + "'");
}
}
/**
* The method <code>content</code> provides means to read the contents of a
* file.
*
* @param name the name of the file
* @return the contents or {@code null}
* @throws IOException in case of an I/O error
* @throws FileNotFoundException in case the file does not exist
*/
public byte[] content(@NonNull String name)
throws FileNotFoundException,
IOException {
if (name.contains("/")) {
return null;
}
var f = new File(dir, name);
try (var in = new FileInputStream(f)) {
return in.readAllBytes();
}
}
/**
* The method <code>list</code> provides means to retrieve the directory
* listing for the lion files.
*
* @return a transport object
*/
public FileListTo list() {
String readme;
try {
readme = Files.readString(new File(dir, "README").toPath());
} catch (IOException e) {
readme = "";
}
List<FileTo> files =
Arrays.stream(dir.listFiles())
.sorted((a, b) -> a.compareTo(b))
.map(f -> FileTo.builder()
.name(f.getName())
.mtime(
DateUtils.formatDateTime(f.lastModified()))
.size(f.length())
.type('f')
.build())
.collect(Collectors.toList());
return FileListTo.builder()
.readme(readme)
.files(files)
.build();
}
}