diff --git a/key.core/src/main/java/de/uka/ilkd/key/control/AbstractUserInterfaceControl.java b/key.core/src/main/java/de/uka/ilkd/key/control/AbstractUserInterfaceControl.java index c8a26ca1100..19cb701e060 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/control/AbstractUserInterfaceControl.java +++ b/key.core/src/main/java/de/uka/ilkd/key/control/AbstractUserInterfaceControl.java @@ -22,6 +22,7 @@ import de.uka.ilkd.key.proof.io.ProblemLoaderException; import de.uka.ilkd.key.proof.io.SingleThreadProblemLoader; import de.uka.ilkd.key.proof.mgt.ProofEnvironment; +import de.uka.ilkd.key.settings.Configuration; import org.key_project.prover.engine.ProverCore; import org.key_project.prover.engine.ProverTaskListener; @@ -257,7 +258,8 @@ public void loadingStarted(AbstractProblemLoader loader) { @Override public void loadingFinished(AbstractProblemLoader loader, LoadedPOContainer poContainer, - ProofAggregate proofList, ReplayResult result) throws ProblemLoaderException { + ProofAggregate proofList, ReplayResult result, Configuration settings) + throws ProblemLoaderException { if (proofList != null) { // avoid double registration at spec repos as that is done already earlier in // createProof diff --git a/key.core/src/main/java/de/uka/ilkd/key/nparser/KeyAst.java b/key.core/src/main/java/de/uka/ilkd/key/nparser/KeyAst.java index e3e50418712..c21de3859ab 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/nparser/KeyAst.java +++ b/key.core/src/main/java/de/uka/ilkd/key/nparser/KeyAst.java @@ -185,6 +185,17 @@ public String getProblemHeader() { } return ""; } + + /// Returns the raw settings within a [de.uka.ilkd.key.proof.io.KeYFile]. + public Configuration findSettings() { + final var cfg = new ConfigurationBuilder(); + if (ctx.preferences() == null || ctx.preferences().cvalue() == null) { + return new Configuration(); + } + + var c = ctx.preferences().cvalue(); + return (Configuration) c.accept(cfg); + } } public static class ConfigurationFile extends KeyAst { diff --git a/key.core/src/main/java/de/uka/ilkd/key/proof/Node.java b/key.core/src/main/java/de/uka/ilkd/key/proof/Node.java index 93c58da43ce..6c7a5079394 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/proof/Node.java +++ b/key.core/src/main/java/de/uka/ilkd/key/proof/Node.java @@ -3,15 +3,7 @@ * SPDX-License-Identifier: GPL-2.0-only */ package de.uka.ilkd.key.proof; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; +import java.util.*; import de.uka.ilkd.key.logic.RenamingTable; import de.uka.ilkd.key.logic.op.IProgramVariable; @@ -50,10 +42,14 @@ public class Node implements Iterable { private static final String NODES = "nodes"; - /** the proof the node belongs to */ + /** + * the proof the node belongs to + */ private final Proof proof; - /** The parent node. **/ + /** + * The parent node. + **/ private @Nullable Node parent = null; /** * The branch location of this proof node. @@ -82,7 +78,9 @@ public class Node implements Iterable { private boolean closed = false; - /** contains non-logical content, used for user feedback */ + /** + * contains non-logical content, used for user feedback + */ private NodeInfo nodeInfo; /** @@ -161,7 +159,9 @@ public void setSequent(Sequent seq) { this.seq = seq; } - /** returns the sequent of this node */ + /** + * returns the sequent of this node + */ public Sequent sequent() { return seq; } @@ -175,7 +175,9 @@ public NodeInfo getNodeInfo() { return nodeInfo; } - /** returns the proof the Node belongs to */ + /** + * returns the proof the Node belongs to + */ public Proof proof() { return proof; } @@ -225,7 +227,9 @@ public RuleApp getAppliedRuleApp() { return appliedRuleApp; } - /** Returns the set of NoPosTacletApps at this node */ + /** + * Returns the set of NoPosTacletApps at this node + */ public Iterable getLocalIntroducedRules() { return localIntroducedRules; } @@ -249,7 +253,7 @@ public void addLocalProgVars(Iterable elements) { /** * Returns the set of freshly created function symbols known to this node. - * + *

* In the resulting list, the newest additions come first. * * @return a non-null immutable list of function symbols. @@ -471,13 +475,14 @@ public Iterator subtreeIterator() { return new SubtreeIterator(this); } - /** @return number of children */ + /** + * @return number of children + */ public int childrenCount() { return children.size(); } /** - * * @param i an index (starting at 0). * @return the i-th child of this node. */ @@ -667,7 +672,9 @@ public boolean sanityCheckDoubleLinks() { return true; } - /** marks a node as closed */ + /** + * marks a node as closed + */ Node close() { closed = true; Node tmp = parent; @@ -684,7 +691,7 @@ Node close() { /** * Opens a previously closed node and all its closed parents. *

- * + *

* This is, for instance, needed for the {@link MergeRule}: In a situation where a merge node * and its associated partners have been closed and the merge node is then pruned away, the * partners have to be reopened again. Otherwise, we have a soundness issue. @@ -699,7 +706,9 @@ void reopen() { clearNameCache(); } - /** @return true iff this inner node is closeable */ + /** + * @return true iff this inner node is closeable + */ private boolean isCloseable() { assert childrenCount() > 0; for (Node child : children) { @@ -838,4 +847,37 @@ public int getStepIndex() { void setStepIndex(int stepIndex) { this.stepIndex = stepIndex; } + + /** + * Calculates an array is the path from root node to this node. Each entry defines the child to + * be selected. + * + * @see #traversePath(Iterator) + */ + public int[] getPosInProof() { + // collect the path from the current to node to the root of the proof. + // each entry is the children position + var path = new LinkedList(); + var current = this; + while (current.parent != null) { + path.add(0, current.parent.getChildNr(current)); + current = current.parent; + } + return path.stream().mapToInt(it -> it).toArray(); + } + + + /** + * Traverses a given iterator (child selection). + * + * @param traverse non-null + */ + public Node traversePath(Iterator traverse) { + var current = this; + while (traverse.hasNext()) { + int child = traverse.next(); + current = current.child(child); + } + return current; + } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/proof/init/KeYUserProblemFile.java b/key.core/src/main/java/de/uka/ilkd/key/proof/init/KeYUserProblemFile.java index 7e9e1fe64b9..064363db938 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/proof/init/KeYUserProblemFile.java +++ b/key.core/src/main/java/de/uka/ilkd/key/proof/init/KeYUserProblemFile.java @@ -34,8 +34,12 @@ * obligation. */ public final class KeYUserProblemFile extends KeYFile implements ProofOblInput { + @Nullable private Sequent problem = null; + @Nullable + private Configuration settings; + // ------------------------------------------------------------------------- // constructors // ------------------------------------------------------------------------- @@ -119,6 +123,13 @@ public ImmutableSet read() throws ProofInputException { return warnings; } + public Configuration readSettings() { + if (settings == null) { + settings = getParseContext().findSettings(); + } + return settings; + } + @Override public void readProblem() throws ProofInputException { if (initConfig == null) { @@ -270,4 +281,5 @@ private Profile getDefaultProfile() { public KeYJavaType getContainerType() { return null; } + } diff --git a/key.core/src/main/java/de/uka/ilkd/key/proof/io/AbstractProblemLoader.java b/key.core/src/main/java/de/uka/ilkd/key/proof/io/AbstractProblemLoader.java index 03c43de3c17..b343ad4b8b2 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/proof/io/AbstractProblemLoader.java +++ b/key.core/src/main/java/de/uka/ilkd/key/proof/io/AbstractProblemLoader.java @@ -279,7 +279,9 @@ public final void load(Consumer callbackProofLoaded) loadSelectedProof(poContainer, proofList, callbackProofLoaded); } } finally { - control.loadingFinished(this, poContainer, proofList, result); + var settings = (envInput instanceof KeYUserProblemFile kupf) ? kupf.readSettings() + : new Configuration(); + control.loadingFinished(this, poContainer, proofList, result, settings); } } @@ -646,9 +648,6 @@ private ReplayResult replayProof(Proof proof) { problemInitializer.tryReadProof(parser, (KeYUserProblemFile) envInput); parserResult = parser.getResult(); - // Parser is no longer needed, set it to null to free memory. - parser = null; - // For loading, we generally turn on one step simplification to be // able to load proofs that used it even if the user has currently // turned OSS off. diff --git a/key.core/src/main/java/de/uka/ilkd/key/proof/io/OutputStreamProofSaver.java b/key.core/src/main/java/de/uka/ilkd/key/proof/io/OutputStreamProofSaver.java index 0db5b7fd9f5..e314a52c437 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/proof/io/OutputStreamProofSaver.java +++ b/key.core/src/main/java/de/uka/ilkd/key/proof/io/OutputStreamProofSaver.java @@ -58,7 +58,10 @@ import org.key_project.prover.sequent.Sequent; import org.key_project.prover.sequent.SequentFormula; import org.key_project.util.collection.ImmutableList; +import org.key_project.util.collection.KeYCollections; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -67,21 +70,30 @@ * * @author Kai Wallisch */ +@NullMarked public class OutputStreamProofSaver { private static final Logger LOGGER = LoggerFactory.getLogger(OutputStreamProofSaver.class); + public static final String KEY_LAST_SELECTED_NODE = "lastSelectedNode"; /** * The proof to save. */ - protected final Proof proof; + private Proof proof; + /** * Currently running KeY version (usually a git commit hash). */ - protected final String internalVersion; + private String internalVersion; + /** * Whether the proof steps should be output (usually true). */ - protected final boolean saveProofSteps; + private boolean saveProofSteps; + + /** + * Last selected node in this proof + */ + private int @Nullable [] pathToLastSelectedNode = null; /** @@ -90,6 +102,7 @@ public class OutputStreamProofSaver { * @param proof the Proof * @return the location of the java source code or null if no such exists */ + @Nullable public static File getJavaSourceLocation(Proof proof) { final String header = proof.header(); final int i = header.indexOf("\\javaSource"); @@ -97,7 +110,7 @@ public static File getJavaSourceLocation(Proof proof) { final int begin = header.indexOf('\"', i); final int end = header.indexOf('\"', begin + 1); final String sourceLocation = header.substring(begin + 1, end); - if (sourceLocation.length() > 0) { + if (!sourceLocation.isEmpty()) { return new File(sourceLocation); } } @@ -157,7 +170,13 @@ public String writeProfile(Profile profile) { } public String writeSettings(ProofSettings ps) { - return String.format("\\settings %s \n", ps.settingsToString()); + // inject the last selected node + var additionalInformation = new TreeMap(); + if (pathToLastSelectedNode != null) { + var lastSelectedNode = KeYCollections.runLengthEncoding(pathToLastSelectedNode); + additionalInformation.put(KEY_LAST_SELECTED_NODE, lastSelectedNode); + } + return String.format("\\settings %s \n", ps.settingsToString(additionalInformation)); } public void save(OutputStream out) throws IOException { @@ -239,6 +258,7 @@ public void save(OutputStream out) throws IOException { } } + @Nullable protected Path getBasePath() throws IOException { File javaSourceLocation = getJavaSourceLocation(proof); if (javaSourceLocation != null) { @@ -262,7 +282,7 @@ protected Path getBasePath() throws IOException { private String makePathsRelative(String header) { final String[] search = { "\\javaSource", "\\bootclasspath", "\\classpath", "\\include" }; - final String basePath; + String basePath; String tmp = header; try { basePath = getBasePath().toString(); @@ -708,8 +728,7 @@ public void node2Proof(Node node, Appendable ps) throws IOException { ps.append(")\n"); } - public static String posInOccurrence2Proof(Sequent seq, - PosInOccurrence pos) { + public static String posInOccurrence2Proof(Sequent seq, @Nullable PosInOccurrence pos) { if (pos == null) { return ""; } @@ -733,9 +752,9 @@ public static String posInTerm2Proof(PosInTerm pos) { /** * Get the "interesting" instantiations of the provided object. * - * @see SVInstantiations#interesting() * @param inst instantiations * @return the "interesting" instantiations (serialized) + * @see SVInstantiations#interesting() */ public Collection getInterestingInstantiations(SVInstantiations inst) { Collection s = new ArrayList<>(); @@ -844,7 +863,8 @@ public static String printAnything(Object val, Services services) { return printAnything(val, services, true); } - public static String printAnything(Object val, Services services, + @Nullable + public static String printAnything(@Nullable Object val, Services services, boolean shortAttrNotation) { if (val instanceof ProgramElement) { return printProgramElement((ProgramElement) val); @@ -865,17 +885,47 @@ public static String printAnything(Object val, Services services, } } - private static String printSequent(Sequent val, Services services) { + private static String printSequent(Sequent val, @Nullable Services services) { final LogicPrinter printer = createLogicPrinter(services, services == null); printer.printSequent(val); return printer.result(); } - private static LogicPrinter createLogicPrinter(Services serv, boolean shortAttrNotation) { - + private static LogicPrinter createLogicPrinter(@Nullable Services serv, + boolean shortAttrNotation) { final NotationInfo ni = new NotationInfo(); - return LogicPrinter.purePrinter(ni, (shortAttrNotation ? serv : null)); } + public String getInternalVersion() { + return internalVersion; + } + + public void setInternalVersion(String internalVersion) { + this.internalVersion = internalVersion; + } + + public boolean isSaveProofSteps() { + return saveProofSteps; + } + + public void setSaveProofSteps(boolean saveProofSteps) { + this.saveProofSteps = saveProofSteps; + } + + public Proof getProof() { + return proof; + } + + public void setProof(Proof proof) { + this.proof = proof; + } + + public int @Nullable [] getPathToLastSelectedNode() { + return pathToLastSelectedNode; + } + + public void setPathToLastSelectedNode(int[] pathToLastSelectedNode) { + this.pathToLastSelectedNode = pathToLastSelectedNode; + } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/proof/io/ProblemLoaderControl.java b/key.core/src/main/java/de/uka/ilkd/key/proof/io/ProblemLoaderControl.java index 01bc5fcb2dc..7c1d313d994 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/proof/io/ProblemLoaderControl.java +++ b/key.core/src/main/java/de/uka/ilkd/key/proof/io/ProblemLoaderControl.java @@ -8,6 +8,7 @@ import de.uka.ilkd.key.proof.init.InitConfig; import de.uka.ilkd.key.proof.init.ProblemInitializer.ProblemInitializerListener; import de.uka.ilkd.key.proof.io.AbstractProblemLoader.ReplayResult; +import de.uka.ilkd.key.settings.Configuration; import de.uka.ilkd.key.speclang.PositionedString; import de.uka.ilkd.key.util.ProgressMonitor; @@ -33,10 +34,12 @@ public interface ProblemLoaderControl extends ProblemInitializerListener, Progre * @param poContainer The loaded {@link LoadedPOContainer}. * @param proofList The created {@link ProofAggregate}. * @param result The occurred {@link ReplayResult}. + * @param settings * @throws ProblemLoaderException Occurred Exception. */ void loadingFinished(AbstractProblemLoader loader, LoadedPOContainer poContainer, - ProofAggregate proofList, ReplayResult result) throws ProblemLoaderException; + ProofAggregate proofList, ReplayResult result, + Configuration settings) throws ProblemLoaderException; /** * This method is called if no {@link LoadedPOContainer} was created via diff --git a/key.core/src/main/java/de/uka/ilkd/key/proof/io/ProofBundleSaver.java b/key.core/src/main/java/de/uka/ilkd/key/proof/io/ProofBundleSaver.java index a40390cd13c..8b508a2eb12 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/proof/io/ProofBundleSaver.java +++ b/key.core/src/main/java/de/uka/ilkd/key/proof/io/ProofBundleSaver.java @@ -39,7 +39,7 @@ public ProofBundleSaver(Proof proof, Path saveFile) { @Override protected void save(Path file) throws IOException { // get the FileRepo from the InitConfig of the Proof - FileRepo repo = proof.getInitConfig().getFileRepo(); + FileRepo repo = getProof().getInitConfig().getFileRepo(); // this ProofSaver can not be used with TrivialFileRepo if (!(repo instanceof AbstractFileRepo)) { @@ -51,7 +51,7 @@ protected void save(Path file) throws IOException { * create a filename for the actual proof file in the FileRepo: We always use the contract * name here (preparation for proof bundle -> saving multiple proofs). */ - String proofFileName = MiscTools.toValidFileName(proof.name() + ".proof"); + String proofFileName = MiscTools.toValidFileName(getProof().name() + ".proof"); // save the proof file to the FileRepo (stream is closed by the save method!) save(repo.createOutputStream(Paths.get(proofFileName))); diff --git a/key.core/src/main/java/de/uka/ilkd/key/settings/Configuration.java b/key.core/src/main/java/de/uka/ilkd/key/settings/Configuration.java index 8efef1a3e1b..736eea251de 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/settings/Configuration.java +++ b/key.core/src/main/java/de/uka/ilkd/key/settings/Configuration.java @@ -268,7 +268,7 @@ public List getList(String name) { /** * Returns a list of strings for the given name. - * + *

* In contrast to the other methods, this method does not throw an exception if the entry does * not * exist in the configuration. Instead, it returns an empty list. @@ -288,6 +288,19 @@ public List getList(String name) { return (List) result; } + /** + * Returns a list of integer values given by the name. + */ + @Nullable + public List getIntList(String name) { + var seq = get(name, List.class); + if (seq == null) + return null; + if (!seq.stream().allMatch(it -> it instanceof Long)) + throw new ClassCastException(); + return seq; + } + /** * Returns string array for the requested entry. {@code defaultValue} is returned if no such * entry exists. @@ -332,7 +345,7 @@ public String[] getStringArray(String name, @NonNull String[] defaultValue) { } /** - * Returns the meta data corresponding to the given entry. + * Returns the meta-data corresponding to the given entry. */ @Nullable public ConfigurationMeta getMeta(String name) { @@ -420,14 +433,19 @@ public void overwriteWith(Configuration other) { } // TODO Add documentation for this. + /** * POJO for metadata of configuration entries. */ public static class ConfigurationMeta { - /** Position of declaration within a file */ + /** + * Position of declaration within a file + */ private Position position; - /** documentation given in the file */ + /** + * documentation given in the file + */ private String documentation; public Position getPosition() { @@ -502,6 +520,8 @@ public ConfigurationWriter printValue(Object value) { printValue(value.toString()); } else if (value == null) { printValue("null"); + } else if (value instanceof int[] array) { + printSeq(array); } else { throw new IllegalArgumentException("Unexpected object: " + value); } @@ -535,6 +555,19 @@ private ConfigurationWriter print(String s) { return this; } + private ConfigurationWriter printSeq(int[] values) { + out.format("["); + for (var i = 0; i < values.length; i++) { + int o = values[i]; + printValue(o); + if (i + 1 < values.length) { + print(", "); + } + } + out.format("]"); + return this; + } + private ConfigurationWriter printSeq(Collection value) { out.format("[ "); indent += 4; diff --git a/key.core/src/main/java/de/uka/ilkd/key/settings/ProofSettings.java b/key.core/src/main/java/de/uka/ilkd/key/settings/ProofSettings.java index b06d2307847..61f4ec77964 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/settings/ProofSettings.java +++ b/key.core/src/main/java/de/uka/ilkd/key/settings/ProofSettings.java @@ -7,13 +7,13 @@ import java.io.*; import java.net.URL; import java.nio.charset.StandardCharsets; -import java.util.LinkedList; -import java.util.List; -import java.util.Properties; +import java.util.*; import de.uka.ilkd.key.util.KeYResourceManager; import org.antlr.v4.runtime.CharStreams; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,6 +32,7 @@ * @see Properties * @see Settings */ +@NullMarked public class ProofSettings { private static final Logger LOGGER = LoggerFactory.getLogger(ProofSettings.class); @@ -45,6 +46,7 @@ public class ProofSettings { .getResourceFile(ProofSettings.class, "default-proof-settings.json"); public static final ProofSettings DEFAULT_SETTINGS = loadedSettings(); + public static final String KEY_ADDITIONAL_DATA = "additionalInformation"; private static ProofSettings loadedSettings() { @@ -70,8 +72,8 @@ private static ProofSettings loadedSettings() { private final NewSMTTranslationSettings newSMTSettings = new NewSMTTranslationSettings(); private final TermLabelSettings termLabelSettings = new TermLabelSettings(); - private Properties lastLoadedProperties = null; - private Configuration lastLoadedConfiguration = null; + private @Nullable Properties lastLoadedProperties = null; + private @Nullable Configuration lastLoadedConfiguration = null; /** * create a proof settings object. When you add a new settings object, PLEASE UPDATE THE LIST @@ -133,10 +135,27 @@ public Configuration getConfiguration() { } /** - * Used by saveSettings() and settingsToString() + * Write the settings to the given stream w/o additional information. + * + * @see #settingsToStream(Writer, Map) */ public void settingsToStream(Writer out) { - getConfiguration().save(out, "Proof-Settings-Config-File"); + settingsToStream(out, null); + } + + /** + * Writes the settings to the given stream storing additional data into + * {@link #KEY_ADDITIONAL_DATA}. + * + * @param out a output destination + * @param additionalInformation a nullable map of additional information + */ + public void settingsToStream(Writer out, @Nullable Map additionalInformation) { + var config = getConfiguration(); + if (additionalInformation != null) { + config.set(KEY_ADDITIONAL_DATA, new Configuration(additionalInformation)); + } + config.save(out, "Proof-Settings-Config-File"); } /** @@ -157,8 +176,12 @@ public void saveSettings() { } public String settingsToString() { + return settingsToString(new HashMap<>()); + } + + public String settingsToString(Map additionalInformation) { StringWriter out = new StringWriter(); - settingsToStream(out); + settingsToStream(out, additionalInformation); return out.getBuffer().toString(); } diff --git a/key.ui/src/main/java/de/uka/ilkd/key/gui/WindowUserInterfaceControl.java b/key.ui/src/main/java/de/uka/ilkd/key/gui/WindowUserInterfaceControl.java index 8f738947e82..5813343e333 100644 --- a/key.ui/src/main/java/de/uka/ilkd/key/gui/WindowUserInterfaceControl.java +++ b/key.ui/src/main/java/de/uka/ilkd/key/gui/WindowUserInterfaceControl.java @@ -34,7 +34,9 @@ import de.uka.ilkd.key.proof.io.*; import de.uka.ilkd.key.proof.io.AbstractProblemLoader.ReplayResult; import de.uka.ilkd.key.rule.IBuiltInRuleApp; +import de.uka.ilkd.key.settings.Configuration; import de.uka.ilkd.key.settings.ProofIndependentSettings; +import de.uka.ilkd.key.settings.ProofSettings; import de.uka.ilkd.key.settings.ViewSettings; import de.uka.ilkd.key.speclang.PositionedString; import de.uka.ilkd.key.strategy.StrategyProperties; @@ -392,6 +394,11 @@ public Path saveProof(Proof proof, String fileExtension) { } else { saver = new ProofSaver(proof, filename, KeYConstants.INTERNAL_VERSION); } + + if (getMediator().getSelectedProof() == proof) { + saver.setPathToLastSelectedNode(getMediator().getSelectedNode().getPosInProof()); + } + String errorMsg; try { getMediator().stopInterface(true); @@ -503,8 +510,10 @@ public void loadingStarted(AbstractProblemLoader loader) { @Override public void loadingFinished(AbstractProblemLoader loader, LoadedPOContainer poContainer, - ProofAggregate proofList, ReplayResult result) throws ProblemLoaderException { - super.loadingFinished(loader, poContainer, proofList, result); + ProofAggregate proofList, ReplayResult result, Configuration settings) + throws ProblemLoaderException { + + super.loadingFinished(loader, poContainer, proofList, result, settings); if (proofList != null) { if (result != null) { if ("".equals(result.getStatus())) { @@ -523,13 +532,24 @@ public void loadingFinished(AbstractProblemLoader loader, LoadedPOContainer poCo } } getMediator().resetNrGoalsClosedByHeuristics(); - if (poContainer != null && poContainer.getProofOblInput() instanceof KeYUserProblemFile) { - ((KeYUserProblemFile) poContainer.getProofOblInput()).close(); + if (poContainer != null + && poContainer.getProofOblInput() instanceof KeYUserProblemFile file) { + // TODO weigl not triggered + var addInfo = settings.getSection(ProofSettings.KEY_ADDITIONAL_DATA); + if (addInfo != null) { + var lastSelectedNodePath = + settings.getIntList(OutputStreamProofSaver.KEY_LAST_SELECTED_NODE); + if (lastSelectedNodePath != null && proofList != null) { + var proof = proofList.getFirstProof(); + var selectedNode = proof.root().traversePath( + lastSelectedNodePath.stream().map(Long::intValue).iterator()); + getMediator().getSelectionModel().setSelectedNode(selectedNode); + } + } + file.close(); } } - - /** * Loads the given location and returns all required references as {@link KeYEnvironment} with * KeY's {@link MainWindow}. diff --git a/key.util/src/main/java/org/key_project/util/collection/KeYCollections.java b/key.util/src/main/java/org/key_project/util/collection/KeYCollections.java index e6193c17e99..676ebc50adb 100644 --- a/key.util/src/main/java/org/key_project/util/collection/KeYCollections.java +++ b/key.util/src/main/java/org/key_project/util/collection/KeYCollections.java @@ -3,10 +3,7 @@ * SPDX-License-Identifier: GPL-2.0-only */ package org.key_project.util.collection; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; +import java.util.*; import java.util.stream.Collectors; import java.util.stream.StreamSupport; @@ -125,4 +122,115 @@ public static String join(Object[] collection, String delimiter) { } return res.toString(); } + + /** + * Creates a run-length encoding from the given positive integer array. The return array has the + * following form: + * every positive entry stands for its own. Every negative integer describes the repetition + * of the following positive integer. + *

+ * For example {@code [1, -5, 2, 1]} stands for {@code [1,2,2,2,2,2,1]}. + * + * @param array an integer array where + */ + public static int[] runLengthEncoding(int[] array) { + int len = array.length; + int[] target = new int[2 * len]; + int used = 0; + for (int i = 0; i < len; i++) { + // Count occurrences of current character + int count = 1; + int symbol = array[i]; + while (i < len - 1 && symbol == array[i + 1]) { + count++; + i++; + } + if (count != 1) { + target[used++] = -count; + } + target[used++] = symbol; + } + + return Arrays.copyOf(target, used); + } + + + public static int[] runLengthEncoding(Collection array) { + var iter = array.iterator(); + class PushbackIterator implements Iterator { + private int pushedBack = -1; + + @Override + public boolean hasNext() { + return pushedBack >= 0 || iter.hasNext(); + } + + @Override + public Integer next() { + if (pushedBack >= 0) { + var v = pushedBack; + pushedBack = -1; + return v; + } + return iter.next(); + } + + public void pushBack(int last) { + pushedBack = last; + } + } + var piter = new PushbackIterator(); + int len = array.size(); + int[] target = new int[2 * len]; + int used = 0; + + while (piter.hasNext()) { + // Count occurrences of current character + int count = 1; + int symbol = piter.next(); + int last = symbol; + while (iter.hasNext() && (symbol == (last = piter.next()))) { + count++; + } + if (last != symbol) { + piter.pushBack(last); + } + if (count != 1) { + target[used++] = -count; + } + target[used++] = symbol; + } + return Arrays.copyOf(target, used); + } + + + /** + * Creates a lazy decoding for the given run-length encoding. + */ + public static Iterator runLengthDecoding(int[] rleArray) { + return new Iterator<>() { + int pos = 0; + int count = 0; + int value = -1; + + @Override + public boolean hasNext() { + return count > 0 || pos < rleArray.length; + } + + @Override + public Integer next() { + if (count == 0) { + value = rleArray[pos++]; + if (value < 0) { + count = (-value) - 1; + value = rleArray[pos++]; + } + } else { + count--; + } + return value; + } + }; + } } diff --git a/key.util/src/test/java/org/key_project/util/collection/KeYCollectionsTest.java b/key.util/src/test/java/org/key_project/util/collection/KeYCollectionsTest.java new file mode 100644 index 00000000000..f76b9b3e0e4 --- /dev/null +++ b/key.util/src/test/java/org/key_project/util/collection/KeYCollectionsTest.java @@ -0,0 +1,64 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ +package org.key_project.util.collection; + +import java.util.List; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.Consumer; +import java.util.stream.StreamSupport; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.key_project.util.collection.KeYCollections.runLengthDecoding; +import static org.key_project.util.collection.KeYCollections.runLengthEncoding; + +/** + * @author Alexander Weigl + * @version 1 (28.12.23) + */ +class KeYCollectionsTest { + + @Test + void runLengthEncoding_test() { + assertArrayEquals(new int[] { 1, -5, 2, 1 }, + runLengthEncoding(new int[] { 1, 2, 2, 2, 2, 2, 1 })); + assertArrayEquals(new int[] { -5, 2 }, runLengthEncoding(new int[] { 2, 2, 2, 2, 2 })); + assertArrayEquals(new int[] {}, runLengthEncoding(new int[] {})); + assertArrayEquals(new int[] { -5, 2, -5, 1 }, + runLengthEncoding(new int[] { 2, 2, 2, 2, 2, 1, 1, 1, 1, 1 })); + } + + @Test + void runLengthEncoding_test2() { + assertArrayEquals(new int[] {}, runLengthEncoding(List.of())); + assertArrayEquals(new int[] { 1, -5, 2, 1 }, + runLengthEncoding(List.of(1, 2, 2, 2, 2, 2, 1))); + assertArrayEquals(new int[] { -5, 2 }, runLengthEncoding(List.of(2, 2, 2, 2, 2))); + assertArrayEquals(new int[] { -5, 2, -5, 1 }, + runLengthEncoding(List.of(2, 2, 2, 2, 2, 1, 1, 1, 1, 1))); + } + + + @Test + void runLengthDecoding_test() { + Consumer tester = original -> { + final var rle = runLengthEncoding(original); + final var iter = runLengthDecoding(rle); + var stream = StreamSupport + .stream(Spliterators.spliteratorUnknownSize(iter, Spliterator.ORDERED), false) + .mapToInt(it -> it) + .toArray(); + System.out.println(stream); + assertArrayEquals(original, stream); + }; + tester.accept(new int[] { 1, 2, 2, 2, 2, 2, 1 }); + tester.accept(new int[] { 2, 2, 2, 2, 2 }); + tester.accept(new int[] {}); + tester.accept(new int[] { 2, 2, 2, 2, 2, 1, 1, 1, 1, 1 }); + } + + +}