001    package org.springframework.richclient.command;
002    
003    import java.util.Iterator;
004    
005    /**
006     * CommandGroupModelBuilder is a helper class that allows to build Object Models
007     * derived from ready command-group structures.
008     * 
009     * These command-group structures are tree-like structures that this class will
010     * traverse. Actual building of specific matching object models (often also
011     * trees) is done through callbacks to specific buildXXXModel methods. (which
012     * are abstract on this generic traversing base-class)
013     * 
014     * Actual use assumes one sublasses and implements those required abstract
015     * methods.
016     * 
017     * Internally this class will traverse the commandGroup-structure and offer
018     * subclasses the opportunity to build up his matching model matching the
019     * command-group nesting by calling the various buildXXXModel methods.
020     */
021    public abstract class CommandGroupModelBuilder
022    {
023    
024        /**
025         * Builds the real root object that will be returned from
026         * {@link #buildModel(CommandGroup)}.
027         * 
028         * @param commandGroup
029         *            at the root of the structure
030         * @return the top level object model to build.
031         */
032        protected abstract Object buildRootModel(CommandGroup commandGroup);
033    
034        /**
035         * Allows the implementation subclass to decide (by overriding) if
036         * traversing the structure should continue deeper down.
037         * 
038         * Implementations can decide based on the current visited commandGroup and
039         * the level information.
040         * 
041         * Default implementation at this abstract class level, is to keep on going
042         * down the structure. (subclasses should only override when needing to
043         * change that.)
044         * 
045         * @param commandGroup
046         *            currently visited.
047         * @param level
048         *            in the structure we are at ATM
049         * @return <code>true</code> if children of the group should be visted,
050         *         <code>false</code> if not.
051         */
052        protected boolean continueDeeper(CommandGroup commandGroup, int level)
053        {
054            return true;
055        }
056    
057        /**
058         * Allows the implementation subclass to build a mapping object-model
059         * corresponding to a visited leaf node in the command-group structure.
060         * <i>(Note: for non-leaf nodes the
061         * {@link #buildGroupModel(Object, CommandGroup, int) version is called)}</i>
062         * 
063         * Since the parentModel is also passed in, the implementation can use it to
064         * downcast that and possibly hook up the new client-structure.
065         * 
066         * @param parentModel
067         *            the objectmodel that was created for the parent-command.
068         * @param command
069         *            currently visited command in the structure.
070         * @param level
071         *            in the structure we are at ATM
072         * @return the top level object model to build.
073         */
074        protected abstract Object buildChildModel(Object parentModel, AbstractCommand command, int level);
075    
076        /**
077         * Allows the implementation subclass to build a mapping object-model
078         * corresponding to a visited non-leaf node in the command-group structure.
079         * <i>(Note: for leaf nodes the
080         * {@link #buildChildModel(Object, CommandGroup, int) version is called)}</i>
081         * 
082         * Since the parentModel is also passed in, this implementation can use it
083         * to downcast and decide to hook up the new client-structure.
084         * 
085         * In a same manner the object-structure returned by this method will be
086         * passed down in the tree as the parentModel for nested nodes.
087         * 
088         * In general, if an implementation subclass is not building extra stuff for
089         * a particular command at a particular level, then it is generally wise to
090         * just pass down the parentModel.
091         * 
092         * @param parentModel
093         *            the objectmodel that was created for the parent-command.
094         * @param command
095         *            currently visited command in the structure.
096         * @param level
097         *            in the structure we are at ATM 
098         * @return the top level object model to build.
099         */
100        protected abstract Object buildGroupModel(Object parentModel, CommandGroup commandGroup, int level);
101    
102        /**
103         * Main service method of this method to call.
104         * 
105         * This builds the complete mapping object-model by traversing the complete
106         * passed in command-group structure by performing the appropriate callbacks
107         * to the subclass implementations of {@link #buildRootModel(CommandGroup)},
108         * {@link #buildChildModel(Object, AbstractCommand, int)}, and
109         * {@link #buildGroupModel(Object, CommandGroup, int)}.
110         * 
111         * Additionally,
112         * 
113         * @param commandGroup
114         *            the root of the structure for which an mapping objectmodel
115         *            will be built.
116         * @return the build object model
117         */
118        public final Object buildModel(CommandGroup commandGroup)
119        {
120            Object model = buildRootModel(commandGroup);
121    
122            recurse(commandGroup, model, 0);
123    
124            return model;
125        }
126    
127        private void recurse(AbstractCommand childCommand, Object parentModel, int level)
128        {
129            if (childCommand instanceof CommandGroup)
130            {
131                CommandGroup commandGroup = (CommandGroup) childCommand;
132                parentModel = buildGroupModel(parentModel, commandGroup, level);
133                if (continueDeeper(commandGroup, level))
134                {
135                    Iterator members = commandGroup.memberIterator();
136                    while (members.hasNext())
137                    {
138                        GroupMember member = (GroupMember) members.next();
139                        AbstractCommand memberCommand = member.getCommand();
140                        recurse(memberCommand, parentModel, level + 1);
141                    }
142                }
143    
144            }
145            else
146            {
147                buildChildModel(parentModel, childCommand, level);
148    
149            }
150    
151        }
152    
153    }
154