KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > form > codestructure > CodeStructure


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.form.codestructure;
21
22 import java.util.*;
23 import java.lang.reflect.*;
24 import org.netbeans.modules.form.FormJavaSource;
25
26 /**
27  * Class representing code structure of one form. Also manages a pool
28  * of variables for code expressions, and a undo/redo queue.
29  *
30  * @author Tomas Pavek
31  */

32
33 public class CodeStructure {
34
35     public static final CodeExpression[] EMPTY_PARAMS = new CodeExpression[0];
36
37     private static final int VARIABLE_CREATE = 1;
38     private static final int VARIABLE_RENAME = 2;
39     private static final int VARIABLE_RELEASE = 3;
40     private static final int VARIABLE_ATTACH = 4;
41     private static final int VARIABLE_DETACH = 5;
42
43     private static UsingCodeObject globalUsingObject;
44
45     private Map namesToVariables = new HashMap(50);
46     private Map expressionsToVariables = new HashMap(50);
47     private Set externalVariables = null;
48
49     private static int globalDefaultVariableType = CodeVariable.FIELD
50                                                    | CodeVariable.PRIVATE;
51     private int defaultVariableType = -1;
52
53     private boolean undoRedoRecording = false;
54     private int undoRedoMark = 0;
55     private int oldestMark = 0;
56     private int lastUndone = -1;
57     private int undoRedoHardLimit = 10000;
58     private Map undoMap;
59     private Map redoMap;
60     
61     private FormJavaSource javaSource;
62         
63     // --------
64
// constructor
65

66     public CodeStructure(boolean startUndoRedoRecording) {
67         if (startUndoRedoRecording)
68             setUndoRedoRecording(true);
69     }
70
71     public void setFormJavaSource(FormJavaSource formJavaSource) {
72     javaSource = formJavaSource;
73     }
74     
75     // -------
76
// expressions
77

78     /** Creates a new expression based on a constructor. */
79     public CodeExpression createExpression(Constructor ctor,
80                                            CodeExpression[] params)
81     {
82         CodeExpressionOrigin origin =
83                             new CodeSupport.ConstructorOrigin(ctor, params);
84         return new DefaultCodeExpression(this, origin);
85     }
86
87     /** Creates a new expression based on a method. */
88     public CodeExpression createExpression(CodeExpression parent,
89                                            Method method,
90                                            CodeExpression[] params)
91     {
92         CodeExpressionOrigin origin = new CodeSupport.MethodOrigin(
93                                                       parent, method, params);
94         return new DefaultCodeExpression(this, origin);
95     }
96
97     /** Creates a new expression based on a field. */
98     public CodeExpression createExpression(CodeExpression parent, Field field) {
99         CodeExpressionOrigin origin = new CodeSupport.FieldOrigin(parent, field);
100         return new DefaultCodeExpression(this, origin);
101     }
102
103     /** Creates a new expression from based on a value. */
104     public CodeExpression createExpression(Class JavaDoc type,
105                                            Object JavaDoc value,
106                                            String JavaDoc javaInitStr)
107     {
108         return new DefaultCodeExpression(this, new CodeSupport.ValueOrigin(
109                                                     type, value, javaInitStr));
110     }
111
112     /** Creates a new expression of an arbitrary origin. /*/
113     public CodeExpression createExpression(CodeExpressionOrigin origin) {
114         return new DefaultCodeExpression(this, origin);
115     }
116
117     /** Creates an expression representing null value. */
118     public CodeExpression createNullExpression(Class JavaDoc type) {
119         return new DefaultCodeExpression(this, new CodeSupport.ValueOrigin(
120                                                     type, null, "null")); // NOI18N
121
}
122
123     /** Creates an expression with no origin. The origin must be set
124      * explicitly before the expression is used. */

125     public CodeExpression createDefaultExpression() {
126         return new DefaultCodeExpression(this);
127     }
128
129     /** Prevents an expression from being removed automatically from structure
130      * when no more used (by any UsingCodeObject). */

131     public void registerExpression(CodeExpression expression) {
132         if (globalUsingObject == null)
133             globalUsingObject = new GlobalUsingObject();
134
135         expression.addUsingObject(globalUsingObject,
136                                   UsedCodeObject.USING,
137                                   CodeStructure.class);
138     }
139
140     /** Removes an expression from the structure completely. */
141     public static void removeExpression(CodeExpression expression) {
142         unregisterUsedCodeObject(expression);
143         unregisterUsingCodeObject(expression);
144
145         expression.getCodeStructure().removeExpressionFromVariable(expression);
146     }
147
148     /** Filters out expressions whose origin uses given or equal meta object.
149      * Passed expressions are returned in an array. */

150     public static CodeExpression[] filterExpressions(Iterator it,
151                                                      Object JavaDoc originMetaObject)
152     {
153         List list = new ArrayList();
154         while (it.hasNext()) {
155             CodeExpression exp = (CodeExpression) it.next();
156             if (originMetaObject.equals(exp.getOrigin().getMetaObject()))
157                 list.add(exp);
158         }
159         return (CodeExpression[]) list.toArray(new CodeExpression[list.size()]);
160     }
161
162     // --------
163
// statements
164

165     /** Creates a new method statement. */
166     public static CodeStatement createStatement(CodeExpression expression,
167                                                 Method m,
168                                                 CodeExpression[] params)
169     {
170         CodeStatement statement = new CodeSupport.MethodStatement(
171                                                       expression, m, params);
172         registerUsingCodeObject(statement);
173         return statement;
174     }
175
176     /** Creates a new field statement. */
177     public static CodeStatement createStatement(CodeExpression expression,
178                                                 Field f,
179                                                 CodeExpression assignExp)
180     {
181         CodeStatement statement = new CodeSupport.FieldStatement(
182                                                     expression, f, assignExp);
183         registerUsingCodeObject(statement);
184         return statement;
185     }
186
187     /** Removes a statement from the structure completely. */
188     public static void removeStatement(CodeStatement statement) {
189         unregisterUsingCodeObject(statement);
190     }
191
192     /** Removes all statements provided by an Iterator. */
193     public static void removeStatements(Iterator it) {
194         List list = new ArrayList();
195         while (it.hasNext())
196             list.add(it.next());
197
198         for (int i=0, n=list.size(); i < n; i++)
199             unregisterUsingCodeObject((CodeStatement) list.get(i));
200     }
201
202     /** Filters out statements using given or equal meta object. Passed
203      * statements are returned in an array. */

204     public static CodeStatement[] filterStatements(Iterator it,
205                                                    Object JavaDoc metaObject)
206     {
207         List list = new ArrayList();
208         while (it.hasNext()) {
209             CodeStatement statement = (CodeStatement) it.next();
210             if (metaObject.equals(statement.getMetaObject()))
211                 list.add(statement);
212         }
213         return (CodeStatement[]) list.toArray(new CodeStatement[list.size()]);
214     }
215
216     // --------
217
// statements code group
218

219     /** Creates a default group of statements. */
220     public CodeGroup createCodeGroup() {
221         return new CodeSupport.DefaultCodeGroup();
222     }
223
224     // --------
225
// origins
226

227     /** Creates an expression origin from a constructor. */
228     public static CodeExpressionOrigin createOrigin(Constructor ctor,
229                                                     CodeExpression[] params)
230     {
231         return new CodeSupport.ConstructorOrigin(ctor, params);
232     }
233
234     /** Creates an expression origin from a method. */
235     public static CodeExpressionOrigin createOrigin(CodeExpression parent,
236                                                     Method m,
237                                                     CodeExpression[] params)
238     {
239         return new CodeSupport.MethodOrigin(parent, m, params);
240     }
241
242     /** Creates an expression origin from a field. */
243     public static CodeExpressionOrigin createOrigin(CodeExpression parent,
244                                                     Field f)
245     {
246         return new CodeSupport.FieldOrigin(parent, f);
247     }
248
249     /** Creates an expression origin from a value (and provided java string). */
250     public static CodeExpressionOrigin createOrigin(Class JavaDoc type,
251                                                     Object JavaDoc value,
252                                                     String JavaDoc javaStr)
253     {
254         return new CodeSupport.ValueOrigin(type, value, javaStr);
255     }
256
257     // -------
258
// getting to expressions and statements dependent on given expression
259
// (used as their parent or parameter)
260

261     /** Returns an iterator of expressions that are defined by given
262      * expression. These expressions use the given expression as the parent
263      * of origin). */

264     public static Iterator getDefinedExpressionsIterator(CodeExpression exp) {
265         return exp.getUsingObjectsIterator(UsedCodeObject.DEFINED,
266                                            CodeExpression.class);
267     }
268
269     /** Returns an iterator of exppressions that use given expression as
270      * a parameter in their origin. */

271     public static Iterator getUsingExpressionsIterator(CodeExpression exp) {
272         return exp.getUsingObjectsIterator(UsedCodeObject.USING,
273                                            CodeExpression.class);
274     }
275
276     /** Returns an iterator of statements that are defined by given
277      * expression. These statements use the given expression as the parent. */

278     public static Iterator getDefinedStatementsIterator(CodeExpression exp) {
279         return exp.getUsingObjectsIterator(UsedCodeObject.DEFINED,
280                                            CodeStatement.class);
281     }
282
283     /** Returns an iterator of statements that use given expression as
284      * a parameter. */

285     public static Iterator getUsingStatementsIterator(CodeExpression exp) {
286         return exp.getUsingObjectsIterator(UsedCodeObject.USING,
287                                            CodeStatement.class);
288     }
289
290     // -------
291
// managing references between code objects
292

293     // Registers usage of expressions used by a statement.
294
static void registerUsingCodeObject(CodeStatement statement) {
295         CodeExpression parent = statement.getParentExpression();
296         if (parent != null)
297             parent.addUsingObject(
298                 statement, UsedCodeObject.DEFINED, CodeStatement.class);
299
300         CodeExpression[] params = statement.getStatementParameters();
301         if (params != null)
302             for (int i=0; i < params.length; i++)
303                 params[i].addUsingObject(
304                     statement, UsedCodeObject.USING, CodeStatement.class);
305     }
306
307     // Registers usage of expressions used by the origin of an expression.
308
static void registerUsingCodeObject(CodeExpression expression) {
309         CodeExpressionOrigin origin = expression.getOrigin();
310         CodeExpression parent = origin.getParentExpression();
311
312         if (parent != null)
313             parent.addUsingObject(expression,
314                                   UsedCodeObject.DEFINED,
315                                   CodeExpression.class);
316
317         CodeExpression[] params = origin.getCreationParameters();
318         if (params != null)
319             for (int i=0; i < params.length; i++)
320                 params[i].addUsingObject(expression,
321                                          UsedCodeObject.USING,
322                                          CodeExpression.class);
323     }
324
325     // Unregisters usage of all objects used by a using object.
326
static void unregisterUsingCodeObject(UsingCodeObject usingObject) {
327         Iterator it = usingObject.getUsedObjectsIterator();
328         while (it.hasNext()) {
329             UsedCodeObject usedObject = (UsedCodeObject) it.next();
330             if (!usedObject.removeUsingObject(usingObject)) {
331                 // usedObject is no more used, so it should be removed
332
if (usedObject instanceof UsingCodeObject)
333                     unregisterUsingCodeObject((UsingCodeObject)usedObject);
334             }
335         }
336     }
337
338     // Unregisters usage of just one object used by a using object.
339
static void unregisterObjectUsage(UsingCodeObject usingObject,
340                                       UsedCodeObject usedObject)
341     {
342         if (!usedObject.removeUsingObject(usingObject)) {
343             // usedObject is no more used, so it should be removed
344
if (usedObject instanceof UsingCodeObject)
345                 unregisterUsingCodeObject((UsingCodeObject)usedObject);
346         }
347     }
348
349     // This method just notifies all objects using given used object that
350
// the used object is removed from the structure.
351
static void unregisterUsedCodeObject(UsedCodeObject usedObject) {
352         List usingObjects = new ArrayList();
353         Iterator it = usedObject.getUsingObjectsIterator(0, null);
354         while (it.hasNext())
355             usingObjects.add(it.next());
356
357         it = usingObjects.iterator();
358         while (it.hasNext()) {
359             UsingCodeObject usingObject = (UsingCodeObject) it.next();
360             if (!usingObject.usedObjectRemoved(usedObject)) {
361                 // usingObject cannot exist without removed usedObject
362
if (usingObject instanceof UsedCodeObject)
363                     unregisterUsedCodeObject((UsedCodeObject)usingObject);
364                 unregisterUsingCodeObject(usingObject);
365             }
366         }
367     }
368
369     private static class GlobalUsingObject implements UsingCodeObject {
370         public void usageRegistered(UsedCodeObject usedObject) {
371         }
372         public boolean usedObjectRemoved(UsedCodeObject usedObject) {
373             return true;
374         }
375         public UsedCodeObject getDefiningObject() {
376             return null;
377         }
378         public Iterator getUsedObjectsIterator() {
379             return null;
380         }
381     }
382
383     // -------
384
// variables
385

386     /** Creates a new variable. It is empty - with no expression attached. */
387     public CodeVariable createVariable(int type,
388                                        Class JavaDoc declaredType,
389                                        String JavaDoc name)
390     {
391         if (getVariable(name) != null)
392             return null; // variable already exists, cannot create new one
393

394         if (type < 0 || name == null)
395             throw new IllegalArgumentException JavaDoc();
396
397         Variable var = new Variable(type, declaredType, name);
398         namesToVariables.put(name, var);
399     
400         if (undoRedoRecording)
401             logUndoableChange(new VariableChange(VARIABLE_CREATE, var));
402
403         return var;
404     }
405
406     /** Renames variable of name oldName to newName. */
407     public boolean renameVariable(String JavaDoc oldName, String JavaDoc newName) {
408         Variable var = (Variable) namesToVariables.get(oldName);
409         if (var == null || newName == null
410                 || newName.equals(var.getName())
411                 || namesToVariables.get(newName) != null)
412             return false;
413
414         namesToVariables.remove(oldName);
415         var.name = newName;
416         namesToVariables.put(newName, var);
417
418         if (undoRedoRecording) {
419             VariableChange change = new VariableChange(VARIABLE_RENAME, var);
420             change.oldName = oldName;
421             change.newName = newName;
422             logUndoableChange(change);
423         }
424
425         return true;
426     }
427
428     /** Releases variable of given name. */
429     public CodeVariable releaseVariable(String JavaDoc name) {
430         Variable var = (Variable) namesToVariables.remove(name);
431         if (var == null)
432             return null; // there is no such variable
433

434         Map expressionsMap = var.expressionsMap;
435         if (expressionsMap == null)
436             return var;
437
438         Iterator it = expressionsMap.values().iterator();
439         while (it.hasNext())
440             expressionsToVariables.remove(it.next());
441
442         if (undoRedoRecording)
443             logUndoableChange(new VariableChange(VARIABLE_RELEASE, var));
444
445         return var;
446     }
447
448     /** Checks whether given name is already used by some variable. */
449     public boolean isVariableNameReserved(String JavaDoc name) {
450         return namesToVariables.get(name) != null || javaSource.containsField(name, true);
451     }
452
453     /** Creates a new variable and attaches given expression to it. If the
454      * requested name is already in use, then a free name is found. If null
455      * is provided as the name, then expression's short class name is used. */

456     public CodeVariable createVariableForExpression(CodeExpression expression,
457                                                     int type,
458                                                     String JavaDoc name)
459     {
460         if (expression == null)
461             throw new IllegalArgumentException JavaDoc();
462
463         if (getVariable(expression) != null)
464             return null; // variable already exists, cannot create new one
465

466         if (type < 0)
467             throw new IllegalArgumentException JavaDoc();
468
469         if (expressionsToVariables.get(expression) != null)
470             removeExpressionFromVariable(expression);
471
472     name = getFreeVariableName(name, expression.getOrigin().getType());
473
474         Variable var = new Variable(type,
475                                     expression.getOrigin().getType(),
476                                     name);
477         CodeStatement statement = createVariableAssignment(var, expression);
478         var.addCodeExpression(expression, statement);
479
480         namesToVariables.put(name, var);
481         expressionsToVariables.put(expression, var);
482
483         if (undoRedoRecording) {
484             logUndoableChange(new VariableChange(VARIABLE_CREATE, var));
485             VariableChange change = new VariableChange(VARIABLE_ATTACH, var);
486             change.expression = expression;
487             change.statement = statement;
488             logUndoableChange(change);
489         }
490
491         return var;
492     }
493
494     private String JavaDoc getFreeVariableName(String JavaDoc name, Class JavaDoc type) {
495         if (name == null || namesToVariables.get(name) != null) {
496             // variable name not provided or already being used
497
int n = 0;
498             String JavaDoc baseName;
499             if (name != null) { // already used name provided
500
// try to find number suffix
501
int i = name.length();
502                 int exp = 1;
503                 while (--i >= 0) {
504                     char c = name.charAt(i);
505                     if (c >= '0' && c <= '9') {
506                         n += (c - '0') * exp;
507                         exp *= 10;
508                     }
509                     else break;
510                 }
511
512                 baseName = i >= 0 ? name.substring(0, i+1) : name;
513             }
514             else { // derive default name from class type, add "1" as suffix
515
String JavaDoc typeName = type.getName();
516                 int i = typeName.lastIndexOf('$'); // NOI18N
517
if (i < 0) {
518                     i = typeName.lastIndexOf('+'); // NOI18N
519
if (i < 0)
520                         i = typeName.lastIndexOf('.'); // NOI18N
521
}
522                 baseName = Character.toLowerCase(typeName.charAt(i+1))
523                            + typeName.substring(i+2);
524             }
525
526         javaSource.refresh();
527             do { // find a free name
528
name = baseName + (++n);
529             }
530         while ( namesToVariables.get(name) != null || javaSource.containsField(name, false) );
531         }
532     return name;
533     }
534     
535     public String JavaDoc getExternalVariableName(Class JavaDoc type, String JavaDoc suggestedName, boolean register) {
536     String JavaDoc name = getFreeVariableName(suggestedName, type);
537         if (register) {
538             createVariable(CodeVariable.LOCAL, type, name);
539             if(externalVariables == null) {
540                 externalVariables = new HashSet();
541             }
542             externalVariables.add(name);
543         }
544     return name;
545     }
546     
547     public void clearExternalVariableNames() {
548     if(externalVariables!=null) {
549         for (Iterator it = externalVariables.iterator(); it.hasNext();) {
550         releaseVariable((String JavaDoc) it.next());
551         }
552         externalVariables.clear();
553     }
554     }
555
556     /** Attaches an expression to a variable. The variable will be used in the
557      * code instead of the expression. */

558     public void attachExpressionToVariable(CodeExpression expression,
559                                            CodeVariable variable)
560     {
561         if (expression == null)
562             return;
563         // [should we check also expression type ??]
564

565         if (variable.getAssignment(expression) != null)
566             return; // expression already attached
567

568         // check if this variable can have multiple expressions attached
569
int mask = CodeVariable.LOCAL
570                    | CodeVariable.EXPLICIT_DECLARATION;
571         if ((variable.getType() & mask) == CodeVariable.LOCAL
572              && variable.getAttachedExpressions().size() > 0)
573         { // local variable without a standalone declaration cannot be used
574
// for multiple expressions
575
throw new IllegalStateException JavaDoc(
576                       "Standalone local variable declaration required for: " // NOI18N
577
+ variable.getName());
578         }
579
580         Variable prevVar = (Variable) expressionsToVariables.get(expression);
581         if (prevVar != null && prevVar != variable)
582             removeExpressionFromVariable(expression);
583
584         Variable var = (Variable) variable;
585         CodeStatement statement = createVariableAssignment(var, expression);
586
587         var.addCodeExpression(expression, statement);
588         expressionsToVariables.put(expression, var);
589
590         if (undoRedoRecording) {
591             VariableChange change = new VariableChange(VARIABLE_ATTACH, var);
592             change.expression = expression;
593             change.statement = statement;
594             logUndoableChange(change);
595         }
596     }
597
598     /** Releases an expression from using a variable. */
599     public void removeExpressionFromVariable(CodeExpression expression) {
600         if (expression == null)
601             return;
602
603         Variable var = (Variable) expressionsToVariables.remove(expression);
604         if (var == null)
605             return;
606
607         CodeStatement statement = var.removeCodeExpression(expression);
608
609         if (undoRedoRecording) {
610             VariableChange change = new VariableChange(VARIABLE_DETACH, var);
611             change.expression = expression;
612             change.statement = statement;
613             logUndoableChange(change);
614         }
615
616         if (var.expressionsMap.isEmpty()
617                 && (var.getType() & CodeVariable.EXPLICIT_RELEASE) == 0)
618             // release unused variable
619
releaseVariable(var.getName());
620     }
621
622     /** Returns variable of given name. */
623     public CodeVariable getVariable(String JavaDoc name) {
624         return (Variable) namesToVariables.get(name);
625     }
626
627     /** Returns variable of an expression. */
628     public CodeVariable getVariable(CodeExpression expression) {
629         return (Variable) expressionsToVariables.get(expression);
630     }
631
632     /** Returns an iterator of variables of given criterions. */
633     public Iterator getVariablesIterator(int type, int typeMask,
634                                          Class JavaDoc declaredType)
635     {
636         return new VariablesIterator(namesToVariables.values().iterator(), type, typeMask, declaredType);
637     }
638
639     /** Returns all variables in this CodeStructure. */
640     public Collection getAllVariables() {
641         return Collections.unmodifiableCollection(namesToVariables.values());
642     }
643
644     // ---------
645

646     /** WARNING: This method will be removed in full two-way editing
647      * implementation. DO NOT USE! */

648     public static void setGlobalDefaultVariableType(int type) {
649         if (type < 0) {
650             globalDefaultVariableType = CodeVariable.FIELD
651                                         | CodeVariable.PRIVATE;
652         }
653         else {
654             type &= CodeVariable.ALL_MASK;
655             if ((type & CodeVariable.SCOPE_MASK) == CodeVariable.NO_VARIABLE)
656                 type |= CodeVariable.FIELD;
657             int fdMask = CodeVariable.EXPLICIT_DECLARATION | CodeVariable.FINAL;
658             if ((type & fdMask) == fdMask)
659                 type &= ~CodeVariable.EXPLICIT_DECLARATION;
660
661             globalDefaultVariableType = type;
662         }
663     }
664
665     /** WARNING: This method will be removed in full two-way editing
666      * implementation. DO NOT USE! */

667     public void setDefaultVariableType(int type) {
668         if (type < 0) {
669             defaultVariableType = -1; // global default will be used
670
}
671         else {
672             type &= CodeVariable.ALL_MASK;
673             if ((type & CodeVariable.SCOPE_MASK) == CodeVariable.NO_VARIABLE)
674                 type |= CodeVariable.FIELD;
675             int fdMask = CodeVariable.EXPLICIT_DECLARATION | CodeVariable.FINAL;
676             if ((type & fdMask) == fdMask)
677                 type &= ~CodeVariable.EXPLICIT_DECLARATION;
678
679             defaultVariableType = type;
680         }
681     }
682
683     static int getGlobalDefaultVariableType() {
684         return globalDefaultVariableType;
685     }
686
687     int getDefaultVariableType() {
688         return defaultVariableType > -1 ?
689                defaultVariableType : globalDefaultVariableType;
690     }
691
692     // ---------
693

694     protected Map getNamesToVariablesMap() {
695         return namesToVariables;
696     }
697
698     protected Map getExpressionsToVariables() {
699         return expressionsToVariables;
700     }
701
702     private CodeStatement createVariableAssignment(CodeVariable var,
703                                                    CodeExpression expression)
704     {
705         CodeStatement statement =
706             new CodeSupport.AssignVariableStatement(var, expression);
707
708         // important: assignment statement does not register usage of code
709
// expressions (assigned expression, parameters) - so it does not hold
710
// the expressions in the structure
711

712         return statement;
713     }
714
715     // --------
716
// undo/redo processing
717

718     public void setUndoRedoRecording(boolean record) {
719         undoRedoRecording = record;
720         if (record && undoMap == null) {
721             undoMap = new HashMap(500);
722             redoMap = new HashMap(100);
723         }
724     }
725
726     public boolean isUndoRedoRecording() {
727         return undoRedoRecording;
728     }
729
730     void logUndoableChange(CodeStructureChange change) {
731         redoMap.clear();
732         lastUndone = -1;
733
734         if (undoMap.size() == 0)
735             oldestMark = undoRedoMark;
736
737         t("adding undoable change "+undoRedoMark); // NOI18N
738

739         undoMap.put(new Integer JavaDoc(undoRedoMark++), change);
740
741         if (undoMap.size() > undoRedoHardLimit)
742             t("undo/redo hard limit reached: " // NOI18N
743
+undoMap.size()+" > "+undoRedoHardLimit); // NOI18N
744

745         while (undoMap.size() > undoRedoHardLimit) {
746             Object JavaDoc mark = new Integer JavaDoc(oldestMark++);
747             undoMap.remove(mark);
748         }
749     }
750
751     public Object JavaDoc markForUndo() {
752         redoMap.clear();
753
754         t("mark for undo: "+undoRedoMark); // NOI18N
755

756         Object JavaDoc newMark = new Integer JavaDoc(undoRedoMark);
757
758         return newMark;
759     }
760
761     public void releaseUndoableChanges(Object JavaDoc fromMark, Object JavaDoc toMark) {
762         int m1 = ((Integer JavaDoc)fromMark).intValue();
763         int m2 = ((Integer JavaDoc)toMark).intValue();
764
765         t("release marks from " + m1 + " to " + m2); // NOI18N
766

767         while (m1 < m2) {
768             Object JavaDoc m = new Integer JavaDoc(m1);
769             undoMap.remove(m);
770             redoMap.remove(m);
771             m1++;
772         }
773     }
774
775     public boolean undoToMark(Object JavaDoc mark) {
776         int lastMark = ((Integer JavaDoc)mark).intValue();
777         int currentMark = undoRedoMark;
778         if (currentMark <= lastMark)
779             return false; // invalid parameter
780

781         t("undo to mark "+mark); // NOI18N
782

783         if (undoMap.get(mark) == null) {
784             t("mark already dropped from the queue"); // NOI18N
785
return false;
786         }
787
788         boolean undoRedoOn = undoRedoRecording;
789         undoRedoRecording = false;
790
791         while (currentMark > lastMark) {
792             Object JavaDoc key = new Integer JavaDoc(--currentMark);
793             CodeStructureChange change = (CodeStructureChange)
794                                          undoMap.remove(key);
795             if (change != null) {
796                 change.undo();
797                 redoMap.put(key, change);
798                 lastUndone = currentMark;
799
800                 t("undone: "+key); // NOI18N
801
}
802         }
803
804         if (undoRedoOn)
805             undoRedoRecording = true;
806
807         return true;
808     }
809
810     public boolean redoToMark(Object JavaDoc mark) {
811         if (lastUndone < 0)
812             return false;
813         int toMark = ((Integer JavaDoc)mark).intValue();
814         if (lastUndone >= toMark || toMark > undoRedoMark)
815             return false; // invalid parameter
816

817         t("redo to mark "+mark); // NOI18N
818

819         boolean undoRedoOn = undoRedoRecording;
820         undoRedoRecording = false;
821
822         while (lastUndone < toMark) {
823             Object JavaDoc key = new Integer JavaDoc(lastUndone++);
824             CodeStructureChange change = (CodeStructureChange)
825                                          redoMap.remove(key);
826             if (change != null) {
827                 change.redo();
828                 undoMap.put(key, change);
829
830                 t("redone: "+key); // NOI18N
831
}
832         }
833
834         if (undoRedoOn)
835             undoRedoRecording = true;
836
837         return true;
838     }
839
840     // --------
841
// inner classes
842

843     final class Variable implements CodeVariable {
844         private int type;
845         private Class JavaDoc declaredType;
846         private String JavaDoc name;
847         private Map expressionsMap;
848         private CodeStatement declarationStatement;
849
850         Variable(int type, Class JavaDoc declaredType, String JavaDoc name) {
851             if ((type & FINAL) != 0)
852                 type &= ~EXPLICIT_DECLARATION;
853             this.type = type;
854             this.declaredType = declaredType;
855             this.name = name;
856         }
857
858         public int getType() {
859             return (type & DEFAULT_TYPE) != DEFAULT_TYPE ?
860                    type : getDefaultVariableType();
861         }
862
863         public String JavaDoc getName() {
864             return name;
865         }
866
867         public Class JavaDoc getDeclaredType() {
868             return declaredType;
869         }
870
871         public Collection getAttachedExpressions() {
872             return expressionsMap != null ?
873                      Collections.unmodifiableCollection(expressionsMap.keySet()) :
874                      Collections.EMPTY_LIST;
875         }
876
877         public CodeStatement getDeclaration() {
878             if (declarationStatement == null)
879                 declarationStatement =
880                     new CodeSupport.DeclareVariableStatement(this);
881             return declarationStatement;
882         }
883
884         public CodeStatement getAssignment(CodeExpression expression) {
885             return expressionsMap != null ?
886                    (CodeStatement) expressionsMap.get(expression) : null;
887         }
888
889         // -------
890

891         void addCodeExpression(CodeExpression expression,
892                                CodeStatement statement)
893         {
894             if (expressionsMap == null)
895                 expressionsMap = new HashMap();
896             expressionsMap.put(expression, statement);
897         }
898
899         CodeStatement removeCodeExpression(CodeExpression expression) {
900             if (expressionsMap != null)
901                 return (CodeStatement) expressionsMap.remove(expression);
902             return null;
903         }
904     }
905
906     private static final class VariablesIterator implements Iterator {
907         private int type;
908         private int typeMask;
909         private Class JavaDoc declaredType;
910
911         private Iterator subIterator;
912
913         private CodeVariable currentVar;
914
915         public VariablesIterator(Iterator subIterator, int type, int typeMask, Class JavaDoc declaredType) {
916             this.type = type;
917             this.typeMask = typeMask;
918             this.declaredType = declaredType;
919             this.subIterator = subIterator;
920         }
921
922         public boolean hasNext() {
923             if (currentVar != null)
924                 return true;
925
926             while (subIterator.hasNext()) {
927                 CodeVariable var = (CodeVariable) subIterator.next();
928                 if ((type < 0
929                         || (type & typeMask) == (var.getType() & typeMask))
930                     &&
931                     (declaredType == null
932                         || declaredType.equals(var.getDeclaredType())))
933                 {
934                     currentVar = var;
935                     return true;
936                 }
937             }
938
939             return false;
940         }
941
942         public Object JavaDoc next() {
943             if (!hasNext())
944                 throw new NoSuchElementException();
945
946             CodeVariable var = currentVar;
947             currentVar = null;
948             return var;
949         }
950
951         public void remove() {
952             throw new UnsupportedOperationException JavaDoc();
953         }
954     }
955
956     // --------
957

958     private class VariableChange implements CodeStructureChange {
959         private int changeType;
960         private Variable variable;
961         private CodeExpression expression;
962         private CodeStatement statement;
963         private String JavaDoc oldName;
964         private String JavaDoc newName;
965
966         VariableChange(int type, Variable var) {
967             changeType = type;
968             variable = var;
969         }
970
971         public void undo() {
972             switch (changeType) {
973                 case VARIABLE_CREATE:
974                     namesToVariables.remove(variable.name);
975                     break;
976                 case VARIABLE_RENAME:
977                     namesToVariables.remove(newName);
978                     variable.name = oldName;
979                     namesToVariables.put(oldName, variable);
980                     break;
981                 case VARIABLE_RELEASE:
982                     Iterator it = variable.expressionsMap.values().iterator();
983                     while (it.hasNext())
984                         expressionsToVariables.put(it.next(), variable);
985                     namesToVariables.put(variable.name, variable);
986                     break;
987                 case VARIABLE_ATTACH:
988                     expressionsToVariables.remove(expression);
989                     variable.expressionsMap.remove(expression);
990                     break;
991                 case VARIABLE_DETACH:
992                     variable.expressionsMap.put(expression, statement);
993                     expressionsToVariables.put(expression, variable);
994                     break;
995             }
996         }
997
998         public void redo() {
999             switch (changeType) {
1000                case VARIABLE_CREATE:
1001                    namesToVariables.put(variable.name, variable);
1002                    break;
1003                case VARIABLE_RENAME:
1004                    namesToVariables.remove(oldName);
1005                    variable.name = newName;
1006                    namesToVariables.put(newName, variable);
1007                    break;
1008                case VARIABLE_RELEASE:
1009                    namesToVariables.remove(variable.name);
1010                    Iterator it = variable.expressionsMap.values().iterator();
1011                    while (it.hasNext())
1012                        expressionsToVariables.remove(it.next());
1013                    break;
1014                case VARIABLE_ATTACH:
1015                    variable.expressionsMap.put(expression, statement);
1016                    expressionsToVariables.put(expression, variable);
1017                    break;
1018                case VARIABLE_DETACH:
1019                    expressionsToVariables.remove(expression);
1020                    variable.expressionsMap.remove(expression);
1021                    break;
1022            }
1023        }
1024    }
1025    
1026    // ---------------
1027

1028    /** For debugging purposes only. */
1029    static private int traceCount = 0;
1030    /** For debugging purposes only. */
1031    static private final boolean TRACE = false;
1032    /** For debugging purposes only. */
1033    static void t(String JavaDoc str) {
1034        if (TRACE)
1035            if (str != null)
1036                System.out.println("CodeStructure "+(++traceCount)+": "+str); // NOI18N
1037
else
1038                System.out.println(""); // NOI18N
1039
}
1040}
1041
Popular Tags