/* * Copyright © 2011, 2012, 2013 On-Site.com. * Copyright © 2015 Chris Jester-Young. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License along * with this work. If not, see <http://www.gnu.org/licenses/>. * * Linking this library statically or dynamically with other modules is * making a combined work based on this library. Thus, the terms and * conditions of the GNU General Public License cover the whole combination. * * As a special exception, the copyright holders of this library give you * permission to link this library with independent modules to produce an * executable, regardless of the license terms of these independent modules, * and to copy and distribute the resulting executable under terms of your * choice, provided that you also meet, for each linked independent module, * the terms and conditions of the license of that module. An independent * module is a module which is not derived from or based on this library. * If you modify this library, you may extend this exception to your * version of the library, but you are not obligated to do so. If you do * not wish to do so, delete this exception statement from your version. */ package com.sun.tools.hat.internal.lang; import java.nio.CharBuffer; import java.util.Arrays; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.sun.tools.hat.internal.model.JavaClass; import com.sun.tools.hat.internal.model.JavaInt; import com.sun.tools.hat.internal.model.JavaObject; import com.sun.tools.hat.internal.model.JavaObjectArray; import com.sun.tools.hat.internal.model.JavaThing; import com.sun.tools.hat.internal.model.JavaValueArray; import com.sun.tools.hat.internal.model.Snapshot; /** * Common functionality used by language-specific models. * * @author Chris Jester-Young */ // I wish Java had package-and-subpackage private public final class Models { /** * Disable instantiation for static class. */ private Models() {} /** * Returns {@code obj} if it's an instance of class {@code cls}, * otherwise null. * * <p>The curious use of the two type parameters is to encourage the * use of this function for downcasts only (as opposed to upcasts * or invalid casts). Since the hat models do not use interfaces, * sidecasts are not a use case we have to consider. * * @param <T> the type to cast from * @param <U> the type to cast to * @param obj the object to cast * @param cls the type key to cast with * @return {@code obj} cast to the given type, or null */ public static <T, U extends T> U safeCast(T obj, Class<U> cls) { return cls.isInstance(obj) ? cls.cast(obj) : null; } private static JavaClass getClass(Snapshot snapshot, String... classNames) { for (String className : classNames) { JavaClass result = snapshot.findClass(className); if (result != null) return result; } return null; } /** * Resolves the class object corresponding to the first found among * the given class names using the given snapshot. If none of the * class names can be found, throw an exception. * * @param snapshot the snapshot to use to look up the classes * @param classNames the names of the classes to look up * @return the class object corresponding to the first found class */ public static JavaClass grabClass(Snapshot snapshot, String... classNames) { return Preconditions.checkNotNull(getClass(snapshot, classNames), "Cannot find class(es) " + Arrays.toString(classNames)); } /** * Returns whether any of the given class names can be found in the * given snapshot. * * @param snapshot the snapshot to use to look up the classes * @param classNames the names of the classes to look up * @return true iff any of the given names can be found */ public static boolean hasClass(Snapshot snapshot, String... classNames) { return getClass(snapshot, classNames) != null; } /** * Returns the value of string object {@code obj} as a {@link CharBuffer}, * or null if {@code obj} is not a string object. * * @param obj the object to get the string value of * @return the string value of {@code obj} as a {@link CharBuffer} */ public static CharBuffer getStringValueAsCharBuffer(JavaObject obj) { if (obj != null && obj.getClazz().isString()) { JavaValueArray value = safeCast(obj.getField("value"), JavaValueArray.class); JavaInt offset = safeCast(obj.getField("offset"), JavaInt.class); JavaInt count = safeCast(obj.getField("count"), JavaInt.class); if (value != null) { if (offset != null && count != null) { return CharBuffer.wrap((char[]) value.getElements(), offset.value, count.value); } else { return CharBuffer.wrap((char[]) value.getElements()); } } } return null; } /** * Returns the value of string object {@code obj}, or null if * {@code obj} is not a string object. * * @param obj the object to get the string value of * @return the string value of {@code obj}, or null */ public static String getStringValue(JavaObject obj) { CharBuffer value = getStringValueAsCharBuffer(obj); return value != null ? value.toString() : null; } /** * Returns the value of array object {@code arr}, which contains * objects of type {@code T}, or null if at least one element of * {@code arr} is not of type {@code T}. * * @param arr the array to get {@code T} objects from * @param typeKey the type key for type {@code T} * @return the elements of {@code arr} if they're all of type {@code T}, or null */ public static <T extends JavaThing> ImmutableList<T> getObjectArrayValue( JavaObjectArray arr, Class<T> typeKey) { if (arr != null) { ImmutableList.Builder<T> builder = ImmutableList.builder(); for (JavaThing t : arr.getElements()) { if (t != null) { if (!typeKey.isInstance(t)) return null; builder.add(typeKey.cast(t)); } } return builder.build(); } return null; } /** * Returns the content of the static field {@code fieldName} in the * given class, if it is a string; otherwise returns null. * * @param clazz the class to get the static string field from * @param fieldName the name of the static string field * @return the contents of the field, as a string, or null */ public static String getStaticString(JavaClass clazz, String fieldName) { return getStringValue(safeCast(clazz.getStaticField(fieldName), JavaObject.class)); } /** * Returns whether the static field {@code fieldName} in the given class * is a string starting with {@code prefix}'s contents. Unlike {@link * #getStaticString}, this method does not throw for invalid arguments, * but simply returns false. * * @param clazz the class to get the static string field from * @param fieldName the name of the static string field * @param prefix the prefix to check for * @return true iff the field starts with the given prefix */ public static boolean checkStaticString(JavaClass clazz, String fieldName, String prefix) { if (clazz != null) { String value = getStaticString(clazz, fieldName); if (value != null) { return value.startsWith(prefix); } } return false; } /** * Returns whether the given object has a field with the given * name. This will return true as long as the field exists, even * if its value is null. * * @param obj the object to check * @param field the field name to look for * @return true iff {@code obj} has a field of the given name */ public static boolean hasField(JavaObject obj, String field) { /* * JavaObject.getField() returns null if the field doesn't * exist, and returns Snapshot.getNullThing() if the field * exists but has a null value. */ return obj.getField(field) != null; } /** * Returns the first given field found (in the sense of * {@link #hasField}) in the given object. * * @param obj the object to search * @param fields the field names to look for * @return the first field that exists in {@code obj}, or null */ public static String findField(JavaObject obj, String... fields) { for (String field : fields) { if (hasField(obj, field)) return field; } return null; } /** * Convenience method for getting the given field from the given * object as a Java object. * * @param obj the object to use * @param field the field to get * @return the value of the field if it's a Java object, else null */ public static JavaObject getFieldObject(JavaObject obj, String field) { return getFieldThing(obj, field, JavaObject.class); } /** * Convenience method for chaining a bunch of field accesses, as if * doing {@code foo.bar.baz.qux}. * * @param obj the object to use * @param fields the fields to chain with * @return the value of the chained accesses, or null */ public static JavaObject getFieldObjectChain(JavaObject obj, String... fields) { for (String field : fields) { if (obj == null) return null; obj = getFieldObject(obj, field); } return obj; } /** * Convenience method for getting the given field from the given * object as a {@code T}. * * @param obj the object to use * @param field the field to get * @param typeKey the type key for {@code T} * @return the value of the field if it's a {@code T}, else null */ public static <T extends JavaThing> T getFieldThing(JavaObject obj, String field, Class<T> typeKey) { return obj == null ? null : safeCast(obj.getField(field), typeKey); } /** * Convenience method for getting the string value of the given field * from the given object. * * @param obj the object to use * @param field the field to get * @return the string value of the field if it's a string, else null */ public static String getFieldString(JavaObject obj, String field) { return getStringValue(getFieldObject(obj, field)); } /** * Convenience method for getting the elements of the object array * value of the given field from the given object. * * @param obj the object to use * @param field the field to get * @param typeKey the type key for {@code T} * @return the elements of the field if it's an object array with all * elements of type {@code T}, else null */ public static <T extends JavaThing> ImmutableList<T> getFieldObjectArray( JavaObject obj, String field, Class<T> typeKey) { return getObjectArrayValue(getFieldThing(obj, field, JavaObjectArray.class), typeKey); } }