001: /*
002: * Copyright 2006-2007 Pentaho Corporation. All rights reserved.
003: * This software was developed by Pentaho Corporation and is provided under the terms
004: * of the Mozilla Public License, Version 1.1, or any later version. You may not use
005: * this file except in compliance with the license. If you need a copy of the license,
006: * please go to http://www.mozilla.org/MPL/MPL-1.1.txt.
007: *
008: * Software distributed under the Mozilla Public License is distributed on an "AS IS"
009: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. Please refer to
010: * the license for the specific language governing your rights and limitations.
011: *
012: * Additional Contributor(s): Martin Schmid gridvision engineering GmbH
013: */
014: package org.pentaho.reportdesigner.lib.client.undo;
015:
016: import org.jetbrains.annotations.NonNls;
017: import org.jetbrains.annotations.NotNull;
018:
019: import java.util.ArrayList;
020: import java.util.LinkedHashSet;
021: import java.util.logging.Level;
022: import java.util.logging.Logger;
023:
024: /**
025: * User: Martin
026: * Date: 24.01.2006
027: * Time: 11:01:55
028: */
029: @SuppressWarnings({"UseOfSystemOutOrSystemErr"})
030: public class Undo {
031: @NotNull
032: @NonNls
033: private static final Logger LOG = Logger.getLogger(Undo.class
034: .getName());
035:
036: private static final int MAXIMUM_UNDO_SIZE = 10000;//to ensure not to be completely unbound, prevents OOME
037:
038: public static void main(@NotNull
039: String[] args) {
040: Undo undo = new Undo();
041:
042: undo.startTransaction("1");
043: undo.add(new UndoEntry() {
044: public void undo() {
045: System.out.println("undo 1");//NON-NLS
046: }
047:
048: public void redo() {
049: System.out.println("redo 1");//NON-NLS
050: }
051: });
052: undo.endTransaction();
053:
054: undo.startTransaction("2");
055: undo.add(new UndoEntry() {
056: public void undo() {
057: System.out.println("undo 2");//NON-NLS
058: }
059:
060: public void redo() {
061: System.out.println("redo 2");//NON-NLS
062: }
063: });
064: undo.endTransaction();
065:
066: undo.startTransaction("3");
067: undo.add(new UndoEntry() {
068: public void undo() {
069: System.out.println("undo 3");//NON-NLS
070: }
071:
072: public void redo() {
073: System.out.println("redo 3");//NON-NLS
074: }
075: });
076: undo.endTransaction();
077:
078: System.out.println("undo2.isUndoable() = " + undo.isUndoable());//NON-NLS
079: System.out.println("undo2.isRedoable() = " + undo.isRedoable());//NON-NLS
080: undo.undo();
081: System.out.println("undo2.isUndoable() = " + undo.isUndoable());//NON-NLS
082: System.out.println("undo2.isRedoable() = " + undo.isRedoable());//NON-NLS
083: undo.undo();
084: System.out.println("undo2.isUndoable() = " + undo.isUndoable());//NON-NLS
085: System.out.println("undo2.isRedoable() = " + undo.isRedoable());//NON-NLS
086: undo.undo();
087: System.out.println("undo2.isUndoable() = " + undo.isUndoable());//NON-NLS
088: System.out.println("undo2.isRedoable() = " + undo.isRedoable());//NON-NLS
089:
090: undo.redo();
091: System.out.println("undo2.isUndoable() = " + undo.isUndoable());//NON-NLS
092: System.out.println("undo2.isRedoable() = " + undo.isRedoable());//NON-NLS
093: undo.redo();
094: System.out.println("undo2.isUndoable() = " + undo.isUndoable());//NON-NLS
095: System.out.println("undo2.isRedoable() = " + undo.isRedoable());//NON-NLS
096: undo.redo();
097: System.out.println("undo2.isUndoable() = " + undo.isUndoable());//NON-NLS
098: System.out.println("undo2.isRedoable() = " + undo.isRedoable());//NON-NLS
099:
100: undo.undo();
101: System.out.println("undo2.isUndoable() = " + undo.isUndoable());//NON-NLS
102: System.out.println("undo2.isRedoable() = " + undo.isRedoable());//NON-NLS
103: undo.undo();
104: System.out.println("undo2.isUndoable() = " + undo.isUndoable());//NON-NLS
105: System.out.println("undo2.isRedoable() = " + undo.isRedoable());//NON-NLS
106: undo.undo();
107: System.out.println("undo2.isUndoable() = " + undo.isUndoable());//NON-NLS
108: System.out.println("undo2.isRedoable() = " + undo.isRedoable());//NON-NLS
109: }
110:
111: @NotNull
112: private ArrayList<UndoEntry> undos;
113: private int index;
114:
115: @NotNull
116: private LinkedHashSet<UndoListener> undoListeners;
117:
118: private boolean startCommand;
119: private boolean inProgress;
120:
121: @NotNull
122: private String transactionName;
123: private int transactionCount;
124:
125: public Undo() {
126: undoListeners = new LinkedHashSet<UndoListener>();
127: undos = new ArrayList<UndoEntry>();
128:
129: inProgress = false;
130: startCommand = true;
131: index = -1;
132:
133: transactionCount = -1;
134: }
135:
136: public void startTransaction(@NotNull
137: String transactionName) {
138: this .transactionName = transactionName;
139: transactionCount++;
140: }
141:
142: public void endTransaction() {
143: transactionCount--;
144: if (transactionCount == -1) {
145: startCommand = true;
146:
147: removeOldEntries();
148: }
149: }
150:
151: public boolean isInProgress() {
152: return inProgress;
153: }
154:
155: public boolean isUndoable() {
156: return index >= 0;
157: }
158:
159: public boolean isRedoable() {
160: return index < undos.size() - 1
161: || (index == -1 && !undos.isEmpty());
162: }
163:
164: public void add(@NotNull
165: UndoEntry e) {
166: //remove all undos after index
167: for (int i = undos.size() - 1; i >= index + 1; i--) {
168: undos.remove(i);
169: }
170: if (transactionCount == -1) {
171: throw new RuntimeException("missed to start a transaction!");
172: }
173:
174: e.transactionName = transactionName;
175: e.startCommand = startCommand;
176: startCommand = false;
177: undos.add(e);
178: index++;
179:
180: fireStateChanged();
181: }
182:
183: private void removeOldEntries() {
184: while (undos.size() > MAXIMUM_UNDO_SIZE) {
185: removeNextTransaction();
186: }
187: }
188:
189: private void removeNextTransaction() {
190: if (LOG.isLoggable(Level.FINE))
191: LOG.log(Level.FINE, "Undo.removeNextTransaction ");
192:
193: UndoEntry undoEntry = undos.remove(0);
194: if (!undoEntry.startCommand) {
195: if (LOG.isLoggable(Level.FINE))
196: LOG
197: .log(Level.FINE,
198: "Undo.removeOldEntries was not a startCommand! eek!");
199: }
200: int removed = 1;
201: //remove complete transaction
202: while (!undos.isEmpty() && !undos.get(0).startCommand) {
203: undos.remove(0);
204: removed++;
205: }
206: if (removed > 0) {
207: if (LOG.isLoggable(Level.FINE))
208: LOG.log(Level.FINE, "Undo.removeOldEntries removed = "
209: + removed);
210: }
211: index -= removed;
212: }
213:
214: public void undo() {
215: inProgress = true;
216:
217: for (int i = index; i >= 0; i--) {
218: index--;
219: if (i >= 0) {
220: UndoEntry undoEntry = undos.get(i);
221: undoEntry.undo();
222: if (undoEntry.startCommand) {
223: break;
224: }
225: }
226: }
227:
228: inProgress = false;
229: fireStateChanged();
230: }
231:
232: public void redo() {
233: inProgress = true;
234:
235: for (int i = index + 1; i < undos.size(); i++) {
236: index++;
237: UndoEntry undoEntry = undos.get(i);
238: undoEntry.redo();
239: if (undos.size() < i + 2 || undos.get(i + 1).startCommand) {
240: break;
241: }
242: }
243:
244: inProgress = false;
245: fireStateChanged();
246: }
247:
248: public void addUndoListener(@NotNull
249: UndoListener undoListener) {
250: undoListeners.add(undoListener);
251: }
252:
253: public void removeUndoListener(@NotNull
254: UndoListener undoListener) {
255: undoListeners.remove(undoListener);
256: }
257:
258: public void fireStateChanged() {
259: ArrayList<UndoListener> undoListeners = new ArrayList<UndoListener>(
260: this .undoListeners);
261: for (UndoListener listener : undoListeners) {
262: listener.stateChanged();
263: }
264: }
265:
266: public void debugPrint() {
267: if (LOG.isLoggable(Level.FINE))
268: LOG.log(Level.FINE, "UNDO Infos");
269: if (LOG.isLoggable(Level.FINE))
270: LOG.log(Level.FINE, "==========");
271: if (LOG.isLoggable(Level.FINE))
272: LOG.log(Level.FINE, "transactionCount = "
273: + transactionCount);
274: if (LOG.isLoggable(Level.FINE))
275: LOG.log(Level.FINE, "transactionName = " + transactionName);
276: if (LOG.isLoggable(Level.FINE))
277: LOG.log(Level.FINE, "index = " + index);
278: if (LOG.isLoggable(Level.FINE))
279: LOG.log(Level.FINE, "undos.size() = " + undos.size());
280: if (LOG.isLoggable(Level.FINE))
281: LOG.log(Level.FINE, "undos = " + undos);
282: }
283:
284: }
|