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