From 709ce6b47f17ab951b327b1bc07f66bf5b8cf8ab Mon Sep 17 00:00:00 2001 From: Rob Greene Date: Sat, 21 Oct 2023 16:59:15 -0500 Subject: [PATCH] Now validating Applesoft import in user interface. #119 --- .../ui/ImportSpecification.java | 28 +++++ lib/ac-swt-common/build.gradle | 1 + .../ui/swt/DiskExplorerTab.java | 112 ++++++------------ .../ImportSelectFilesWizardPane.java | 54 ++++----- .../swt/wizard/importfile/ImportWizard.java | 56 ++++++++- 5 files changed, 137 insertions(+), 114 deletions(-) diff --git a/lib/ac-api/src/main/java/com/webcodepro/applecommander/ui/ImportSpecification.java b/lib/ac-api/src/main/java/com/webcodepro/applecommander/ui/ImportSpecification.java index 50806ca..ec7ec52 100644 --- a/lib/ac-api/src/main/java/com/webcodepro/applecommander/ui/ImportSpecification.java +++ b/lib/ac-api/src/main/java/com/webcodepro/applecommander/ui/ImportSpecification.java @@ -33,6 +33,16 @@ public class ImportSpecification { private String filetype; private int address; private boolean rawFileImport; + private byte[] fileData; + + public ImportSpecification(ImportSpecification orig) { + this.sourceFilename = orig.sourceFilename; + this.targetFilename = orig.targetFilename; + this.filetype = orig.filetype; + this.address = orig.address; + this.rawFileImport = orig.rawFileImport; + this.fileData = orig.fileData; + } /** * Create the ImportSpecification with default values. */ @@ -118,4 +128,22 @@ public class ImportSpecification { public void setRawFileImport(boolean rawFileImport) { this.rawFileImport = rawFileImport; } + /** + * Set the binary file data (only done with validated file types). + */ + public void setFileData(byte[] fileData) { + this.fileData = fileData; + } + /** + * Get the binary file data (only done with validated file types). + */ + public byte[] getFileData() { + return fileData; + } + /** + * Answers true if this specification has file data attached. + */ + public boolean hasFileData() { + return fileData != null; + } } diff --git a/lib/ac-swt-common/build.gradle b/lib/ac-swt-common/build.gradle index 9841d14..ede8802 100644 --- a/lib/ac-swt-common/build.gradle +++ b/lib/ac-swt-common/build.gradle @@ -17,6 +17,7 @@ dependencies { implementation project(':lib:ac-api') implementation "net.sf.applecommander:applesingle-api:$asVersion" + implementation "net.sf.applecommander:bastools-api:$btVersion" testImplementation "junit:junit:$junitVersion" testImplementation "org.apache.commons:commons-lang3:$commonsLang3Version" diff --git a/lib/ac-swt-common/src/main/java/com/webcodepro/applecommander/ui/swt/DiskExplorerTab.java b/lib/ac-swt-common/src/main/java/com/webcodepro/applecommander/ui/swt/DiskExplorerTab.java index 4daa6f7..ce9d535 100644 --- a/lib/ac-swt-common/src/main/java/com/webcodepro/applecommander/ui/swt/DiskExplorerTab.java +++ b/lib/ac-swt-common/src/main/java/com/webcodepro/applecommander/ui/swt/DiskExplorerTab.java @@ -19,85 +19,14 @@ */ package com.webcodepro.applecommander.ui.swt; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.custom.CTabFolder; -import org.eclipse.swt.custom.CTabItem; -import org.eclipse.swt.custom.SashForm; -import org.eclipse.swt.events.MenuAdapter; -import org.eclipse.swt.events.MenuEvent; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.graphics.Font; -import org.eclipse.swt.graphics.GC; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.printing.PrintDialog; -import org.eclipse.swt.printing.Printer; -import org.eclipse.swt.printing.PrinterData; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Event; -import org.eclipse.swt.widgets.FileDialog; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Listener; -import org.eclipse.swt.widgets.Menu; -import org.eclipse.swt.widgets.MenuItem; -import org.eclipse.swt.widgets.MessageBox; -import org.eclipse.swt.widgets.ProgressBar; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.Table; -import org.eclipse.swt.widgets.TableColumn; -import org.eclipse.swt.widgets.TableItem; -import org.eclipse.swt.widgets.ToolBar; -import org.eclipse.swt.widgets.ToolItem; -import org.eclipse.swt.widgets.Tree; -import org.eclipse.swt.widgets.TreeItem; - import com.webcodepro.applecommander.compiler.ApplesoftCompiler; -import com.webcodepro.applecommander.storage.DirectoryEntry; -import com.webcodepro.applecommander.storage.Disk; -import com.webcodepro.applecommander.storage.DiskException; -import com.webcodepro.applecommander.storage.FileEntry; -import com.webcodepro.applecommander.storage.FileEntryComparator; import com.webcodepro.applecommander.storage.FileFilter; -import com.webcodepro.applecommander.storage.FormattedDisk; +import com.webcodepro.applecommander.storage.*; import com.webcodepro.applecommander.storage.FormattedDisk.FileColumnHeader; -import com.webcodepro.applecommander.storage.filters.AppleWorksDataBaseFileFilter; -import com.webcodepro.applecommander.storage.filters.AppleWorksSpreadSheetFileFilter; -import com.webcodepro.applecommander.storage.filters.AppleWorksWordProcessorFileFilter; -import com.webcodepro.applecommander.storage.filters.ApplesoftFileFilter; -import com.webcodepro.applecommander.storage.filters.AssemblySourceFileFilter; -import com.webcodepro.applecommander.storage.filters.BinaryFileFilter; -import com.webcodepro.applecommander.storage.filters.BusinessBASICFileFilter; -import com.webcodepro.applecommander.storage.filters.GraphicsFileFilter; -import com.webcodepro.applecommander.storage.filters.GutenbergFileFilter; -import com.webcodepro.applecommander.storage.filters.IntegerBasicFileFilter; -import com.webcodepro.applecommander.storage.filters.PascalTextFileFilter; -import com.webcodepro.applecommander.storage.filters.TextFileFilter; +import com.webcodepro.applecommander.storage.filters.*; import com.webcodepro.applecommander.storage.os.prodos.ProdosDiskSizeDoesNotMatchException; import com.webcodepro.applecommander.storage.os.prodos.ProdosFormatDisk; -import com.webcodepro.applecommander.storage.physical.ByteArrayImageLayout; -import com.webcodepro.applecommander.storage.physical.DosOrder; -import com.webcodepro.applecommander.storage.physical.ImageOrder; -import com.webcodepro.applecommander.storage.physical.NibbleOrder; -import com.webcodepro.applecommander.storage.physical.ProdosOrder; +import com.webcodepro.applecommander.storage.physical.*; import com.webcodepro.applecommander.ui.ImportSpecification; import com.webcodepro.applecommander.ui.UiBundle; import com.webcodepro.applecommander.ui.UserPreferences; @@ -111,8 +40,28 @@ import com.webcodepro.applecommander.util.AppleUtil; import com.webcodepro.applecommander.util.Host; import com.webcodepro.applecommander.util.StreamUtil; import com.webcodepro.applecommander.util.TextBundle; - import io.github.applecommander.applesingle.AppleSingle; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.CTabFolder; +import org.eclipse.swt.custom.CTabItem; +import org.eclipse.swt.custom.SashForm; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.printing.PrintDialog; +import org.eclipse.swt.printing.Printer; +import org.eclipse.swt.printing.PrinterData; +import org.eclipse.swt.widgets.*; + +import java.io.*; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.List; +import java.util.*; /** * Build the Disk File tab for the Disk Window. @@ -1193,10 +1142,15 @@ public class DiskExplorerTab { i+1, specs.size())); nameLabel.setText(spec.getSourceFilename()); progressBar.setSelection(i); - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - InputStream input = new FileInputStream(spec.getSourceFilename()); - StreamUtil.copy(input, buffer); - byte[] fileData = buffer.toByteArray(); + byte[] fileData = null; + if (spec.hasFileData()) { + fileData = spec.getFileData(); + } else { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + InputStream input = new FileInputStream(spec.getSourceFilename()); + StreamUtil.copy(input, buffer); + fileData = buffer.toByteArray(); + } FileEntry fileEntry = directory.createFile(); fileEntry.setFilename(spec.getTargetFilename()); fileEntry.setFiletype(spec.getFiletype()); diff --git a/lib/ac-swt-common/src/main/java/com/webcodepro/applecommander/ui/swt/wizard/importfile/ImportSelectFilesWizardPane.java b/lib/ac-swt-common/src/main/java/com/webcodepro/applecommander/ui/swt/wizard/importfile/ImportSelectFilesWizardPane.java index 11714d4..3121d0b 100644 --- a/lib/ac-swt-common/src/main/java/com/webcodepro/applecommander/ui/swt/wizard/importfile/ImportSelectFilesWizardPane.java +++ b/lib/ac-swt-common/src/main/java/com/webcodepro/applecommander/ui/swt/wizard/importfile/ImportSelectFilesWizardPane.java @@ -19,34 +19,6 @@ */ package com.webcodepro.applecommander.ui.swt.wizard.importfile; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.util.Iterator; -import java.util.Optional; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.layout.FillLayout; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.layout.RowData; -import org.eclipse.swt.layout.RowLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Combo; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.FileDialog; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.MessageBox; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.Table; -import org.eclipse.swt.widgets.TableColumn; -import org.eclipse.swt.widgets.TableItem; -import org.eclipse.swt.widgets.Text; - import com.webcodepro.applecommander.storage.os.prodos.ProdosFormatDisk; import com.webcodepro.applecommander.ui.ImportSpecification; import com.webcodepro.applecommander.ui.UiBundle; @@ -55,11 +27,19 @@ import com.webcodepro.applecommander.ui.swt.util.SwtUtil; import com.webcodepro.applecommander.ui.swt.wizard.WizardPane; import com.webcodepro.applecommander.util.AppleUtil; import com.webcodepro.applecommander.util.TextBundle; - import io.github.applecommander.applesingle.AppleSingle; import io.github.applecommander.applesingle.AppleSingleReader; import io.github.applecommander.applesingle.ProdosFileInfo; import io.github.applecommander.applesingle.Utilities; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.*; +import org.eclipse.swt.widgets.*; + +import java.io.*; +import java.util.Iterator; +import java.util.Optional; /** * Allow the used to choose the files to import into the disk image. @@ -362,6 +342,22 @@ public class ImportSelectFilesWizardPane extends WizardPane { dialog.setDefaultButton(button); button.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { + try { + // Make an attempt at validating before accepting the change + ImportSpecification temp = new ImportSpecification(spec); + temp.setTargetFilename(getWizard().getDisk(). + getSuggestedFilename(filenameText.getText())); + temp.setFiletype(filetypes.getItem( + filetypes.getSelectionIndex())); + temp.setAddress(AppleUtil.convertFormattedWord( + getAddressText().getText())); + temp.setRawFileImport(getRawCheckBox().getSelection()); + wizard.validate(temp); + } catch (Throwable t) { + SwtUtil.showErrorDialog(wizard.getDialog(), "Error", t.getMessage()); + return; + } + spec.setTargetFilename(getWizard().getDisk(). getSuggestedFilename(filenameText.getText())); spec.setFiletype(filetypes.getItem( diff --git a/lib/ac-swt-common/src/main/java/com/webcodepro/applecommander/ui/swt/wizard/importfile/ImportWizard.java b/lib/ac-swt-common/src/main/java/com/webcodepro/applecommander/ui/swt/wizard/importfile/ImportWizard.java index 3f640ba..6d81ec9 100644 --- a/lib/ac-swt-common/src/main/java/com/webcodepro/applecommander/ui/swt/wizard/importfile/ImportWizard.java +++ b/lib/ac-swt-common/src/main/java/com/webcodepro/applecommander/ui/swt/wizard/importfile/ImportWizard.java @@ -19,17 +19,29 @@ */ package com.webcodepro.applecommander.ui.swt.wizard.importfile; -import java.util.ArrayList; -import java.util.List; - -import org.eclipse.swt.widgets.Shell; - import com.webcodepro.applecommander.storage.FormattedDisk; import com.webcodepro.applecommander.ui.ImportSpecification; import com.webcodepro.applecommander.ui.UiBundle; import com.webcodepro.applecommander.ui.swt.util.ImageManager; import com.webcodepro.applecommander.ui.swt.wizard.Wizard; import com.webcodepro.applecommander.ui.swt.wizard.WizardPane; +import com.webcodepro.applecommander.util.ApplesoftTokenizer; +import io.github.applecommander.bastools.api.Configuration; +import io.github.applecommander.bastools.api.Parser; +import io.github.applecommander.bastools.api.TokenReader; +import io.github.applecommander.bastools.api.Visitors; +import io.github.applecommander.bastools.api.model.Program; +import io.github.applecommander.bastools.api.model.Token; +import org.eclipse.swt.widgets.Shell; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Queue; +import java.util.Set; /** * The Disk Import Wizard. @@ -38,6 +50,7 @@ import com.webcodepro.applecommander.ui.swt.wizard.WizardPane; * @author Rob Greene */ public class ImportWizard extends Wizard { + private static Set APPLESOFT_FILETYPES = Set.of("B", "BAS"); private FormattedDisk disk; private List importSpecifications; /** @@ -58,9 +71,40 @@ public class ImportWizard extends Wizard { /** * Add an import specification. */ - public void addImportSpecification(ImportSpecification importSpecification) { + public void addImportSpecification(ImportSpecification importSpecification) throws IOException { + validate(importSpecification); getImportSpecifications().add(importSpecification); } + + /** + * Perform validation for some problematic file type imports. + */ + public void validate(ImportSpecification importSpecification) throws IOException { + if (APPLESOFT_FILETYPES.contains(importSpecification.getFiletype())) { + try { + // 1. Validate that this is a binary Applesoft file. + byte[] data = Files.readAllBytes(Path.of(importSpecification.getSourceFilename())); + ApplesoftTokenizer tokenizer = new ApplesoftTokenizer(data); + while (tokenizer.hasMoreTokens()) { + tokenizer.getNextToken(); // Make sure we can loop through entire program + } + importSpecification.setFileData(data); + } catch (Throwable ignored) { + try { + // 2. Make an attempt at tokenizing the file (assuming it's text). + File file = new File(importSpecification.getSourceFilename()); + Configuration config = Configuration.builder().sourceFile(file).build(); + Queue tokens = TokenReader.tokenize(config.sourceFile); + Parser parser = new Parser(tokens); + Program program = parser.parse(); + byte[] data = Visitors.byteVisitor(config).dump(program); + importSpecification.setFileData(data); + } catch (Throwable ignored2) { + throw new IOException("File does not appear to be an Applesoft program"); + } + } + } + } /** * Remove an import specification. */