Last active
November 2, 2020 21:34
-
-
Save jenetics/279c6c620b45c8eb0c540acbee4aea71 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.io.BufferedInputStream; | |
import java.io.BufferedOutputStream; | |
import java.io.EOFException; | |
import java.io.FileInputStream; | |
import java.io.FileOutputStream; | |
import java.io.IOException; | |
import java.io.ObjectInputStream; | |
import java.io.ObjectOutputStream; | |
import java.io.OutputStream; | |
import java.io.UncheckedIOException; | |
import java.nio.file.Files; | |
import java.nio.file.Path; | |
import java.util.Arrays; | |
import java.util.Collection; | |
import java.util.Objects; | |
import java.util.concurrent.atomic.AtomicBoolean; | |
import java.util.function.Supplier; | |
import java.util.stream.Stream; | |
import io.jenetics.incubator.util.Lifecycle.CloseableValue; | |
import io.jenetics.incubator.util.Lifecycle.ResourceCollector; | |
public final class AppendableObjectWriter { | |
private AppendableObjectWriter() { | |
} | |
/** | |
* Writes the given {@code objects} to the given {@code file}, using the | |
* Java serialization. If the {@code file} already exists, the objects are | |
* appended. | |
* | |
* @see #read(Path) | |
* | |
* @param file the destination where the {@code objects} are written to | |
* @param objects the {@code objects} to write | |
* @throws IOException if writing the objects fails | |
* @throws NullPointerException if one of the arguments is {@code null} | |
*/ | |
public static void write(final Path file, final Collection<?> objects) | |
throws IOException | |
{ | |
if (!objects.isEmpty()) { | |
final var append = new AtomicBoolean(!isEmpty(file)); | |
final class Output extends ObjectOutputStream { | |
Output(final OutputStream out) throws IOException { | |
super(out); | |
} | |
@Override | |
protected void writeStreamHeader() throws IOException { | |
if (!append.get()) { | |
super.writeStreamHeader(); | |
} | |
} | |
} | |
try (var fos = new FileOutputStream(file.toFile(), true); | |
var bos = new BufferedOutputStream(fos); | |
var out = new Output(bos)) | |
{ | |
for (var obj : objects) { | |
out.writeObject(obj); | |
out.reset(); | |
append.set(true); | |
} | |
} | |
} | |
} | |
private static boolean isEmpty(final Path file) throws IOException { | |
return !Files.exists(file) || Files.size(file) == 0; | |
} | |
/** | |
* Writes the given {@code objects} to the given {@code file}, using the | |
* Java serialization. If the {@code file} already exists, the objects are | |
* appended. | |
* | |
* @param file the destination where the {@code objects} are written to | |
* @param objects the {@code objects} to write | |
* @throws IOException if writing the objects fails | |
* @throws NullPointerException if one of the arguments is {@code null} | |
*/ | |
public static void write(final Path file, final Object... objects) | |
throws IOException | |
{ | |
write(file, Arrays.asList(objects)); | |
} | |
/** | |
* Reads the object from the given {@code file}, which were previously | |
* written with the {@link #write(Path, Collection)} method. The caller is | |
* responsible for closing the returned object stream | |
* | |
* @param file the data file | |
* @return a stream of the read objects | |
* @throws java.io.FileNotFoundException if the given file could not be read | |
* @throws IOException if the object stream couldn't be created | |
*/ | |
public static Stream<Object> read(final Path file) throws IOException { | |
final var result = CloseableValue.build(resources -> | |
objectStream(file, resources) | |
); | |
return result.get().onClose(result::uncheckedClose); | |
} | |
private static Stream<Object> | |
objectStream(final Path file, final ResourceCollector resources) throws IOException { | |
if (isEmpty(file)) { | |
return Stream.empty(); | |
} else { | |
final var fin = resources.add(new FileInputStream(file.toFile())); | |
final var bin = resources.add(new BufferedInputStream(fin)); | |
final var oin = resources.add(new ObjectInputStream(bin)); | |
final Supplier<Object> readObject = () -> { | |
try { | |
return oin.readObject(); | |
} catch (EOFException|ClassNotFoundException e) { | |
return null; | |
} catch (IOException e) { | |
throw new UncheckedIOException(e); | |
} | |
}; | |
return Stream.generate(readObject) | |
.takeWhile(Objects::nonNull); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment