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