1 35 package org.jruby; 36 37 import java.util.HashMap ; 38 import java.util.Iterator ; 39 import java.util.Map ; 40 import org.jruby.runtime.ClassIndex; 41 import org.jruby.runtime.builtin.IRubyObject; 42 import org.jruby.runtime.marshal.UnmarshalStream; 43 44 48 public class RubySymbol extends RubyObject { 49 private final String symbol; 50 private final int id; 51 52 private RubySymbol(Ruby runtime, String symbol) { 53 super(runtime, runtime.getClass("Symbol")); 54 this.symbol = symbol; 55 56 runtime.symbolLastId++; 57 this.id = runtime.symbolLastId; 58 } 59 60 public int getNativeTypeIndex() { 61 return ClassIndex.SYMBOL; 62 } 63 64 68 public String asSymbol() { 69 return symbol; 70 } 71 72 public boolean isImmediate() { 73 return true; 74 } 75 76 public static String getSymbol(Ruby runtime, long id) { 77 RubySymbol result = runtime.getSymbolTable().lookup(id); 78 if (result != null) { 79 return result.symbol; 80 } 81 return null; 82 } 83 84 87 88 public static RubySymbol newSymbol(Ruby runtime, String name) { 89 RubySymbol result; 90 synchronized (RubySymbol.class) { 91 94 result = runtime.getSymbolTable().lookup(name); 95 if (result == null) { 96 result = new RubySymbol(runtime, name); 97 runtime.getSymbolTable().store(result); 98 } 99 } 100 return result; 101 } 102 103 public IRubyObject equal(IRubyObject other) { 104 return RubyBoolean.newBoolean(getRuntime(), this == other); 107 } 108 109 public RubyFixnum to_i() { 110 return getRuntime().newFixnum(id); 111 } 112 113 public IRubyObject inspect() { 114 return getRuntime().newString(":" + 115 (isSymbolName(symbol) ? symbol : getRuntime().newString(symbol).dump().toString())); 116 } 117 118 public IRubyObject to_s() { 119 return getRuntime().newString(symbol); 120 } 121 122 public RubyFixnum hash() { 123 return getRuntime().newFixnum(hashCode()); 124 } 125 126 public int hashCode() { 127 return id; 128 } 129 130 public boolean equals(Object other) { 131 return other == this; 132 } 133 134 public IRubyObject to_sym() { 135 return this; 136 } 137 138 public IRubyObject freeze() { 139 return this; 140 } 141 142 public IRubyObject taint() { 143 return this; 144 } 145 146 private static boolean isIdentStart(char c) { 147 return ((c >= 'a' && c <= 'z')|| (c >= 'A' && c <= 'Z') 148 || c == '_'); 149 } 150 private static boolean isIdentChar(char c) { 151 return ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') 152 || c == '_'); 153 } 154 155 private static boolean isIdentifier(String s) { 156 if (s == null || s.length() <= 0) { 157 return false; 158 } 159 160 if (!isIdentStart(s.charAt(0))) { 161 return false; 162 } 163 for (int i = 1; i < s.length(); i++) { 164 if (!isIdentChar(s.charAt(i))) { 165 return false; 166 } 167 } 168 169 return true; 170 } 171 172 177 private static boolean isSpecialGlobalName(String s) { 178 if (s == null || s.length() <= 0) { 179 return false; 180 } 181 182 int length = s.length(); 183 184 switch (s.charAt(0)) { 185 case '~': case '*': case '$': case '?': case '!': case '@': case '/': case '\\': 186 case ';': case ',': case '.': case '=': case ':': case '<': case '>': case '\"': 187 case '&': case '`': case '\'': case '+': case '0': 188 return length == 1; 189 case '-': 190 return (length == 1 || (length == 2 && isIdentChar(s.charAt(1)))); 191 192 default: 193 for (int i = 0; i < length; i++) { 195 if (!Character.isDigit(s.charAt(i))) { 196 return false; 197 } 198 } 199 } 200 return true; 201 } 202 203 private static boolean isSymbolName(String s) { 204 if (s == null || s.length() < 1) { 205 return false; 206 } 207 208 int length = s.length(); 209 210 char c = s.charAt(0); 211 switch (c) { 212 case '$': 213 return length > 1 && isSpecialGlobalName(s.substring(1)); 214 case '@': 215 int offset = 1; 216 if (length >= 2 && s.charAt(1) == '@') { 217 offset++; 218 } 219 220 return isIdentifier(s.substring(offset)); 221 case '<': 222 return (length == 1 || (length == 2 && (s.equals("<<") || s.equals("<="))) || 223 (length == 3 && s.equals("<=>"))); 224 case '>': 225 return (length == 1) || (length == 2 && (s.equals(">>") || s.equals(">="))); 226 case '=': 227 return ((length == 2 && (s.equals("==") || s.equals("=~"))) || 228 (length == 3 && s.equals("==="))); 229 case '*': 230 return (length == 1 || (length == 2 && s.equals("**"))); 231 case '+': 232 return (length == 1 || (length == 2 && s.equals("+@"))); 233 case '-': 234 return (length == 1 || (length == 2 && s.equals("-@"))); 235 case '|': case '^': case '&': case '/': case '%': case '~': case '`': 236 return length == 1; 237 case '[': 238 return s.equals("[]") || s.equals("[]="); 239 } 240 241 if (!isIdentStart(c)) { 242 return false; 243 } 244 245 boolean localID = (c >= 'a' && c <= 'z'); 246 int last = 1; 247 248 for (; last < length; last++) { 249 char d = s.charAt(last); 250 251 if (!isIdentChar(d)) { 252 break; 253 } 254 } 255 256 if (last == length) { 257 return true; 258 } else if (localID && last == length - 1) { 259 char d = s.charAt(last); 260 261 return d == '!' || d == '?' || d == '='; 262 } 263 264 return false; 265 } 266 267 268 269 public static RubySymbol unmarshalFrom(UnmarshalStream input) throws java.io.IOException { 270 RubySymbol result = RubySymbol.newSymbol(input.getRuntime(), RubyString.byteListToString(input.unmarshalString())); 271 input.registerLinkTarget(result); 272 return result; 273 } 274 275 public static class SymbolTable { 276 277 private Map table = new HashMap (); 278 279 public IRubyObject[] all_symbols() { 280 int length = table.size(); 281 IRubyObject[] array = new IRubyObject[length]; 282 System.arraycopy(table.values().toArray(), 0, array, 0, length); 283 return array; 284 } 285 286 public RubySymbol lookup(long symbolId) { 287 Iterator iter = table.values().iterator(); 288 while (iter.hasNext()) { 289 RubySymbol symbol = (RubySymbol) iter.next(); 290 if (symbol != null) { 291 if (symbol.id == symbolId) { 292 return symbol; 293 } 294 } 295 } 296 return null; 297 } 298 299 public RubySymbol lookup(String name) { 300 return (RubySymbol) table.get(name); 301 } 302 303 public void store(RubySymbol symbol) { 304 table.put(symbol.asSymbol(), symbol); 305 } 306 307 } 308 309 } 310 | Popular Tags |