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