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 private final static String ENCODING = "UTF-8"; //$NON-NLS-1$ 43 44 // Used to get a new XmlPullParser from the client. 45 @Nullable 46 private static com.android.ide.common.rendering.api.ParserFactory sParserFactory; 47 setParserFactory( @ullable com.android.ide.common.rendering.api.ParserFactory parserFactory)48 public static void setParserFactory( 49 @Nullable com.android.ide.common.rendering.api.ParserFactory parserFactory) { 50 sParserFactory = parserFactory; 51 } 52 53 @NonNull create(@onNull File f)54 public static XmlPullParser create(@NonNull File f) 55 throws XmlPullParserException, FileNotFoundException { 56 return create(f, false); 57 } 58 create(@onNull File f, boolean isLayout)59 public static XmlPullParser create(@NonNull File f, boolean isLayout) 60 throws XmlPullParserException, FileNotFoundException { 61 InputStream stream = new FileInputStream(f); 62 return create(stream, f.getName(), f.length(), isLayout); 63 } 64 @NonNull create(@onNull InputStream stream, @Nullable String name)65 public static XmlPullParser create(@NonNull InputStream stream, @Nullable String name) 66 throws XmlPullParserException { 67 return create(stream, name, -1, false); 68 } 69 70 @NonNull create(@onNull InputStream stream, @Nullable String name, long size, boolean isLayout)71 private static XmlPullParser create(@NonNull InputStream stream, @Nullable String name, 72 long size, boolean isLayout) throws XmlPullParserException { 73 XmlPullParser parser = instantiateParser(name); 74 75 stream = readAndClose(stream, name, size); 76 77 parser.setInput(stream, ENCODING); 78 if (isLayout) { 79 try { 80 return new LayoutParserWrapper(parser).peekTillLayoutStart(); 81 } catch (IOException e) { 82 throw new XmlPullParserException(null, parser, e); 83 } 84 } 85 return parser; 86 } 87 88 @NonNull instantiateParser(@ullable String name)89 public static XmlPullParser instantiateParser(@Nullable String name) 90 throws XmlPullParserException { 91 if (sParserFactory == null) { 92 throw new XmlPullParserException("ParserFactory not initialized."); 93 } 94 XmlPullParser parser = sParserFactory.createParser(name); 95 parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); 96 return parser; 97 } 98 99 @NonNull readAndClose(@onNull InputStream stream, @Nullable String name, long size)100 private static InputStream readAndClose(@NonNull InputStream stream, @Nullable String name, 101 long size) throws XmlPullParserException { 102 // just a sanity check. It's doubtful we'll have such big files! 103 if (size > Integer.MAX_VALUE) { 104 throw new XmlPullParserException("File " + name + " is too big to be parsed"); 105 } 106 int intSize = (int) size; 107 108 // create a buffered reader to facilitate reading. 109 BufferedInputStream bufferedStream = new BufferedInputStream(stream); 110 try { 111 int avail; 112 if (intSize != -1) { 113 avail = intSize; 114 } else { 115 // get the size to read. 116 avail = bufferedStream.available(); 117 } 118 119 // create the initial buffer and read it. 120 byte[] buffer = new byte[avail]; 121 int read = stream.read(buffer); 122 123 // this is the easy case. 124 if (read == intSize) { 125 return new ByteArrayInputStream(buffer); 126 } 127 128 // check if there is more to read (read() does not necessarily read all that 129 // available() returned!) 130 while ((avail = bufferedStream.available()) > 0) { 131 if (read + avail > buffer.length) { 132 // just allocate what is needed. We're mostly reading small files 133 // so it shouldn't be too problematic. 134 byte[] moreBuffer = new byte[read + avail]; 135 System.arraycopy(buffer, 0, moreBuffer, 0, read); 136 buffer = moreBuffer; 137 } 138 139 read += stream.read(buffer, read, avail); 140 } 141 142 // return a new stream encapsulating this buffer. 143 return new ByteArrayInputStream(buffer); 144 145 } catch (IOException e) { 146 throw new XmlPullParserException("Failed to read " + name, null, e); 147 } finally { 148 try { 149 bufferedStream.close(); 150 } catch (IOException ignored) { 151 } 152 } 153 } 154 } 155