alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.netbeans.modules.form.codestructure;

import java.util.*;
import java.lang.reflect.*;

/**
 * Class representing code structure of one form. Also manages a pool
 * of variables for code expressions, and a undo/redo queue.
 *
 * @author Tomas Pavek
 */

public class CodeStructure {

    public static final CodeExpression[] EMPTY_PARAMS = new CodeExpression[0];

    private static final int VARIABLE_CREATE = 1;
    private static final int VARIABLE_RENAME = 2;
    private static final int VARIABLE_RELEASE = 3;
    private static final int VARIABLE_ATTACH = 4;
    private static final int VARIABLE_DETACH = 5;

    private static UsingCodeObject globalUsingObject;

    private Map namesToVariables = new HashMap(50);
    private Map expressionsToVariables = new HashMap(50);

    private static int globalDefaultVariableType = CodeVariable.FIELD
                                                   | CodeVariable.PRIVATE;
    private int defaultVariableType = -1;

    private boolean undoRedoRecording = false;
    private int undoRedoMark = 0;
    private int oldestMark = 0;
    private int undoRedoHardLimit = 10000;
    private Map undoMap;
    private Map redoMap;
    private Set providedMarksUndo;
    private Set providedMarksRedo;

    // --------
    // constructor

    public CodeStructure(boolean startUndoRedoRecording) {
        if (startUndoRedoRecording)
            setUndoRedoRecording(true);
    }

    // -------
    // expressions

    /** Creates a new expression based on a constructor. */
    public CodeExpression createExpression(Constructor ctor,
                                           CodeExpression[] params)
    {
        CodeExpressionOrigin origin =
                            new CodeSupport.ConstructorOrigin(ctor, params);
        return new DefaultCodeExpression(this, origin);
    }

    /** Creates a new expression based on a method. */
    public CodeExpression createExpression(CodeExpression parent,
                                           Method method,
                                           CodeExpression[] params)
    {
        CodeExpressionOrigin origin = new CodeSupport.MethodOrigin(
                                                      parent, method, params);
        return new DefaultCodeExpression(this, origin);
    }

    /** Creates a new expression based on a field. */
    public CodeExpression createExpression(CodeExpression parent, Field field) {
        CodeExpressionOrigin origin = new CodeSupport.FieldOrigin(parent, field);
        return new DefaultCodeExpression(this, origin);
    }

    /** Creates a new expression from based on a value. */
    public CodeExpression createExpression(Class type,
                                           Object value,
                                           String javaInitStr)
    {
        return new DefaultCodeExpression(this, new CodeSupport.ValueOrigin(
                                                    type, value, javaInitStr));
    }

    /** Creates a new expression of an arbitrary origin. /*/
    public CodeExpression createExpression(CodeExpressionOrigin origin) {
        return new DefaultCodeExpression(this, origin);
    }

    /** Creates an expression representing null value. */
    public CodeExpression createNullExpression(Class type) {
        return new DefaultCodeExpression(this, new CodeSupport.ValueOrigin(
                                                    type, null, "null")); // NOI18N
    }

    /** Creates an expression with no origin. The origin must be set
     * explicitly before the expression is used. */
    public CodeExpression createDefaultExpression() {
        return new DefaultCodeExpression(this);
    }

    /** Prevents an expression from being removed automatically from structure
     * when no more used (by any UsingCodeObject). */
    public void registerExpression(CodeExpression expression) {
        if (globalUsingObject == null)
            globalUsingObject = new GlobalUsingObject();

        expression.addUsingObject(globalUsingObject,
                                  UsedCodeObject.USING,
                                  CodeStructure.class);
    }

    /** Removes an expression from the structure completely. */
    public static void removeExpression(CodeExpression expression) {
        unregisterUsedCodeObject(expression);
        unregisterUsingCodeObject(expression);

        expression.getCodeStructure().removeExpressionFromVariable(expression);
    }

    /** Filters out expressions whose origin uses given or equal meta object.
     * Passed expressions are returned in an array. */
    public static CodeExpression[] filterExpressions(Iterator it,
                                                     Object originMetaObject)
    {
        List list = new ArrayList();
        while (it.hasNext()) {
            CodeExpression exp = (CodeExpression) it.next();
            if (originMetaObject.equals(exp.getOrigin().getMetaObject()))
                list.add(exp);
        }
        return (CodeExpression[]) list.toArray(new CodeExpression[list.size()]);
    }

    // --------
    // statements

    /** Creates a new method statement. */
    public static CodeStatement createStatement(CodeExpression expression,
                                                Method m,
                                                CodeExpression[] params)
    {
        CodeStatement statement = new CodeSupport.MethodStatement(
                                                      expression, m, params);
        registerUsingCodeObject(statement);
        return statement;
    }

    /** Creates a new field statement. */
    public static CodeStatement createStatement(CodeExpression expression,
                                                Field f,
                                                CodeExpression assignExp)
    {
        CodeStatement statement = new CodeSupport.FieldStatement(
                                                    expression, f, assignExp);
        registerUsingCodeObject(statement);
        return statement;
    }

    /** Removes a statement from the structure completely. */
    public static void removeStatement(CodeStatement statement) {
        unregisterUsingCodeObject(statement);
    }

    /** Removes all statements provided by an Iterator. */
    public static void removeStatements(Iterator it) {
        List list = new ArrayList();
        while (it.hasNext())
            list.add(it.next());

        for (int i=0, n=list.size(); i < n; i++)
            unregisterUsingCodeObject((CodeStatement) list.get(i));
    }

    /** Filters out statements using given or equal meta object. Passed
     * statements are returned in an array. */
    public static CodeStatement[] filterStatements(Iterator it,
                                                   Object metaObject)
    {
        List list = new ArrayList();
        while (it.hasNext()) {
            CodeStatement statement = (CodeStatement) it.next();
            if (metaObject.equals(statement.getMetaObject()))
                list.add(statement);
        }
        return (CodeStatement[]) list.toArray(new CodeStatement[list.size()]);
    }

    // --------
    // statements code group

    /** Creates a default group of statements. */
    public CodeGroup createCodeGroup() {
        return new CodeSupport.DefaultCodeGroup();
    }

    // --------
    // origins

    /** Creates an expression origin from a constructor. */
    public static CodeExpressionOrigin createOrigin(Constructor ctor,
                                                    CodeExpression[] params)
    {
        return new CodeSupport.ConstructorOrigin(ctor, params);
    }

    /** Creates an expression origin from a method. */
    public static CodeExpressionOrigin createOrigin(CodeExpression parent,
                                                    Method m,
                                                    CodeExpression[] params)
    {
        return new CodeSupport.MethodOrigin(parent, m, params);
    }

    /** Creates an expression origin from a field. */
    public static CodeExpressionOrigin createOrigin(CodeExpression parent,
                                                    Field f)
    {
        return new CodeSupport.FieldOrigin(parent, f);
    }

    /** Creates an expression origin from a value (and provided java string). */
    public static CodeExpressionOrigin createOrigin(Class type,
                                                    Object value,
                                                    String javaStr)
    {
        return new CodeSupport.ValueOrigin(type, value, javaStr);
    }

    // -------
    // getting to expressions and statements dependent on given expression
    // (used as their parent or parameter)

    /** Returns an iterator of expressions that are defined by given
     * expression. These expressions use the given expression as the parent
     * of origin). */
    public static Iterator getDefinedExpressionsIterator(CodeExpression exp) {
        return exp.getUsingObjectsIterator(UsedCodeObject.DEFINED,
                                           CodeExpression.class);
    }

    /** Returns an iterator of exppressions that use given expression as
     * a parameter in their origin. */
    public static Iterator getUsingExpressionsIterator(CodeExpression exp) {
        return exp.getUsingObjectsIterator(UsedCodeObject.USING,
                                           CodeExpression.class);
    }

    /** Returns an iterator of statements that are defined by given
     * expression. These statements use the given expression as the parent. */
    public static Iterator getDefinedStatementsIterator(CodeExpression exp) {
        return exp.getUsingObjectsIterator(UsedCodeObject.DEFINED,
                                           CodeStatement.class);
    }

    /** Returns an iterator of statements that use given expression as
     * a parameter. */
    public static Iterator getUsingStatementsIterator(CodeExpression exp) {
        return exp.getUsingObjectsIterator(UsedCodeObject.USING,
                                           CodeStatement.class);
    }

    // -------
    // managing references between code objects

    // Registers usage of expressions used by a statement.
    static void registerUsingCodeObject(CodeStatement statement) {
        CodeExpression parent = statement.getParentExpression();
        if (parent != null)
            parent.addUsingObject(
                statement, UsedCodeObject.DEFINED, CodeStatement.class);

        CodeExpression[] params = statement.getStatementParameters();
        if (params != null)
            for (int i=0; i < params.length; i++)
                params[i].addUsingObject(
                    statement, UsedCodeObject.USING, CodeStatement.class);
    }

    // Registers usage of expressions used by the origin of an expression.
    static void registerUsingCodeObject(CodeExpression expression) {
        CodeExpressionOrigin origin = expression.getOrigin();
        CodeExpression parent = origin.getParentExpression();

        if (parent != null)
            parent.addUsingObject(expression,
                                  UsedCodeObject.DEFINED,
                                  CodeExpression.class);

        CodeExpression[] params = origin.getCreationParameters();
        if (params != null)
            for (int i=0; i < params.length; i++)
                params[i].addUsingObject(expression,
                                         UsedCodeObject.USING,
                                         CodeExpression.class);
    }

    // Unregisters usage of all objects used by a using object.
    static void unregisterUsingCodeObject(UsingCodeObject usingObject) {
        Iterator it = usingObject.getUsedObjectsIterator();
        while (it.hasNext()) {
            UsedCodeObject usedObject = (UsedCodeObject) it.next();
            if (!usedObject.removeUsingObject(usingObject)) {
                // usedObject is no more used, so it should be removed
                if (usedObject instanceof UsingCodeObject)
                    unregisterUsingCodeObject((UsingCodeObject)usedObject);
            }
        }
    }

    // Unregisters usage of just one object used by a using object.
    static void unregisterObjectUsage(UsingCodeObject usingObject,
                                      UsedCodeObject usedObject)
    {
        if (!usedObject.removeUsingObject(usingObject)) {
            // usedObject is no more used, so it should be removed
            if (usedObject instanceof UsingCodeObject)
                unregisterUsingCodeObject((UsingCodeObject)usedObject);
        }
    }

    // This method just notifies all objects using given used object that
    // the used object is removed from the structure.
    static void unregisterUsedCodeObject(UsedCodeObject usedObject) {
        List usingObjects = new ArrayList();
        Iterator it = usedObject.getUsingObjectsIterator(0, null);
        while (it.hasNext())
            usingObjects.add(it.next());

        it = usingObjects.iterator();
        while (it.hasNext()) {
            UsingCodeObject usingObject = (UsingCodeObject) it.next();
            if (!usingObject.usedObjectRemoved(usedObject)) {
                // usingObject cannot exist without removed usedObject
                if (usingObject instanceof UsedCodeObject)
                    unregisterUsedCodeObject((UsedCodeObject)usingObject);
                unregisterUsingCodeObject(usingObject);
            }
        }
    }

    private static class GlobalUsingObject implements UsingCodeObject {
        public void usageRegistered(UsedCodeObject usedObject) {
        }
        public boolean usedObjectRemoved(UsedCodeObject usedObject) {
            return true;
        }
        public UsedCodeObject getDefiningObject() {
            return null;
        }
        public Iterator getUsedObjectsIterator() {
            return null;
        }
    }

    // -------
    // variables

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

        if (type < 0 || name == null)
            throw new IllegalArgumentException();

        Variable var = new Variable(type, declaredType, name);
        namesToVariables.put(name, var);

        if (undoRedoRecording)
            logUndoableChange(new VariableChange(VARIABLE_CREATE, var));

        return var;
    }

    /** Renames variable of name oldName to newName. */
    public boolean renameVariable(String oldName, String newName) {
        Variable var = (Variable) namesToVariables.get(oldName);
        if (var == null || newName == null
                || newName.equals(var.getName())
                || namesToVariables.get(newName) != null)
            return false;

        namesToVariables.remove(oldName);
        var.name = newName;
        namesToVariables.put(newName, var);

        if (undoRedoRecording) {
            VariableChange change = new VariableChange(VARIABLE_RENAME, var);
            change.oldName = oldName;
            change.newName = newName;
            logUndoableChange(change);
        }

        return true;
    }

    /** Releases variable of given name. */
    public CodeVariable releaseVariable(String name) {
        Variable var = (Variable) namesToVariables.remove(name);
        if (var == null)
            return null; // there is no such variable

        Map expressionsMap = var.expressionsMap;
        if (expressionsMap == null)
            return var;

        Iterator it = expressionsMap.values().iterator();
        while (it.hasNext())
            expressionsToVariables.remove(it.next());

        if (undoRedoRecording)
            logUndoableChange(new VariableChange(VARIABLE_RELEASE, var));

        return var;
    }

    /** Checks whether given name is already used by some variable. */
    public boolean isVariableNameReserved(String name) {
        return namesToVariables.get(name) != null;
    }

    /** Creates a new variable and attaches given expression to it. If the
     * requested name is already in use, then a free name is found. If null
     * is provided as the name, then expression's short class name is used. */
    public CodeVariable createVariableForExpression(CodeExpression expression,
                                                    int type,
                                                    String name)
    {
        if (expression == null)
            throw new IllegalArgumentException();

        if (getVariable(expression) != null)
            return null; // variable already exists, cannot create new one

        if (type < 0)
            throw new IllegalArgumentException();

        if (expressionsToVariables.get(expression) != null)
            removeExpressionFromVariable(expression);

        if (name == null || namesToVariables.get(name) != null) {
            // variable name not provided or already being used
            int n = 0;
            String baseName;
            if (name != null) { // already used name provided
                // try to find number suffix
                int i = name.length();
                int exp = 1;
                while (--i >= 0) {
                    char c = name.charAt(i);
                    if (c >= '0' && c <= '9') {
                        n += (c - '0') * exp;
                        exp *= 10;
                    }
                    else break;
                }

                baseName = i >= 0 ? name.substring(0, i+1) : name;
            }
            else { // derive default name from class type, add "1" as suffix
                String typeName = expression.getOrigin().getType().getName();
                int i = typeName.lastIndexOf('$'); // NOI18N
                if (i < 0) {
                    i = typeName.lastIndexOf('+'); // NOI18N
                    if (i < 0)
                        i = typeName.lastIndexOf('.'); // NOI18N
                }
                baseName = Character.toLowerCase(typeName.charAt(i+1))
                           + typeName.substring(i+2);
            }

            do { // find a free name
                name = baseName + (++n);
            }
            while (namesToVariables.get(name) != null);
        }

        Variable var = new Variable(type,
                                    expression.getOrigin().getType(),
                                    name);
        CodeStatement statement = createVariableAssignment(var, expression);
        var.addCodeExpression(expression, statement);

        namesToVariables.put(name, var);
        expressionsToVariables.put(expression, var);

        if (undoRedoRecording) {
            logUndoableChange(new VariableChange(VARIABLE_CREATE, var));
            VariableChange change = new VariableChange(VARIABLE_ATTACH, var);
            change.expression = expression;
            change.statement = statement;
            logUndoableChange(change);
        }

        return var;
    }

    /** Attaches an expression to a variable. The variable will be used in the
     * code instead of the expression. */
    public void attachExpressionToVariable(CodeExpression expression,
                                           CodeVariable variable)
    {
        if (expression == null)
            return;
        // [should we check also expression type ??]

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

        // check if this variable can have multiple expressions attached
        int mask = CodeVariable.LOCAL
                   | CodeVariable.EXPLICIT_DECLARATION;
        if ((variable.getType() & mask) == CodeVariable.LOCAL
             && variable.getAttachedExpressions().size() > 0)
        {   // local variable without a standalone declaration cannot be used
            // for multiple expressions
            throw new IllegalStateException(
                      "Standalone local variable declaration required for: " // NOI18N
                      + variable.getName());
        }

        Variable prevVar = (Variable) expressionsToVariables.get(expression);
        if (prevVar != null && prevVar != variable)
            removeExpressionFromVariable(expression);

        Variable var = (Variable) variable;
        CodeStatement statement = createVariableAssignment(var, expression);

        var.addCodeExpression(expression, statement);
        expressionsToVariables.put(expression, var);

        if (undoRedoRecording) {
            VariableChange change = new VariableChange(VARIABLE_ATTACH, var);
            change.expression = expression;
            change.statement = statement;
            logUndoableChange(change);
        }
    }

    /** Releases an expression from using a variable. */
    public void removeExpressionFromVariable(CodeExpression expression) {
        if (expression == null)
            return;

        Variable var = (Variable) expressionsToVariables.remove(expression);
        if (var == null)
            return;

        CodeStatement statement = var.removeCodeExpression(expression);

        if (undoRedoRecording) {
            VariableChange change = new VariableChange(VARIABLE_DETACH, var);
            change.expression = expression;
            change.statement = statement;
            logUndoableChange(change);
        }

        if (var.expressionsMap.isEmpty() 
                && (var.getType() & CodeVariable.EXPLICIT_RELEASE) == 0)
            // release unused variable
            releaseVariable(var.getName());
    }

    /** Returns variable of given name. */
    public CodeVariable getVariable(String name) {
        return (Variable) namesToVariables.get(name);
    }

    /** Returns variable of an expression. */
    public CodeVariable getVariable(CodeExpression expression) {
        return (Variable) expressionsToVariables.get(expression);
    }

    /** Returns an iterator of variables of given criterions. */
    public Iterator getVariablesIterator(int type, int typeMask,
                                         Class declaredType)
    {
        return new VariablesIterator(type, typeMask, declaredType);
    }

    /** Returns all variables in this CodeStructure. */
    public Collection getAllVariables() {
        return Collections.unmodifiableCollection(namesToVariables.values());
    }

    // ---------

    /** WARNING: This method will be removed in full two-way editing
     *           implementation. DO NOT USE! */
    public static void setGlobalDefaultVariableType(int type) {
        if (type < 0) {
            globalDefaultVariableType = CodeVariable.FIELD
                                        | CodeVariable.PRIVATE;
        }
        else {
            type &= CodeVariable.ALL_MASK;
            if ((type & CodeVariable.SCOPE_MASK) == CodeVariable.NO_VARIABLE)
                type |= CodeVariable.FIELD;
            int fdMask = CodeVariable.EXPLICIT_DECLARATION | CodeVariable.FINAL;
            if ((type & fdMask) == fdMask)
                type &= ~CodeVariable.EXPLICIT_DECLARATION;

            globalDefaultVariableType = type;
        }
    }

    /** WARNING: This method will be removed in full two-way editing
     *           implementation. DO NOT USE! */
    public void setDefaultVariableType(int type) {
        if (type < 0) {
            defaultVariableType = -1; // global default will be used
        }
        else {
            type &= CodeVariable.ALL_MASK;
            if ((type & CodeVariable.SCOPE_MASK) == CodeVariable.NO_VARIABLE)
                type |= CodeVariable.FIELD;
            int fdMask = CodeVariable.EXPLICIT_DECLARATION | CodeVariable.FINAL;
            if ((type & fdMask) == fdMask)
                type &= ~CodeVariable.EXPLICIT_DECLARATION;

            defaultVariableType = type;
        }
    }

    static int getGlobalDefaultVariableType() {
        return globalDefaultVariableType;
    }

    int getDefaultVariableType() {
        return defaultVariableType > -1 ?
               defaultVariableType : globalDefaultVariableType;
    }

    // ---------

    protected Map getNamesToVariablesMap() {
        return namesToVariables;
    }

    protected Map getExpressionsToVariables() {
        return expressionsToVariables;
    }

    private CodeStatement createVariableAssignment(CodeVariable var,
                                                   CodeExpression expression)
    {
        CodeStatement statement =
            new CodeSupport.AssignVariableStatement(var, expression);

        // important: assignment statement does not register usage of code
        // expressions (assigned expression, parameters) - so it does not hold
        // the expressions in the structure

        return statement;
    }

    // --------
    // undo/redo processing

    public void setUndoRedoRecording(boolean record) {
        undoRedoRecording = record;
        if (record && undoMap == null) {
            undoMap = new HashMap(500);
            redoMap = new HashMap(100);
            providedMarksUndo = new HashSet(100);
            providedMarksRedo = new HashSet(100);
        }
    }

    public boolean isUndoRedoRecording() {
        return undoRedoRecording;
    }

    void logUndoableChange(CodeStructureChange change) {
        undoRedoMark += redoMap.size(); // to ensure unique marks
        redoMap.clear();
        providedMarksRedo.clear();

        if (undoMap.size() == 0)
            oldestMark = undoRedoMark;

        t("adding undoable change "+undoRedoMark); // NOI18N

        undoMap.put(new Integer(undoRedoMark++), change);

        if (undoMap.size() > undoRedoHardLimit)
            t("undo/redo hard limit reached: " // NOI18N
              +undoMap.size()+" > "+undoRedoHardLimit); // NOI18N

        while (undoMap.size() > undoRedoHardLimit) {
            Object mark = new Integer(oldestMark++);
            undoMap.remove(mark);
            providedMarksUndo.remove(mark);
        }
    }

    public Object markForUndo() {
        undoRedoMark += redoMap.size(); // to ensure unique marks
        redoMap.clear();
        providedMarksRedo.clear();

        t("mark for undo: "+undoRedoMark); // NOI18N

        Object newMark = new Integer(undoRedoMark);
        if (!providedMarksUndo.contains(newMark))
            providedMarksUndo.add(newMark);

        return newMark;
    }

    public boolean releaseUndoableChanges(Object fromMark, Object toMark) {
        int m1 = ((Integer)fromMark).intValue();
        int m2 = ((Integer)toMark).intValue();

        t("release marks from " + m1 + " to " + m2); // NOI18N

        if (undoMap.get(fromMark) != null) {
            while (m1 < m2) {
                Object m = new Integer(m1);
                undoMap.remove(m);
                providedMarksUndo.remove(m);
                m1++;
            }
        }
        else if (redoMap.get(fromMark) != null) {
            while (m1 < m2) {
                Object m = new Integer(m1);
                redoMap.remove(m);
                providedMarksRedo.remove(m);
                m1++;
            }
        }
        else return false;

        return true;
    }

    public boolean undoToMark(Object mark) {
        int lastMark = ((Integer)mark).intValue();
        if (undoRedoMark <= lastMark)
            return false; // invalid parameter

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

        if (undoMap.get(mark) == null || !providedMarksUndo.contains(mark)) {
            t("mark already dropped from the queue"); // NOI18N
            return false;
        }

        boolean undoRedoOn = undoRedoRecording;
        undoRedoRecording = false;

        while (undoRedoMark > lastMark) {
            Object key = new Integer(--undoRedoMark);
            CodeStructureChange change = (CodeStructureChange)
                                         undoMap.remove(key);
            if (change != null) {
                change.undo();
                redoMap.put(key, change);

                t("undone: "+key); // NOI18N
            }

            if (providedMarksUndo.remove(key))
                providedMarksRedo.add(key);
        }

        if (undoRedoOn)
            undoRedoRecording = true;

        return true;
    }

    public boolean redoToMark(Object mark) {
        int toMark = ((Integer)mark).intValue();
        if (undoRedoMark >= toMark)
            return false; // invalid parameter

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

        boolean undoRedoOn = undoRedoRecording;
        undoRedoRecording = false;

        while (undoRedoMark < toMark) {
            Object key = new Integer(undoRedoMark++);
            CodeStructureChange change = (CodeStructureChange)
                                         redoMap.remove(key);
            if (change != null) {
                change.redo();
                undoMap.put(key, change);

                t("redone: "+key); // NOI18N
            }

            if (providedMarksRedo.remove(key))
                providedMarksUndo.add(key);
        }

        if (undoRedoOn)
            undoRedoRecording = true;

        return true;
    }

    // --------
    // inner classes

    final class Variable implements CodeVariable {
        private int type;
        private Class declaredType;
        private String name;
        private Map expressionsMap;
        private CodeStatement declarationStatement;

        Variable(int type, Class declaredType, String name) {
            if ((type & FINAL) != 0)
                type &= ~EXPLICIT_DECLARATION;
            this.type = type;
            this.declaredType = declaredType;
            this.name = name;
        }

        public int getType() {
            return (type & DEFAULT_TYPE) != DEFAULT_TYPE ?
                   type : getDefaultVariableType();
        }

        public String getName() {
            return name;
        }

        public Class getDeclaredType() {
            return declaredType;
        }

        public Collection getAttachedExpressions() {
            return expressionsMap != null ?
                     Collections.unmodifiableCollection(expressionsMap.keySet()) :
                     Collections.EMPTY_LIST;
        }

        public CodeStatement getDeclaration() {
            if (declarationStatement == null)
                declarationStatement =
                    new CodeSupport.DeclareVariableStatement(this);
            return declarationStatement;
        }

        public CodeStatement getAssignment(CodeExpression expression) {
            return expressionsMap != null ?
                   (CodeStatement) expressionsMap.get(expression) : null;
        }

        // -------

        void addCodeExpression(CodeExpression expression,
                               CodeStatement statement)
        {
            if (expressionsMap == null)
                expressionsMap = new HashMap();
            expressionsMap.put(expression, statement);
        }

        CodeStatement removeCodeExpression(CodeExpression expression) {
            if (expressionsMap != null)
                return (CodeStatement) expressionsMap.remove(expression);
            return null;
        }
    }

    private final class VariablesIterator implements Iterator {
        private int type;
        private int typeMask;
        private Class declaredType;

        private Iterator subIterator;

        private CodeVariable currentVar;

        public VariablesIterator(int type, int typeMask, Class declaredType) {
            this.type = type;
            this.typeMask = typeMask;
            this.declaredType = declaredType;

            subIterator = namesToVariables.values().iterator();
        }

        public boolean hasNext() {
            if (currentVar != null)
                return true;

            while (subIterator.hasNext()) {
                CodeVariable var = (CodeVariable) subIterator.next();
                if ((type < 0
                        || (type & typeMask) == (var.getType() & typeMask))
                    &&
                    (declaredType == null
                        || declaredType.equals(var.getDeclaredType())))
                {
                    currentVar = var;
                    return true;
                }
            }

            return false;
        }

        public Object next() {
            if (!hasNext())
                throw new NoSuchElementException();

            CodeVariable var = currentVar;
            currentVar = null;
            return var;
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }
    }

    // --------

    private class VariableChange implements CodeStructureChange {
        private int changeType;
        private Variable variable;
        private CodeExpression expression;
        private CodeStatement statement;
        private String oldName;
        private String newName;

        VariableChange(int type, Variable var) {
            changeType = type;
            variable = var;
        }

        public void undo() {
            switch (changeType) {
                case VARIABLE_CREATE:
                    namesToVariables.remove(variable.name);
                    break;
                case VARIABLE_RENAME:
                    namesToVariables.remove(newName);
                    variable.name = oldName;
                    namesToVariables.put(oldName, variable);
                    break;
                case VARIABLE_RELEASE:
                    Iterator it = variable.expressionsMap.values().iterator();
                    while (it.hasNext())
                        expressionsToVariables.put(it.next(), variable);
                    namesToVariables.put(variable.name, variable);
                    break;
                case VARIABLE_ATTACH:
                    expressionsToVariables.remove(expression);
                    variable.expressionsMap.remove(expression);
                    break;
                case VARIABLE_DETACH:
                    variable.expressionsMap.put(expression, statement);
                    expressionsToVariables.put(expression, variable);
                    break;
            }
        }

        public void redo() {
            switch (changeType) {
                case VARIABLE_CREATE:
                    namesToVariables.put(variable.name, variable);
                    break;
                case VARIABLE_RENAME:
                    namesToVariables.remove(oldName);
                    variable.name = newName;
                    namesToVariables.put(newName, variable);
                    break;
                case VARIABLE_RELEASE:
                    namesToVariables.remove(variable.name);
                    Iterator it = variable.expressionsMap.values().iterator();
                    while (it.hasNext())
                        expressionsToVariables.remove(it.next());
                    break;
                case VARIABLE_ATTACH:
                    variable.expressionsMap.put(expression, statement);
                    expressionsToVariables.put(expression, variable);
                    break;
                case VARIABLE_DETACH:
                    expressionsToVariables.remove(expression);
                    variable.expressionsMap.remove(expression);
                    break;
            }
        }
    }

    // ---------------

    /** For debugging purposes only. */
    static private int traceCount = 0;
    /** For debugging purposes only. */
    static private final boolean TRACE = false;
    /** For debugging purposes only. */
    static void t(String str) {
        if (TRACE)
            if (str != null)
                System.out.println("CodeStructure "+(++traceCount)+": "+str); // NOI18N
            else
                System.out.println(""); // NOI18N
    }
}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.