1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.layoutlib.bridge.impl; 18 19 20 import org.xmlpull.v1.XmlPullParser; 21 import org.xmlpull.v1.XmlPullParserException; 22 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 26 import java.io.BufferedInputStream; 27 import java.io.ByteArrayInputStream; 28 import java.io.File; 29 import java.io.FileInputStream; 30 import java.io.FileNotFoundException; 31 import java.io.IOException; 32 import java.io.InputStream; 33 34 /** 35 * A factory for {@link XmlPullParser}. 36 * 37 */ 38 public class ParserFactory { 39 40 public final static boolean LOG_PARSER = false; 41 42 // Used to get a new XmlPullParser from the client. 43 @Nullable 44 private static com.android.ide.common.rendering.api.ParserFactory sParserFactory; 45 setParserFactory( @ullable com.android.ide.common.rendering.api.ParserFactory parserFactory)46 public static void setParserFactory( 47 @Nullable com.android.ide.common.rendering.api.ParserFactory parserFactory) { 48 sParserFactory = parserFactory; 49 } 50 51 @NonNull create(@onNull File f)52 public static XmlPullParser create(@NonNull File f) 53 throws XmlPullParserException, FileNotFoundException { 54 return create(f, false); 55 } 56 create(@onNull File f, boolean isLayout)57 public static XmlPullParser create(@NonNull File f, boolean isLayout) 58 throws XmlPullParserException, FileNotFoundException { 59 InputStream stream = new FileInputStream(f); 60 return create(stream, f.getName(), f.length(), isLayout); 61 } 62 @NonNull create(@onNull InputStream stream, @Nullable String name)63 public static XmlPullParser create(@NonNull InputStream stream, @Nullable String name) 64 throws XmlPullParserException { 65 return create(stream, name, -1, false); 66 } 67 68 @NonNull create(@onNull InputStream stream, @Nullable String name, long size, boolean isLayout)69 private static XmlPullParser create(@NonNull InputStream stream, @Nullable String name, 70 long size, boolean isLayout) throws XmlPullParserException { 71 XmlPullParser parser = instantiateParser(name); 72 73 stream = readAndClose(stream, name, size); 74 75 parser.setInput(stream, null); 76 if (isLayout) { 77 try { 78 return new LayoutParserWrapper(parser).peekTillLayoutStart(); 79 } catch (IOException e) { 80 throw new XmlPullParserException(null, parser, e); 81 } 82 } 83 return parser; 84 } 85 86 @NonNull instantiateParser(@ullable String name)87 public static XmlPullParser instantiateParser(@Nullable String name) 88 throws XmlPullParserException { 89 if (sParserFactory == null) { 90 throw new XmlPullParserException("ParserFactory not initialized."); 91 } 92 XmlPullParser parser = sParserFactory.createParser(name); 93 parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); 94 return parser; 95 } 96 97 @NonNull readAndClose(@onNull InputStream stream, @Nullable String name, long size)98 private static InputStream readAndClose(@NonNull InputStream stream, @Nullable String name, 99 long size) throws XmlPullParserException { 100 // just a sanity check. It's doubtful we'll have such big files! 101 if (size > Integer.MAX_VALUE) { 102 throw new XmlPullParserException("File " + name + " is too big to be parsed"); 103 } 104 int intSize = (int) size; 105 106 // create a buffered reader to facilitate reading. 107 BufferedInputStream bufferedStream = new BufferedInputStream(stream); 108 try { 109 int avail; 110 if (intSize != -1) { 111 avail = intSize; 112 } else { 113 // get the size to read. 114 avail = bufferedStream.available(); 115 } 116 117 // create the initial buffer and read it. 118 byte[] buffer = new byte[avail]; 119 int read = stream.read(buffer); 120 121 // this is the easy case. 122 if (read == intSize) { 123 return new ByteArrayInputStream(buffer); 124 } 125 126 // check if there is more to read (read() does not necessarily read all that 127 // available() returned!) 128 while ((avail = bufferedStream.available()) > 0) { 129 if (read + avail > buffer.length) { 130 // just allocate what is needed. We're mostly reading small files 131 // so it shouldn't be too problematic. 132 byte[] moreBuffer = new byte[read + avail]; 133 System.arraycopy(buffer, 0, moreBuffer, 0, read); 134 buffer = moreBuffer; 135 } 136 137 read += stream.read(buffer, read, avail); 138 } 139 140 // return a new stream encapsulating this buffer. 141 return new ByteArrayInputStream(buffer); 142 143 } catch (IOException e) { 144 throw new XmlPullParserException("Failed to read " + name, null, e); 145 } finally { 146 try { 147 bufferedStream.close(); 148 } catch (IOException ignored) { 149 } 150 } 151 } 152 } 153