Merge pull request 'Basic API Structure' (#2) from f/basics into develop
zdm/undo-redo/pipeline/head This commit looks good Details

Reviewed-on: https://git.kske.dev/zdm/undo-redo/pulls/2
Reviewed-by: kske <kai@kske.dev>
Reviewed-by: delvh <leon@kske.dev>
This commit is contained in:
Kai S. K. Engelbart 2021-12-11 21:45:58 +01:00
commit 4b07626155
Signed by: Käfer & Engelbart Git
GPG Key ID: 70F2F9206EDC1FCE
15 changed files with 416 additions and 12 deletions

7
.gitignore vendored
View File

@ -1,2 +1,5 @@
/target/
/.settings/
# Maven build directories
target/
# Eclipse settings directories
.settings/

View File

@ -5,11 +5,6 @@
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
@ -17,7 +12,6 @@
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

40
Jenkinsfile vendored Normal file
View File

@ -0,0 +1,40 @@
pipeline {
agent any
options {
ansiColor('xterm')
}
stages {
stage('Build') {
steps {
sh 'mvn -DskipTests clean package'
}
}
stage('Test') {
steps {
sh 'mvn test'
}
post {
always {
junit '*/target/surefire-reports/*.xml'
}
}
}
stage('SonarQube Analysis') {
when {
branch 'develop'
}
steps {
withSonarQubeEnv('KSKE SonarQube') {
sh 'mvn org.sonarsource.scanner.maven:sonar-maven-plugin:3.9.1.2184:sonar'
}
}
}
}
post {
success {
archiveArtifacts artifacts: '*/target/undo-redo-*.jar'
}
}
}

23
core/.project Normal file
View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>undo-redo-core</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

15
core/pom.xml Normal file
View File

@ -0,0 +1,15 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>undo-redo-core</artifactId>
<name>Undo-Redo Core</name>
<parent>
<groupId>dev.kske</groupId>
<artifactId>undo-redo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
</project>

View File

@ -0,0 +1,33 @@
package dev.kske.undoredo.core;
/**
* Base interface for changes to be registered in an undo manager.
*
* @author Maximilian K&auml;fer
* @since 0.0.1
*/
public interface Change {
/**
* Performs the action implemented by this change.
*
* @since 0.0.1
*/
void apply();
/**
* Inverts this change.
*
* @implSpec This method is not supposed to alter the state of this change, but rather to create
* a new complementary change.
* @return the inverted change
* @since 0.0.1
*/
Change invert();
/**
* @return whether the application of this change would result in an identical state
* @since 0.0.1
*/
boolean isIdentity();
}

View File

@ -0,0 +1,73 @@
package dev.kske.undoredo.core;
import java.util.List;
/**
* A change manager keeps track of subsequent changes and allows un- and redoing them. A specific
* change can be marked using {@link #mark()} to keep track of a saved state in the application that
* uses the manager.
*
* @param <C> the change type to store in this change manager
* @author Maximilian K&auml;fer
* @author Kai S. K. Engelbart
* @since 0.0.1
*/
public interface ChangeManager<C extends Change> {
/**
* Applies the given change and appends it to the change list.
*
* @param change the change to add
* @since 0.0.1
*/
void addChange(C change);
/**
* Undoes the current change.
*
* @return whether an action was performed
* @since 0.1.0
*/
boolean undo();
/**
* Applies the change that was undone before.
*
* @return whether an action was performed
* @since 0.0.1
*/
boolean redo();
/**
* Marks the current change.
*
* @since 0.0.1
*/
void mark();
/**
* @return whether the current change is marked
* @since 0.0.1
*/
boolean isAtMarkedIndex();
/**
* @return whether a change is present that can be undone
* @since 0.0.1
*/
boolean isUndoAvailable();
/**
* @return whether a change is present that can be redone
* @since 0.0.1
*/
boolean isRedoAvailable();
/**
* Provides an unmodifiable view of the changes stored in this change manager.
*
* @return all stored changes
* @since 0.0.1
*/
List<C> getChanges();
}

View File

@ -0,0 +1,69 @@
package dev.kske.undoredo.core;
import java.util.*;
/**
* @param <C> the change type to store in this change manager
* @author Maximilian K&auml;fer
* @author Kai S. K. Engelbart
* @since 0.0.1
*/
public final class UnlimitedChangeManager<C extends Change> implements ChangeManager<C> {
private final List<C> changes = new ArrayList<>();
private int index = -1;
private int markedIndex = -1;
@Override
public void addChange(C change) {
change.apply();
changes.add(change);
++index;
}
@Override
public boolean undo() {
if (isUndoAvailable()) {
changes.get(index).invert().apply();
--index;
return true;
}
return false;
}
@Override
public boolean redo() {
if (isRedoAvailable()) {
changes.get(index + 1).apply();
++index;
return true;
}
return false;
}
@Override
public void mark() {
markedIndex = index;
}
@Override
public boolean isAtMarkedIndex() {
return markedIndex == index;
}
@Override
public boolean isUndoAvailable() {
return index > -1;
}
@Override
public boolean isRedoAvailable() {
return index < changes.size() - 1;
}
@Override
public List<C> getChanges() {
return Collections.unmodifiableList(changes);
}
}

View File

@ -5,4 +5,4 @@
* @author Kai S. K. Engelbart
* @since 0.0.1
*/
package dev.kske.undoredo;
package dev.kske.undoredo.core;

View File

@ -5,7 +5,7 @@
* @author Kai S. K. Engelbart
* @since 0.0.1
*/
module dev.kske.undoredo {
module dev.kske.undoredo.core {
exports dev.kske.undoredo;
exports dev.kske.undoredo.core;
}

View File

@ -0,0 +1,104 @@
package dev.kske.undoredo.core;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.*;
/**
* @author Kai S. K. Engelbart
* @since 0.0.1
*/
class ChangeManagerTest {
ChangeManager<IntChange> manager;
IntWrapper wrapper;
IntChange change;
@BeforeEach
void prepareChangeManager() {
manager = new UnlimitedChangeManager<>();
wrapper = new IntWrapper();
change = new IntChange(wrapper, 1);
}
/**
* Tests adding a change.
*
* @since 0.0.1
*/
@Test
@Order(1)
void testAddChange() {
assertSame(0, wrapper.value);
manager.addChange(change);
assertSame(1, wrapper.value);
}
/**
* Tests the consistency of the change list.
*
* @since 0.0.1
*/
@Test
@Order(2)
void testGetChanges() {
assertTrue(manager.getChanges().isEmpty());
manager.addChange(change);
assertSame(1, manager.getChanges().size());
assertEquals(change, manager.getChanges().get(0));
}
/**
* Test undoing a change.
*
* @since 0.0.1
*/
@Test
@Order(2)
void testUndo() {
assertFalse(manager.isUndoAvailable());
assertFalse(manager.undo());
manager.addChange(change);
assertTrue(manager.isUndoAvailable());
assertTrue(manager.undo());
assertFalse(manager.isUndoAvailable());
assertFalse(manager.undo());
}
/**
* Tests redoing a change.
*
* @since 0.0.1
*/
@Test
@Order(4)
void testRedo() {
assertFalse(manager.isRedoAvailable());
assertFalse(manager.redo());
manager.addChange(change);
assertFalse(manager.isRedoAvailable());
assertFalse(manager.redo());
manager.undo();
assertTrue(manager.isRedoAvailable());
assertTrue(manager.redo());
assertFalse(manager.isRedoAvailable());
assertFalse(manager.redo());
}
/**
* Tests marking a change.
*
* @since 0.0.1
*/
@Test
@Order(5)
void testMark() {
assertTrue(manager.isAtMarkedIndex());
manager.addChange(change);
assertFalse(manager.isAtMarkedIndex());
manager.mark();
assertTrue(manager.isAtMarkedIndex());
manager.undo();
assertFalse(manager.isAtMarkedIndex());
}
}

View File

@ -0,0 +1,31 @@
package dev.kske.undoredo.core;
/**
* @author Kai S. K. Engelbart
* @since 0.0.1
*/
class IntChange implements Change {
private final IntWrapper wrapper;
private final int value;
IntChange(IntWrapper wrapper, int value) {
this.wrapper = wrapper;
this.value = value;
}
@Override
public void apply() {
wrapper.value += value;
}
@Override
public Change invert() {
return new IntChange(wrapper, -value);
}
@Override
public boolean isIdentity() {
return value == 0;
}
}

View File

@ -0,0 +1,10 @@
package dev.kske.undoredo.core;
/**
* @author Kai S. K. Engelbart
* @since 0.0.1
*/
class IntWrapper {
int value;
}

11
pom.xml
View File

@ -6,10 +6,15 @@
<groupId>dev.kske</groupId>
<artifactId>undo-redo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Undo-Redo</name>
<description>A Java library for managing changes in an editor history.</description>
<url>https://git.kske.dev/kske/event-bus</url>
<modules>
<module>core</module>
</modules>
<licenses>
<license>
@ -165,8 +170,12 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<configuration>
<argLine>--add-opens dev.kske.undoredo.core/dev.kske.undoredo.core=ALL-UNNAMED</argLine>
</configuration>
</plugin>
</plugins>
</build>
</project>