1282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski/*
2282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Copyright (C) 2011 The Android Open Source Project
3282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
4282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Licensed under the Apache License, Version 2.0 (the "License");
5282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * you may not use this file except in compliance with the License.
6282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * You may obtain a copy of the License at
7282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
8282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *      http://www.apache.org/licenses/LICENSE-2.0
9282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
10282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Unless required by applicable law or agreed to in writing, software
11282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * distributed under the License is distributed on an "AS IS" BASIS,
12282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * See the License for the specific language governing permissions and
14282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * limitations under the License.
15282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */
16282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
17282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskipackage com.android.layoutlib.bridge.impl;
18282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
19282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
20282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport org.kxml2.io.KXmlParser;
21282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport org.xmlpull.v1.XmlPullParser;
22282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport org.xmlpull.v1.XmlPullParserException;
23282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
24282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.io.BufferedInputStream;
25282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.io.ByteArrayInputStream;
26282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.io.File;
27282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.io.FileInputStream;
28282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.io.FileNotFoundException;
29282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.io.IOException;
30282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.io.InputStream;
31282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
32282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski/**
33282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * A factory for {@link XmlPullParser}.
34282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
35282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */
36282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskipublic class ParserFactory {
37282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
38282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private final static String ENCODING = "UTF-8"; //$NON-NLS-1$
39282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
40282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public final static boolean LOG_PARSER = false;
41282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
42282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public static XmlPullParser create(File f)
43282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            throws XmlPullParserException, FileNotFoundException {
44282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        InputStream stream = new FileInputStream(f);
45282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return create(stream, f.getName(), f.length());
46282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
47282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
48282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public static XmlPullParser create(InputStream stream, String name)
49282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        throws XmlPullParserException {
50282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return create(stream, name, -1);
51282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
52282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
53282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private static XmlPullParser create(InputStream stream, String name, long size)
54282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            throws XmlPullParserException {
55282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        KXmlParser parser = instantiateParser(name);
56282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
57282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        stream = readAndClose(stream, name, size);
58282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
59282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        parser.setInput(stream, ENCODING);
60282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return parser;
61282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
62282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
63282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private static KXmlParser instantiateParser(String name) throws XmlPullParserException {
64282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        KXmlParser parser;
65282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (name != null) {
66282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            parser = new CustomParser(name);
67282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else {
68282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            parser = new KXmlParser();
69282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
70282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
71282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return parser;
72282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
73282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
74282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private static InputStream readAndClose(InputStream stream, String name, long size)
75282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            throws XmlPullParserException {
76282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // just a sanity check. It's doubtful we'll have such big files!
77282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (size > Integer.MAX_VALUE) {
78282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            throw new XmlPullParserException("File " + name + " is too big to be parsed");
79282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
80282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        int intSize = (int) size;
81282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
82282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // create a buffered reader to facilitate reading.
83282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        BufferedInputStream bufferedStream = new BufferedInputStream(stream);
84282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        try {
85282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            int avail;
86282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (intSize != -1) {
87282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                avail = intSize;
88282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            } else {
89282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                // get the size to read.
90282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                avail = bufferedStream.available();
91282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
92282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
93282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // create the initial buffer and read it.
94282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            byte[] buffer = new byte[avail];
95282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            int read = stream.read(buffer);
96282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
97282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // this is the easy case.
98282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (read == intSize) {
99282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                return new ByteArrayInputStream(buffer);
100282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
101282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
102282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // check if there is more to read (read() does not necessarily read all that
103282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // available() returned!)
104282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            while ((avail = bufferedStream.available()) > 0) {
105282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                if (read + avail > buffer.length) {
106282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    // just allocate what is needed. We're mostly reading small files
107282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    // so it shouldn't be too problematic.
108282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    byte[] moreBuffer = new byte[read + avail];
109282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    System.arraycopy(buffer, 0, moreBuffer, 0, read);
110282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    buffer = moreBuffer;
111282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
112282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
113282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                read += stream.read(buffer, read, avail);
114282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
115282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
116282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // return a new stream encapsulating this buffer.
117282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return new ByteArrayInputStream(buffer);
118282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
119282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } catch (IOException e) {
120282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            throw new XmlPullParserException("Failed to read " + name, null, e);
121282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } finally {
122282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            try {
123282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                bufferedStream.close();
124282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            } catch (IOException e) {
125282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
126282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
127282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
128282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
129282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private static class CustomParser extends KXmlParser {
130282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        private final String mName;
131282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
132282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        CustomParser(String name) {
133282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            super();
134282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mName = name;
135282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
136282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
137282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        @Override
138282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        public String toString() {
139282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return mName;
140282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
141282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
142282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
143