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 }