001    /*
002     * Copyright (C) 2012 eXo Platform SAS.
003     *
004     * This is free software; you can redistribute it and/or modify it
005     * under the terms of the GNU Lesser General Public License as
006     * published by the Free Software Foundation; either version 2.1 of
007     * the License, or (at your option) any later version.
008     *
009     * This software is distributed in the hope that it will be useful,
010     * but WITHOUT ANY WARRANTY; without even the implied warranty of
011     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012     * Lesser General Public License for more details.
013     *
014     * You should have received a copy of the GNU Lesser General Public
015     * License along with this software; if not, write to the Free
016     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
017     * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
018     */
019    
020    /*
021     * Copyright (C) 2012 eXo Platform SAS.
022     *
023     * This is free software; you can redistribute it and/or modify it
024     * under the terms of the GNU Lesser General Public License as
025     * published by the Free Software Foundation; either version 2.1 of
026     * the License, or (at your option) any later version.
027     *
028     * This software is distributed in the hope that it will be useful,
029     * but WITHOUT ANY WARRANTY; without even the implied warranty of
030     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
031     * Lesser General Public License for more details.
032     *
033     * You should have received a copy of the GNU Lesser General Public
034     * License along with this software; if not, write to the Free
035     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
036     * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
037     */
038    
039    package org.crsh.cli.descriptor;
040    
041    import org.crsh.cli.impl.descriptor.IllegalParameterException;
042    import org.crsh.cli.impl.descriptor.IllegalValueTypeException;
043    import org.crsh.cli.impl.Multiplicity;
044    import org.crsh.cli.impl.ParameterType;
045    import org.crsh.cli.impl.SyntaxException;
046    import org.crsh.cli.spi.Completer;
047    import org.crsh.cli.type.ValueType;
048    
049    import java.io.IOException;
050    import java.lang.annotation.Annotation;
051    import java.util.ArrayList;
052    import java.util.BitSet;
053    import java.util.Collections;
054    import java.util.List;
055    
056    public class OptionDescriptor extends ParameterDescriptor {
057    
058      /** . */
059      private static final BitSet A = new BitSet(256);
060    
061      /** . */
062      private static final BitSet B = new BitSet(256);
063    
064      static {
065        for (char c = 'a';c <= 'z';c++) {
066          A.set(c);
067          A.set(c + 'A' - 'a');
068        }
069        B.or(A);
070        B.set('-');
071      }
072    
073      private static void checkChar(String s, int index, BitSet authorized) throws IllegalParameterException {
074        if (!authorized.get(s.charAt(index))) {
075          throw new IllegalParameterException("Option name " + s + " cannot contain "  + s.charAt(index) + " at position " + index);
076        }
077      }
078    
079      /** . */
080      private final int arity;
081    
082      /** . */
083      private final List<String> names;
084    
085      public OptionDescriptor(
086          ParameterType<?> type,
087          List<String> names,
088          Description info,
089          boolean required,
090          boolean password,
091          boolean unquote,
092          Class<? extends Completer> completerType,
093          Annotation annotation) throws IllegalValueTypeException, IllegalParameterException {
094        super(
095            type,
096          info,
097          required,
098          password,
099          unquote,
100          completerType,
101          annotation);
102    
103        //
104        if (getMultiplicity() == Multiplicity.MULTI && getType() == ValueType.BOOLEAN) {
105          throw new IllegalParameterException();
106        }
107    
108        //
109        names = new ArrayList<String>(names);
110        for (String name : names) {
111          if (name == null) {
112            throw new IllegalParameterException("Option name must not be null");
113          }
114          int length = name.length();
115          if (length == 0) {
116            throw new IllegalParameterException("Option name cannot be empty");
117          }
118          if (!A.get(name.charAt(0))) {
119            throw new IllegalParameterException("Option name " + name + " cannot start with " + name.charAt(0));
120          }
121          checkChar(name, 0, A);
122          checkChar(name, length - 1, A);
123          for (int i = 1;i < length - 1;i++) {
124            checkChar(name, i, B);
125          }
126        }
127    
128        //
129        if (getType() == ValueType.BOOLEAN) {
130          arity = 0;
131        } else {
132          arity = 1;
133        }
134    
135        //
136        this.names = Collections.unmodifiableList(names);
137      }
138    
139      public int getArity() {
140        return arity;
141      }
142    
143      public List<String> getNames() {
144        return names;
145      }
146    
147      @Override
148      public Object parse(List<String> values) throws SyntaxException {
149        if (arity == 0) {
150          if (values.size() > 0) {
151            throw new SyntaxException("Too many values " + values + " for option " + names.get(0));
152          }
153          // It's a boolean and it is true
154          return Boolean.TRUE;
155        } else {
156          if (getMultiplicity() == Multiplicity.SINGLE) {
157            if (values.size() > 1) {
158              throw new SyntaxException("Too many values " + values + " for option " + names.get(0));
159            }
160            if (values.size() == 0) {
161              throw new SyntaxException("Missing option " + names.get(0) + " value");
162            }
163            String value = values.get(0);
164            try {
165              return parse(value);
166            } catch (Exception e) {
167              throw new SyntaxException("Could not parse value <" + value + "> for option " + names.get(0));
168            }
169          } else {
170            List<Object> v = new ArrayList<Object>(values.size());
171            for (String value : values) {
172              try {
173                v.add(parse(value));
174              } catch (Exception e) {
175                throw new SyntaxException("Could not parse value <" + value + "> for option " + names.get(0));
176              }
177            }
178            return v;
179          }
180        }
181      }
182    
183      /**
184       * Prints the option names as an alternative of switches surrounded by a square brace,
185       * for instance:  "[-f --foo]"
186       *
187       * @param writer the writer to print to
188       * @throws IOException any io exception
189       */
190      public void printUsage(Appendable writer) throws IOException {
191        writer.append("[");
192        boolean a = false;
193        for (String optionName : names) {
194          if (a) {
195            writer.append(" | ");
196          }
197          writer.append(optionName.length() == 1 ? "-" : "--").append(optionName);
198          a = true;
199        }
200        writer.append("]");
201      }
202    
203      @Override
204      public String toString() {
205        return "OptionDescriptor[" + names + "]";
206      }
207    }