MOTIVATION
On a youtube video of Venkat Subramaniam, he tells about how not use AutoCloseable, but try to use a function like "use". Because, one may forget to implement try block on a class which implements AutoCloseable.
QUESTION
I have implemented this strategy on many cases, however there is one case that I could not able to.
I have many file writers according to their file types. All of them uses same interface. So main macro code can be parsed and file actions can be processed for every kind of file types. (on the blog, I have reduced the function names to only "addText" and file-types as 2: "FileWeb" and "FileTable".)
And, users decides which type of files they want on everytime.
The sequencial way of doing is as below. (There, in function "constructFileAccordingToMacroCode", "the parsing of macrocode line by line" is processed twice).
{//SEQUENTIAL
boolean produceFileTable = true;
if (produceFileTable) {
FileTable.use(dir, fileLabel, fileTable -> {
constructFileAccordingToMacroCode(macroCode, fileTable);
});
}
boolean produceFileWeb = true;
if (produceFileWeb) {
FileWeb.use(dir, fileLabel, fileWeb -> {
constructFileAccordingToMacroCode(macroCode, fileWeb);
});
}
}
Kind of parallel way of doing is as below. (There, the function "constructFileAccordingToMacroCode" runs once, hence, "the parsing of macrocode line by line" is processed once).
{//KIND OF PARALLEL
boolean produceFileTable = true;//?
boolean produceFileWeb = true;//?
FileTable.use(dir, fileLabel, fileTable -> {
FileWeb.use(dir, fileLabel, fileWeb -> {
constructFileAccordingToMacroCode(macroCode, fileTable, fileWeb);
});
});
}
However, If I am gonna implement it, I will lose the option of not creating the unwanted file types.
Is there more smart way of doing things?
RELEVANT EXAMPLE CLASS FILES
Main
public class Main {
public static void main(String... s) {
var macroCode = """
LINE1: ADD_FILE_TYPE
LINE2: ADD_SYSTEM_MILLIS
""";
var dir = Path.of("c:\\fileFirectory");
var fileLabel = "fileLabel";
{//SEQUENTIAL
boolean produceFileTable = true;
if (produceFileTable) {
FileTable.use(dir, fileLabel, fileTable -> {
constructFileAccordingToMacroCode(macroCode, fileTable);
});
}
boolean produceFileWeb = true;
if (produceFileWeb) {
FileWeb.use(dir, fileLabel, fileWeb -> {
constructFileAccordingToMacroCode(macroCode, fileWeb);
});
}
}
{//KIND OF PARALLEL
boolean produceFileTable = true;//?
boolean produceFileWeb = true;//?
FileTable.use(dir, fileLabel, fileTable -> {
FileWeb.use(dir, fileLabel, fileWeb -> {
constructFileAccordingToMacroCode(macroCode, fileTable, fileWeb);
});
});
}
}
private static boolean constructFileAccordingToMacroCode(String macroCode, FileInterface... files) {
boolean result;
for (var file : files) {
//parse macroCode line by line in a for loop {
/*FOREXAMPLE LINE1: from macroCode*/
result = file.addText("I am a %s type file.".formatted(file.getClass().getSimpleName()));
if (!result) {
return false;
}
/*FOREXAMPLE LINE2: from macroCode*/
file.addText(", and time is %L".formatted(System.currentTimeMillis()));
if (!result) {
return false;
}
//}
}
return true;
}
}
TGS_RunnableType1
public interface TGS_RunnableType1<A> {
public void run(A result);
}
FileInterface
public interface FileInterface /*implements AutoCloseable*/{
public boolean addText(CharSequence text);
}
FileTable
public class FileTable implements FileInterface {
private FileTable(Path dir) {
this.dir = dir;
}
final public Path dir;
@Override
public boolean addText(CharSequence text) {
//TODO add text code
return true;
}
public static void use(Path dir, String fileLabel, TGS_RunnableType1<FileTable> fileTable) {
var instance = new FileTable(dir);
try {
instance.open();
fileTable.run(instance);
instance.close();
} catch (Exception e) {//SILENTLY CLOSE
try {
instance.close();
} catch (Exception e2) {
if (e2 instanceof InterruptedException) {//let InterruptedException propagate
throw e2;
}
}
if (e instanceof InterruptedException) {//let InterruptedException propagate
throw e;
}
}
}
private void open() {
//open according to dir & fileLabel & type
}
private void close() {
//close according to dir & fileLabel & type
}
}
FileWeb
public class FileWeb implements FileInterface {
private FileWeb(Path dir) {
this.dir = dir;
}
final public Path dir;
@Override
public boolean addText(CharSequence text) {
//TODO add text code
return true;
}
public static void use(Path dir, String fileLabel, TGS_RunnableType1<FileWeb> fileWeb) {
var instance = new FileWeb(dir);
try {
instance.open();
fileWeb.run(instance);
instance.close();
} catch (Exception e) {//SILENTLY CLOSE
try {
instance.close();
} catch (Exception e2) {
if (e2 instanceof InterruptedException) {//let InterruptedException propagate
throw e2;
}
}
if (e instanceof InterruptedException) {//let InterruptedException propagate
throw e;
}
}
}
private void open() {
//open according to dir & fileLabel & type
}
private void close() {
//close according to dir & fileLabel & type
}
//WILL POSSIBLY CREATE OUT OF MEMORY ERROR
private static boolean constructFileAccordingToMacroCode(String macroCode, FileInterface... files) {
//parse macroCode line by line in a for loop {
var errorPresent = IntStream.range(0, files.length).parallel()
.mapToObj(i -> {
var file = files[i];
/*FOREXAMPLE LINE1: from macroCode*/
var result = file.addText("I am a %s type file.".formatted(file.getClass().getSimpleName()));
if (!result) {
return false;
}
/*FOREXAMPLE LINE2: from macroCode*/
file.addText(", and time is %L".formatted(System.currentTimeMillis()));
if (!result) {
return false;
}
return true;
})
.filter(result -> false)
.findAny().isPresent();
if (errorPresent) {
return false;
}
//}
return true;
}
This is what i have come up with, but readability of nested code is far from good.
public abstract class TS_FileCommonAbstract {
public TS_FileCommonAbstract(boolean enabled, Path localFile, TGS_Url remoteFile) {
isEnabled = enabled;
if (!enabled) {
setClosed();
}
...
}
public boolean isEnabled() {
return isEnabled;
}
private boolean isEnabled = false;
public boolean isClosed() {
return isClosed;
}
private boolean isClosed = false;
final protected void setClosed() {
isClosed = true;
}
public abstract boolean createNewPage(int pageSizeAX, boolean landscape, Integer marginLeft, Integer marginRight, Integer marginTop, Integer marginBottom);
public abstract boolean beginText(int allign_Left0_center1_right2_just3);
public abstract boolean addText(String text);
public abstract boolean endText();
public abstract boolean saveFile(String errorSource);
....
}
public class TS_FileDocx extends TS_FileCommonAbstract {
private TS_FileCommonConfig fileCommonConfig;
private TS_FileDocx(boolean enabled, Path localFile, TGS_Url remoteFile) {
super(enabled, localFile, remoteFile);
}
public static void use(boolean enabled, TS_FileCommonConfig fileCommonConfig, Path localFile, TGS_Url remoteFile, TGS_RunnableType1<TS_FileDocx> docx) {
var instance = new TS_FileDocx(enabled, localFile, remoteFile);
try {
instance.use_init(fileCommonConfig);
docx.run(instance);
} catch (Exception e) {
instance.saveFile(e.getMessage());
throw e;
} finally {
instance.saveFile(null);
}
}
private void use_init(TS_FileCommonConfig fileCommonConfig) {
this.fileCommonConfig = fileCommonConfig;
if (isClosed()) {
return;
}
docx = new TS_FileDocxUtils(localFile);
}
...
}
public class TS_FileTmcrFileHandler {
private TS_FileTmcrFileHandler(TS_FileCommonConfig fileCommonConfig, Path localfileZIP, TGS_Url remotefileZIP, TS_FileCommonAbstract... files) {
this.fileCommonConfig = fileCommonConfig;
this.localfileZIP = localfileZIP;
this.remotefileZIP = remotefileZIP;
this.files = TGS_StreamUtils.toLst(Arrays.stream(files));
}
public Path localfileZIP;
public TGS_Url remotefileZIP;
public static boolean use(TS_FileCommonConfig fileCommonConfig, TS_SQLConnAnchor anchor,
TGS_RunnableType2<String, Integer> progressUpdate_with_userDotTable_and_percentage,
TGS_RunnableType1<TS_FileTmcrFileHandler> fileHandler
) {
TS_FileTmcrFileHandler.use_do(fileCommonConfig, fileHandler);
...
}
private static void use_do(TS_FileCommonConfig fileCommonConfig, TGS_RunnableType1<TS_FileTmcrFileHandler> fileHandler) {
...
TS_FileTmcrFileTMCR.use(enableTMCR, fileCommonConfig, localfileTMCR, remotefileTMCR, tmcr -> {
TS_FileHtml.use(enableHTML, fileCommonConfig, localfileHTML, remotefileHTML, webHTMLBase64, webWidthScalePercent, webFontHightPercent, (webHTM, imageLoc) -> TS_FileTmcrFileSetName.urlFromPath(fileCommonConfig, imageLoc), webHTML -> {
TS_FileHtml.use(enableHTM, fileCommonConfig, localfileHTM, remotefileHTM, webHTMBase64, webWidthScalePercent, webFontHightPercent, (webHTM, imageLoc) -> TS_FileTmcrFileSetName.urlFromPath(fileCommonConfig, imageLoc), webHTM -> {
TS_FilePdf.use(enablePDF, fileCommonConfig, localfilePDF, remotefilePDF, pdf -> {
TS_FileXlsx.use(enableXLSX, fileCommonConfig, localfileXLSX, remotefileXLSX, xlsx -> {
TS_FileDocx.use(enableDOCX, fileCommonConfig, localfileDOCX, remotefileDOCX, docx -> {
var instance = new TS_FileTmcrFileHandler(fileCommonConfig, localfileZIP, remotefileZIP,
tmcr, webHTML, webHTM, pdf, xlsx, docx
);
fileHandler.run(instance);
});
});
});
}
);
});
}
);
}
}
public class Main {
public static void main(String... s) {
List<String> macroLines = TGS_ListUtils.of(
TS_FileTmcrCodePageWriter.INSERT_PAGE(4, true),
TS_FileTmcrCodeTableWriter.BEGIN_TABLE(1),
TS_FileTmcrCodeTableWriter.BEGIN_TABLECELL(1, 1, null),
TS_FileTmcrCodeTextWriter.BEGIN_TEXT_LEFT(),
TS_FileTmcrCodeTextWriter.ADD_TEXT("Tuğalsan Karabacak ♠☀☁☃☎☛ ŞşİiIıÜüÖöÇ窺Ğğ"),
TS_FileTmcrCodeTextWriter.END_TEXT(),
TS_FileTmcrCodeTableWriter.END_TABLECELL(),
TS_FileTmcrCodeTableWriter.END_TABLE()
);
var result = TS_FileTmcrFileHandler.use(toConfig(macroLines), createDbAnchor("test"), progressUpdate);
d.cr("main", "result", result);
}
...
}