15bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam/*
25bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam * Copyright (C) 2007 The Android Open Source Project
35bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam *
45bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam * Licensed under the Apache License, Version 2.0 (the "License");
55bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam * you may not use this file except in compliance with the License.
65bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam * You may obtain a copy of the License at
75bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam *
85bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam *      http://www.apache.org/licenses/LICENSE-2.0
95bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam *
105bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam * Unless required by applicable law or agreed to in writing, software
115bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam * distributed under the License is distributed on an "AS IS" BASIS,
125bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam * See the License for the specific language governing permissions and
145bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam * limitations under the License.
155bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam */
165bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
175bf291fde3dfd64f264d525534730514a279c8fcMaurice Lampackage com.android.setupwizardlib.items;
185bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
195bf291fde3dfd64f264d525534730514a279c8fcMaurice Lamimport java.io.IOException;
205bf291fde3dfd64f264d525534730514a279c8fcMaurice Lamimport java.lang.reflect.Constructor;
215bf291fde3dfd64f264d525534730514a279c8fcMaurice Lamimport java.util.HashMap;
225bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
235bf291fde3dfd64f264d525534730514a279c8fcMaurice Lamimport org.xmlpull.v1.XmlPullParser;
245bf291fde3dfd64f264d525534730514a279c8fcMaurice Lamimport org.xmlpull.v1.XmlPullParserException;
255bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
265bf291fde3dfd64f264d525534730514a279c8fcMaurice Lamimport android.content.Context;
275bf291fde3dfd64f264d525534730514a279c8fcMaurice Lamimport android.content.res.XmlResourceParser;
285bf291fde3dfd64f264d525534730514a279c8fcMaurice Lamimport android.util.AttributeSet;
295bf291fde3dfd64f264d525534730514a279c8fcMaurice Lamimport android.util.Log;
305bf291fde3dfd64f264d525534730514a279c8fcMaurice Lamimport android.util.Xml;
315bf291fde3dfd64f264d525534730514a279c8fcMaurice Lamimport android.view.ContextThemeWrapper;
325bf291fde3dfd64f264d525534730514a279c8fcMaurice Lamimport android.view.InflateException;
335bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
345bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam/**
355bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam * Generic XML inflater. This class is modeled after {@code android.preference.GenericInflater},
365bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam * which is in turn modeled after {@code LayoutInflater}. This can be used to recursively inflate a
375bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam * hierarchy of items. All items in the hierarchy must inherit the generic type {@code T}, and the
385bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam * specific implementation is expected to handle inserting child items into the parent, by
395bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam * implementing {@link #onAddChildItem(Object, Object)}.
405bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam *
415bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam * @param <T> Type of the items to inflate
425bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam */
435bf291fde3dfd64f264d525534730514a279c8fcMaurice Lampublic abstract class GenericInflater<T> {
445bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
455bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    private static final String TAG = "GenericInflater";
465bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    private static final boolean DEBUG = false;
475bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
485bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    protected final Context mContext;
495bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
505bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    // these are optional, set by the caller
515bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    private boolean mFactorySet;
525bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    private Factory<T> mFactory;
535bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
545bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    private final Object[] mConstructorArgs = new Object[2];
555bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
565bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    private static final Class[] mConstructorSignature = new Class[] {
575bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            Context.class, AttributeSet.class};
585bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
595bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    private static final HashMap<String, Constructor<?>> sConstructorMap = new HashMap<>();
605bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
615bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    private String mDefaultPackage;
625bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
635bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    public interface Factory<T> {
645bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        /**
655bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam         * Hook you can supply that is called when inflating from a
665bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam         * inflater. You can use this to customize the tag
675bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam         * names available in your XML files.
685bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam         * <p>
695bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam         * Note that it is good practice to prefix these custom names with your
705bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam         * package (i.e., com.coolcompany.apps) to avoid conflicts with system
715bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam         * names.
725bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam         *
735bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam         * @param name Tag name to be inflated.
745bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam         * @param context The context the item is being created in.
755bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam         * @param attrs Inflation attributes as specified in XML file.
765bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam         * @return Newly created item. Return null for the default behavior.
775bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam         */
785bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        T onCreateItem(String name, Context context, AttributeSet attrs);
795bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    }
805bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
815bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    private static class FactoryMerger<T> implements Factory<T> {
825bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        private final Factory<T> mF1, mF2;
835bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
845bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        FactoryMerger(Factory<T> f1, Factory<T> f2) {
855bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            mF1 = f1;
865bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            mF2 = f2;
875bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        }
885bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
895bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        public T onCreateItem(String name, Context context, AttributeSet attrs) {
905bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            T v = mF1.onCreateItem(name, context, attrs);
915bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            if (v != null) return v;
925bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            return mF2.onCreateItem(name, context, attrs);
935bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        }
945bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    }
955bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
965bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    /**
975bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * Create a new inflater instance associated with a
985bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * particular Context.
995bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *
1005bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * @param context The Context in which this inflater will
1015bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *            create its items; most importantly, this supplies the theme
1025bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *            from which the default values for their attributes are
1035bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *            retrieved.
1045bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     */
1055bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    protected GenericInflater(Context context) {
1065bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        mContext = context;
1075bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    }
1085bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
1095bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    /**
1105bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * Create a new inflater instance that is a copy of an
1115bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * existing inflater, optionally with its Context
1125bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * changed. For use in implementing {@link #cloneInContext}.
1135bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *
1145bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * @param original The original inflater to copy.
1155bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * @param newContext The new Context to use.
1165bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     */
1175bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    protected GenericInflater(GenericInflater<T> original, Context newContext) {
1185bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        mContext = newContext;
1195bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        mFactory = original.mFactory;
1205bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    }
1215bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
1225bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    /**
1235bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * Create a copy of the existing inflater object, with the copy
1245bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * pointing to a different Context than the original.  This is used by
1255bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * {@link ContextThemeWrapper} to create a new inflater to go along
1265bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * with the new Context theme.
1275bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *
1285bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * @param newContext The new Context to associate with the new inflater.
1295bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * May be the same as the original Context if desired.
1305bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *
1315bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * @return Returns a brand spanking new inflater object associated with
1325bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * the given Context.
1335bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     */
1345bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    public abstract GenericInflater cloneInContext(Context newContext);
1355bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
1365bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    /**
1375bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * Sets the default package that will be searched for classes to construct
1385bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * for tag names that have no explicit package.
1395bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *
1405bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * @param defaultPackage The default package. This will be prepended to the
1415bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *            tag name, so it should end with a period.
1425bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     */
1435bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    public void setDefaultPackage(String defaultPackage) {
1445bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        mDefaultPackage = defaultPackage;
1455bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    }
1465bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
1475bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    /**
1485bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * Returns the default package, or null if it is not set.
1495bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *
1505bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * @see #setDefaultPackage(String)
1515bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * @return The default package.
1525bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     */
1535bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    public String getDefaultPackage() {
1545bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        return mDefaultPackage;
1555bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    }
1565bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
1575bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    /**
1585bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * Return the context we are running in, for access to resources, class
1595bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * loader, etc.
1605bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     */
1615bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    public Context getContext() {
1625bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        return mContext;
1635bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    }
1645bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
1655bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    /**
1665bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * Return the current factory (or null). This is called on each element
1675bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * name. If the factory returns an item, add that to the hierarchy. If it
1685bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * returns null, proceed to call onCreateItem(name).
1695bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     */
1705bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    public final Factory<T> getFactory() {
1715bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        return mFactory;
1725bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    }
1735bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
1745bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    /**
1755bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * Attach a custom Factory interface for creating items while using this
1765bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * inflater. This must not be null, and can only be set
1775bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * once; after setting, you can not change the factory. This is called on
1785bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * each element name as the XML is parsed. If the factory returns an item,
1795bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * that is added to the hierarchy. If it returns null, the next factory
1805bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * default {@link #onCreateItem} method is called.
1815bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * <p>
1825bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * If you have an existing inflater and want to add your
1835bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * own factory to it, use {@link #cloneInContext} to clone the existing
1845bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * instance and then you can use this function (once) on the returned new
1855bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * instance. This will merge your own factory with whatever factory the
1865bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * original instance is using.
1875bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     */
1885bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    public void setFactory(Factory<T> factory) {
1895bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        if (mFactorySet) {
1905bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            throw new IllegalStateException("" +
1915bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                    "A factory has already been set on this inflater");
1925bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        }
1935bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        if (factory == null) {
1945bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            throw new NullPointerException("Given factory can not be null");
1955bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        }
1965bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        mFactorySet = true;
1975bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        if (mFactory == null) {
1985bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            mFactory = factory;
1995bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        } else {
2005bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            mFactory = new FactoryMerger<>(factory, mFactory);
2015bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        }
2025bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    }
2035bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
2045bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    public T inflate(int resource) {
2055bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        return inflate(resource, null);
2065bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    }
2075bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
2085bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
2095bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    /**
2105bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * Inflate a new item hierarchy from the specified xml resource. Throws
2115bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * InflaterException if there is an error.
2125bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *
2135bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * @param resource ID for an XML resource to load (e.g.,
2145bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *        <code>R.layout.main_page</code>)
2155bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * @param root Optional parent of the generated hierarchy.
2165bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * @return The root of the inflated hierarchy. If root was supplied,
2175bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *         this is the root item; otherwise it is the root of the inflated
2185bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *         XML file.
2195bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     */
2205bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    public T inflate(int resource, T root) {
2215bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        return inflate(resource, root, root != null);
2225bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    }
2235bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
2245bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    /**
2255bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * Inflate a new hierarchy from the specified xml node. Throws
2265bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * InflaterException if there is an error. *
2275bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * <p>
2285bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * <em><strong>Important</strong></em>&nbsp;&nbsp;&nbsp;For performance
2295bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * reasons, inflation relies heavily on pre-processing of XML files
2305bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * that is done at build time. Therefore, it is not currently possible to
2315bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * use inflater with an XmlPullParser over a plain XML file at runtime.
2325bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *
2335bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * @param parser XML dom node containing the description of the
2345bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *        hierarchy.
2355bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * @param root Optional parent of the generated hierarchy.
2365bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * @return The root of the inflated hierarchy. If root was supplied,
2375bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *         this is the that; otherwise it is the root of the inflated
2385bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *         XML file.
2395bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     */
2405bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    public T inflate(XmlPullParser parser, T root) {
2415bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        return inflate(parser, root, root != null);
2425bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    }
2435bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
2445bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    /**
2455bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * Inflate a new hierarchy from the specified xml resource. Throws
2465bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * InflaterException if there is an error.
2475bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *
2485bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * @param resource ID for an XML resource to load (e.g.,
2495bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *        <code>R.layout.main_page</code>)
2505bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * @param root Optional root to be the parent of the generated hierarchy (if
2515bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *        <em>attachToRoot</em> is true), or else simply an object that
2525bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *        provides a set of values for root of the returned
2535bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *        hierarchy (if <em>attachToRoot</em> is false.)
2545bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * @param attachToRoot Whether the inflated hierarchy should be attached to
2555bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *        the root parameter?
2565bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * @return The root of the inflated hierarchy. If root was supplied and
2575bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *         attachToRoot is true, this is root; otherwise it is the root of
2585bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *         the inflated XML file.
2595bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     */
2605bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    public T inflate(int resource, T root, boolean attachToRoot) {
2615bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        if (DEBUG) Log.v(TAG, "INFLATING from resource: " + resource);
2625bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        XmlResourceParser parser = getContext().getResources().getXml(resource);
2635bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        try {
2645bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            return inflate(parser, root, attachToRoot);
2655bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        } finally {
2665bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            parser.close();
2675bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        }
2685bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    }
2695bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
2705bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    /**
2715bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * Inflate a new hierarchy from the specified XML node. Throws
2725bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * InflaterException if there is an error.
2735bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * <p>
2745bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * <em><strong>Important</strong></em>&nbsp;&nbsp;&nbsp;For performance
2755bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * reasons, inflation relies heavily on pre-processing of XML files
2765bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * that is done at build time. Therefore, it is not currently possible to
2775bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * use inflater with an XmlPullParser over a plain XML file at runtime.
2785bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *
2795bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * @param parser XML dom node containing the description of the
2805bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *        hierarchy.
2815bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * @param root Optional to be the parent of the generated hierarchy (if
2825bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *        <em>attachToRoot</em> is true), or else simply an object that
2835bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *        provides a set of values for root of the returned
2845bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *        hierarchy (if <em>attachToRoot</em> is false.)
2855bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * @param attachToRoot Whether the inflated hierarchy should be attached to
2865bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *        the root parameter?
2875bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * @return The root of the inflated hierarchy. If root was supplied and
2885bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *         attachToRoot is true, this is root; otherwise it is the root of
2895bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *         the inflated XML file.
2905bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     */
2915bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    public T inflate(XmlPullParser parser, T root, boolean attachToRoot) {
2925bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        synchronized (mConstructorArgs) {
2935bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            final AttributeSet attrs = Xml.asAttributeSet(parser);
2945bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            mConstructorArgs[0] = mContext;
2955bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            T result;
2965bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
2975bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            try {
2985bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                // Look for the root node.
2995bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                int type;
3005bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                while ((type = parser.next()) != XmlPullParser.START_TAG
3015bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                        && type != XmlPullParser.END_DOCUMENT) {
3025bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                }
3035bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
3045bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                if (type != XmlPullParser.START_TAG) {
3055bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                    throw new InflateException(parser.getPositionDescription()
3065bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                            + ": No start tag found!");
3075bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                }
3085bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
3095bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                if (DEBUG) {
3105bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                    Log.v(TAG, "**************************");
3115bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                    Log.v(TAG, "Creating root: "
3125bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                            + parser.getName());
3135bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                    Log.v(TAG, "**************************");
3145bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                }
3155bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                // Temp is the root that was found in the xml
3165bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                T xmlRoot = createItemFromTag(parser, parser.getName(), attrs);
3175bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
3185bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                result = onMergeRoots(root, attachToRoot, xmlRoot);
3195bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
3205bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                if (DEBUG) Log.v(TAG, "-----> start inflating children");
3215bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                // Inflate all children under temp
3225bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                rInflate(parser, result, attrs);
3235bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                if (DEBUG) Log.v(TAG, "-----> done inflating children");
3245bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            } catch (XmlPullParserException e) {
3255bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                InflateException ex = new InflateException(e.getMessage());
3265bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                ex.initCause(e);
3275bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                throw ex;
3285bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            } catch (IOException e) {
3295bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                InflateException ex = new InflateException(
3305bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                        parser.getPositionDescription()
3315bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                                + ": " + e.getMessage());
3325bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                ex.initCause(e);
3335bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                throw ex;
3345bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            }
3355bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
3365bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            return result;
3375bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        }
3385bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    }
3395bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
3405bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    /**
3415bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * Low-level function for instantiating by name. This attempts to
3425bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * instantiate class of the given <var>name</var> found in this
3435bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * inflater's ClassLoader.
3445bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *
3455bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * <p>
3465bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * There are two things that can happen in an error case: either the
3475bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * exception describing the error will be thrown, or a null will be
3485bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * returned. You must deal with both possibilities -- the former will happen
3495bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * the first time createItem() is called for a class of a particular name,
3505bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * the latter every time there-after for that class name.
3515bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *
3525bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * @param name The full name of the class to be instantiated.
3535bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * @param attrs The XML attributes supplied for this instance.
3545bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *
3555bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * @return The newly instantiated item, or null.
3565bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     */
3575bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    public final T createItem(String name, String prefix, AttributeSet attrs)
3585bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            throws ClassNotFoundException, InflateException {
3595bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        Constructor constructor = sConstructorMap.get(name);
3605bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
3615bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        try {
3625bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            if (constructor == null) {
3635bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                // Class not found in the cache, see if it's real,
3645bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                // and try to add it
3655bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                Class<?> clazz = mContext.getClassLoader().loadClass(
3665bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                        prefix != null ? (prefix + name) : name);
3675bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                constructor = clazz.getConstructor(mConstructorSignature);
3685bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                constructor.setAccessible(true);
3695bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                sConstructorMap.put(name, constructor);
3705bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            }
3715bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
3725bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            Object[] args = mConstructorArgs;
3735bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            args[1] = attrs;
3745bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            //noinspection unchecked
3755bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            return (T) constructor.newInstance(args);
3765bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        } catch (NoSuchMethodException e) {
3775bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            InflateException ie = new InflateException(attrs.getPositionDescription()
3785bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                    + ": Error inflating class "
3795bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                    + (prefix != null ? (prefix + name) : name));
3805bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            ie.initCause(e);
3815bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            throw ie;
3825bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
3835bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        } catch (ClassNotFoundException e) {
3845bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            // If loadClass fails, we should propagate the exception.
3855bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            throw e;
3865bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        } catch (Exception e) {
3875bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            InflateException ie = new InflateException(attrs.getPositionDescription()
3885bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                    + ": Error inflating class "
3895bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                    + (prefix != null ? (prefix + name) : name));
3905bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            ie.initCause(e);
3915bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            throw ie;
3925bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        }
3935bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    }
3945bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
3955bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    /**
3965bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * This routine is responsible for creating the correct subclass of item
3975bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * given the xml element name. Override it to handle custom item objects. If
3985bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * you override this in your subclass be sure to call through to
3995bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * super.onCreateItem(name) for names you do not recognize.
4005bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *
4015bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * @param name The fully qualified class name of the item to be create.
4025bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * @param attrs An AttributeSet of attributes to apply to the item.
4035bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * @return The item created.
4045bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     */
4055bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    protected T onCreateItem(String name, AttributeSet attrs) throws ClassNotFoundException {
4065bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        return createItem(name, mDefaultPackage, attrs);
4075bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    }
4085bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
4095bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    private T createItemFromTag(XmlPullParser parser, String name, AttributeSet attrs) {
4105bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        if (DEBUG) Log.v(TAG, "******** Creating item: " + name);
4115bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
4125bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        try {
4135bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            T item = (mFactory == null) ? null : mFactory.onCreateItem(name, mContext, attrs);
4145bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
4155bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            if (item == null) {
4165bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                if (-1 == name.indexOf('.')) {
4175bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                    item = onCreateItem(name, attrs);
4185bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                } else {
4195bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                    item = createItem(name, null, attrs);
4205bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                }
4215bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            }
4225bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
4235bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            if (DEBUG) Log.v(TAG, "Created item is: " + item);
4245bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            return item;
4255bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
4265bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        } catch (InflateException e) {
4275bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            throw e;
4285bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
4295bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        } catch (Exception e) {
4305bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            InflateException ie = new InflateException(attrs
4315bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                    .getPositionDescription()
4325bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                    + ": Error inflating class " + name);
4335bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            ie.initCause(e);
4345bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            throw ie;
4355bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        }
4365bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    }
4375bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
4385bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    /**
4395bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * Recursive method used to descend down the xml hierarchy and instantiate
4405bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * items, instantiate their children, and then call onFinishInflate().
4415bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     */
4425bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    private void rInflate(XmlPullParser parser, T node, final AttributeSet attrs)
4435bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            throws XmlPullParserException, IOException {
4445bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        final int depth = parser.getDepth();
4455bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
4465bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        int type;
4475bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        while (((type = parser.next()) != XmlPullParser.END_TAG ||
4485bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
4495bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
4505bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            if (type != XmlPullParser.START_TAG) {
4515bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                continue;
4525bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            }
4535bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
4545bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            if (onCreateCustomFromTag(parser, node, attrs)) {
4555bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam                continue;
4565bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            }
4575bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
4585bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            if (DEBUG) Log.v(TAG, "Now inflating tag: " + parser.getName());
4595bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            String name = parser.getName();
4605bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
4615bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            T item = createItemFromTag(parser, name, attrs);
4625bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
4635bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            if (DEBUG) Log.v(TAG, "Creating params from parent: " + node);
4645bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
4655bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            onAddChildItem(node, item);
4665bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
4675bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
4685bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            if (DEBUG) Log.v(TAG, "-----> start inflating children");
4695bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            rInflate(parser, item, attrs);
4705bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            if (DEBUG) Log.v(TAG, "-----> done inflating children");
4715bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        }
4725bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    }
4735bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
4745bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    /**
4755bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * Before this inflater tries to create an item from the tag, this method
4765bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * will be called. The parser will be pointing to the start of a tag, you
4775bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * must stop parsing and return when you reach the end of this element!
4785bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *
4795bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * @param parser XML dom node containing the description of the hierarchy.
4805bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * @param node The item that should be the parent of whatever you create.
4815bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * @param attrs An AttributeSet of attributes to apply to the item.
4825bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     * @return Whether you created a custom object (true), or whether this
4835bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     *         inflater should proceed to create an item.
4845bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam     */
4855bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    protected boolean onCreateCustomFromTag(XmlPullParser parser, T node,
4865bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam            final AttributeSet attrs) throws XmlPullParserException {
4875bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        return false;
4885bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    }
4895bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
4905bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    protected abstract void onAddChildItem(T parent, T child);
4915bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam
4925bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    protected T onMergeRoots(T givenRoot, boolean attachToGivenRoot, T xmlRoot) {
4935bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam        return xmlRoot;
4945bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam    }
4955bf291fde3dfd64f264d525534730514a279c8fcMaurice Lam}
496