Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9afed286 authored by Gilles Debunne's avatar Gilles Debunne
Browse files

Raw types warnings removed.

Raw types replaced. Added a asSubClass method in LayoutInflater which
will (correctly) throw a ClassCastException when the inflated class is
not a View subclass.

Reduced the number of warnings in GenericInflater, but those remaining
are valid. A lot of unsafe class casts happen between parent (P) and
item (T) types that will generate runtime errors if the XML is not valid.

Change-Id: I887fd67769a51ab54c6092e1270dbe3bfb6313ca
parent 6dee0a3f
Loading
Loading
Loading
Loading
+36 −29
Original line number Diff line number Diff line
@@ -16,10 +16,6 @@

package android.preference;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.HashMap;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

@@ -31,17 +27,21 @@ import android.view.ContextThemeWrapper;
import android.view.InflateException;
import android.view.LayoutInflater;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.HashMap;

// TODO: fix generics
/**
 * Generic XML inflater. This has been adapted from {@link LayoutInflater} and
 * quickly passed over to use generics.
 * 
 * @hide
 * @param T The type of the items to inflate
 * @param P The type of parents (that is those items that contain other items).
 * @param <T> The type of the items to inflate
 * @param <P> The type of parents (that is those items that contain other items).
 *            Must implement {@link GenericInflater.Parent}
 */
abstract class GenericInflater<T, P extends GenericInflater.Parent> {
abstract class GenericInflater<T, P extends GenericInflater.Parent<T>> {
    private final boolean DEBUG = false;

    protected final Context mContext;
@@ -52,10 +52,11 @@ abstract class GenericInflater<T, P extends GenericInflater.Parent> {

    private final Object[] mConstructorArgs = new Object[2];

    private static final Class[] mConstructorSignature = new Class[] {
    private static final Class<?>[] mConstructorSignature = new Class[] {
            Context.class, AttributeSet.class};

    private static final HashMap sConstructorMap = new HashMap();
    private static final HashMap<String, Constructor<?>> sConstructorMap =
        new HashMap<String, Constructor<?>>();

    private String mDefaultPackage;

@@ -134,7 +135,7 @@ abstract class GenericInflater<T, P extends GenericInflater.Parent> {
     * @return Returns a brand spanking new inflater object associated with
     * the given Context.
     */
    public abstract GenericInflater cloneInContext(Context newContext);
    public abstract GenericInflater<T,P> cloneInContext(Context newContext);
    
    /**
     * Sets the default package that will be searched for classes to construct
@@ -287,22 +288,21 @@ abstract class GenericInflater<T, P extends GenericInflater.Parent> {
     *         attachToRoot is true, this is root; otherwise it is the root of
     *         the inflated XML file.
     */
    public T inflate(XmlPullParser parser, P root,
            boolean attachToRoot) {
    public T inflate(XmlPullParser parser, P root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
            final AttributeSet attrs = Xml.asAttributeSet(parser);
            mConstructorArgs[0] = mContext;
            T result = (T) root;
            P result = root;

            try {
                // Look for the root node.
                int type;
                while ((type = parser.next()) != parser.START_TAG
                        && type != parser.END_DOCUMENT) {
                    ;
                while ((type = parser.next()) != XmlPullParser.START_TAG
                        && type != XmlPullParser.END_DOCUMENT) {
                    // Do nothing
                }

                if (type != parser.START_TAG) {
                if (type != XmlPullParser.START_TAG) {
                    throw new InflateException(parser.getPositionDescription()
                            + ": No start tag found!");
                }
@@ -317,7 +317,8 @@ abstract class GenericInflater<T, P extends GenericInflater.Parent> {
                T xmlRoot = createItemFromTag(parser, parser.getName(),
                        attrs);

                result = (T) onMergeRoots(root, attachToRoot, (P) xmlRoot);
                // Unsafe cast. The current name is not guaranteed to be a P.
                result = onMergeRoots(root, attachToRoot, (P) xmlRoot);
                
                if (DEBUG) {
                    System.out.println("-----> start inflating children");
@@ -343,7 +344,8 @@ abstract class GenericInflater<T, P extends GenericInflater.Parent> {
                throw ex;
            }

            return result;
            // Unsafe cast
            return (T) result;
        }
    }

@@ -362,17 +364,17 @@ abstract class GenericInflater<T, P extends GenericInflater.Parent> {
     * @param name The full name of the class to be instantiated.
     * @param attrs The XML attributes supplied for this instance.
     * 
     * @return The newly instantied item, or null.
     * @return The newly instantiated item, or null.
     */
    public final T createItem(String name, String prefix, AttributeSet attrs)
            throws ClassNotFoundException, InflateException {
        Constructor constructor = (Constructor) sConstructorMap.get(name);
        Constructor<?> constructor = sConstructorMap.get(name);

        try {
            if (null == constructor) {
                // Class not found in the cache, see if it's real,
                // and try to add it
                Class clazz = mContext.getClassLoader().loadClass(
                Class<?> clazz = mContext.getClassLoader().loadClass(
                        prefix != null ? (prefix + name) : name);
                constructor = clazz.getConstructor(mConstructorSignature);
                sConstructorMap.put(name, constructor);
@@ -380,6 +382,8 @@ abstract class GenericInflater<T, P extends GenericInflater.Parent> {

            Object[] args = mConstructorArgs;
            args[1] = attrs;
            // This cast is NOT safe. The name class name is not guaranteed to be a
            // child class of T.
            return (T) constructor.newInstance(args);

        } catch (NoSuchMethodException e) {
@@ -457,15 +461,15 @@ abstract class GenericInflater<T, P extends GenericInflater.Parent> {
     * Recursive method used to descend down the xml hierarchy and instantiate
     * items, instantiate their children, and then call onFinishInflate().
     */
    private void rInflate(XmlPullParser parser, T parent, final AttributeSet attrs)
    private void rInflate(XmlPullParser parser, P parent, final AttributeSet attrs)
            throws XmlPullParserException, IOException {
        final int depth = parser.getDepth();

        int type;
        while (((type = parser.next()) != parser.END_TAG || 
                parser.getDepth() > depth) && type != parser.END_DOCUMENT) {
        while (((type = parser.next()) != XmlPullParser.END_TAG ||
                parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {

            if (type != parser.START_TAG) {
            if (type != XmlPullParser.START_TAG) {
                continue;
            }

@@ -485,12 +489,13 @@ abstract class GenericInflater<T, P extends GenericInflater.Parent> {
                        .println("Creating params from parent: " + parent);
            }

            ((P) parent).addItemFromInflater(item);
            parent.addItemFromInflater(item);

            if (DEBUG) {
                System.out.println("-----> start inflating children");
            }
            rInflate(parser, item, attrs);
            // Unsafe cast
            rInflate(parser, (P)item, attrs);
            if (DEBUG) {
                System.out.println("-----> done inflating children");
            }
@@ -508,8 +513,10 @@ abstract class GenericInflater<T, P extends GenericInflater.Parent> {
     * @param attrs An AttributeSet of attributes to apply to the item.
     * @return Whether you created a custom object (true), or whether this
     *         inflater should proceed to create an item.
     *
     * @throws XmlPullParserException In case of parsing error.
     */
    protected boolean onCreateCustomFromTag(XmlPullParser parser, T parent,
    protected boolean onCreateCustomFromTag(XmlPullParser parser, P parent,
            final AttributeSet attrs) throws XmlPullParserException {
        return false;
    }
+3 −5
Original line number Diff line number Diff line
@@ -16,18 +16,16 @@

package android.preference;

import java.io.IOException;
import java.util.Map;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import android.app.AliasActivity;
import android.content.Context;
import android.content.Intent;
import android.util.AttributeSet;
import android.util.Log;

import java.io.IOException;

/**
 * The {@link PreferenceInflater} is used to inflate preference hierarchies from
 * XML files.
@@ -63,7 +61,7 @@ class PreferenceInflater extends GenericInflater<Preference, PreferenceGroup> {
    }

    @Override
    protected boolean onCreateCustomFromTag(XmlPullParser parser, Preference parentPreference,
    protected boolean onCreateCustomFromTag(XmlPullParser parser, PreferenceGroup parentPreference,
            AttributeSet attrs) throws XmlPullParserException {
        final String tag = parser.getName();
        
+19 −12
Original line number Diff line number Diff line
@@ -16,15 +16,15 @@

package android.view;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import android.content.Context;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.util.AttributeSet;
import android.util.Xml;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.HashMap;
@@ -71,11 +71,11 @@ public abstract class LayoutInflater {

    private final Object[] mConstructorArgs = new Object[2];

    private static final Class[] mConstructorSignature = new Class[] {
    private static final Class<?>[] mConstructorSignature = new Class[] {
            Context.class, AttributeSet.class};

    private static final HashMap<String, Constructor> sConstructorMap =
            new HashMap<String, Constructor>();
    private static final HashMap<String, Constructor<? extends View>> sConstructorMap =
            new HashMap<String, Constructor<? extends View>>();
    
    private HashMap<String, Boolean> mFilterMap;

@@ -453,18 +453,18 @@ public abstract class LayoutInflater {
     * @param name The full name of the class to be instantiated.
     * @param attrs The XML attributes supplied for this instance.
     * 
     * @return View The newly instantied view, or null.
     * @return View The newly instantiated view, or null.
     */
    public final View createView(String name, String prefix, AttributeSet attrs)
            throws ClassNotFoundException, InflateException {
        Constructor constructor = sConstructorMap.get(name);
        Class clazz = null;
        Constructor<? extends View> constructor = sConstructorMap.get(name);
        Class<? extends View> clazz = null;

        try {
            if (constructor == null) {
                // Class not found in the cache, see if it's real, and try to add it
                clazz = mContext.getClassLoader().loadClass(
                        prefix != null ? (prefix + name) : name);
                        prefix != null ? (prefix + name) : name).asSubclass(View.class);
                
                if (mFilter != null && clazz != null) {
                    boolean allowed = mFilter.onLoadClass(clazz);
@@ -482,7 +482,7 @@ public abstract class LayoutInflater {
                    if (allowedState == null) {
                        // New class -- remember whether it is allowed
                        clazz = mContext.getClassLoader().loadClass(
                                prefix != null ? (prefix + name) : name);
                                prefix != null ? (prefix + name) : name).asSubclass(View.class);
                        
                        boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
                        mFilterMap.put(name, allowed);
@@ -497,7 +497,7 @@ public abstract class LayoutInflater {

            Object[] args = mConstructorArgs;
            args[1] = attrs;
            return (View) constructor.newInstance(args);
            return constructor.newInstance(args);

        } catch (NoSuchMethodException e) {
            InflateException ie = new InflateException(attrs.getPositionDescription()
@@ -506,6 +506,13 @@ public abstract class LayoutInflater {
            ie.initCause(e);
            throw ie;

        } catch (ClassCastException e) {
            // If loaded class is not a View subclass
            InflateException ie = new InflateException(attrs.getPositionDescription()
                    + ": Class is not a View "
                    + (prefix != null ? (prefix + name) : name));
            ie.initCause(e);
            throw ie;
        } catch (ClassNotFoundException e) {
            // If loadClass fails, we should propagate the exception.
            throw e;