001package org.unix4j.codegen.optset;
002
003import java.util.ArrayList;
004import java.util.Collection;
005import java.util.LinkedHashMap;
006import java.util.List;
007import java.util.Map;
008import java.util.Set;
009import java.util.TreeMap;
010
011import org.unix4j.codegen.command.def.CommandDef;
012import org.unix4j.codegen.command.def.OptionDef;
013import org.unix4j.codegen.def.TypeDef;
014import org.unix4j.codegen.optset.constraint.OptionConstraint;
015import org.unix4j.codegen.optset.def.ActiveSetDef;
016import org.unix4j.codegen.optset.def.OptionGroupDef;
017import org.unix4j.codegen.optset.def.OptionSetDef;
018
019public class OptionSetDefinitionLoader {
020
021        public OptionSetDef create(CommandDef commandDef) {
022                final OptionSetDef def = createOptionSetDef(commandDef);
023                final Collection<OptionConstraint> constraints = Constraints.getOptionConstraints(commandDef);
024                final Map<String, ActiveSetDef> initialActiveSets = new ActiveSetPermutationBuilder(constraints).generateActiveSetsForGroup(commandDef.options);
025//              print("", initialActiveSets);
026//              if (true) return null;
027                addGroupsDerivedFromActiveSets(def, initialActiveSets);
028                return def;
029        }
030
031        @SuppressWarnings("unused")
032        private void print(String indent, Map<String, ActiveSetDef> activeSets) {
033                for (final Map.Entry<String, ActiveSetDef> e : activeSets.entrySet()) {
034                        System.out.println(indent + e.getKey() + ": " + e.getValue().name);
035                        print(indent + "\t", e.getValue().next);
036                }
037        }
038
039        private void addGroupsDerivedFromActiveSets(OptionSetDef def, Map<String, ActiveSetDef> initialActiveSets) {
040                //create groups first
041                final Map<Set<String>, OptionGroupDef> optionsToGroup = createGroupsFromActiveSets(def, initialActiveSets);
042
043//              //initial level
044                for (final ActiveSetDef initialSet : initialActiveSets.values()) {
045                        addGroupsDerivedFromActiveSets(optionsToGroup, initialSet);
046                }
047
048                //recurse all other levels now
049                int level = 1;
050                while (populateLevelActiveSetsToGroups(optionsToGroup, level)) {
051                        level++;
052                }
053                
054                //set initial group
055                def.initialGroup = optionsToGroup.get(def.command.options.keySet());
056                
057                //now add groups to def
058                def.groups.addAll(optionsToGroup.values());
059        }
060
061        private Map<Set<String>, OptionGroupDef> createGroupsFromActiveSets(OptionSetDef def, Map<String, ActiveSetDef> initialActiveSets) {
062                final Map<Set<String>, OptionGroupDef> optionsToGroup = new LinkedHashMap<Set<String>, OptionGroupDef>();
063                //first, simply add the options
064                addGroupsForActiveSets(def.command, optionsToGroup, initialActiveSets.values());
065                final Set<String> allOptions = def.command.options.keySet();
066                if (!optionsToGroup.containsKey(allOptions)) {
067                        optionsToGroup.put(allOptions, createGroupFor(def.command, allOptions));
068                }
069                //now, fill the optionToNextGroup values
070                final OptionGroupDef allOptionsGroup = getGroupForOptions(optionsToGroup, allOptions);
071                fillOptionToNextGroup(optionsToGroup, allOptionsGroup, initialActiveSets);
072                //done
073                return optionsToGroup;
074        }
075
076        private void addGroupsForActiveSets(CommandDef commandDef, Map<Set<String>, OptionGroupDef> optionsToGroup, Iterable<ActiveSetDef> activeSets) {
077                for (final ActiveSetDef activeSet : activeSets) {
078                        final Set<String> options = activeSet.getAllOptions();
079                        OptionGroupDef grp = optionsToGroup.get(options);
080                        if (grp == null) {
081                                grp = createGroupFor(commandDef, options);
082                                optionsToGroup.put(options, grp);
083                                addGroupsForActiveSets(commandDef, optionsToGroup, activeSet.next.values());
084                        }
085                }
086        }
087
088        private void addGroupsDerivedFromActiveSets(final Map<Set<String>, OptionGroupDef> optionsToGroup, ActiveSetDef activeSet) {
089                final OptionGroupDef group = getGroupForActiveSet(optionsToGroup, activeSet);
090                if (group == null) {
091                        throw new IllegalArgumentException("group not found for active options " + activeSet.getAllOptions() + ", group options=" + optionsToGroup.keySet());
092                }
093                getLevelSetFor(group, activeSet.active.size(), true).put(activeSet.name, activeSet);
094        }
095        
096        private void fillOptionToNextGroup(Map<Set<String>, OptionGroupDef> optionsToGroup, OptionGroupDef group, Map<String, ActiveSetDef> newActiveToActiveSet) {
097                for (final Map.Entry<String, ActiveSetDef> e : newActiveToActiveSet.entrySet()) {
098                        final String newActive = e.getKey(); 
099                        final ActiveSetDef activeSet = e.getValue();
100                        final OptionGroupDef nextGroup = getGroupForActiveSet(optionsToGroup, activeSet);
101                        group.optionToNextGroup.put(newActive, nextGroup);
102                        fillOptionToNextGroup(optionsToGroup, nextGroup, activeSet.next);
103                }
104        }
105
106        private OptionGroupDef getGroupForActiveSet(final Map<Set<String>, OptionGroupDef> optionsToGroup, final ActiveSetDef activeSet) {
107                return getGroupForOptions(optionsToGroup, activeSet.getAllOptions());
108        }
109        private OptionGroupDef getGroupForOptions(final Map<Set<String>, OptionGroupDef> optionsToGroup, final Set<String> options) {
110                final OptionGroupDef grp = optionsToGroup.get(options);
111                if (grp == null) {
112                        throw new IllegalArgumentException("no option group for options=" + options + ", groups exist for " + optionsToGroup.keySet());
113                }
114                return grp;
115        }
116
117        private boolean populateLevelActiveSetsToGroups(final Map<Set<String>, OptionGroupDef> optionsToGroup, int level) {
118                boolean anyAdded = false;
119                for (final OptionGroupDef group : optionsToGroup.values()) {
120                        final Map<String, ActiveSetDef> setsForThisLevel = getLevelSetFor(group, level, false);
121                        for (final ActiveSetDef activeSet : setsForThisLevel.values()) {
122                                for (ActiveSetDef next : activeSet.next.values()) {
123                                        addGroupsDerivedFromActiveSets(optionsToGroup, next);
124                                        anyAdded = true;
125                                }
126                        }
127                }
128                return anyAdded;
129        }
130
131        private Map<String, ActiveSetDef> getLevelSetFor(OptionGroupDef group, int level, boolean create) {
132                final Map<String, ActiveSetDef> lastLevelSet = getLastLevelSet(group);
133                if (lastLevelSet != null && lastLevelSet.get(lastLevelSet.keySet().iterator().next()).active.size() == level) {
134                        return lastLevelSet;
135                }
136                if (create) {
137                        final Map<String, ActiveSetDef> newLastLevelSet = new LinkedHashMap<String, ActiveSetDef>();
138                        group.levelActiveSets.add(newLastLevelSet);
139                        return newLastLevelSet;
140                }
141                return new TreeMap<String, ActiveSetDef>();
142        }
143        private Map<String, ActiveSetDef> getLastLevelSet(OptionGroupDef group) {
144                final int size = group.levelActiveSets.size();
145                return size == 0 ? null : group.levelActiveSets.get(size - 1);
146        }
147        
148        private OptionSetDef createOptionSetDef(CommandDef commandDef) {
149                final TypeDef optionType = new TypeDef(commandDef.command.simpleName + "Option", commandDef.pkg.name);
150                final OptionSetDef def = new OptionSetDef(commandDef, optionType);
151                return def;
152        }
153
154        private OptionGroupDef createGroupFor(CommandDef commandDef, Set<String> options) {
155                final List<OptionDef> optionDefs = new ArrayList<OptionDef>(options.size());
156                for (final String opt : options) {
157                        optionDefs.add(commandDef.options.get(opt));
158                }
159                return createGroupFor(commandDef, optionDefs);
160        }
161        private OptionGroupDef createGroupFor(CommandDef commandDef, Collection<OptionDef> options) {
162                final OptionGroupDef grp = new OptionGroupDef(commandDef, options);
163                for (final OptionDef opt : options) {
164                        grp.options.put(opt.name, opt);
165                }
166                return grp;
167        }
168
169}