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.java;
020
021import java.io.ByteArrayInputStream;
022import java.io.IOException;
023import java.io.InputStream;
024import java.io.OutputStream;
025import java.nio.charset.StandardCharsets;
026import java.time.Instant;
027import java.util.function.Predicate;
028import org.jdrupes.builder.api.IOResource;
029import org.jdrupes.builder.core.ResourceObject;
030
031/// A temporary resource that is used to store the combined
032/// `META-INF/services/` entries for a given service. The class can be
033/// used for this purpose only. In particular, it does not support
034/// `hashCode` or `equals`.
035///
036public class ServicesEntryResource extends ResourceObject
037        implements IOResource {
038    @SuppressWarnings("PMD.AvoidStringBufferField")
039    private final StringBuilder content = new StringBuilder();
040    private Instant asOf = Instant.MIN;
041
042    /// Initializes a new services entry resource.
043    ///
044    public ServicesEntryResource() {
045        // Makes javadoc happy.
046    }
047
048    @Override
049    public Instant asOf() {
050        return asOf;
051    }
052
053    /// Adds the given resource which must be a `META-INF/services/*`
054    /// entry from a jar.
055    ///
056    /// @param resource the resource
057    /// @throws IOException Signals that an I/O exception has occurred.
058    ///
059    public void add(IOResource resource) throws IOException {
060        try (InputStream toRead = resource.inputStream()) {
061            new String(toRead.readAllBytes(), StandardCharsets.UTF_8)
062                .lines().filter(Predicate.not(String::isBlank))
063                .forEach(l -> content.append(l).append('\n'));
064        }
065        if (resource.asOf().isAfter(asOf)) {
066            asOf = resource.asOf();
067        }
068    }
069
070    @Override
071    public InputStream inputStream() throws IOException {
072        return new ByteArrayInputStream(
073            content.toString().getBytes(StandardCharsets.UTF_8));
074    }
075
076    @Override
077    public OutputStream outputStream() throws IOException {
078        throw new UnsupportedOperationException();
079    }
080
081}