1ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski/*
2ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * Copyright (C) 2011 The Android Open Source Project
3ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski *
4ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * Licensed under the Apache License, Version 2.0 (the "License");
5ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * you may not use this file except in compliance with the License.
6ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * You may obtain a copy of the License at
7ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski *
8ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski *      http://www.apache.org/licenses/LICENSE-2.0
9ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski *
10ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * Unless required by applicable law or agreed to in writing, software
11ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * distributed under the License is distributed on an "AS IS" BASIS,
12ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * See the License for the specific language governing permissions and
14ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * limitations under the License.
15ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski */
16ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
17ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinskipackage com.android.layoutlib.bridge.impl;
18ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
19ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
20ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinskiimport org.xmlpull.v1.XmlPullParser;
21ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinskiimport org.xmlpull.v1.XmlPullParserException;
22ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
23442aee6bc1abfb143dcfa1ba60d696e576d066c4Deepanshu Guptaimport android.annotation.NonNull;
24442aee6bc1abfb143dcfa1ba60d696e576d066c4Deepanshu Guptaimport android.annotation.Nullable;
25442aee6bc1abfb143dcfa1ba60d696e576d066c4Deepanshu Gupta
26ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinskiimport java.io.BufferedInputStream;
27ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinskiimport java.io.ByteArrayInputStream;
28ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinskiimport java.io.File;
29ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinskiimport java.io.FileInputStream;
30ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinskiimport java.io.FileNotFoundException;
31ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinskiimport java.io.IOException;
32ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinskiimport java.io.InputStream;
33ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
34ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski/**
35ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * A factory for {@link XmlPullParser}.
36ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski *
37ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski */
38ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinskipublic class ParserFactory {
39ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
404bf28b6af45f76a33b466ad8157aa1844716cbcbDeepanshu Gupta    public final static boolean LOG_PARSER = false;
414bf28b6af45f76a33b466ad8157aa1844716cbcbDeepanshu Gupta
424bf28b6af45f76a33b466ad8157aa1844716cbcbDeepanshu Gupta    // Used to get a new XmlPullParser from the client.
434bf28b6af45f76a33b466ad8157aa1844716cbcbDeepanshu Gupta    @Nullable
44246fef51fb2edd88af7fe801a20dd6d21a6ec36eDeepanshu Gupta    private static com.android.ide.common.rendering.api.ParserFactory sParserFactory;
45ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
46246fef51fb2edd88af7fe801a20dd6d21a6ec36eDeepanshu Gupta    public static void setParserFactory(
47246fef51fb2edd88af7fe801a20dd6d21a6ec36eDeepanshu Gupta            @Nullable com.android.ide.common.rendering.api.ParserFactory parserFactory) {
48246fef51fb2edd88af7fe801a20dd6d21a6ec36eDeepanshu Gupta        sParserFactory = parserFactory;
494bf28b6af45f76a33b466ad8157aa1844716cbcbDeepanshu Gupta    }
504bf28b6af45f76a33b466ad8157aa1844716cbcbDeepanshu Gupta
514bf28b6af45f76a33b466ad8157aa1844716cbcbDeepanshu Gupta    @NonNull
524bf28b6af45f76a33b466ad8157aa1844716cbcbDeepanshu Gupta    public static XmlPullParser create(@NonNull File f)
53ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            throws XmlPullParserException, FileNotFoundException {
5497ec082dda2f1ca25d38437716583c8254740866Deepanshu Gupta        return create(f, false);
55ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski    }
56ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
5797ec082dda2f1ca25d38437716583c8254740866Deepanshu Gupta    public static XmlPullParser create(@NonNull File f, boolean isLayout)
5897ec082dda2f1ca25d38437716583c8254740866Deepanshu Gupta      throws XmlPullParserException, FileNotFoundException {
5997ec082dda2f1ca25d38437716583c8254740866Deepanshu Gupta        InputStream stream = new FileInputStream(f);
6097ec082dda2f1ca25d38437716583c8254740866Deepanshu Gupta        return create(stream, f.getName(), f.length(), isLayout);
6197ec082dda2f1ca25d38437716583c8254740866Deepanshu Gupta    }
624bf28b6af45f76a33b466ad8157aa1844716cbcbDeepanshu Gupta    @NonNull
634bf28b6af45f76a33b466ad8157aa1844716cbcbDeepanshu Gupta    public static XmlPullParser create(@NonNull InputStream stream, @Nullable String name)
64ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        throws XmlPullParserException {
6597ec082dda2f1ca25d38437716583c8254740866Deepanshu Gupta        return create(stream, name, -1, false);
66ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski    }
67ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
684bf28b6af45f76a33b466ad8157aa1844716cbcbDeepanshu Gupta    @NonNull
694bf28b6af45f76a33b466ad8157aa1844716cbcbDeepanshu Gupta    private static XmlPullParser create(@NonNull InputStream stream, @Nullable String name,
7097ec082dda2f1ca25d38437716583c8254740866Deepanshu Gupta            long size, boolean isLayout) throws XmlPullParserException {
714bf28b6af45f76a33b466ad8157aa1844716cbcbDeepanshu Gupta        XmlPullParser parser = instantiateParser(name);
72ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
73ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        stream = readAndClose(stream, name, size);
74ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
7527f5ec1e32cbdb5e3050e7d552e01d479d8248acDiego Perez        parser.setInput(stream, null);
7697ec082dda2f1ca25d38437716583c8254740866Deepanshu Gupta        if (isLayout) {
7797ec082dda2f1ca25d38437716583c8254740866Deepanshu Gupta            try {
7897ec082dda2f1ca25d38437716583c8254740866Deepanshu Gupta                return new LayoutParserWrapper(parser).peekTillLayoutStart();
7997ec082dda2f1ca25d38437716583c8254740866Deepanshu Gupta            } catch (IOException e) {
8097ec082dda2f1ca25d38437716583c8254740866Deepanshu Gupta                throw new XmlPullParserException(null, parser, e);
8197ec082dda2f1ca25d38437716583c8254740866Deepanshu Gupta            }
8297ec082dda2f1ca25d38437716583c8254740866Deepanshu Gupta        }
83ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        return parser;
84ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski    }
85ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
864bf28b6af45f76a33b466ad8157aa1844716cbcbDeepanshu Gupta    @NonNull
874bf28b6af45f76a33b466ad8157aa1844716cbcbDeepanshu Gupta    public static XmlPullParser instantiateParser(@Nullable String name)
884bf28b6af45f76a33b466ad8157aa1844716cbcbDeepanshu Gupta            throws XmlPullParserException {
89246fef51fb2edd88af7fe801a20dd6d21a6ec36eDeepanshu Gupta        if (sParserFactory == null) {
904bf28b6af45f76a33b466ad8157aa1844716cbcbDeepanshu Gupta            throw new XmlPullParserException("ParserFactory not initialized.");
91ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        }
92246fef51fb2edd88af7fe801a20dd6d21a6ec36eDeepanshu Gupta        XmlPullParser parser = sParserFactory.createParser(name);
93ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
94ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        return parser;
95ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski    }
96ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
974bf28b6af45f76a33b466ad8157aa1844716cbcbDeepanshu Gupta    @NonNull
984bf28b6af45f76a33b466ad8157aa1844716cbcbDeepanshu Gupta    private static InputStream readAndClose(@NonNull InputStream stream, @Nullable String name,
994bf28b6af45f76a33b466ad8157aa1844716cbcbDeepanshu Gupta            long size) throws XmlPullParserException {
100ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        // just a sanity check. It's doubtful we'll have such big files!
101ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        if (size > Integer.MAX_VALUE) {
102ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            throw new XmlPullParserException("File " + name + " is too big to be parsed");
103ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        }
104ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        int intSize = (int) size;
105ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
106ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        // create a buffered reader to facilitate reading.
107ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        BufferedInputStream bufferedStream = new BufferedInputStream(stream);
108ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        try {
109ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            int avail;
110ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            if (intSize != -1) {
111ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                avail = intSize;
112ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            } else {
113ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                // get the size to read.
114ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                avail = bufferedStream.available();
115ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            }
116ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
117ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            // create the initial buffer and read it.
118ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            byte[] buffer = new byte[avail];
119ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            int read = stream.read(buffer);
120ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
121ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            // this is the easy case.
122ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            if (read == intSize) {
123ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                return new ByteArrayInputStream(buffer);
124ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            }
125ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
126ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            // check if there is more to read (read() does not necessarily read all that
127ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            // available() returned!)
128ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            while ((avail = bufferedStream.available()) > 0) {
129ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                if (read + avail > buffer.length) {
130ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                    // just allocate what is needed. We're mostly reading small files
131ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                    // so it shouldn't be too problematic.
132ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                    byte[] moreBuffer = new byte[read + avail];
133ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                    System.arraycopy(buffer, 0, moreBuffer, 0, read);
134ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                    buffer = moreBuffer;
135ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                }
136ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
137ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                read += stream.read(buffer, read, avail);
138ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            }
139ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
140ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            // return a new stream encapsulating this buffer.
141ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            return new ByteArrayInputStream(buffer);
142ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
143ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        } catch (IOException e) {
144ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            throw new XmlPullParserException("Failed to read " + name, null, e);
145ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        } finally {
146ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            try {
147ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                bufferedStream.close();
1484bf28b6af45f76a33b466ad8157aa1844716cbcbDeepanshu Gupta            } catch (IOException ignored) {
149ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            }
150ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        }
151ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski    }
152ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski}
153