mirror of
https://github.com/AppleCommander/AppleCommander.git
synced 2025-03-11 05:34:51 +00:00
Adding filename disk comparison strategy.
This commit is contained in:
parent
fdb4a6d566
commit
e6cbda9908
@ -82,6 +82,10 @@ public class CompareCommand extends ReadOnlyDiskImageCommandOptions {
|
||||
private void selectTrackSectorGeometry(boolean flag) {
|
||||
strategy = this::trackSectorGeometry;
|
||||
}
|
||||
@Option(names = { "--filename" }, description = "Compare by filename.")
|
||||
private void selectByFilename(boolean flag) {
|
||||
strategy = this::filename;
|
||||
}
|
||||
|
||||
private void nativeGeometry(DiskDiff.Builder builder) {
|
||||
builder.selectCompareByNativeGeometry();
|
||||
@ -92,5 +96,8 @@ public class CompareCommand extends ReadOnlyDiskImageCommandOptions {
|
||||
private void trackSectorGeometry(DiskDiff.Builder builder) {
|
||||
builder.selectCompareByTrackSectorGeometry();
|
||||
}
|
||||
private void filename(DiskDiff.Builder builder) {
|
||||
builder.selectCompareByFileName();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41,15 +41,15 @@ import com.webcodepro.applecommander.storage.os.prodos.ProdosFormatDisk;
|
||||
import com.webcodepro.applecommander.util.AppleUtil;
|
||||
import com.webcodepro.applecommander.util.StreamUtil;
|
||||
import com.webcodepro.applecommander.util.TranslatorStream;
|
||||
import com.webcodepro.applecommander.util.readerwriter.FileEntryReader;
|
||||
import com.webcodepro.applecommander.util.readerwriter.OverrideFileEntryReader;
|
||||
import com.webcodepro.shrinkit.HeaderBlock;
|
||||
import com.webcodepro.shrinkit.NuFileArchive;
|
||||
import com.webcodepro.shrinkit.ThreadRecord;
|
||||
|
||||
import io.github.applecommander.acx.base.ReadWriteDiskCommandOptions;
|
||||
import io.github.applecommander.acx.converter.IntegerTypeConverter;
|
||||
import io.github.applecommander.acx.fileutil.FileEntryReader;
|
||||
import io.github.applecommander.acx.fileutil.FileUtils;
|
||||
import io.github.applecommander.acx.fileutil.OverrideFileEntryReader;
|
||||
import io.github.applecommander.applesingle.AppleSingle;
|
||||
import io.github.applecommander.applesingle.FileDatesInfo;
|
||||
import io.github.applecommander.applesingle.ProdosFileInfo;
|
||||
|
@ -25,6 +25,8 @@ import java.util.logging.Logger;
|
||||
import com.webcodepro.applecommander.storage.DirectoryEntry;
|
||||
import com.webcodepro.applecommander.storage.DiskException;
|
||||
import com.webcodepro.applecommander.storage.FileEntry;
|
||||
import com.webcodepro.applecommander.util.readerwriter.FileEntryReader;
|
||||
import com.webcodepro.applecommander.util.readerwriter.FileEntryWriter;
|
||||
|
||||
import io.github.applecommander.acx.command.CopyFileCommand;
|
||||
|
||||
|
@ -21,17 +21,25 @@ package com.webcodepro.applecommander.storage.compare;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.webcodepro.applecommander.storage.Disk;
|
||||
import com.webcodepro.applecommander.storage.DiskException;
|
||||
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;
|
||||
import com.webcodepro.applecommander.util.Range;
|
||||
import com.webcodepro.applecommander.util.filestreamer.FileStreamer;
|
||||
import com.webcodepro.applecommander.util.filestreamer.FileTuple;
|
||||
import com.webcodepro.applecommander.util.filestreamer.TypeOfFile;
|
||||
import com.webcodepro.applecommander.util.readerwriter.FileEntryReader;
|
||||
|
||||
/**
|
||||
* Perform a disk comparison based on selected strategy.
|
||||
@ -174,6 +182,99 @@ public class DiskDiff {
|
||||
}
|
||||
}
|
||||
|
||||
public void compareByFileName(FormattedDisk formattedDiskA, FormattedDisk formattedDiskB) {
|
||||
try {
|
||||
Map<String,List<FileTuple>> filesA = FileStreamer.forDisk(formattedDiskA)
|
||||
.includeTypeOfFile(TypeOfFile.FILE)
|
||||
.recursive(true)
|
||||
.stream()
|
||||
.collect(Collectors.groupingBy(FileTuple::fullPath));
|
||||
Map<String,List<FileTuple>> filesB = FileStreamer.forDisk(formattedDiskB)
|
||||
.includeTypeOfFile(TypeOfFile.FILE)
|
||||
.recursive(true)
|
||||
.stream()
|
||||
.collect(Collectors.groupingBy(FileTuple::fullPath));
|
||||
|
||||
Set<String> pathsOnlyA = new HashSet<>(filesA.keySet());
|
||||
pathsOnlyA.removeAll(filesB.keySet());
|
||||
if (!pathsOnlyA.isEmpty()) {
|
||||
results.addError("Files only in %s: %s", formattedDiskA.getFilename(), String.join(", ", pathsOnlyA));
|
||||
}
|
||||
|
||||
Set<String> pathsOnlyB = new HashSet<>(filesB.keySet());
|
||||
pathsOnlyB.removeAll(filesA.keySet());
|
||||
if (!pathsOnlyB.isEmpty()) {
|
||||
results.addError("Files only in %s: %s", formattedDiskB.getFilename(), String.join(", ", pathsOnlyB));
|
||||
}
|
||||
|
||||
Set<String> pathsInAB = new HashSet<>(filesA.keySet());
|
||||
pathsInAB.retainAll(filesB.keySet());
|
||||
for (String path : pathsInAB) {
|
||||
List<FileTuple> tuplesA = filesA.get(path);
|
||||
List<FileTuple> tuplesB = filesB.get(path);
|
||||
|
||||
// Since this is by name, we expect a single file; report oddities
|
||||
FileTuple tupleA = tuplesA.get(0);
|
||||
if (tuplesA.size() > 1) {
|
||||
results.addWarning("Path %s on disk %s has %d entries.", path, formattedDiskA.getFilename(), tuplesA.size());
|
||||
}
|
||||
FileTuple tupleB = tuplesB.get(0);
|
||||
if (tuplesB.size() > 1) {
|
||||
results.addWarning("Path %s on disk %s has %d entries.", path, formattedDiskB.getFilename(), tuplesB.size());
|
||||
}
|
||||
|
||||
// Do our own custom compare so we can capture a description of differences:
|
||||
FileEntryReader readerA = FileEntryReader.get(tupleA.fileEntry);
|
||||
FileEntryReader readerB = FileEntryReader.get(tupleB.fileEntry);
|
||||
List<String> differences = compare(readerA, readerB);
|
||||
if (!differences.isEmpty()) {
|
||||
results.addWarning("Path %s differ: %s", path, String.join(", ", differences));
|
||||
}
|
||||
}
|
||||
} catch (DiskException ex) {
|
||||
results.addError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void compareByFileContent(FormattedDisk formattedDiskA, FormattedDisk formattedDiskB) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
private List<String> compare(FileEntryReader readerA, FileEntryReader readerB) {
|
||||
List<String> differences = new ArrayList<>();
|
||||
if (!readerA.getFilename().equals(readerB.getFilename())) {
|
||||
differences.add("filename");
|
||||
}
|
||||
if (!readerA.getProdosFiletype().equals(readerB.getProdosFiletype())) {
|
||||
differences.add("filetype");
|
||||
}
|
||||
if (!readerA.isLocked().equals(readerB.isLocked())) {
|
||||
differences.add("locked");
|
||||
}
|
||||
if (!Arrays.equals(readerA.getFileData().orElse(null), readerB.getFileData().orElse(null))) {
|
||||
differences.add("file data");
|
||||
}
|
||||
if (!Arrays.equals(readerA.getResourceData().orElse(null), readerB.getResourceData().orElse(null))) {
|
||||
differences.add("resource fork");
|
||||
}
|
||||
if (!readerA.getBinaryAddress().equals(readerB.getBinaryAddress())) {
|
||||
differences.add("address");
|
||||
}
|
||||
if (!readerA.getBinaryLength().equals(readerB.getBinaryLength())) {
|
||||
differences.add("length");
|
||||
}
|
||||
if (!readerA.getAuxiliaryType().equals(readerB.getAuxiliaryType())) {
|
||||
differences.add("aux. type");
|
||||
}
|
||||
if (!readerA.getCreationDate().equals(readerB.getCreationDate())) {
|
||||
differences.add("create date");
|
||||
}
|
||||
if (!readerA.getLastModificationDate().equals(readerB.getLastModificationDate())) {
|
||||
differences.add("mod. date");
|
||||
}
|
||||
return differences;
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private DiskDiff diff;
|
||||
|
||||
@ -195,6 +296,16 @@ public class DiskDiff {
|
||||
diff.diskComparisonStrategy = diff::compareByBlockGeometry;
|
||||
return this;
|
||||
}
|
||||
/** Compare disks by files ensuring that all filenames match. */
|
||||
public Builder selectCompareByFileName() {
|
||||
diff.diskComparisonStrategy = diff::compareByFileName;
|
||||
return this;
|
||||
}
|
||||
/** Compare disks by files based on content; allowing files to have moved or been renamed. */
|
||||
public Builder selectCompareByFileContent() {
|
||||
diff.diskComparisonStrategy = diff::compareByFileContent;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ComparisonResult compare() {
|
||||
return diff.compare();
|
||||
|
@ -72,6 +72,9 @@ public class FileStreamer {
|
||||
public static FileStreamer forDisk(Disk disk) throws DiskUnrecognizedException {
|
||||
return new FileStreamer(disk);
|
||||
}
|
||||
public static FileStreamer forFormattedDisks(FormattedDisk... disks) {
|
||||
return new FileStreamer(disks);
|
||||
}
|
||||
|
||||
private FormattedDisk[] formattedDisks = null;
|
||||
|
||||
@ -89,7 +92,10 @@ public class FileStreamer {
|
||||
private List<PathMatcher> pathMatchers = new ArrayList<>();
|
||||
|
||||
private FileStreamer(Disk disk) throws DiskUnrecognizedException {
|
||||
this.formattedDisks = disk.getFormattedDisks();
|
||||
this(disk.getFormattedDisks());
|
||||
}
|
||||
private FileStreamer(FormattedDisk... disks) {
|
||||
this.formattedDisks = disks;
|
||||
}
|
||||
|
||||
public FileStreamer ignoreErrors(boolean flag) {
|
||||
|
@ -29,6 +29,7 @@ import com.webcodepro.applecommander.storage.FileEntry;
|
||||
import com.webcodepro.applecommander.storage.FormattedDisk;
|
||||
|
||||
public class FileTuple {
|
||||
public static final String SEPARATOR = "/";
|
||||
private static final Logger LOG = Logger.getLogger(FileTuple.class.getName());
|
||||
public final FormattedDisk formattedDisk;
|
||||
public final List<String> paths;
|
||||
@ -54,6 +55,9 @@ public class FileTuple {
|
||||
public FileTuple of(FileEntry fileEntry) {
|
||||
return new FileTuple(formattedDisk, paths, directoryEntry, fileEntry);
|
||||
}
|
||||
public String fullPath() {
|
||||
return String.join(SEPARATOR, String.join(SEPARATOR, paths), fileEntry.getFilename());
|
||||
}
|
||||
|
||||
public static FileTuple of(FormattedDisk disk) {
|
||||
return new FileTuple(disk, new ArrayList<String>(), (DirectoryEntry)disk, null);
|
||||
|
@ -17,7 +17,7 @@
|
||||
* 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.fileutil;
|
||||
package com.webcodepro.applecommander.util.readerwriter;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
@ -17,8 +17,9 @@
|
||||
* 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.fileutil;
|
||||
package com.webcodepro.applecommander.util.readerwriter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.Optional;
|
||||
|
||||
@ -52,6 +53,19 @@ public interface FileEntryReader {
|
||||
// ProdosFileEntry / PascalFileEntry specific
|
||||
public default Optional<Date> getLastModificationDate() { return Optional.empty(); }
|
||||
|
||||
public default boolean equals(FileEntryReader reader) {
|
||||
return getFilename().equals(reader.getFilename())
|
||||
&& getProdosFiletype().equals(reader.getProdosFiletype())
|
||||
&& isLocked().equals(reader.isLocked())
|
||||
&& Arrays.equals(getFileData().orElse(null), reader.getFileData().orElse(null))
|
||||
&& Arrays.equals(getResourceData().orElse(null), reader.getResourceData().orElse(null))
|
||||
&& getBinaryAddress().equals(reader.getBinaryAddress())
|
||||
&& getBinaryLength().equals(reader.getBinaryLength())
|
||||
&& getAuxiliaryType().equals(reader.getAuxiliaryType())
|
||||
&& getCreationDate().equals(reader.getCreationDate())
|
||||
&& getLastModificationDate().equals(reader.getLastModificationDate());
|
||||
}
|
||||
|
||||
public static FileEntryReader get(FileEntry fileEntry) {
|
||||
if (fileEntry instanceof DosFileEntry) {
|
||||
return new DosFileEntryReaderWriter((DosFileEntry)fileEntry);
|
@ -17,7 +17,7 @@
|
||||
* 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.fileutil;
|
||||
package com.webcodepro.applecommander.util.readerwriter;
|
||||
|
||||
import java.util.Date;
|
||||
|
@ -17,7 +17,7 @@
|
||||
* 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.fileutil;
|
||||
package com.webcodepro.applecommander.util.readerwriter;
|
||||
|
||||
import java.util.Optional;
|
||||
|
@ -17,7 +17,7 @@
|
||||
* 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.fileutil;
|
||||
package com.webcodepro.applecommander.util.readerwriter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
@ -17,7 +17,7 @@
|
||||
* 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.fileutil;
|
||||
package com.webcodepro.applecommander.util.readerwriter;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
@ -17,7 +17,7 @@
|
||||
* 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.fileutil;
|
||||
package com.webcodepro.applecommander.util.readerwriter;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Optional;
|
@ -17,7 +17,7 @@
|
||||
* 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.fileutil;
|
||||
package com.webcodepro.applecommander.util.readerwriter;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
@ -116,7 +116,24 @@ public class CompareDisksResultsPane extends WizardPane {
|
||||
2, t.getLocalizedMessage()));
|
||||
}
|
||||
if (disk1 != null && disk2 != null) {
|
||||
ComparisonResult result = DiskDiff.compare(disk1, disk2);
|
||||
DiskDiff.Builder builder = DiskDiff.create(disk1, disk2);
|
||||
switch (wizard.getComparisonStrategy()) {
|
||||
case 0:
|
||||
builder.selectCompareByNativeGeometry();
|
||||
break;
|
||||
case 1:
|
||||
builder.selectCompareByTrackSectorGeometry();
|
||||
break;
|
||||
case 2:
|
||||
builder.selectCompareByBlockGeometry();
|
||||
break;
|
||||
case 3:
|
||||
builder.selectCompareByFileName();
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("missing a comparison strategy");
|
||||
}
|
||||
ComparisonResult result = builder.compare();
|
||||
errorMessages.addAll(result.getLimitedMessages(wizard.getMessageLimit()));
|
||||
}
|
||||
if (errorMessages.size() == 0) {
|
||||
|
@ -27,6 +27,7 @@ import org.eclipse.swt.events.SelectionEvent;
|
||||
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;
|
||||
@ -49,6 +50,7 @@ public class CompareDisksStartPane extends WizardPane {
|
||||
private CompareDisksWizard wizard;
|
||||
private Text diskname1Text;
|
||||
private Text diskname2Text;
|
||||
private Combo comparisonStrategyCombo;
|
||||
private Text limitText;
|
||||
/**
|
||||
* Constructor for CompareDisksStartPane.
|
||||
@ -133,6 +135,22 @@ public class CompareDisksStartPane extends WizardPane {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
label = new Label(control, SWT.WRAP);
|
||||
label.setText("Select comparison time:");
|
||||
|
||||
comparisonStrategyCombo = new Combo(control, SWT.BORDER | SWT.READ_ONLY);
|
||||
comparisonStrategyCombo.setItems("Compare by native geometry",
|
||||
"Compare by track/sector geometry",
|
||||
"Compare by block geometry",
|
||||
"Compare by filename");
|
||||
comparisonStrategyCombo.select(getWizard().getComparisonStrategy());
|
||||
comparisonStrategyCombo.addSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
getWizard().setComparisonStrategy(comparisonStrategyCombo.getSelectionIndex());
|
||||
}
|
||||
});
|
||||
|
||||
label = new Label(control, SWT.WRAP);
|
||||
label.setText("Set limit on messages displayed:");
|
||||
@ -178,9 +196,9 @@ public class CompareDisksStartPane extends WizardPane {
|
||||
|
||||
protected void limitTextModifyListener(ModifyEvent event) {
|
||||
try {
|
||||
wizard.setMessageLimit(Integer.parseInt(limitText.getText()));
|
||||
getWizard().setMessageLimit(Integer.parseInt(limitText.getText()));
|
||||
} catch (NumberFormatException e) {
|
||||
limitText.setText(Integer.toString(wizard.getMessageLimit()));
|
||||
limitText.setText(Integer.toString(getWizard().getMessageLimit()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ import com.webcodepro.applecommander.ui.swt.wizard.WizardPane;
|
||||
public class CompareDisksWizard extends Wizard {
|
||||
private String diskname1;
|
||||
private String diskname2;
|
||||
private int comparisonStrategy = 0;
|
||||
private int messageLimit = 10;
|
||||
/**
|
||||
* Constructor for ExportWizard.
|
||||
@ -55,6 +56,9 @@ public class CompareDisksWizard extends Wizard {
|
||||
public String getDiskname2() {
|
||||
return diskname2;
|
||||
}
|
||||
public int getComparisonStrategy() {
|
||||
return comparisonStrategy;
|
||||
}
|
||||
public int getMessageLimit() {
|
||||
return messageLimit;
|
||||
}
|
||||
@ -64,6 +68,9 @@ public class CompareDisksWizard extends Wizard {
|
||||
public void setDiskname2(String string) {
|
||||
diskname2 = string;
|
||||
}
|
||||
public void setComparisonStrategy(int comparisonStrategy) {
|
||||
this.comparisonStrategy = comparisonStrategy;
|
||||
}
|
||||
public void setMessageLimit(int messageLimit) {
|
||||
this.messageLimit = messageLimit;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user