001    /*--------------------------------------------------------------------------+
002    $Id: StringUndoStackBase.java 26283 2010-02-18 11:18:57Z juergens $
003    |                                                                          |
004    | Copyright 2005-2010 Technische Universitaet Muenchen                     |
005    |                                                                          |
006    | Licensed under the Apache License, Version 2.0 (the "License");          |
007    | you may not use this file except in compliance with the License.         |
008    | You may obtain a copy of the License at                                  |
009    |                                                                          |
010    |    http://www.apache.org/licenses/LICENSE-2.0                            |
011    |                                                                          |
012    | Unless required by applicable law or agreed to in writing, software      |
013    | distributed under the License is distributed on an "AS IS" BASIS,        |
014    | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
015    | See the License for the specific language governing permissions and      |
016    | limitations under the License.                                           |
017    +--------------------------------------------------------------------------*/
018    package edu.tum.cs.commons.string;
019    
020    import java.util.ArrayList;
021    import java.util.List;
022    
023    import edu.tum.cs.commons.algo.Diff;
024    import edu.tum.cs.commons.algo.Diff.Delta;
025    import edu.tum.cs.commons.assertion.CCSMPre;
026    
027    /**
028     * Base class for an undo stack using a string as the underlying model.
029     * 
030     * Please refer to the test case for a demonstration and further explanation of
031     * this class.
032     * 
033     * @author hummelb
034     * @author $Author: juergens $
035     * @version $Rev: 26283 $
036     * @levd.rating GREEN Hash: 6C0282A04515FF5028992B67673ED9DD
037     */
038    public abstract class StringUndoStackBase {
039    
040            /** The stack of versions. */
041            private final List<Delta<String>> deltas = new ArrayList<Delta<String>>();
042    
043            /** The current string. */
044            private String currentVersion;
045    
046            /** The index of the currently used version/delta. */
047            private int currentVersionIndex = -1;
048    
049            /** The last position used for saving. */
050            private int savePosition = -1;
051    
052            /** Constructor. */
053            protected StringUndoStackBase(String initialString) {
054                    currentVersion = initialString;
055            }
056    
057            /** Returns whether undo is possible. */
058            public boolean canUndo() {
059                    return currentVersionIndex >= 0;
060            }
061    
062            /** Performs one undo step. */
063            public void undo() {
064                    CCSMPre.isTrue(canUndo(), "Must be allowed to undo!");
065                    currentVersion = join(deltas.get(currentVersionIndex--).backwardPatch(
066                                    split(currentVersion)));
067                    setModelFromString(currentVersion);
068                    fireStackChanged();
069            }
070    
071            /** Returns whether redo is possible. */
072            public boolean canRedo() {
073                    return currentVersionIndex + 1 < deltas.size();
074            }
075    
076            /** Performs one redo step. */
077            public void redo() {
078                    CCSMPre.isTrue(canRedo(), "Must be allowed to redo!");
079                    currentVersion = join(deltas.get(++currentVersionIndex).forwardPatch(
080                                    split(currentVersion)));
081                    setModelFromString(currentVersion);
082                    fireStackChanged();
083            }
084    
085            /** Returns whether something changed compared to the last safe. */
086            public boolean isDirty() {
087                    return currentVersionIndex != savePosition;
088            }
089    
090            /** Mark the current position as saved (affects dirty calculation). */
091            public void doSave() {
092                    savePosition = currentVersionIndex;
093            }
094    
095            /** Inserts a new version of the model (as a string) into this stack. */
096            protected void insertNewVersion(String s) {
097                    ++currentVersionIndex;
098                    if (savePosition >= currentVersionIndex) {
099                            savePosition = -1;
100                    }
101    
102                    // discard later versions/deltas
103                    while (deltas.size() > currentVersionIndex) {
104                            deltas.remove(deltas.size() - 1);
105                    }
106    
107                    deltas.add(Diff.computeDelta(split(currentVersion), split(s)));
108                    currentVersion = s;
109                    fireStackChanged();
110            }
111    
112            /**
113             * Splits the given string (as reported from the implementing class) into
114             * suitable parts used for diffing (lines, words, tokens, etc.).
115             */
116            protected abstract List<String> split(String s);
117    
118            /** Joins the parts created by {@link #split(String)}. */
119            protected abstract String join(List<String> parts);
120    
121            /**
122             * This should write back the stack content to the model. This is called for
123             * every undo and redo operation.
124             */
125            protected abstract void setModelFromString(String s);
126    
127            /** Something about this stack has changed. */
128            protected abstract void fireStackChanged();
129    
130            /** Prints the amount of memory currently used by this stack. */
131            protected int debugGetSize() {
132                    int size = 2 * currentVersion.length();
133                    for (Delta<String> delta : deltas) {
134                            for (int i = 0; i < delta.getSize(); ++i) {
135                                    size += 4 + 2 * delta.getT(i).length();
136                            }
137                    }
138                    return 2 * size;
139            }
140    }