Last active
August 29, 2015 14:05
-
-
Save blueskyfish/f6fc640025ae2058059c to your computer and use it in GitHub Desktop.
NumberModel with Command Pattern
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2014. BlueSkyFish <[email protected]> | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
* in the Software without restriction, including without limitation the rights | |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | |
* of the Software, and to permit persons to whom the Software is furnished to do so, | |
* subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be included in all | |
* copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | |
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A | |
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | |
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | |
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE | |
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
*/ | |
package kirchnerei.number.model; | |
/** | |
* Interface for a command | |
*/ | |
public interface Command { | |
/** | |
* Executes the command | |
*/ | |
void doIt(); | |
/** | |
* Undo the command | |
*/ | |
void undo(); | |
/** | |
* Creates a exact copy from the command with a new cursor position. | |
* | |
* @param cursor the new cursor position | |
* @return a copy of the command | |
*/ | |
Command clone(int cursor); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2014. BlueSkyFish <[email protected]> | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
* in the Software without restriction, including without limitation the rights | |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | |
* of the Software, and to permit persons to whom the Software is furnished to do so, | |
* subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be included in all | |
* copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | |
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A | |
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | |
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | |
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE | |
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
*/ | |
package kirchnerei.number.model; | |
/** | |
* Deletes an sign on the cursor position. | |
*/ | |
public class DeleteCommand implements Command { | |
private final StringBuilder text; | |
private final int cursor; | |
private char sign; | |
public DeleteCommand(int cursor, StringBuilder text) { | |
this.text = text; | |
this.cursor = cursor; | |
} | |
@Override | |
public void doIt() { | |
int start = cursor - 1; | |
sign = text.charAt(start); | |
text.deleteCharAt(start); | |
} | |
@Override | |
public void undo() { | |
text.insert(cursor - 1, sign); | |
} | |
@Override | |
public Command clone(int cursor) { | |
return new DeleteCommand(cursor, text); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2014. BlueSkyFish <[email protected]> | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
* in the Software without restriction, including without limitation the rights | |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | |
* of the Software, and to permit persons to whom the Software is furnished to do so, | |
* subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be included in all | |
* copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | |
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A | |
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | |
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | |
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE | |
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
*/ | |
package kirchnerei.number.model; | |
/** | |
* Insert a digit sign. | |
*/ | |
public class InsertCommand implements Command { | |
private final char sign; | |
private final int cursor; | |
private final StringBuilder text; | |
public InsertCommand(char sign, int cursor, StringBuilder text) { | |
this.sign = sign; | |
this.cursor = cursor; | |
this.text = text; | |
} | |
@Override | |
public void doIt() { | |
text.insert(cursor, sign); | |
} | |
@Override | |
public void undo() { | |
text.deleteCharAt(cursor); | |
} | |
@Override | |
public Command clone(int cursor) { | |
return new InsertCommand(sign, cursor, text); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2014. Mulder3 BlueSkyFish <[email protected]> | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
* in the Software without restriction, including without limitation the rights | |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | |
* of the Software, and to permit persons to whom the Software is furnished to do so, | |
* subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be included in all | |
* copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | |
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A | |
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | |
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | |
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE | |
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
*/ | |
package kirchnerei.number.model; | |
import java.util.Stack; | |
/** | |
* The number model contains a string with digit signs. Also an undo stack is available. | |
*/ | |
public class NumberModel { | |
private static final int DEFAULT_CAPACITY = 32; | |
private final Stack<Command> commands; | |
private final StringBuilder text; | |
private int cursor = 0; | |
public NumberModel() { | |
this(DEFAULT_CAPACITY); | |
} | |
public NumberModel(int capacity) { | |
this.commands = new Stack<Command>(); | |
this.text = new StringBuilder(capacity); | |
} | |
public NumberModel(String template, int capacity) { | |
this(capacity); | |
this.setTemplate(template); | |
} | |
/** | |
* Set the template into the internal text. Only digit signs will be insert. | |
* | |
* @param template the template (e.g: "27.08.2012") | |
*/ | |
public final void setTemplate(String template) { | |
text.setLength(0); | |
for (char ch : template.toCharArray()) { | |
if (Character.isDigit(ch)) { | |
text.append(ch); | |
} | |
} | |
} | |
/** | |
* Move the cursor to the given position. If the new position is outside of the range, then correct the position. | |
* | |
* @param cursor the new position | |
*/ | |
public void cursorMove(int cursor) { | |
this.cursor = cursor; | |
adjustCursorPosition(); | |
} | |
/** | |
* Returns the current position | |
* @return the position | |
*/ | |
public int getCursorPosition() { | |
return cursor; | |
} | |
/** | |
* Insert the digit sign at the cursor position. | |
* | |
* @param sign the digit | |
* @return the current cursor position after insert, or -1 when the sign is not a digit | |
*/ | |
public int insert(char sign) { | |
if (Character.isDigit(sign)) { | |
InsertCommand cmd = new InsertCommand(sign, cursor, text); | |
commands.push(cmd); | |
cmd.doIt(); | |
cursor++; | |
adjustCursorPosition(); | |
return cursor; | |
} | |
return -1; | |
} | |
/** | |
* Delete the sign on the current cursor position. If the cursor is on the position 0 then returns -1. | |
* | |
* @return the current cursor position after deleting (may be the same position as before), or -1 if the cursor is at 0. | |
*/ | |
public int delete() { | |
if (cursor > 0) { | |
DeleteCommand cmd = new DeleteCommand(cursor, text); | |
commands.push(cmd); | |
cmd.doIt(); | |
adjustCursorPosition(); | |
return cursor; | |
} | |
return -1; | |
} | |
/** | |
* Takes the last change and update the text again. | |
* | |
* @return the current cursor position or -1 when the command stack is empty. | |
*/ | |
public int repeat() { | |
if (!commands.isEmpty()) { | |
Command cmd = commands.peek(); | |
Command repCmd = cmd.clone(cursor); | |
commands.push(repCmd); | |
repCmd.doIt(); | |
if (repCmd instanceof InsertCommand) { | |
cursor++; | |
} | |
adjustCursorPosition(); | |
return cursor; | |
} | |
return -1; | |
} | |
/** | |
* Undo the last change. | |
*/ | |
public int undo() { | |
if (!commands.isEmpty()) { | |
Command cmd = commands.pop(); | |
cmd.undo(); | |
adjustCursorPosition(); | |
return cursor; | |
} | |
return -1; | |
} | |
@Override | |
public String toString() { | |
return text.toString(); | |
} | |
/** | |
* Copy the digit text into a template. If the sign in the template is <code>#</code> then set the sign from | |
* the digit text. | |
* | |
* <pre><code> | |
* | |
* NumberModel model = new NumberModel(); | |
* | |
* model.insert("2"); | |
* model.insert("1"); | |
* model.insert("0"); | |
* model.insert("8"); | |
* model.insert("2"); | |
* model.insert("0"); | |
* model.insert("1"); | |
* model.insert("4"); | |
* | |
* assertEquals("21082014", model.toString()); | |
* | |
* assertEquals("21.08.2014", model.toString("##.##.####")); | |
* | |
* </code></pre> | |
* | |
* @param template the template | |
* @return the merged text. | |
*/ | |
public String toString(String template) { | |
StringBuilder sb = new StringBuilder(template); | |
int index = 0; | |
for (int i = 0; i < template.length() && index < text.length() ; i++) { | |
char ch = sb.charAt(i); | |
if (ch == '#') { | |
char sign = text.charAt(index++); | |
sb.deleteCharAt(i).insert(i, sign); | |
} | |
} | |
return sb.toString(); | |
} | |
private void adjustCursorPosition() { | |
if (cursor < 0) { | |
cursor = 0; | |
} | |
if (cursor > text.length()) { | |
cursor = text.length(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment