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

Commit 03dec841 authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

Refine TypedXmlPullParser API interface.

Techincally our additions should match the shape of the upstream
XmlPullParser interface, which throws XmlPullParserException when
there is a parsing issue; IOException should be reserved for actual
disk I/O issues.

Improve BinaryXmlPullParser handling of invalid inputs to match
how ForcedTypedXmlPullParser throws on any parsing trouble.

Bug: 171832118
Test: atest FrameworksCoreTests:android.util.XmlTest
Test: atest FrameworksCoreTests:android.util.BinaryXmlTest
Change-Id: I8f0e95d536b53570055a9e174f0849cf635da48c
parent bfdd1a33
Loading
Loading
Loading
Loading
+49 −24
Original line number Diff line number Diff line
@@ -20,8 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;

import org.xmlpull.v1.XmlPullParser;

import java.io.IOException;
import org.xmlpull.v1.XmlPullParserException;

/**
 * Specialization of {@link XmlPullParser} which adds explicit methods to
@@ -31,67 +30,93 @@ import java.io.IOException;
 */
public interface TypedXmlPullParser extends XmlPullParser {
    /**
     * @return decoded strongly-typed {@link #getAttributeValue}, or
     *         {@code null} if malformed or undefined
     * @return decoded strongly-typed {@link #getAttributeValue}
     * @throws XmlPullParserException if the value is malformed or undefined
     */
    @Nullable byte[] getAttributeBytesHex(@Nullable String namespace, @NonNull String name)
            throws IOException;
    @NonNull byte[] getAttributeBytesHex(@Nullable String namespace, @NonNull String name)
            throws XmlPullParserException;

    /**
     * @return decoded strongly-typed {@link #getAttributeValue}, or
     *         {@code null} if malformed or undefined
     * @return decoded strongly-typed {@link #getAttributeValue}
     * @throws XmlPullParserException if the value is malformed or undefined
     */
    @Nullable byte[] getAttributeBytesBase64(@Nullable String namespace, @NonNull String name)
            throws IOException;
    @NonNull byte[] getAttributeBytesBase64(@Nullable String namespace, @NonNull String name)
            throws XmlPullParserException;

    /**
     * @return decoded strongly-typed {@link #getAttributeValue}
     * @throws IOException if the value is malformed or undefined
     * @throws XmlPullParserException if the value is malformed or undefined
     */
    int getAttributeInt(@Nullable String namespace, @NonNull String name)
            throws IOException;
            throws XmlPullParserException;

    /**
     * @return decoded strongly-typed {@link #getAttributeValue}
     * @throws IOException if the value is malformed or undefined
     * @throws XmlPullParserException if the value is malformed or undefined
     */
    int getAttributeIntHex(@Nullable String namespace, @NonNull String name)
            throws IOException;
            throws XmlPullParserException;

    /**
     * @return decoded strongly-typed {@link #getAttributeValue}
     * @throws IOException if the value is malformed or undefined
     * @throws XmlPullParserException if the value is malformed or undefined
     */
    long getAttributeLong(@Nullable String namespace, @NonNull String name)
            throws IOException;
            throws XmlPullParserException;

    /**
     * @return decoded strongly-typed {@link #getAttributeValue}
     * @throws IOException if the value is malformed or undefined
     * @throws XmlPullParserException if the value is malformed or undefined
     */
    long getAttributeLongHex(@Nullable String namespace, @NonNull String name)
            throws IOException;
            throws XmlPullParserException;

    /**
     * @return decoded strongly-typed {@link #getAttributeValue}
     * @throws IOException if the value is malformed or undefined
     * @throws XmlPullParserException if the value is malformed or undefined
     */
    float getAttributeFloat(@Nullable String namespace, @NonNull String name)
            throws IOException;
            throws XmlPullParserException;

    /**
     * @return decoded strongly-typed {@link #getAttributeValue}
     * @throws IOException if the value is malformed or undefined
     * @throws XmlPullParserException if the value is malformed or undefined
     */
    double getAttributeDouble(@Nullable String namespace, @NonNull String name)
            throws IOException;
            throws XmlPullParserException;

    /**
     * @return decoded strongly-typed {@link #getAttributeValue}
     * @throws IOException if the value is malformed or undefined
     * @throws XmlPullParserException if the value is malformed or undefined
     */
    boolean getAttributeBoolean(@Nullable String namespace, @NonNull String name)
            throws IOException;
            throws XmlPullParserException;

    /**
     * @return decoded strongly-typed {@link #getAttributeValue}, otherwise
     *         default value if the value is malformed or undefined
     */
    default @Nullable byte[] getAttributeBytesHex(@Nullable String namespace,
            @NonNull String name, @Nullable byte[] defaultValue) {
        try {
            return getAttributeBytesHex(namespace, name);
        } catch (Exception ignored) {
            return defaultValue;
        }
    }

    /**
     * @return decoded strongly-typed {@link #getAttributeValue}, otherwise
     *         default value if the value is malformed or undefined
     */
    default @Nullable byte[] getAttributeBytesBase64(@Nullable String namespace,
            @NonNull String name, @Nullable byte[] defaultValue) {
        try {
            return getAttributeBytesBase64(namespace, name);
        } catch (Exception ignored) {
            return defaultValue;
        }
    }

    /**
     * @return decoded strongly-typed {@link #getAttributeValue}, otherwise
+3 −3
Original line number Diff line number Diff line
@@ -36,21 +36,21 @@ public interface TypedXmlSerializer extends XmlSerializer {
     * canonicalized, similar to {@link String#intern()}.
     */
    @NonNull XmlSerializer attributeInterned(@Nullable String namespace, @NonNull String name,
            @Nullable String value) throws IOException;
            @NonNull String value) throws IOException;

    /**
     * Encode the given strongly-typed value and serialize using
     * {@link #attribute(String, String, String)}.
     */
    @NonNull XmlSerializer attributeBytesHex(@Nullable String namespace, @NonNull String name,
            byte[] value) throws IOException;
            @NonNull byte[] value) throws IOException;

    /**
     * Encode the given strongly-typed value and serialize using
     * {@link #attribute(String, String, String)}.
     */
    @NonNull XmlSerializer attributeBytesBase64(@Nullable String namespace, @NonNull String name,
            byte[] value) throws IOException;
            @NonNull byte[] value) throws IOException;

    /**
     * Encode the given strongly-typed value and serialize using
+95 −43
Original line number Diff line number Diff line
@@ -418,13 +418,14 @@ public final class BinaryXmlPullParser implements TypedXmlPullParser {
     * Search through the pool of currently allocated {@link Attribute}
     * instances for one that matches the given name.
     */
    private @NonNull Attribute findAttribute(@NonNull String name) throws IOException {
    private @NonNull Attribute findAttribute(@NonNull String name)
            throws XmlPullParserException {
        for (int i = 0; i < mAttributeCount; i++) {
            if (Objects.equals(mAttributes[i].name, name)) {
                return mAttributes[i];
            }
        }
        throw new IOException("Missing attribute " + name);
        throw new XmlPullParserException("Missing attribute " + name);
    }

    @Override
@@ -432,7 +433,7 @@ public final class BinaryXmlPullParser implements TypedXmlPullParser {
        if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
        try {
            return findAttribute(name).getValueString();
        } catch (IOException e) {
        } catch (XmlPullParserException e) {
            // Missing attributes default to null
            return null;
        }
@@ -444,55 +445,64 @@ public final class BinaryXmlPullParser implements TypedXmlPullParser {
    }

    @Override
    public byte[] getAttributeBytesHex(String namespace, String name) throws IOException {
    public byte[] getAttributeBytesHex(String namespace, String name)
            throws XmlPullParserException {
        if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
        return findAttribute(name).getValueBytesHex();
    }

    @Override
    public byte[] getAttributeBytesBase64(String namespace, String name) throws IOException {
    public byte[] getAttributeBytesBase64(String namespace, String name)
            throws XmlPullParserException {
        if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
        return findAttribute(name).getValueBytesBase64();
    }

    @Override
    public int getAttributeInt(String namespace, String name) throws IOException {
    public int getAttributeInt(String namespace, String name)
            throws XmlPullParserException {
        if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
        return findAttribute(name).getValueInt();
    }

    @Override
    public int getAttributeIntHex(String namespace, String name) throws IOException {
    public int getAttributeIntHex(String namespace, String name)
            throws XmlPullParserException {
        if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
        return findAttribute(name).getValueIntHex();
    }

    @Override
    public long getAttributeLong(String namespace, String name) throws IOException {
    public long getAttributeLong(String namespace, String name)
            throws XmlPullParserException {
        if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
        return findAttribute(name).getValueLong();
    }

    @Override
    public long getAttributeLongHex(String namespace, String name) throws IOException {
    public long getAttributeLongHex(String namespace, String name)
            throws XmlPullParserException {
        if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
        return findAttribute(name).getValueLongHex();
    }

    @Override
    public float getAttributeFloat(String namespace, String name) throws IOException {
    public float getAttributeFloat(String namespace, String name)
            throws XmlPullParserException {
        if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
        return findAttribute(name).getValueFloat();
    }

    @Override
    public double getAttributeDouble(String namespace, String name) throws IOException {
    public double getAttributeDouble(String namespace, String name)
            throws XmlPullParserException {
        if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
        return findAttribute(name).getValueDouble();
    }

    @Override
    public boolean getAttributeBoolean(String namespace, String name) throws IOException {
    public boolean getAttributeBoolean(String namespace, String name)
            throws XmlPullParserException {
        if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
        return findAttribute(name).getValueBoolean();
    }
@@ -742,7 +752,7 @@ public final class BinaryXmlPullParser implements TypedXmlPullParser {
            }
        }

        public @Nullable byte[] getValueBytesHex() throws IOException {
        public @Nullable byte[] getValueBytesHex() throws XmlPullParserException {
            switch (type) {
                case TYPE_NULL:
                    return null;
@@ -750,13 +760,18 @@ public final class BinaryXmlPullParser implements TypedXmlPullParser {
                case TYPE_BYTES_BASE64:
                    return valueBytes;
                case TYPE_STRING:
                case TYPE_STRING_INTERNED:
                    try {
                        return hexStringToBytes(valueString);
                    } catch (Exception e) {
                        throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
                    }
                default:
                    throw new IOException("Invalid conversion from " + type);
                    throw new XmlPullParserException("Invalid conversion from " + type);
            }
        }

        public @Nullable byte[] getValueBytesBase64() throws IOException {
        public @Nullable byte[] getValueBytesBase64() throws XmlPullParserException {
            switch (type) {
                case TYPE_NULL:
                    return null;
@@ -764,98 +779,135 @@ public final class BinaryXmlPullParser implements TypedXmlPullParser {
                case TYPE_BYTES_BASE64:
                    return valueBytes;
                case TYPE_STRING:
                case TYPE_STRING_INTERNED:
                    try {
                        return Base64.decode(valueString, Base64.NO_WRAP);
                    } catch (Exception e) {
                        throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
                    }
                default:
                    throw new IOException("Invalid conversion from " + type);
                    throw new XmlPullParserException("Invalid conversion from " + type);
            }
        }

        public int getValueInt() throws IOException {
        public int getValueInt() throws XmlPullParserException {
            switch (type) {
                case TYPE_INT:
                case TYPE_INT_HEX:
                    return valueInt;
                case TYPE_STRING:
                case TYPE_STRING_INTERNED:
                    try {
                        return Integer.parseInt(valueString);
                    } catch (Exception e) {
                        throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
                    }
                default:
                    throw new IOException("Invalid conversion from " + type);
                    throw new XmlPullParserException("Invalid conversion from " + type);
            }
        }

        public int getValueIntHex() throws IOException {
        public int getValueIntHex() throws XmlPullParserException {
            switch (type) {
                case TYPE_INT:
                case TYPE_INT_HEX:
                    return valueInt;
                case TYPE_STRING:
                case TYPE_STRING_INTERNED:
                    try {
                        return Integer.parseInt(valueString, 16);
                    } catch (Exception e) {
                        throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
                    }
                default:
                    throw new IOException("Invalid conversion from " + type);
                    throw new XmlPullParserException("Invalid conversion from " + type);
            }
        }

        public long getValueLong() throws IOException {
        public long getValueLong() throws XmlPullParserException {
            switch (type) {
                case TYPE_LONG:
                case TYPE_LONG_HEX:
                    return valueLong;
                case TYPE_STRING:
                case TYPE_STRING_INTERNED:
                    try {
                        return Long.parseLong(valueString);
                    } catch (Exception e) {
                        throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
                    }
                default:
                    throw new IOException("Invalid conversion from " + type);
                    throw new XmlPullParserException("Invalid conversion from " + type);
            }
        }

        public long getValueLongHex() throws IOException {
        public long getValueLongHex() throws XmlPullParserException {
            switch (type) {
                case TYPE_LONG:
                case TYPE_LONG_HEX:
                    return valueLong;
                case TYPE_STRING:
                case TYPE_STRING_INTERNED:
                    try {
                        return Long.parseLong(valueString, 16);
                    } catch (Exception e) {
                        throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
                    }
                default:
                    throw new IOException("Invalid conversion from " + type);
                    throw new XmlPullParserException("Invalid conversion from " + type);
            }
        }

        public float getValueFloat() throws IOException {
        public float getValueFloat() throws XmlPullParserException {
            switch (type) {
                case TYPE_FLOAT:
                    return valueFloat;
                case TYPE_STRING:
                case TYPE_STRING_INTERNED:
                    try {
                        return Float.parseFloat(valueString);
                    } catch (Exception e) {
                        throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
                    }
                default:
                    throw new IOException("Invalid conversion from " + type);
                    throw new XmlPullParserException("Invalid conversion from " + type);
            }
        }

        public double getValueDouble() throws IOException {
        public double getValueDouble() throws XmlPullParserException {
            switch (type) {
                case TYPE_DOUBLE:
                    return valueDouble;
                case TYPE_STRING:
                case TYPE_STRING_INTERNED:
                    try {
                        return Double.parseDouble(valueString);
                    } catch (Exception e) {
                        throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
                    }
                default:
                    throw new IOException("Invalid conversion from " + type);
                    throw new XmlPullParserException("Invalid conversion from " + type);
            }
        }

        public boolean getValueBoolean() throws IOException {
        public boolean getValueBoolean() throws XmlPullParserException {
            switch (type) {
                case TYPE_BOOLEAN_TRUE:
                    return true;
                case TYPE_BOOLEAN_FALSE:
                    return false;
                case TYPE_STRING:
                case TYPE_STRING_INTERNED:
                    if ("true".equalsIgnoreCase(valueString)) {
                        return true;
                    } else if ("false".equalsIgnoreCase(valueString)) {
                        return false;
                    } else {
                        throw new IOException("Invalid boolean: " + valueString);
                        throw new XmlPullParserException(
                                "Invalid attribute " + name + ": " + valueString);
                    }
                default:
                    throw new IOException("Invalid conversion from " + type);
                    throw new XmlPullParserException("Invalid conversion from " + type);
            }
        }
    }
@@ -865,11 +917,11 @@ public final class BinaryXmlPullParser implements TypedXmlPullParser {
    private final static char[] HEX_DIGITS =
            { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

    private static int toByte(char c) throws IOException {
    private static int toByte(char c) {
        if (c >= '0' && c <= '9') return (c - '0');
        if (c >= 'A' && c <= 'F') return (c - 'A' + 10);
        if (c >= 'a' && c <= 'f') return (c - 'a' + 10);
        throw new IOException("Invalid hex char '" + c + "'");
        throw new IllegalArgumentException("Invalid hex char '" + c + "'");
    }

    static String bytesToHexString(byte[] value) {
@@ -884,10 +936,10 @@ public final class BinaryXmlPullParser implements TypedXmlPullParser {
        return new String(buf);
    }

    static byte[] hexStringToBytes(String value) throws IOException {
    static byte[] hexStringToBytes(String value) {
        final int length = value.length();
        if (length % 2 != 0) {
            throw new IOException("Invalid hex length " + length);
            throw new IllegalArgumentException("Invalid hex length " + length);
        }
        byte[] buffer = new byte[length / 2];
        for (int i = 0; i < length; i += 2) {
+33 −25
Original line number Diff line number Diff line
@@ -140,87 +140,95 @@ public class XmlUtils {
        }

        @Override
        public byte[] getAttributeBytesHex(String namespace, String name) throws IOException {
        public byte[] getAttributeBytesHex(String namespace, String name)
                throws XmlPullParserException {
            try {
                return HexDump.hexStringToByteArray(getAttributeValue(namespace, name));
            } catch (Exception e) {
                throw new IOException("Invalid attribute " + name, e);
                throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
            }
        }

        @Override
        public byte[] getAttributeBytesBase64(String namespace, String name) throws IOException {
        public byte[] getAttributeBytesBase64(String namespace, String name)
                throws XmlPullParserException {
            try {
                return Base64.decode(getAttributeValue(namespace, name), Base64.NO_WRAP);
            } catch (Exception e) {
                throw new IOException("Invalid attribute " + name, e);
                throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
            }
        }

        @Override
        public int getAttributeInt(String namespace, String name) throws IOException {
        public int getAttributeInt(String namespace, String name)
                throws XmlPullParserException {
            try {
                return Integer.parseInt(getAttributeValue(namespace, name));
            } catch (NumberFormatException e) {
                throw new IOException("Invalid attribute " + name, e);
            } catch (Exception e) {
                throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
            }
        }

        @Override
        public int getAttributeIntHex(String namespace, String name) throws IOException {
        public int getAttributeIntHex(String namespace, String name)
                throws XmlPullParserException {
            try {
                return Integer.parseInt(getAttributeValue(namespace, name), 16);
            } catch (NumberFormatException e) {
                throw new IOException("Invalid attribute " + name, e);
            } catch (Exception e) {
                throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
            }
        }

        @Override
        public long getAttributeLong(String namespace, String name) throws IOException {
        public long getAttributeLong(String namespace, String name)
                throws XmlPullParserException {
            try {
                return Long.parseLong(getAttributeValue(namespace, name));
            } catch (NumberFormatException e) {
                throw new IOException("Invalid attribute " + name, e);
            } catch (Exception e) {
                throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
            }
        }

        @Override
        public long getAttributeLongHex(String namespace, String name) throws IOException {
        public long getAttributeLongHex(String namespace, String name)
                throws XmlPullParserException {
            try {
                return Long.parseLong(getAttributeValue(namespace, name), 16);
            } catch (NumberFormatException e) {
                throw new IOException("Invalid attribute " + name, e);
            } catch (Exception e) {
                throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
            }
        }

        @Override
        public float getAttributeFloat(String namespace, String name) throws IOException {
        public float getAttributeFloat(String namespace, String name)
                throws XmlPullParserException {
            try {
                return Float.parseFloat(getAttributeValue(namespace, name));
            } catch (NumberFormatException e) {
                throw new IOException("Invalid attribute " + name, e);
            } catch (Exception e) {
                throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
            }
        }

        @Override
        public double getAttributeDouble(String namespace, String name) throws IOException {
        public double getAttributeDouble(String namespace, String name)
                throws XmlPullParserException {
            try {
                return Double.parseDouble(getAttributeValue(namespace, name));
            } catch (NumberFormatException e) {
                throw new IOException("Invalid attribute " + name, e);
            } catch (Exception e) {
                throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
            }
        }

        @Override
        public boolean getAttributeBoolean(String namespace, String name) throws IOException {
        public boolean getAttributeBoolean(String namespace, String name)
                throws XmlPullParserException {
            final String value = getAttributeValue(namespace, name);
            if ("true".equalsIgnoreCase(value)) {
                return true;
            } else if ("false".equalsIgnoreCase(value)) {
                return false;
            } else {
                throw new IOException("Invalid attribute " + name,
                        new IllegalArgumentException("For input string: \"" + value + "\""));
                throw new XmlPullParserException("Invalid attribute " + name + ": " + value);
            }
        }
    }
+24 −5

File changed.

Preview size limit exceeded, changes collapsed.

Loading