0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.modules.form.codestructure;
0043:
0044: import java.util.*;
0045: import java.lang.reflect.*;
0046: import org.netbeans.modules.form.FormJavaSource;
0047:
0048: /**
0049: * Class representing code structure of one form. Also manages a pool
0050: * of variables for code expressions, and a undo/redo queue.
0051: *
0052: * @author Tomas Pavek
0053: */
0054:
0055: public class CodeStructure {
0056:
0057: public static final CodeExpression[] EMPTY_PARAMS = new CodeExpression[0];
0058:
0059: private static final int VARIABLE_CREATE = 1;
0060: private static final int VARIABLE_RENAME = 2;
0061: private static final int VARIABLE_RELEASE = 3;
0062: private static final int VARIABLE_ATTACH = 4;
0063: private static final int VARIABLE_DETACH = 5;
0064:
0065: private static UsingCodeObject globalUsingObject;
0066:
0067: private Map<String, Variable> namesToVariables = new HashMap<String, Variable>(
0068: 50);
0069: private Map<Object/*?*/, Variable> expressionsToVariables = new HashMap<Object, Variable>(
0070: 50);
0071: private Set<String> externalVariables = null;
0072:
0073: private int defaultVariableType = -1;
0074:
0075: private boolean undoRedoRecording = false;
0076: private int undoRedoMark = 0;
0077: private int oldestMark = 0;
0078: private int lastUndone = -1;
0079: private int undoRedoHardLimit = 10000;
0080: private Map<Integer, CodeStructureChange> undoMap;
0081: private Map<Integer, CodeStructureChange> redoMap;
0082:
0083: private FormJavaSource javaSource;
0084:
0085: // --------
0086: // constructor
0087:
0088: public CodeStructure(boolean startUndoRedoRecording) {
0089: if (startUndoRedoRecording)
0090: setUndoRedoRecording(true);
0091: }
0092:
0093: public void setFormJavaSource(FormJavaSource formJavaSource) {
0094: javaSource = formJavaSource;
0095: }
0096:
0097: // -------
0098: // expressions
0099:
0100: /** Creates a new expression based on a constructor. */
0101: public CodeExpression createExpression(Constructor ctor,
0102: CodeExpression[] params) {
0103: CodeExpressionOrigin origin = new CodeSupport.ConstructorOrigin(
0104: ctor, params);
0105: return new DefaultCodeExpression(this , origin);
0106: }
0107:
0108: /** Creates a new expression based on a method. */
0109: public CodeExpression createExpression(CodeExpression parent,
0110: Method method, CodeExpression[] params) {
0111: CodeExpressionOrigin origin = new CodeSupport.MethodOrigin(
0112: parent, method, params);
0113: return new DefaultCodeExpression(this , origin);
0114: }
0115:
0116: /** Creates a new expression based on a field. */
0117: public CodeExpression createExpression(CodeExpression parent,
0118: Field field) {
0119: CodeExpressionOrigin origin = new CodeSupport.FieldOrigin(
0120: parent, field);
0121: return new DefaultCodeExpression(this , origin);
0122: }
0123:
0124: /** Creates a new expression from based on a value. */
0125: public CodeExpression createExpression(Class type, Object value,
0126: String javaInitStr) {
0127: return new DefaultCodeExpression(this ,
0128: new CodeSupport.ValueOrigin(type, value, javaInitStr));
0129: }
0130:
0131: /** Creates a new expression of an arbitrary origin. /*/
0132: public CodeExpression createExpression(CodeExpressionOrigin origin) {
0133: return new DefaultCodeExpression(this , origin);
0134: }
0135:
0136: /** Creates an expression representing null value. */
0137: public CodeExpression createNullExpression(Class type) {
0138: return new DefaultCodeExpression(this ,
0139: new CodeSupport.ValueOrigin(type, null, "null")); // NOI18N
0140: }
0141:
0142: /** Creates an expression with no origin. The origin must be set
0143: * explicitly before the expression is used. */
0144: public CodeExpression createDefaultExpression() {
0145: return new DefaultCodeExpression(this );
0146: }
0147:
0148: /** Prevents an expression from being removed automatically from structure
0149: * when no more used (by any UsingCodeObject). */
0150: public void registerExpression(CodeExpression expression) {
0151: if (globalUsingObject == null)
0152: globalUsingObject = new GlobalUsingObject();
0153:
0154: expression.addUsingObject(globalUsingObject,
0155: UsedCodeObject.USING, CodeStructure.class);
0156: }
0157:
0158: /** Removes an expression from the structure completely. */
0159: public static void removeExpression(CodeExpression expression) {
0160: unregisterUsedCodeObject(expression);
0161: unregisterUsingCodeObject(expression);
0162:
0163: expression.getCodeStructure().removeExpressionFromVariable(
0164: expression);
0165: }
0166:
0167: /** Filters out expressions whose origin uses given or equal meta object.
0168: * Passed expressions are returned in an array. */
0169: public static CodeExpression[] filterExpressions(Iterator it,
0170: Object originMetaObject) {
0171: List<CodeExpression> list = new ArrayList<CodeExpression>();
0172: while (it.hasNext()) {
0173: CodeExpression exp = (CodeExpression) it.next();
0174: if (originMetaObject
0175: .equals(exp.getOrigin().getMetaObject()))
0176: list.add(exp);
0177: }
0178: return list.toArray(new CodeExpression[list.size()]);
0179: }
0180:
0181: // --------
0182: // statements
0183:
0184: /** Creates a new method statement. */
0185: public static CodeStatement createStatement(
0186: CodeExpression expression, Method m, CodeExpression[] params) {
0187: CodeStatement statement = new CodeSupport.MethodStatement(
0188: expression, m, params);
0189: registerUsingCodeObject(statement);
0190: return statement;
0191: }
0192:
0193: /** Creates a new field statement. */
0194: public static CodeStatement createStatement(
0195: CodeExpression expression, Field f, CodeExpression assignExp) {
0196: CodeStatement statement = new CodeSupport.FieldStatement(
0197: expression, f, assignExp);
0198: registerUsingCodeObject(statement);
0199: return statement;
0200: }
0201:
0202: /** Removes a statement from the structure completely. */
0203: public static void removeStatement(CodeStatement statement) {
0204: unregisterUsingCodeObject(statement);
0205: }
0206:
0207: /** Removes all statements provided by an Iterator. */
0208: public static void removeStatements(Iterator it) {
0209: List list = new ArrayList();
0210: while (it.hasNext())
0211: list.add(it.next());
0212:
0213: for (int i = 0, n = list.size(); i < n; i++)
0214: unregisterUsingCodeObject((CodeStatement) list.get(i));
0215: }
0216:
0217: /** Filters out statements using given or equal meta object. Passed
0218: * statements are returned in an array. */
0219: public static CodeStatement[] filterStatements(Iterator it,
0220: Object metaObject) {
0221: List<CodeStatement> list = new ArrayList<CodeStatement>();
0222: while (it.hasNext()) {
0223: CodeStatement statement = (CodeStatement) it.next();
0224: if (metaObject.equals(statement.getMetaObject()))
0225: list.add(statement);
0226: }
0227: return list.toArray(new CodeStatement[list.size()]);
0228: }
0229:
0230: // --------
0231: // statements code group
0232:
0233: /** Creates a default group of statements. */
0234: public CodeGroup createCodeGroup() {
0235: return new CodeSupport.DefaultCodeGroup();
0236: }
0237:
0238: // --------
0239: // origins
0240:
0241: /** Creates an expression origin from a constructor. */
0242: public static CodeExpressionOrigin createOrigin(Constructor ctor,
0243: CodeExpression[] params) {
0244: return new CodeSupport.ConstructorOrigin(ctor, params);
0245: }
0246:
0247: /** Creates an expression origin from a method. */
0248: public static CodeExpressionOrigin createOrigin(
0249: CodeExpression parent, Method m, CodeExpression[] params) {
0250: return new CodeSupport.MethodOrigin(parent, m, params);
0251: }
0252:
0253: /** Creates an expression origin from a field. */
0254: public static CodeExpressionOrigin createOrigin(
0255: CodeExpression parent, Field f) {
0256: return new CodeSupport.FieldOrigin(parent, f);
0257: }
0258:
0259: /** Creates an expression origin from a value (and provided java string). */
0260: public static CodeExpressionOrigin createOrigin(Class type,
0261: Object value, String javaStr) {
0262: return new CodeSupport.ValueOrigin(type, value, javaStr);
0263: }
0264:
0265: // -------
0266: // getting to expressions and statements dependent on given expression
0267: // (used as their parent or parameter)
0268:
0269: /** Returns an iterator of expressions that are defined by given
0270: * expression. These expressions use the given expression as the parent
0271: * of origin). */
0272: public static Iterator getDefinedExpressionsIterator(
0273: CodeExpression exp) {
0274: return exp.getUsingObjectsIterator(UsedCodeObject.DEFINED,
0275: CodeExpression.class);
0276: }
0277:
0278: /** Returns an iterator of exppressions that use given expression as
0279: * a parameter in their origin. */
0280: public static Iterator getUsingExpressionsIterator(
0281: CodeExpression exp) {
0282: return exp.getUsingObjectsIterator(UsedCodeObject.USING,
0283: CodeExpression.class);
0284: }
0285:
0286: /** Returns an iterator of statements that are defined by given
0287: * expression. These statements use the given expression as the parent. */
0288: public static Iterator getDefinedStatementsIterator(
0289: CodeExpression exp) {
0290: return exp.getUsingObjectsIterator(UsedCodeObject.DEFINED,
0291: CodeStatement.class);
0292: }
0293:
0294: /** Returns an iterator of statements that use given expression as
0295: * a parameter. */
0296: public static Iterator getUsingStatementsIterator(CodeExpression exp) {
0297: return exp.getUsingObjectsIterator(UsedCodeObject.USING,
0298: CodeStatement.class);
0299: }
0300:
0301: // -------
0302: // managing references between code objects
0303:
0304: // Registers usage of expressions used by a statement.
0305: static void registerUsingCodeObject(CodeStatement statement) {
0306: CodeExpression parent = statement.getParentExpression();
0307: if (parent != null)
0308: parent.addUsingObject(statement, UsedCodeObject.DEFINED,
0309: CodeStatement.class);
0310:
0311: CodeExpression[] params = statement.getStatementParameters();
0312: if (params != null)
0313: for (int i = 0; i < params.length; i++)
0314: params[i].addUsingObject(statement,
0315: UsedCodeObject.USING, CodeStatement.class);
0316: }
0317:
0318: // Registers usage of expressions used by the origin of an expression.
0319: static void registerUsingCodeObject(CodeExpression expression) {
0320: CodeExpressionOrigin origin = expression.getOrigin();
0321: CodeExpression parent = origin.getParentExpression();
0322:
0323: if (parent != null)
0324: parent.addUsingObject(expression, UsedCodeObject.DEFINED,
0325: CodeExpression.class);
0326:
0327: CodeExpression[] params = origin.getCreationParameters();
0328: if (params != null)
0329: for (int i = 0; i < params.length; i++)
0330: params[i].addUsingObject(expression,
0331: UsedCodeObject.USING, CodeExpression.class);
0332: }
0333:
0334: // Unregisters usage of all objects used by a using object.
0335: static void unregisterUsingCodeObject(UsingCodeObject usingObject) {
0336: Iterator it = usingObject.getUsedObjectsIterator();
0337: while (it.hasNext()) {
0338: UsedCodeObject usedObject = (UsedCodeObject) it.next();
0339: if (!usedObject.removeUsingObject(usingObject)) {
0340: // usedObject is no more used, so it should be removed
0341: if (usedObject instanceof UsingCodeObject)
0342: unregisterUsingCodeObject((UsingCodeObject) usedObject);
0343: }
0344: }
0345: }
0346:
0347: // Unregisters usage of just one object used by a using object.
0348: static void unregisterObjectUsage(UsingCodeObject usingObject,
0349: UsedCodeObject usedObject) {
0350: if (!usedObject.removeUsingObject(usingObject)) {
0351: // usedObject is no more used, so it should be removed
0352: if (usedObject instanceof UsingCodeObject)
0353: unregisterUsingCodeObject((UsingCodeObject) usedObject);
0354: }
0355: }
0356:
0357: // This method just notifies all objects using given used object that
0358: // the used object is removed from the structure.
0359: static void unregisterUsedCodeObject(UsedCodeObject usedObject) {
0360: List usingObjects = new ArrayList();
0361: Iterator it = usedObject.getUsingObjectsIterator(0, null);
0362: while (it.hasNext())
0363: usingObjects.add(it.next());
0364:
0365: it = usingObjects.iterator();
0366: while (it.hasNext()) {
0367: UsingCodeObject usingObject = (UsingCodeObject) it.next();
0368: if (!usingObject.usedObjectRemoved(usedObject)) {
0369: // usingObject cannot exist without removed usedObject
0370: if (usingObject instanceof UsedCodeObject)
0371: unregisterUsedCodeObject((UsedCodeObject) usingObject);
0372: unregisterUsingCodeObject(usingObject);
0373: }
0374: }
0375: }
0376:
0377: private static class GlobalUsingObject implements UsingCodeObject {
0378: public void usageRegistered(UsedCodeObject usedObject) {
0379: }
0380:
0381: public boolean usedObjectRemoved(UsedCodeObject usedObject) {
0382: return true;
0383: }
0384:
0385: public UsedCodeObject getDefiningObject() {
0386: return null;
0387: }
0388:
0389: public Iterator getUsedObjectsIterator() {
0390: return null;
0391: }
0392: }
0393:
0394: // -------
0395: // variables
0396:
0397: /** Creates a new variable. It is empty - with no expression attached. */
0398: public CodeVariable createVariable(int type, Class declaredType,
0399: String name) {
0400: if (getVariable(name) != null)
0401: return null; // variable already exists, cannot create new one
0402:
0403: if (type < 0 || name == null)
0404: throw new IllegalArgumentException();
0405:
0406: Variable var = new Variable(type, declaredType, "", name); // NOI18N
0407: namesToVariables.put(name, var);
0408:
0409: if (undoRedoRecording)
0410: logUndoableChange(new VariableChange(VARIABLE_CREATE, var));
0411:
0412: return var;
0413: }
0414:
0415: /** Renames variable of name oldName to newName. */
0416: public boolean renameVariable(String oldName, String newName) {
0417: Variable var = namesToVariables.get(oldName);
0418: if (var == null || newName == null
0419: || newName.equals(var.getName())
0420: || namesToVariables.get(newName) != null)
0421: return false;
0422:
0423: namesToVariables.remove(oldName);
0424: var.name = newName;
0425: namesToVariables.put(newName, var);
0426:
0427: if (undoRedoRecording) {
0428: VariableChange change = new VariableChange(VARIABLE_RENAME,
0429: var);
0430: change.oldName = oldName;
0431: change.newName = newName;
0432: logUndoableChange(change);
0433: }
0434:
0435: return true;
0436: }
0437:
0438: /** Releases variable of given name. */
0439: public CodeVariable releaseVariable(String name) {
0440: Variable var = namesToVariables.remove(name);
0441: if (var == null)
0442: return null; // there is no such variable
0443:
0444: Map expressionsMap = var.expressionsMap;
0445: if (expressionsMap == null)
0446: return var;
0447:
0448: Iterator it = expressionsMap.values().iterator();
0449: while (it.hasNext())
0450: expressionsToVariables.remove(it.next());
0451:
0452: if (undoRedoRecording)
0453: logUndoableChange(new VariableChange(VARIABLE_RELEASE, var));
0454:
0455: return var;
0456: }
0457:
0458: /** Checks whether given name is already used by some variable. */
0459: public boolean isVariableNameReserved(String name) {
0460: return namesToVariables.get(name) != null
0461: || javaSource.containsField(name, true);
0462: }
0463:
0464: public CodeVariable createVariableForExpression(
0465: CodeExpression expression, int type, String name) {
0466: CodeVariable var = (expression == null) ? null : expression
0467: .getVariable();
0468: String typeParameters = (var == null) ? "" : var
0469: .getDeclaredTypeParameters(); // NOI18N
0470: return createVariableForExpression(expression, type,
0471: typeParameters, name);
0472: }
0473:
0474: /** Creates a new variable and attaches given expression to it. If the
0475: * requested name is already in use, then a free name is found. If null
0476: * is provided as the name, then expression's short class name is used. */
0477: public CodeVariable createVariableForExpression(
0478: CodeExpression expression, int type, String typeParameters,
0479: String name) {
0480: if (expression == null)
0481: throw new IllegalArgumentException();
0482:
0483: if (getVariable(expression) != null)
0484: return null; // variable already exists, cannot create new one
0485:
0486: if (type < 0)
0487: throw new IllegalArgumentException();
0488:
0489: if (expressionsToVariables.get(expression) != null)
0490: removeExpressionFromVariable(expression);
0491:
0492: name = getFreeVariableName(name, expression.getOrigin()
0493: .getType());
0494:
0495: Variable var = new Variable(type, expression.getOrigin()
0496: .getType(), typeParameters, name);
0497: CodeStatement statement = createVariableAssignment(var,
0498: expression);
0499: var.addCodeExpression(expression, statement);
0500:
0501: namesToVariables.put(name, var);
0502: expressionsToVariables.put(expression, var);
0503:
0504: if (undoRedoRecording) {
0505: logUndoableChange(new VariableChange(VARIABLE_CREATE, var));
0506: VariableChange change = new VariableChange(VARIABLE_ATTACH,
0507: var);
0508: change.expression = expression;
0509: change.statement = statement;
0510: logUndoableChange(change);
0511: }
0512:
0513: return var;
0514: }
0515:
0516: private String getFreeVariableName(String name, Class type) {
0517: if (name == null || namesToVariables.get(name) != null) {
0518: // variable name not provided or already being used
0519: int n = 0;
0520: String baseName;
0521: if (name != null) { // already used name provided
0522: // try to find number suffix
0523: int i = name.length();
0524: int exp = 1;
0525: while (--i >= 0) {
0526: char c = name.charAt(i);
0527: if (c >= '0' && c <= '9') {
0528: n += (c - '0') * exp;
0529: exp *= 10;
0530: } else
0531: break;
0532: }
0533:
0534: baseName = i >= 0 ? name.substring(0, i + 1) : name;
0535: } else { // derive default name from class type, add "1" as suffix
0536: String typeName = type.getName();
0537: int i = typeName.lastIndexOf('$'); // NOI18N
0538: if (i < 0) {
0539: i = typeName.lastIndexOf('+'); // NOI18N
0540: if (i < 0)
0541: i = typeName.lastIndexOf('.'); // NOI18N
0542: }
0543: baseName = Character
0544: .toLowerCase(typeName.charAt(i + 1))
0545: + typeName.substring(i + 2);
0546: }
0547:
0548: javaSource.refresh();
0549: do { // find a free name
0550: name = baseName + (++n);
0551: } while (namesToVariables.get(name) != null
0552: || javaSource.containsField(name, false));
0553: }
0554: return name;
0555: }
0556:
0557: public String getExternalVariableName(Class type,
0558: String suggestedName, boolean register) {
0559: String name = getFreeVariableName(suggestedName, type);
0560: if (register) {
0561: createVariable(CodeVariable.LOCAL, type, name);
0562: if (externalVariables == null) {
0563: externalVariables = new HashSet<String>();
0564: }
0565: externalVariables.add(name);
0566: }
0567: return name;
0568: }
0569:
0570: public void clearExternalVariableNames() {
0571: if (externalVariables != null) {
0572: for (Iterator it = externalVariables.iterator(); it
0573: .hasNext();) {
0574: releaseVariable((String) it.next());
0575: }
0576: externalVariables.clear();
0577: }
0578: }
0579:
0580: /** Attaches an expression to a variable. The variable will be used in the
0581: * code instead of the expression. */
0582: public void attachExpressionToVariable(CodeExpression expression,
0583: CodeVariable variable) {
0584: if (expression == null)
0585: return;
0586: // [should we check also expression type ??]
0587:
0588: if (variable.getAssignment(expression) != null)
0589: return; // expression already attached
0590:
0591: // check if this variable can have multiple expressions attached
0592: int mask = CodeVariable.LOCAL
0593: | CodeVariable.EXPLICIT_DECLARATION;
0594: if ((variable.getType() & mask) == CodeVariable.LOCAL
0595: && variable.getAttachedExpressions().size() > 0) { // local variable without a standalone declaration cannot be used
0596: // for multiple expressions
0597: throw new IllegalStateException(
0598: "Standalone local variable declaration required for: " // NOI18N
0599: + variable.getName());
0600: }
0601:
0602: Variable prevVar = expressionsToVariables.get(expression);
0603: if (prevVar != null && prevVar != variable)
0604: removeExpressionFromVariable(expression);
0605:
0606: Variable var = (Variable) variable;
0607: CodeStatement statement = createVariableAssignment(var,
0608: expression);
0609:
0610: var.addCodeExpression(expression, statement);
0611: expressionsToVariables.put(expression, var);
0612:
0613: if (undoRedoRecording) {
0614: VariableChange change = new VariableChange(VARIABLE_ATTACH,
0615: var);
0616: change.expression = expression;
0617: change.statement = statement;
0618: logUndoableChange(change);
0619: }
0620: }
0621:
0622: /** Releases an expression from using a variable. */
0623: public void removeExpressionFromVariable(CodeExpression expression) {
0624: if (expression == null)
0625: return;
0626:
0627: Variable var = expressionsToVariables.remove(expression);
0628: if (var == null)
0629: return;
0630:
0631: CodeStatement statement = var.removeCodeExpression(expression);
0632:
0633: if (undoRedoRecording) {
0634: VariableChange change = new VariableChange(VARIABLE_DETACH,
0635: var);
0636: change.expression = expression;
0637: change.statement = statement;
0638: logUndoableChange(change);
0639: }
0640:
0641: if (var.expressionsMap.isEmpty()
0642: && (var.getType() & CodeVariable.EXPLICIT_RELEASE) == 0)
0643: // release unused variable
0644: releaseVariable(var.getName());
0645: }
0646:
0647: /** Returns variable of given name. */
0648: public CodeVariable getVariable(String name) {
0649: return namesToVariables.get(name);
0650: }
0651:
0652: /** Returns variable of an expression. */
0653: public CodeVariable getVariable(CodeExpression expression) {
0654: return expressionsToVariables.get(expression);
0655: }
0656:
0657: /** Returns an iterator of variables of given criterions. */
0658: public Iterator getVariablesIterator(int type, int typeMask,
0659: Class declaredType) {
0660: return new VariablesIterator(namesToVariables.values()
0661: .iterator(), type, typeMask, declaredType);
0662: }
0663:
0664: /** Returns all variables in this CodeStructure. */
0665: public Collection getAllVariables() {
0666: return Collections.unmodifiableCollection(namesToVariables
0667: .values());
0668: }
0669:
0670: // ---------
0671:
0672: /** WARNING: This method will be removed in full two-way editing
0673: * implementation. DO NOT USE! */
0674: public void setDefaultVariableType(int type) {
0675: if (type < 0) {
0676: defaultVariableType = -1; // global default will be used
0677: } else {
0678: type &= CodeVariable.ALL_MASK;
0679: if ((type & CodeVariable.SCOPE_MASK) == CodeVariable.NO_VARIABLE)
0680: type |= CodeVariable.FIELD;
0681: int fdMask = CodeVariable.EXPLICIT_DECLARATION
0682: | CodeVariable.FINAL;
0683: if ((type & fdMask) == fdMask)
0684: type &= ~CodeVariable.EXPLICIT_DECLARATION;
0685:
0686: defaultVariableType = type;
0687: }
0688: }
0689:
0690: int getDefaultVariableType() {
0691: return defaultVariableType > -1 ? defaultVariableType
0692: : CodeVariable.FIELD | CodeVariable.PRIVATE;
0693: }
0694:
0695: // ---------
0696:
0697: protected Map getNamesToVariablesMap() {
0698: return namesToVariables;
0699: }
0700:
0701: protected Map getExpressionsToVariables() {
0702: return expressionsToVariables;
0703: }
0704:
0705: private CodeStatement createVariableAssignment(CodeVariable var,
0706: CodeExpression expression) {
0707: CodeStatement statement = new CodeSupport.AssignVariableStatement(
0708: var, expression);
0709:
0710: // important: assignment statement does not register usage of code
0711: // expressions (assigned expression, parameters) - so it does not hold
0712: // the expressions in the structure
0713:
0714: return statement;
0715: }
0716:
0717: // --------
0718: // undo/redo processing
0719:
0720: public void setUndoRedoRecording(boolean record) {
0721: undoRedoRecording = record;
0722: if (record && undoMap == null) {
0723: undoMap = new HashMap<Integer, CodeStructureChange>(500);
0724: redoMap = new HashMap<Integer, CodeStructureChange>(100);
0725: }
0726: }
0727:
0728: public boolean isUndoRedoRecording() {
0729: return undoRedoRecording;
0730: }
0731:
0732: void logUndoableChange(CodeStructureChange change) {
0733: redoMap.clear();
0734: lastUndone = -1;
0735:
0736: if (undoMap.size() == 0)
0737: oldestMark = undoRedoMark;
0738:
0739: t("adding undoable change " + undoRedoMark); // NOI18N
0740:
0741: undoMap.put(new Integer(undoRedoMark++), change);
0742:
0743: if (undoMap.size() > undoRedoHardLimit)
0744: t("undo/redo hard limit reached: " // NOI18N
0745: + undoMap.size() + " > " + undoRedoHardLimit); // NOI18N
0746:
0747: while (undoMap.size() > undoRedoHardLimit) {
0748: Object mark = new Integer(oldestMark++);
0749: undoMap.remove(mark);
0750: }
0751: }
0752:
0753: public Object markForUndo() {
0754: redoMap.clear();
0755:
0756: t("mark for undo: " + undoRedoMark); // NOI18N
0757:
0758: Object newMark = new Integer(undoRedoMark);
0759:
0760: return newMark;
0761: }
0762:
0763: public void releaseUndoableChanges(Object fromMark, Object toMark) {
0764: int m1 = ((Integer) fromMark).intValue();
0765: int m2 = ((Integer) toMark).intValue();
0766:
0767: t("release marks from " + m1 + " to " + m2); // NOI18N
0768:
0769: while (m1 < m2) {
0770: Object m = new Integer(m1);
0771: undoMap.remove(m);
0772: redoMap.remove(m);
0773: m1++;
0774: }
0775: }
0776:
0777: public boolean undoToMark(Object mark) {
0778: int lastMark = ((Integer) mark).intValue();
0779: int currentMark = undoRedoMark;
0780: if (currentMark <= lastMark)
0781: return false; // invalid parameter
0782:
0783: t("undo to mark " + mark); // NOI18N
0784:
0785: if (undoMap.get(mark) == null) {
0786: t("mark already dropped from the queue"); // NOI18N
0787: return false;
0788: }
0789:
0790: boolean undoRedoOn = undoRedoRecording;
0791: undoRedoRecording = false;
0792:
0793: while (currentMark > lastMark) {
0794: Integer key = new Integer(--currentMark);
0795: CodeStructureChange change = undoMap.remove(key);
0796: if (change != null) {
0797: change.undo();
0798: redoMap.put(key, change);
0799: lastUndone = currentMark;
0800:
0801: t("undone: " + key); // NOI18N
0802: }
0803: }
0804:
0805: if (undoRedoOn)
0806: undoRedoRecording = true;
0807:
0808: return true;
0809: }
0810:
0811: public boolean redoToMark(Object mark) {
0812: if (lastUndone < 0)
0813: return false;
0814: int toMark = ((Integer) mark).intValue();
0815: if (lastUndone >= toMark || toMark > undoRedoMark)
0816: return false; // invalid parameter
0817:
0818: t("redo to mark " + mark); // NOI18N
0819:
0820: boolean undoRedoOn = undoRedoRecording;
0821: undoRedoRecording = false;
0822:
0823: while (lastUndone < toMark) {
0824: Integer key = new Integer(lastUndone++);
0825: CodeStructureChange change = redoMap.remove(key);
0826: if (change != null) {
0827: change.redo();
0828: undoMap.put(key, change);
0829:
0830: t("redone: " + key); // NOI18N
0831: }
0832: }
0833:
0834: if (undoRedoOn)
0835: undoRedoRecording = true;
0836:
0837: return true;
0838: }
0839:
0840: // --------
0841: // inner classes
0842:
0843: final class Variable implements CodeVariable {
0844: private int type;
0845: private Class declaredType;
0846: private String declaredTypeParameters;
0847: private String name;
0848: private Map<CodeExpression, CodeStatement> expressionsMap;
0849: private CodeStatement declarationStatement;
0850:
0851: Variable(int type, Class declaredType,
0852: String declaredTypeParameters, String name) {
0853: if ((type & FINAL) != 0)
0854: type &= ~EXPLICIT_DECLARATION;
0855: this .type = type;
0856: this .declaredType = declaredType;
0857: this .declaredTypeParameters = declaredTypeParameters;
0858: this .name = name;
0859: }
0860:
0861: public int getType() {
0862: return (type & DEFAULT_TYPE) != DEFAULT_TYPE ? type
0863: : getDefaultVariableType();
0864: }
0865:
0866: public String getName() {
0867: return name;
0868: }
0869:
0870: public Class getDeclaredType() {
0871: return declaredType;
0872: }
0873:
0874: public String getDeclaredTypeParameters() {
0875: return declaredTypeParameters;
0876: }
0877:
0878: public Collection getAttachedExpressions() {
0879: return expressionsMap != null ? Collections
0880: .unmodifiableCollection(expressionsMap.keySet())
0881: : Collections.EMPTY_LIST;
0882: }
0883:
0884: public CodeStatement getDeclaration() {
0885: if (declarationStatement == null)
0886: declarationStatement = new CodeSupport.DeclareVariableStatement(
0887: this );
0888: return declarationStatement;
0889: }
0890:
0891: public CodeStatement getAssignment(CodeExpression expression) {
0892: return expressionsMap != null ? expressionsMap
0893: .get(expression) : null;
0894: }
0895:
0896: // -------
0897:
0898: void addCodeExpression(CodeExpression expression,
0899: CodeStatement statement) {
0900: if (expressionsMap == null)
0901: expressionsMap = new HashMap<CodeExpression, CodeStatement>();
0902: expressionsMap.put(expression, statement);
0903: }
0904:
0905: CodeStatement removeCodeExpression(CodeExpression expression) {
0906: if (expressionsMap != null)
0907: return expressionsMap.remove(expression);
0908: return null;
0909: }
0910: }
0911:
0912: private static final class VariablesIterator implements Iterator {
0913: private int type;
0914: private int typeMask;
0915: private Class declaredType;
0916:
0917: private Iterator subIterator;
0918:
0919: private CodeVariable currentVar;
0920:
0921: public VariablesIterator(Iterator subIterator, int type,
0922: int typeMask, Class declaredType) {
0923: this .type = type;
0924: this .typeMask = typeMask;
0925: this .declaredType = declaredType;
0926: this .subIterator = subIterator;
0927: }
0928:
0929: public boolean hasNext() {
0930: if (currentVar != null)
0931: return true;
0932:
0933: while (subIterator.hasNext()) {
0934: CodeVariable var = (CodeVariable) subIterator.next();
0935: if ((type < 0 || (type & typeMask) == (var.getType() & typeMask))
0936: && (declaredType == null || declaredType
0937: .equals(var.getDeclaredType()))) {
0938: currentVar = var;
0939: return true;
0940: }
0941: }
0942:
0943: return false;
0944: }
0945:
0946: public Object next() {
0947: if (!hasNext())
0948: throw new NoSuchElementException();
0949:
0950: CodeVariable var = currentVar;
0951: currentVar = null;
0952: return var;
0953: }
0954:
0955: public void remove() {
0956: throw new UnsupportedOperationException();
0957: }
0958: }
0959:
0960: // --------
0961:
0962: private class VariableChange implements CodeStructureChange {
0963: private int changeType;
0964: private Variable variable;
0965: private CodeExpression expression;
0966: private CodeStatement statement;
0967: private String oldName;
0968: private String newName;
0969:
0970: VariableChange(int type, Variable var) {
0971: changeType = type;
0972: variable = var;
0973: }
0974:
0975: public void undo() {
0976: switch (changeType) {
0977: case VARIABLE_CREATE:
0978: namesToVariables.remove(variable.name);
0979: break;
0980: case VARIABLE_RENAME:
0981: namesToVariables.remove(newName);
0982: variable.name = oldName;
0983: namesToVariables.put(oldName, variable);
0984: break;
0985: case VARIABLE_RELEASE:
0986: Iterator<CodeStatement> it = variable.expressionsMap
0987: .values().iterator();
0988: while (it.hasNext())
0989: expressionsToVariables.put(it.next(), variable);
0990: namesToVariables.put(variable.name, variable);
0991: break;
0992: case VARIABLE_ATTACH:
0993: expressionsToVariables.remove(expression);
0994: variable.expressionsMap.remove(expression);
0995: break;
0996: case VARIABLE_DETACH:
0997: variable.expressionsMap.put(expression, statement);
0998: expressionsToVariables.put(expression, variable);
0999: break;
1000: }
1001: }
1002:
1003: public void redo() {
1004: switch (changeType) {
1005: case VARIABLE_CREATE:
1006: namesToVariables.put(variable.name, variable);
1007: break;
1008: case VARIABLE_RENAME:
1009: namesToVariables.remove(oldName);
1010: variable.name = newName;
1011: namesToVariables.put(newName, variable);
1012: break;
1013: case VARIABLE_RELEASE:
1014: namesToVariables.remove(variable.name);
1015: Iterator it = variable.expressionsMap.values()
1016: .iterator();
1017: while (it.hasNext())
1018: expressionsToVariables.remove(it.next());
1019: break;
1020: case VARIABLE_ATTACH:
1021: variable.expressionsMap.put(expression, statement);
1022: expressionsToVariables.put(expression, variable);
1023: break;
1024: case VARIABLE_DETACH:
1025: expressionsToVariables.remove(expression);
1026: variable.expressionsMap.remove(expression);
1027: break;
1028: }
1029: }
1030: }
1031:
1032: // ---------------
1033:
1034: /** For debugging purposes only. */
1035: static private int traceCount = 0;
1036: /** For debugging purposes only. */
1037: static private final boolean TRACE = false;
1038:
1039: /** For debugging purposes only. */
1040: static void t(String str) {
1041: if (TRACE)
1042: if (str != null)
1043: System.out.println("CodeStructure " + (++traceCount)
1044: + ": " + str); // NOI18N
1045: else
1046: System.out.println(""); // NOI18N
1047: }
1048: }
|