Adding DiskDiff and enhancing the basic disk comparison capabilities.

This commit is contained in:
Rob Greene 2022-03-13 18:03:53 -05:00
parent be12ac4c52
commit 087df7535e
17 changed files with 461 additions and 82 deletions

View File

@ -24,6 +24,7 @@ import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import io.github.applecommander.acx.command.CompareCommand;
import io.github.applecommander.acx.command.ConvertCommand;
import io.github.applecommander.acx.command.CopyFileCommand;
import io.github.applecommander.acx.command.CreateDiskCommand;
@ -53,6 +54,7 @@ import picocli.CommandLine.Option;
optionListHeading = "%nOptions:%n",
description = "'acx' experimental utility",
subcommands = {
CompareCommand.class,
ConvertCommand.class,
CopyFileCommand.class,
CreateDiskCommand.class,

View File

@ -0,0 +1,87 @@
/*
* AppleCommander - An Apple ][ image utility.
* Copyright (C) 2019-2022 by Robert Greene and others
* robgreene at users.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package io.github.applecommander.acx.command;
import java.util.function.Consumer;
import com.webcodepro.applecommander.storage.Disk;
import com.webcodepro.applecommander.storage.compare.ComparisonResult;
import com.webcodepro.applecommander.storage.compare.DiskDiff;
import io.github.applecommander.acx.base.ReadOnlyDiskImageCommandOptions;
import io.github.applecommander.acx.converter.DiskConverter;
import picocli.CommandLine.ArgGroup;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;
@Command(name = "compare", description = "Compare two disk images.")
public class CompareCommand extends ReadOnlyDiskImageCommandOptions {
@Parameters(arity = "1", converter = DiskConverter.class, description = "Second image to compare to.")
private Disk disk2;
@ArgGroup(heading = "%nComparison Strategy Selection:%n")
private StrategySelection strategySelection = new StrategySelection();
@Override
public int handleCommand() throws Exception {
DiskDiff.Builder builder = DiskDiff.create(disk, disk2);
strategySelection.strategy.accept(builder);
ComparisonResult result = builder.compare();
if (result.getDifferenceCount() == 0) {
System.out.println("The disks match.");
}
else {
System.out.println("The disks do not match.");
result.getErrors().forEach(System.out::println);
result.getWarnings().forEach(System.out::println);
}
return 0;
}
public static class StrategySelection {
private Consumer<DiskDiff.Builder> strategy = this::nativeGeometry;
@Option(names = "--native", description = "Compare by native geometry.")
private void selectNativeGeometry(boolean flag) {
strategy = this::nativeGeometry;
}
@Option(names = "--block", description = "Compare by block geometry.")
private void selectBlockGeometry(boolean flag) {
strategy = this::blockGeometry;
}
@Option(names = { "--track-sector", "--ts" }, description = "Compare by track/sector geometry.")
private void selectTrackSectorGeometry(boolean flag) {
strategy = this::trackSectorGeometry;
}
private void nativeGeometry(DiskDiff.Builder builder) {
builder.selectCompareByNativeGeometry();
}
private void blockGeometry(DiskDiff.Builder builder) {
builder.selectCompareByBlockGeometry();
}
private void trackSectorGeometry(DiskDiff.Builder builder) {
builder.selectCompareByTrackSectorGeometry();
}
}
}

View File

@ -0,0 +1,39 @@
/*
* AppleCommander - An Apple ][ image utility.
* Copyright (C) 2021-2022 by Robert Greene and others
* robgreene at users.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.webcodepro.applecommander.storage;
/**
* Indicates the broad disk geometry - track/sector or block.
* Note that BLOCK is meant to include only ProDOS/Pascal 512-byte
* blocks and not the RDOS 256 "blocks" (RDOS should remain under
* the track/sector geometry.)
*/
public enum DiskGeometry {
TRACK_SECTOR(256, "Track/Sector"),
BLOCK(512, "Block");
public int size;
public String text;
private DiskGeometry(int size, String text) {
this.size = size;
this.text = text;
}
}

View File

@ -399,4 +399,9 @@ public abstract class FormattedDisk extends Disk implements DirectoryEntry {
* Typically, the FileEntry.setFileData method should be used.
*/
public abstract void setFileData(FileEntry fileEntry, byte[] fileData) throws DiskFullException;
/**
* Gives an indication on how this disk's geometry should be handled.
*/
public abstract DiskGeometry getDiskGeometry();
}

View File

@ -0,0 +1,52 @@
/*
* AppleCommander - An Apple ][ image utility.
* Copyright (C) 2021-2022 by Robert Greene and others
* robgreene at users.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.webcodepro.applecommander.storage.compare;
import java.util.ArrayList;
import java.util.List;
public class ComparisonResult {
private List<String> errors = new ArrayList<>();
private List<String> warnings = new ArrayList<>();
public boolean hasErrors() {
return !errors.isEmpty();
}
public int getDifferenceCount() {
return errors.size() + warnings.size();
}
public void addError(Exception ex) {
errors.add(ex.getMessage());
}
public void addError(String fmt, Object... args) {
errors.add(String.format(fmt, args));
}
public void addWarning(String fmt, Object... args) {
warnings.add(String.format(fmt, args));
}
public List<String> getErrors() {
return errors;
}
public List<String> getWarnings() {
return warnings;
}
}

View File

@ -0,0 +1,189 @@
/*
* AppleCommander - An Apple ][ image utility.
* Copyright (C) 2021-2022 by Robert Greene and others
* robgreene at users.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.webcodepro.applecommander.storage.compare;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.function.BiConsumer;
import com.webcodepro.applecommander.storage.Disk;
import com.webcodepro.applecommander.storage.DiskGeometry;
import com.webcodepro.applecommander.storage.DiskUnrecognizedException;
import com.webcodepro.applecommander.storage.FormattedDisk;
import com.webcodepro.applecommander.storage.physical.ImageOrder;
/**
* Perform a disk comparison based on selected strategy.
*/
public class DiskDiff {
public static ComparisonResult compare(Disk diskA, Disk diskB) {
return new DiskDiff(diskA, diskB).compare();
}
public static Builder create(Disk diskA, Disk diskB) {
return new Builder(diskA, diskB);
}
private Disk diskA;
private Disk diskB;
private ComparisonResult results = new ComparisonResult();
private BiConsumer<FormattedDisk,FormattedDisk> diskComparisonStrategy = this::compareByNativeGeometry;
private DiskDiff(Disk diskA, Disk diskB) {
Objects.requireNonNull(diskA);
Objects.requireNonNull(diskB);
this.diskA = diskA;
this.diskB = diskB;
}
public ComparisonResult compare() {
FormattedDisk[] formattedDisksA = null;
try {
formattedDisksA = diskA.getFormattedDisks();
} catch (DiskUnrecognizedException e) {
results.addError(e);
}
FormattedDisk[] formattedDisksB = null;
try {
formattedDisksB = diskB.getFormattedDisks();
} catch (DiskUnrecognizedException e) {
results.addError(e);
}
if (!results.hasErrors()) {
compareAll(formattedDisksA, formattedDisksB);
}
return results;
}
public void compareAll(FormattedDisk[] formattedDisksA, FormattedDisk[] formattedDisksB) {
Objects.requireNonNull(formattedDisksA);
Objects.requireNonNull(formattedDisksB);
if (formattedDisksA.length != formattedDisksB.length) {
results.addWarning("Cannot compare all disks; %s has %d while %s has %d.",
diskA.getFilename(), formattedDisksA.length,
diskB.getFilename(), formattedDisksB.length);
}
int min = Math.min(formattedDisksA.length, formattedDisksB.length);
for (int i=0; i<min; i++) {
this.diskComparisonStrategy.accept(formattedDisksA[i], formattedDisksB[i]);
}
}
/** Compare disks by whatever native geometry the disks have. Fails if geometries do not match. */
public void compareByNativeGeometry(FormattedDisk formattedDiskA, FormattedDisk formattedDiskB) {
DiskGeometry geometryA = formattedDiskA.getDiskGeometry();
DiskGeometry geometryB = formattedDiskB.getDiskGeometry();
if (geometryA != geometryB) {
results.addError("Disks are different geometry (block versus track/sector)");
return;
}
switch (geometryA) {
case BLOCK:
compareByBlockGeometry(formattedDiskA, formattedDiskB);
break;
case TRACK_SECTOR:
compareByTrackSectorGeometry(formattedDiskA, formattedDiskB);
break;
default:
results.addError("Unknown geometry: %s", geometryA);
}
}
/** Compare disks by 512-byte ProDOS/Pascal blocks. */
public void compareByBlockGeometry(FormattedDisk formattedDiskA, FormattedDisk formattedDiskB) {
ImageOrder orderA = formattedDiskA.getImageOrder();
ImageOrder orderB = formattedDiskB.getImageOrder();
if (orderA.getBlocksOnDevice() != orderB.getBlocksOnDevice()) {
results.addError("Different sized disks do not equal. (Blocks: %d <> %d)",
orderA.getBlocksOnDevice(), orderB.getBlocksOnDevice());
return;
}
for (int block=0; block<orderA.getBlocksOnDevice(); block++) {
byte[] blockA = orderA.readBlock(block);
byte[] blockB = orderB.readBlock(block);
if (!Arrays.equals(blockA, blockB)) {
results.addError("Block #%d does not match.", block);
}
}
}
/** Compare disks by 256-byte DOS sectors. */
public void compareByTrackSectorGeometry(FormattedDisk formattedDiskA, FormattedDisk formattedDiskB) {
ImageOrder orderA = formattedDiskA.getImageOrder();
ImageOrder orderB = formattedDiskB.getImageOrder();
if (orderA.getSectorsPerDisk() != orderB.getSectorsPerDisk()) {
results.addError("Different sized disks do not equal. (Sectors: %d <> %d)",
orderA.getSectorsPerDisk(), orderB.getSectorsPerDisk());
return;
}
for (int track=0; track<orderA.getTracksPerDisk(); track++) {
List<String> unequalSectors = new ArrayList<>();
for (int sector=0; sector<orderA.getSectorsPerTrack(); sector++) {
byte[] sectorA = orderA.readSector(track, sector);
byte[] sectorB = orderB.readSector(track, sector);
if (!Arrays.equals(sectorA, sectorB)) {
unequalSectors.add(Integer.toString(sector));
}
}
if (!unequalSectors.isEmpty()) {
results.addError("Track %d does not match on sectors %s", track,
String.join(",", unequalSectors));
}
}
}
public static class Builder {
private DiskDiff diff;
public Builder(Disk diskA, Disk diskB) {
diff = new DiskDiff(diskA, diskB);
}
/** Compare disks by whatever native geometry the disks have. Fails if geometries do not match. */
public Builder selectCompareByNativeGeometry() {
diff.diskComparisonStrategy = diff::compareByNativeGeometry;
return this;
}
/** Compare disks by 256-byte DOS sectors. */
public Builder selectCompareByTrackSectorGeometry() {
diff.diskComparisonStrategy = diff::compareByTrackSectorGeometry;
return this;
}
/** Compare disks by 512-byte ProDOS/Pascal blocks. */
public Builder selectCompareByBlockGeometry() {
diff.diskComparisonStrategy = diff::compareByBlockGeometry;
return this;
}
public ComparisonResult compare() {
return diff.compare();
}
}
}

View File

@ -27,6 +27,7 @@ import java.util.StringTokenizer;
import com.webcodepro.applecommander.storage.DirectoryEntry;
import com.webcodepro.applecommander.storage.DiskFullException;
import com.webcodepro.applecommander.storage.DiskGeometry;
import com.webcodepro.applecommander.storage.FileEntry;
import com.webcodepro.applecommander.storage.FormattedDisk;
import com.webcodepro.applecommander.storage.StorageBundle;
@ -557,4 +558,11 @@ public class CpmFormatDisk extends FormattedDisk {
public DirectoryEntry createDirectory(String name) throws DiskFullException {
throw new UnsupportedOperationException(textBundle.get("DirectoryCreationNotSupported")); //$NON-NLS-1$
}
/**
* Gives an indication on how this disk's geometry should be handled.
*/
public DiskGeometry getDiskGeometry() {
return DiskGeometry.TRACK_SECTOR;
}
}

View File

@ -28,6 +28,7 @@ import com.webcodepro.applecommander.storage.DirectoryEntry;
import com.webcodepro.applecommander.storage.DiskException;
import com.webcodepro.applecommander.storage.DiskCorruptException;
import com.webcodepro.applecommander.storage.DiskFullException;
import com.webcodepro.applecommander.storage.DiskGeometry;
import com.webcodepro.applecommander.storage.FileEntry;
import com.webcodepro.applecommander.storage.FormattedDisk;
import com.webcodepro.applecommander.storage.StorageBundle;
@ -773,4 +774,11 @@ public class DosFormatDisk extends FormattedDisk {
public DirectoryEntry createDirectory(String name) throws DiskFullException {
throw new UnsupportedOperationException(textBundle.get("DirectoryCreationNotSupported")); //$NON-NLS-1$
}
/**
* Gives an indication on how this disk's geometry should be handled.
*/
public DiskGeometry getDiskGeometry() {
return DiskGeometry.TRACK_SECTOR;
}
}

View File

@ -24,6 +24,7 @@ import java.util.List;
import com.webcodepro.applecommander.storage.DirectoryEntry;
import com.webcodepro.applecommander.storage.DiskFullException;
import com.webcodepro.applecommander.storage.DiskGeometry;
import com.webcodepro.applecommander.storage.FileEntry;
import com.webcodepro.applecommander.storage.FormattedDisk;
import com.webcodepro.applecommander.storage.StorageBundle;
@ -699,4 +700,11 @@ public class GutenbergFormatDisk extends FormattedDisk {
public DirectoryEntry createDirectory(String name) throws DiskFullException {
throw new UnsupportedOperationException(textBundle.get("DirectoryCreationNotSupported")); //$NON-NLS-1$
}
/**
* Gives an indication on how this disk's geometry should be handled.
*/
public DiskGeometry getDiskGeometry() {
return DiskGeometry.TRACK_SECTOR;
}
}

View File

@ -24,6 +24,7 @@ import java.util.List;
import com.webcodepro.applecommander.storage.DirectoryEntry;
import com.webcodepro.applecommander.storage.DiskFullException;
import com.webcodepro.applecommander.storage.DiskGeometry;
import com.webcodepro.applecommander.storage.FileEntry;
import com.webcodepro.applecommander.storage.FormattedDisk;
import com.webcodepro.applecommander.storage.StorageBundle;
@ -537,4 +538,11 @@ public class NakedosFormatDisk extends FormattedDisk {
public DirectoryEntry createDirectory(String name) throws DiskFullException {
throw new UnsupportedOperationException(textBundle.get("DirectoryCreationNotSupported")); //$NON-NLS-1$
}
/**
* Gives an indication on how this disk's geometry should be handled.
*/
public DiskGeometry getDiskGeometry() {
return DiskGeometry.TRACK_SECTOR;
}
}

View File

@ -28,6 +28,7 @@ import java.util.List;
import com.webcodepro.applecommander.storage.DirectoryEntry;
import com.webcodepro.applecommander.storage.DiskFullException;
import com.webcodepro.applecommander.storage.DiskGeometry;
import com.webcodepro.applecommander.storage.FileEntry;
import com.webcodepro.applecommander.storage.FormattedDisk;
import com.webcodepro.applecommander.storage.StorageBundle;
@ -667,4 +668,11 @@ public class PascalFormatDisk extends FormattedDisk {
public DirectoryEntry createDirectory(String name) throws DiskFullException {
throw new UnsupportedOperationException(textBundle.get("DirectoryCreationNotSupported")); //$NON-NLS-1$
}
/**
* Gives an indication on how this disk's geometry should be handled.
*/
public DiskGeometry getDiskGeometry() {
return DiskGeometry.BLOCK;
}
}

View File

@ -32,6 +32,7 @@ import com.webcodepro.applecommander.storage.DirectoryEntry;
import com.webcodepro.applecommander.storage.DiskException;
import com.webcodepro.applecommander.storage.DiskCorruptException;
import com.webcodepro.applecommander.storage.DiskFullException;
import com.webcodepro.applecommander.storage.DiskGeometry;
import com.webcodepro.applecommander.storage.FileEntry;
import com.webcodepro.applecommander.storage.FormattedDisk;
import com.webcodepro.applecommander.storage.StorageBundle;
@ -1450,4 +1451,11 @@ public class ProdosFormatDisk extends FormattedDisk {
throw new DiskFullException(textBundle.get("ProdosFormatDisk.UnableToAllocateFileEntry"), this.getFilename());
}
}
/**
* Gives an indication on how this disk's geometry should be handled.
*/
public DiskGeometry getDiskGeometry() {
return DiskGeometry.BLOCK;
}
}

View File

@ -25,6 +25,7 @@ import java.util.List;
import com.webcodepro.applecommander.storage.DirectoryEntry;
import com.webcodepro.applecommander.storage.DiskFullException;
import com.webcodepro.applecommander.storage.DiskGeometry;
import com.webcodepro.applecommander.storage.FileEntry;
import com.webcodepro.applecommander.storage.FormattedDisk;
import com.webcodepro.applecommander.storage.StorageBundle;
@ -518,4 +519,11 @@ public class RdosFormatDisk extends FormattedDisk {
public DirectoryEntry createDirectory(String name) throws DiskFullException {
throw new UnsupportedOperationException(textBundle.get("DirectoryCreationNotSupported")); //$NON-NLS-1$
}
/**
* Gives an indication on how this disk's geometry should be handled.
*/
public DiskGeometry getDiskGeometry() {
return DiskGeometry.TRACK_SECTOR;
}
}

View File

@ -22,12 +22,10 @@ package com.webcodepro.applecommander.util;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import com.webcodepro.applecommander.storage.FormattedDisk;
import com.webcodepro.applecommander.storage.physical.ImageOrder;
/**
@ -615,28 +613,6 @@ public class AppleUtil {
protected static boolean sameSectorsPerDisk(ImageOrder sourceOrder, ImageOrder targetOrder) {
return sourceOrder.getSectorsPerDisk() == targetOrder.getSectorsPerDisk();
}
/**
* Compare two disks by track and sector.
*/
public static boolean disksEqualByTrackAndSector(FormattedDisk sourceDisk, FormattedDisk targetDisk) {
ImageOrder sourceOrder = sourceDisk.getImageOrder();
ImageOrder targetOrder = targetDisk.getImageOrder();
if (!sameSectorsPerDisk(sourceOrder, targetOrder)) {
throw new IllegalArgumentException(textBundle.
get("AppleUtil.CannotCompareDisks")); //$NON-NLS-1$
}
for (int track = 0; track < sourceOrder.getTracksPerDisk(); track++) {
for (int sector = 0; sector < sourceOrder.getSectorsPerTrack(); sector++) {
byte[] sourceData = sourceOrder.readSector(track, sector);
byte[] targetData = targetOrder.readSector(track, sector);
if (!Arrays.equals(sourceData, targetData)) {
return false;
}
}
}
return true;
}
/**
* Change ImageOrder from source order to target order by copying block by block.
@ -658,24 +634,4 @@ public class AppleUtil {
protected static boolean sameBlocksPerDisk(ImageOrder sourceOrder, ImageOrder targetOrder) {
return sourceOrder.getBlocksOnDevice() == targetOrder.getBlocksOnDevice();
}
/**
* Compare two disks block by block.
*/
public static boolean disksEqualByBlock(FormattedDisk sourceDisk, FormattedDisk targetDisk) {
ImageOrder sourceOrder = sourceDisk.getImageOrder();
ImageOrder targetOrder = targetDisk.getImageOrder();
if (!sameBlocksPerDisk(sourceOrder, targetOrder)) {
throw new IllegalArgumentException(textBundle.
get("AppleUtil.CannotCompareDisks")); //$NON-NLS-1$
}
for (int block = 0; block < sourceOrder.getBlocksOnDevice(); block++) {
byte[] sourceData = sourceOrder.readBlock(block);
byte[] targetData = targetOrder.readBlock(block);
if (!Arrays.equals(sourceData, targetData)) {
return false;
}
}
return true;
}
}

View File

@ -289,10 +289,10 @@ CompareDisksStartPane.DiskNLabel=Please select disk image \#{0}:
# CompareDisksResultsPane
CompareDisksResultsPane.RestartText=If you wish to compare more disks, click back and start again.
CompareDisksResultsPane.UnableToLoadDiskN=Unable to load disk \#{0}: {1}\n
CompareDisksResultsPane.DifferentSizeError=The two disks are of differing formats - unable to compare.\n
CompareDisksResultsPane.DataDiffersMessage=The two disks do not contain the same data.\n
CompareDisksResultsPane.DifferentDataFormatError=The two disks are not the same data format.\n
CompareDisksResultsPane.UnableToLoadDiskN=Unable to load disk \#{0}: {1}
CompareDisksResultsPane.DifferentSizeError=The two disks are of differing formats - unable to compare.
CompareDisksResultsPane.DataDiffersMessage=The two disks do not contain the same data.
CompareDisksResultsPane.DifferentDataFormatError=The two disks are not the same data format.
CompareDisksResultsPane.DisksMatch=The disk images match.
# GraphicsFilterAdapter

View File

@ -28,6 +28,8 @@ import org.junit.Test;
import com.webcodepro.applecommander.storage.Disk;
import com.webcodepro.applecommander.storage.DiskFullException;
import com.webcodepro.applecommander.storage.FileEntry;
import com.webcodepro.applecommander.storage.compare.ComparisonResult;
import com.webcodepro.applecommander.storage.compare.DiskDiff;
import com.webcodepro.applecommander.storage.os.dos33.DosFormatDisk;
import com.webcodepro.applecommander.storage.os.prodos.ProdosFormatDisk;
import com.webcodepro.applecommander.storage.physical.ByteArrayImageLayout;
@ -92,7 +94,9 @@ public class AppleUtilTest {
AppleUtil.changeImageOrderByTrackAndSector(dosDiskDosOrder.getImageOrder(),
dosDiskNibbleOrder.getImageOrder());
// Confirm that these disks are identical:
assertTrue(AppleUtil.disksEqualByTrackAndSector(dosDiskDosOrder, dosDiskNibbleOrder));
ComparisonResult result = DiskDiff.create(dosDiskDosOrder, dosDiskDosOrder)
.selectCompareByTrackSectorGeometry().compare();
assertEquals("Expected disks to have no differences", 0, result.getDifferenceCount());
}
@Test
@ -112,7 +116,9 @@ public class AppleUtilTest {
AppleUtil.changeImageOrderByBlock(prodosDiskDosOrder.getImageOrder(),
prodosDiskNibbleOrder.getImageOrder());
// Confirm that these disks are identical:
assertTrue(AppleUtil.disksEqualByBlock(prodosDiskDosOrder, prodosDiskNibbleOrder));
ComparisonResult result = DiskDiff.create(prodosDiskDosOrder, prodosDiskNibbleOrder)
.selectCompareByBlockGeometry().compare();
assertEquals("Expected disks to have no differences", 0, result.getDifferenceCount());
}
@Test

View File

@ -19,16 +19,19 @@
*/
package com.webcodepro.applecommander.ui.swt.wizard.comparedisks;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import com.webcodepro.applecommander.storage.Disk;
import com.webcodepro.applecommander.storage.FormattedDisk;
import com.webcodepro.applecommander.storage.compare.ComparisonResult;
import com.webcodepro.applecommander.storage.compare.DiskDiff;
import com.webcodepro.applecommander.ui.UiBundle;
import com.webcodepro.applecommander.ui.swt.wizard.WizardPane;
import com.webcodepro.applecommander.util.AppleUtil;
import com.webcodepro.applecommander.util.TextBundle;
/**
@ -75,6 +78,8 @@ public class CompareDisksResultsPane extends WizardPane {
label = new Label(control, SWT.WRAP);
label.setText(textBundle.get("CompareDisksResultsPane.RestartText")); //$NON-NLS-1$
parent.pack();
}
/**
* Get the next pane. A null return indicates the end of the wizard.
@ -93,49 +98,31 @@ public class CompareDisksResultsPane extends WizardPane {
}
protected String compareDisks() {
StringBuffer errorMessages = new StringBuffer();
FormattedDisk[] disk1 = null;
List<String> errorMessages = new ArrayList<>();
Disk disk1 = null;
try {
disk1 = new Disk(wizard.getDiskname1()).getFormattedDisks();
disk1 = new Disk(wizard.getDiskname1());
} catch (Throwable t) {
errorMessages.append(textBundle.
errorMessages.add(textBundle.
format("CompareDisksResultsPane.UnableToLoadDiskN", //$NON-NLS-1$
1, t.getLocalizedMessage()));
}
FormattedDisk[] disk2 = null;
Disk disk2 = null;
try {
disk2 = new Disk(wizard.getDiskname2()).getFormattedDisks();
disk2 = new Disk(wizard.getDiskname2());
} catch (Throwable t) {
errorMessages.append(textBundle.
errorMessages.add(textBundle.
format("CompareDisksResultsPane.UnableToLoadDiskN", //$NON-NLS-1$
2, t.getLocalizedMessage()));
}
if (disk1 != null && disk2 != null) {
if (disk1.length != disk2.length) {
errorMessages.append(textBundle.get(
"CompareDisksResultsPane.DifferentSizeError")); //$NON-NLS-1$
} else {
boolean disk1TSformat = disk1[0].isCpmFormat() || disk1[0].isDosFormat() || disk1[0].isRdosFormat();
boolean disk2TSformat = disk2[0].isCpmFormat() || disk2[0].isDosFormat() || disk2[0].isRdosFormat();
if (disk1TSformat && disk2TSformat) {
if (!AppleUtil.disksEqualByTrackAndSector(disk1[0], disk2[0])) {
errorMessages.append(textBundle.get(
"CompareDisksResultsPane.DataDiffersMessage")); //$NON-NLS-1$
}
} else if (!disk1TSformat && !disk2TSformat) {
if (!AppleUtil.disksEqualByBlock(disk1[0], disk2[0])) {
errorMessages.append(textBundle.get(
"CompareDisksResultsPane.DataDiffersMessage")); //$NON-NLS-1$
}
} else {
errorMessages.append(textBundle.get(
"CompareDisksResultsPane.DifferentDataFormatError")); //$NON-NLS-1$
}
}
ComparisonResult result = DiskDiff.compare(disk1, disk2);
errorMessages.addAll(result.getErrors());
errorMessages.addAll(result.getWarnings());
}
if (errorMessages.length() == 0) {
if (errorMessages.size() == 0) {
return textBundle.get("CompareDisksResultsPane.DisksMatch"); //$NON-NLS-1$
}
return errorMessages.toString();
return String.join("\n", errorMessages);
}
}