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    package org.crsh.cli.type;
021    
022    import org.crsh.cli.completers.EmptyCompleter;
023    import org.crsh.cli.completers.EnumCompleter;
024    import org.crsh.cli.completers.FileCompleter;
025    import org.crsh.cli.completers.ObjectNameCompleter;
026    import org.crsh.cli.completers.ThreadCompleter;
027    import org.crsh.cli.spi.Completer;
028    
029    import javax.management.ObjectName;
030    import java.io.File;
031    import java.util.Properties;
032    import java.util.StringTokenizer;
033    
034    /**
035     * Defines a type for values, this is used for transforming a textual value into a type, for command
036     * argument and options. A value type defines:
037     *
038     * <ul>
039     *   <li>The generic value type that is converted to.</li>
040     *   <li>The implementation of the {@link #parse(Class, String)} method that transforms the string into a value.</li>
041     *   <li>An optional completer.</li>
042     * </ul>
043     *
044     * @param <V> the generic value type
045     */
046    public abstract class ValueType<V> {
047    
048      /** Identity. */
049      public static final ValueType<String> STRING = new ValueType<String>(String.class) {
050        @Override
051        public <S extends String> S parse(Class<S> type, String s) throws Exception {
052          return type.cast(s);
053        }
054      };
055    
056      /** Integer. */
057      public static final ValueType<Integer> INTEGER = new ValueType<Integer>(Integer.class) {
058        @Override
059        public <S extends Integer> S parse(Class<S> type, String s) throws Exception {
060          return type.cast(Integer.parseInt(s));
061        }
062      };
063    
064      /** Boolean. */
065      public static final ValueType<Boolean> BOOLEAN = new ValueType<Boolean>(Boolean.class) {
066        @Override
067        public <S extends Boolean> S parse(Class<S> type, String s) throws Exception {
068          return type.cast(Boolean.parseBoolean(s));
069        }
070      };
071    
072      /** Any Java enum. */
073      public static final ValueType<Enum> ENUM = new ValueType<Enum>(Enum.class, EnumCompleter.class) {
074        @Override
075        public <S extends Enum> S parse(Class<S> type, String s) {
076          // We cannot express S extends Enum<S> type
077          // so we need this necessary cast to make the java compiler happy
078          S s1 = (S)Enum.valueOf(type, s);
079          return s1;
080        }
081      };
082    
083      /** Properties as semi colon separated values. */
084      public static final ValueType<Properties> PROPERTIES = new ValueType<Properties>(Properties.class) {
085        @Override
086        public <S extends Properties> S parse(Class<S> type, String s) throws Exception {
087          java.util.Properties props = new java.util.Properties();
088          StringTokenizer tokenizer = new StringTokenizer(s, ";", false);
089          while(tokenizer.hasMoreTokens()){
090            String token = tokenizer.nextToken();
091            if(token.contains("=")) {
092              String key = token.substring(0, token.indexOf('='));
093              String value = token.substring(token.indexOf('=') + 1, token.length());
094              props.put(key, value);
095            }
096          }
097          return type.cast(props);
098        }
099      };
100    
101      /** A JMX object name value type. */
102      public static final ValueType<ObjectName> OBJECT_NAME = new ValueType<ObjectName>(ObjectName.class, ObjectNameCompleter.class) {
103        @Override
104        public <S extends ObjectName> S parse(Class<S> type, String s) throws Exception {
105          return type.cast(ObjectName.getInstance(s));
106        }
107      };
108    
109      /** A value type for threads. */
110      public static final ValueType<Thread> THREAD = new ValueType<Thread>(Thread.class, ThreadCompleter.class) {
111        @Override
112        public <S extends Thread> S parse(Class<S> type, String s) throws Exception {
113          long id = Long.parseLong(s);
114          for (Thread t : Thread.getAllStackTraces().keySet()) {
115            if (t.getId() == id) {
116              return type.cast(t);
117            }
118          }
119          throw new IllegalArgumentException("No thread " + s );
120        }
121      };
122    
123      /** A value type for files. */
124      public static final ValueType<File> FILE = new ValueType<File>(File.class, FileCompleter.class) {
125        @Override
126        public <S extends File> S parse(Class<S> type, String s) throws Exception {
127          return type.cast(new File(s));
128        }
129      };
130    
131      /** . */
132      protected final Class<V> type;
133    
134      /** . */
135      protected final Class<? extends Completer> completer;
136    
137      protected ValueType(Class<V> type, Class<? extends Completer> completer) throws NullPointerException {
138        if (type == null) {
139          throw new NullPointerException("No null value type accepted");
140        }
141        if (completer == null) {
142          throw new NullPointerException("No null completer accepted");
143        }
144    
145        //
146        this.completer = completer;
147        this.type = type;
148      }
149    
150      protected ValueType(Class<V> type) throws NullPointerException {
151        if (type == null) {
152          throw new NullPointerException("No null value type accepted");
153        }
154    
155        //
156        this.completer = EmptyCompleter.class;
157        this.type = type;
158      }
159    
160      final int getDistance(Class<?> clazz) {
161        if (type == clazz) {
162          return 0;
163        } else if (type.isAssignableFrom(clazz)) {
164          int degree = 0;
165          for (Class<?> current = clazz;current != type;current = current.getSuperclass()) {
166            degree++;
167          }
168          return degree;
169        } else {
170          return -1;
171        }
172      }
173    
174      @Override
175      public final int hashCode() {
176        return type.hashCode();
177      }
178    
179      @Override
180      public final boolean equals(Object obj) {
181        if (obj == null) {
182          return false;
183        } else {
184          if (obj == this) {
185            return true;
186          } else {
187            if (obj.getClass() == ValueType.class) {
188              ValueType that = (ValueType)obj;
189              return type == that.type;
190            } else {
191              return false;
192            }
193          }
194        }
195      }
196    
197      public Class<? extends Completer> getCompleter() {
198        return completer;
199      }
200    
201      public final Class<V> getType() {
202        return type;
203      }
204    
205      public final V parse(String s) throws Exception {
206        return parse(type, s);
207      }
208    
209      /**
210       * Parse the <code>s</code> argument into a value of type S that is a subclass of the
211       * generic value type V.
212       *
213       * @param type the target type of the value
214       * @param s the string to convert
215       * @param <S> the generic type of the converted value
216       * @return the converted value
217       * @throws Exception any exception that would prevent the conversion to happen
218       */
219      public abstract <S extends V> S parse(Class<S> type, String s) throws Exception;
220    
221    }