001/*-------------------------------------------------------------------------+
002|                                                                          |
003| Copyright 2005-2011 The ConQAT Project                                   |
004|                                                                          |
005| Licensed under the Apache License, Version 2.0 (the "License");          |
006| you may not use this file except in compliance with the License.         |
007| You may obtain a copy of the License at                                  |
008|                                                                          |
009|    http://www.apache.org/licenses/LICENSE-2.0                            |
010|                                                                          |
011| Unless required by applicable law or agreed to in writing, software      |
012| distributed under the License is distributed on an "AS IS" BASIS,        |
013| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
014| See the License for the specific language governing permissions and      |
015| limitations under the License.                                           |
016+-------------------------------------------------------------------------*/
017package org.conqat.lib.commons.options;
018
019import java.lang.reflect.Method;
020import java.util.ArrayList;
021import java.util.HashMap;
022import java.util.List;
023import java.util.Map;
024
025/**
026 * This class supports collecting options from several objects whose methods
027 * have been annotated with {@link Option}.
028 *
029 * @author Benjamin Hummel
030 */
031public class OptionRegistry {
032
033        /** Mapping from short option names to actual option applicator. */
034        private final Map<Character, OptionApplicator> shortOptions = new HashMap<>();
035
036        /** Mapping from long option names to actual option applicator. */
037        private final Map<String, OptionApplicator> longOptions = new HashMap<>();
038
039        /** List of all options (including description). */
040        private final List<Option> allOptions = new ArrayList<>();
041
042        /**
043         * Default constructor. Does nothing but we want to allow this too.
044         */
045        public OptionRegistry() {
046                // nothing to do
047        }
048
049        /**
050         * Construct a new option registry and register the given option handler.
051         *
052         * @param optionHandler
053         *            the option handler to register.
054         */
055        public OptionRegistry(Object optionHandler) {
056                registerOptionHandler(optionHandler);
057        }
058
059        /**
060         * Adds all options provided by the given object to this registry. Options are
061         * public methods annotated with AOption.
062         *
063         * @param optionHandler
064         *            The object to extract the options from.
065         */
066        public void registerOptionHandler(Object optionHandler) {
067                for (Method m : optionHandler.getClass().getMethods()) {
068                        Option optionDescriptor = m.getAnnotation(Option.class);
069                        if (optionDescriptor != null) {
070                                allOptions.add(optionDescriptor);
071                                OptionApplicator applicator = new OptionApplicator(optionHandler, m, optionDescriptor.greedy());
072                                if (optionDescriptor.shortName() != 0) {
073                                        registerApplicator(applicator, shortOptions, optionDescriptor.shortName());
074                                }
075                                if (optionDescriptor.longName().length() > 0) {
076                                        registerApplicator(applicator, longOptions, optionDescriptor.longName());
077                                }
078                        }
079                }
080        }
081
082        /**
083         * Registers an applicator under the given name in a map. Throws an exception if
084         * the name was already taken.
085         *
086         * @param <T>
087         *            type of the option name used in the map.
088         * @param applicator
089         *            the option applicator to register.
090         * @param map
091         *            the map to add the applicator to.
092         * @param optionName
093         *            the name of the option.
094         */
095        private static <T> void registerApplicator(OptionApplicator applicator, Map<T, OptionApplicator> map,
096                        T optionName) {
097                if (map.containsKey(optionName)) {
098                        throw new IllegalArgumentException("An option of the name " + optionName + " already exists!");
099                }
100                map.put(optionName, applicator);
101        }
102
103        /**
104         * Returns the OptionApplicator for the given short option name or null if no
105         * such options exists.
106         *
107         * @param name
108         *            the name of the requested option.
109         * @return the OptionApplicator for the given short option name or null if no
110         *         such options exists.
111         */
112        public OptionApplicator getShortOption(char name) {
113                return shortOptions.get(name);
114        }
115
116        /**
117         * Returns the OptionApplicator for the given short option name or null if no
118         * such options exists.
119         *
120         * @param name
121         *            the name of the requested option.
122         * @return the OptionApplicator for the given short option name or null if no
123         *         such options exists.
124         */
125        public OptionApplicator getLongOption(String name) {
126                return longOptions.get(name);
127        }
128
129        /**
130         * Returns a list containing all supported options.
131         *
132         * @return a list containing all supported options.
133         */
134        public List<Option> getAllOptions() {
135                return allOptions;
136        }
137}