1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.ddmlib.log; 18 19 import com.android.ddmlib.log.LogReceiver.LogEntry; 20 21 import java.util.regex.Matcher; 22 import java.util.regex.Pattern; 23 24 /** 25 * Represents an event and its data. 26 */ 27 public class EventContainer { 28 29 /** 30 * Comparison method for {@link EventContainer#testValue(int, Object, com.android.ddmlib.log.EventContainer.CompareMethod)} 31 * 32 */ 33 public enum CompareMethod { 34 EQUAL_TO("equals", "=="), 35 LESSER_THAN("less than or equals to", "<="), 36 LESSER_THAN_STRICT("less than", "<"), 37 GREATER_THAN("greater than or equals to", ">="), 38 GREATER_THAN_STRICT("greater than", ">"), 39 BIT_CHECK("bit check", "&"); 40 41 private final String mName; 42 private final String mTestString; 43 CompareMethod(String name, String testString)44 private CompareMethod(String name, String testString) { 45 mName = name; 46 mTestString = testString; 47 } 48 49 /** 50 * Returns the display string. 51 */ 52 @Override toString()53 public String toString() { 54 return mName; 55 } 56 57 /** 58 * Returns a short string representing the comparison. 59 */ testString()60 public String testString() { 61 return mTestString; 62 } 63 } 64 65 66 /** 67 * Type for event data. 68 */ 69 public static enum EventValueType { 70 UNKNOWN(0), 71 INT(1), 72 LONG(2), 73 STRING(3), 74 LIST(4), 75 TREE(5); 76 77 private final static Pattern STORAGE_PATTERN = Pattern.compile("^(\\d+)@(.*)$"); //$NON-NLS-1$ 78 79 private int mValue; 80 81 /** 82 * Returns a {@link EventValueType} from an integer value, or <code>null</code> if no match 83 * was found. 84 * @param value the integer value. 85 */ getEventValueType(int value)86 static EventValueType getEventValueType(int value) { 87 for (EventValueType type : values()) { 88 if (type.mValue == value) { 89 return type; 90 } 91 } 92 93 return null; 94 } 95 96 /** 97 * Returns a storage string for an {@link Object} of type supported by 98 * {@link EventValueType}. 99 * <p/> 100 * Strings created by this method can be reloaded with 101 * {@link #getObjectFromStorageString(String)}. 102 * <p/> 103 * NOTE: for now, only {@link #STRING}, {@link #INT}, and {@link #LONG} are supported. 104 * @param object the object to "convert" into a storage string. 105 * @return a string storing the object and its type or null if the type was not recognized. 106 */ getStorageString(Object object)107 public static String getStorageString(Object object) { 108 if (object instanceof String) { 109 return STRING.mValue + "@" + (String)object; //$NON-NLS-1$ 110 } else if (object instanceof Integer) { 111 return INT.mValue + "@" + object.toString(); //$NON-NLS-1$ 112 } else if (object instanceof Long) { 113 return LONG.mValue + "@" + object.toString(); //$NON-NLS-1$ 114 } 115 116 return null; 117 } 118 119 /** 120 * Creates an {@link Object} from a storage string created with 121 * {@link #getStorageString(Object)}. 122 * @param value the storage string 123 * @return an {@link Object} or null if the string or type were not recognized. 124 */ getObjectFromStorageString(String value)125 public static Object getObjectFromStorageString(String value) { 126 Matcher m = STORAGE_PATTERN.matcher(value); 127 if (m.matches()) { 128 try { 129 EventValueType type = getEventValueType(Integer.parseInt(m.group(1))); 130 131 if (type == null) { 132 return null; 133 } 134 135 switch (type) { 136 case STRING: 137 return m.group(2); 138 case INT: 139 return Integer.valueOf(m.group(2)); 140 case LONG: 141 return Long.valueOf(m.group(2)); 142 } 143 } catch (NumberFormatException nfe) { 144 return null; 145 } 146 } 147 148 return null; 149 } 150 151 152 /** 153 * Returns the integer value of the enum. 154 */ getValue()155 public int getValue() { 156 return mValue; 157 } 158 159 @Override toString()160 public String toString() { 161 return super.toString().toLowerCase(); 162 } 163 EventValueType(int value)164 private EventValueType(int value) { 165 mValue = value; 166 } 167 } 168 169 public int mTag; 170 public int pid; /* generating process's pid */ 171 public int tid; /* generating process's tid */ 172 public int sec; /* seconds since Epoch */ 173 public int nsec; /* nanoseconds */ 174 175 private Object mData; 176 177 /** 178 * Creates an {@link EventContainer} from a {@link LogEntry}. 179 * @param entry the LogEntry from which pid, tid, and time info is copied. 180 * @param tag the event tag value 181 * @param data the data of the EventContainer. 182 */ EventContainer(LogEntry entry, int tag, Object data)183 EventContainer(LogEntry entry, int tag, Object data) { 184 getType(data); 185 mTag = tag; 186 mData = data; 187 188 pid = entry.pid; 189 tid = entry.tid; 190 sec = entry.sec; 191 nsec = entry.nsec; 192 } 193 194 /** 195 * Creates an {@link EventContainer} with raw data 196 */ EventContainer(int tag, int pid, int tid, int sec, int nsec, Object data)197 EventContainer(int tag, int pid, int tid, int sec, int nsec, Object data) { 198 getType(data); 199 mTag = tag; 200 mData = data; 201 202 this.pid = pid; 203 this.tid = tid; 204 this.sec = sec; 205 this.nsec = nsec; 206 } 207 208 /** 209 * Returns the data as an int. 210 * @throws InvalidTypeException if the data type is not {@link EventValueType#INT}. 211 * @see #getType() 212 */ getInt()213 public final Integer getInt() throws InvalidTypeException { 214 if (getType(mData) == EventValueType.INT) { 215 return (Integer)mData; 216 } 217 218 throw new InvalidTypeException(); 219 } 220 221 /** 222 * Returns the data as a long. 223 * @throws InvalidTypeException if the data type is not {@link EventValueType#LONG}. 224 * @see #getType() 225 */ getLong()226 public final Long getLong() throws InvalidTypeException { 227 if (getType(mData) == EventValueType.LONG) { 228 return (Long)mData; 229 } 230 231 throw new InvalidTypeException(); 232 } 233 234 /** 235 * Returns the data as a String. 236 * @throws InvalidTypeException if the data type is not {@link EventValueType#STRING}. 237 * @see #getType() 238 */ getString()239 public final String getString() throws InvalidTypeException { 240 if (getType(mData) == EventValueType.STRING) { 241 return (String)mData; 242 } 243 244 throw new InvalidTypeException(); 245 } 246 247 /** 248 * Returns a value by index. The return type is defined by its type. 249 * @param valueIndex the index of the value. If the data is not a list, this is ignored. 250 */ getValue(int valueIndex)251 public Object getValue(int valueIndex) { 252 return getValue(mData, valueIndex, true); 253 } 254 255 /** 256 * Returns a value by index as a double. 257 * @param valueIndex the index of the value. If the data is not a list, this is ignored. 258 * @throws InvalidTypeException if the data type is not {@link EventValueType#INT}, 259 * {@link EventValueType#LONG}, {@link EventValueType#LIST}, or if the item in the 260 * list at index <code>valueIndex</code> is not of type {@link EventValueType#INT} or 261 * {@link EventValueType#LONG}. 262 * @see #getType() 263 */ getValueAsDouble(int valueIndex)264 public double getValueAsDouble(int valueIndex) throws InvalidTypeException { 265 return getValueAsDouble(mData, valueIndex, true); 266 } 267 268 /** 269 * Returns a value by index as a String. 270 * @param valueIndex the index of the value. If the data is not a list, this is ignored. 271 * @throws InvalidTypeException if the data type is not {@link EventValueType#INT}, 272 * {@link EventValueType#LONG}, {@link EventValueType#STRING}, {@link EventValueType#LIST}, 273 * or if the item in the list at index <code>valueIndex</code> is not of type 274 * {@link EventValueType#INT}, {@link EventValueType#LONG}, or {@link EventValueType#STRING} 275 * @see #getType() 276 */ getValueAsString(int valueIndex)277 public String getValueAsString(int valueIndex) throws InvalidTypeException { 278 return getValueAsString(mData, valueIndex, true); 279 } 280 281 /** 282 * Returns the type of the data. 283 */ getType()284 public EventValueType getType() { 285 return getType(mData); 286 } 287 288 /** 289 * Returns the type of an object. 290 */ getType(Object data)291 public final EventValueType getType(Object data) { 292 if (data instanceof Integer) { 293 return EventValueType.INT; 294 } else if (data instanceof Long) { 295 return EventValueType.LONG; 296 } else if (data instanceof String) { 297 return EventValueType.STRING; 298 } else if (data instanceof Object[]) { 299 // loop through the list to see if we have another list 300 Object[] objects = (Object[])data; 301 for (Object obj : objects) { 302 EventValueType type = getType(obj); 303 if (type == EventValueType.LIST || type == EventValueType.TREE) { 304 return EventValueType.TREE; 305 } 306 } 307 return EventValueType.LIST; 308 } 309 310 return EventValueType.UNKNOWN; 311 } 312 313 /** 314 * Checks that the <code>index</code>-th value of this event against a provided value. 315 * @param index the index of the value to test 316 * @param value the value to test against 317 * @param compareMethod the method of testing 318 * @return true if the test passed. 319 * @throws InvalidTypeException in case of type mismatch between the value to test and the value 320 * to test against, or if the compare method is incompatible with the type of the values. 321 * @see CompareMethod 322 */ testValue(int index, Object value, CompareMethod compareMethod)323 public boolean testValue(int index, Object value, 324 CompareMethod compareMethod) throws InvalidTypeException { 325 EventValueType type = getType(mData); 326 if (index > 0 && type != EventValueType.LIST) { 327 throw new InvalidTypeException(); 328 } 329 330 Object data = mData; 331 if (type == EventValueType.LIST) { 332 data = ((Object[])mData)[index]; 333 } 334 335 if (data.getClass().equals(data.getClass()) == false) { 336 throw new InvalidTypeException(); 337 } 338 339 switch (compareMethod) { 340 case EQUAL_TO: 341 return data.equals(value); 342 case LESSER_THAN: 343 if (data instanceof Integer) { 344 return (((Integer)data).compareTo((Integer)value) <= 0); 345 } else if (data instanceof Long) { 346 return (((Long)data).compareTo((Long)value) <= 0); 347 } 348 349 // other types can't use this compare method. 350 throw new InvalidTypeException(); 351 case LESSER_THAN_STRICT: 352 if (data instanceof Integer) { 353 return (((Integer)data).compareTo((Integer)value) < 0); 354 } else if (data instanceof Long) { 355 return (((Long)data).compareTo((Long)value) < 0); 356 } 357 358 // other types can't use this compare method. 359 throw new InvalidTypeException(); 360 case GREATER_THAN: 361 if (data instanceof Integer) { 362 return (((Integer)data).compareTo((Integer)value) >= 0); 363 } else if (data instanceof Long) { 364 return (((Long)data).compareTo((Long)value) >= 0); 365 } 366 367 // other types can't use this compare method. 368 throw new InvalidTypeException(); 369 case GREATER_THAN_STRICT: 370 if (data instanceof Integer) { 371 return (((Integer)data).compareTo((Integer)value) > 0); 372 } else if (data instanceof Long) { 373 return (((Long)data).compareTo((Long)value) > 0); 374 } 375 376 // other types can't use this compare method. 377 throw new InvalidTypeException(); 378 case BIT_CHECK: 379 if (data instanceof Integer) { 380 return (((Integer)data).intValue() & ((Integer)value).intValue()) != 0; 381 } else if (data instanceof Long) { 382 return (((Long)data).longValue() & ((Long)value).longValue()) != 0; 383 } 384 385 // other types can't use this compare method. 386 throw new InvalidTypeException(); 387 default : 388 throw new InvalidTypeException(); 389 } 390 } 391 getValue(Object data, int valueIndex, boolean recursive)392 private final Object getValue(Object data, int valueIndex, boolean recursive) { 393 EventValueType type = getType(data); 394 395 switch (type) { 396 case INT: 397 case LONG: 398 case STRING: 399 return data; 400 case LIST: 401 if (recursive) { 402 Object[] list = (Object[]) data; 403 if (valueIndex >= 0 && valueIndex < list.length) { 404 return getValue(list[valueIndex], valueIndex, false); 405 } 406 } 407 } 408 409 return null; 410 } 411 getValueAsDouble(Object data, int valueIndex, boolean recursive)412 private final double getValueAsDouble(Object data, int valueIndex, boolean recursive) 413 throws InvalidTypeException { 414 EventValueType type = getType(data); 415 416 switch (type) { 417 case INT: 418 return ((Integer)data).doubleValue(); 419 case LONG: 420 return ((Long)data).doubleValue(); 421 case STRING: 422 throw new InvalidTypeException(); 423 case LIST: 424 if (recursive) { 425 Object[] list = (Object[]) data; 426 if (valueIndex >= 0 && valueIndex < list.length) { 427 return getValueAsDouble(list[valueIndex], valueIndex, false); 428 } 429 } 430 } 431 432 throw new InvalidTypeException(); 433 } 434 getValueAsString(Object data, int valueIndex, boolean recursive)435 private final String getValueAsString(Object data, int valueIndex, boolean recursive) 436 throws InvalidTypeException { 437 EventValueType type = getType(data); 438 439 switch (type) { 440 case INT: 441 return ((Integer)data).toString(); 442 case LONG: 443 return ((Long)data).toString(); 444 case STRING: 445 return (String)data; 446 case LIST: 447 if (recursive) { 448 Object[] list = (Object[]) data; 449 if (valueIndex >= 0 && valueIndex < list.length) { 450 return getValueAsString(list[valueIndex], valueIndex, false); 451 } 452 } else { 453 throw new InvalidTypeException( 454 "getValueAsString() doesn't support EventValueType.TREE"); 455 } 456 } 457 458 throw new InvalidTypeException( 459 "getValueAsString() unsupported type:" + type); 460 } 461 } 462