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 020package org.crsh.cli.type; 021 022import org.crsh.cli.completers.EmptyCompleter; 023import org.crsh.cli.completers.EnumCompleter; 024import org.crsh.cli.completers.FileCompleter; 025import org.crsh.cli.completers.ThreadCompleter; 026import org.crsh.cli.spi.Completer; 027 028import javax.management.ObjectName; 029import java.io.File; 030import java.util.Properties; 031import 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 */ 045public 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}