1   /**
2    * Distribution License:
3    * JSword is free software; you can redistribute it and/or modify it under
4    * the terms of the GNU Lesser General Public License, version 2.1 or later
5    * as published by the Free Software Foundation. This program is distributed
6    * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
7    * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
8    * See the GNU Lesser General Public License for more details.
9    *
10   * The License is available on the internet at:
11   *      http://www.gnu.org/copyleft/lgpl.html
12   * or by writing to:
13   *      Free Software Foundation, Inc.
14   *      59 Temple Place - Suite 330
15   *      Boston, MA 02111-1307, USA
16   *
17   * © CrossWire Bible Society, 2008 - 2016
18   *
19   */
20  package org.crosswire.common.options;
21  
22  import java.util.ArrayList;
23  import java.util.LinkedHashMap;
24  import java.util.List;
25  import java.util.Map;
26  
27  /**
28   * An OptionList contains an ordered set of Options. The primary ability of an
29   * OptionList is to find the matches for an Option.
30   * 
31   * @see gnu.lgpl.License The GNU Lesser General Public License for details.
32   * @author DM Smith
33   */
34  public class OptionList {
35      public OptionList() {
36          longOptions = new LinkedHashMap<String, Option>();
37          shortOptions = new LinkedHashMap<String, Option>();
38      }
39  
40      /**
41       * Adds an Option to the end of this OptionList. It is an error with
42       * "undefined" behavior for an Option's short or long name to already be
43       * known.
44       * 
45       * @param option the option to append
46       */
47      public void add(Option option) {
48          char shortName = option.getShortName();
49          String longName = option.getLongName();
50          if (shortName != '\u0000') {
51              String optionName = Character.toString(shortName);
52              assert !shortOptions.containsKey(optionName) : optionName + " already present";
53              shortOptions.put(optionName, option);
54          }
55  
56          if (longName != null) {
57              assert !longOptions.containsKey(longName) : longName + " already present";
58              longOptions.put(longName, option);
59          }
60      }
61  
62      /**
63       * Get a list of Options that match the Option's long name. Return all
64       * Options where the key is a prefix of its long name. If there is an exact
65       * match then it is at the head of the list. It is up to the program to
66       * decide how to handle ambiguity.
67       * 
68       * @param key
69       *            the input to match
70       * @return a list of all matches, or an empty list
71       */
72      public List<Option> getLongOptions(String key) {
73          List<Option> matches = new ArrayList<Option>();
74          if (longOptions.containsKey(key)) {
75              matches.add(longOptions.get(key));
76          }
77  
78          for (Map.Entry<String, Option> entry : longOptions.entrySet()) {
79              String entryKey = entry.getKey();
80              Option entryValue = entry.getValue();
81              if (entryKey.startsWith(key) && !matches.contains(entryValue)) {
82                  matches.add(entryValue);
83              }
84          }
85  
86          return matches;
87      }
88  
89      /**
90       * Get the Option that matches the key on the Option's short name.
91       * 
92       * @param key
93       *            the input to match
94       * @return the matching Option, null otherwise.
95       */
96      public Option getShortOption(char key) {
97          String optionName = Character.toString(key);
98  
99          Option match = null;
100         if (shortOptions.containsKey(optionName)) {
101             match = shortOptions.get(optionName);
102         }
103 
104         return match;
105     }
106 
107     /**
108      * Get a list of Options that match the Option's short or long name.
109      * Obviously, if the key is longer than a single character it won't match a
110      * short name. Return all Options where the key is a prefix of its long
111      * name. If there is an exact match then it is at the head of the list. It
112      * is up to the program to decide how to handle ambiguity.
113      * 
114      * @param key
115      *            the input to match
116      * @return a list of all matches, or an empty list
117      */
118     public List<Option> getOptions(String key) {
119         List<Option> matches = new ArrayList<Option>();
120         if (key.length() == 1) {
121             Option match = getShortOption(key.charAt(0));
122             if (match != null) {
123                 matches.add(match);
124             }
125         }
126 
127         for (Option match : getLongOptions(key)) {
128             if (!matches.contains(match)) {
129                 matches.add(match);
130             }
131         }
132 
133         return matches;
134     }
135 
136     private Map<String, Option> shortOptions;
137     private Map<String, Option> longOptions;
138 }
139