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 import com.android.ide.common.rendering.api.XmlParserFactory;
20 
21 import org.xmlpull.v1.XmlPullParser;
22 import org.xmlpull.v1.XmlPullParserException;
23 
24 import android.annotation.NonNull;
25 import android.annotation.Nullable;
26 
27 import java.io.BufferedInputStream;
28 import java.io.ByteArrayInputStream;
29 import java.io.IOException;
30 import java.io.InputStream;
31 
32 /**
33  * A factory for {@link XmlPullParser}.
34  */
35 public class ParserFactory {
36     public final static boolean LOG_PARSER = false;
37 
38     // Used to get a new XmlPullParser from the client.
39     @Nullable
40     private static XmlParserFactory sParserFactory;
41 
setParserFactory(@ullable XmlParserFactory parserFactory)42     public static void setParserFactory(@Nullable XmlParserFactory parserFactory) {
43         sParserFactory = parserFactory;
44     }
45 
46     @Nullable
create(@onNull String filePath)47     public static XmlPullParser create(@NonNull String filePath)
48             throws XmlPullParserException {
49         return create(filePath, false);
50     }
51 
52     @Nullable
create(@onNull String filePath, boolean isLayout)53     public static XmlPullParser create(@NonNull String filePath, boolean isLayout)
54             throws XmlPullParserException {
55         XmlPullParser parser = sParserFactory.createXmlParserForFile(filePath);
56         if (parser != null && isLayout) {
57             try {
58                 return new LayoutParserWrapper(parser).peekTillLayoutStart();
59             } catch (IOException e) {
60                 throw new XmlPullParserException(null, parser, e);
61             }
62         }
63         return parser;
64     }
65 
66     @NonNull
create(@onNull InputStream stream, @Nullable String name)67     public static XmlPullParser create(@NonNull InputStream stream, @Nullable String name)
68             throws XmlPullParserException {
69         XmlPullParser parser = create();
70 
71         stream = readAndClose(stream, name);
72 
73         parser.setInput(stream, null);
74         return parser;
75     }
76 
77     @NonNull
create()78     public static XmlPullParser create() throws XmlPullParserException {
79         if (sParserFactory == null) {
80             throw new XmlPullParserException("ParserFactory not initialized.");
81         }
82         XmlPullParser parser = sParserFactory.createXmlParser();
83         parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
84         return parser;
85     }
86 
87     @NonNull
readAndClose(@onNull InputStream stream, @Nullable String name)88     private static InputStream readAndClose(@NonNull InputStream stream, @Nullable String name)
89             throws XmlPullParserException {
90         // Create a buffered stream to facilitate reading.
91         try (BufferedInputStream bufferedStream = new BufferedInputStream(stream)) {
92             int avail = bufferedStream.available();
93 
94             // Create the initial buffer and read it.
95             byte[] buffer = new byte[avail];
96             int read = stream.read(buffer);
97 
98             // Check if there is more to read (read() does not necessarily read all that
99             // available() returned!)
100             while ((avail = bufferedStream.available()) > 0) {
101                 if (read + avail > buffer.length) {
102                     // just allocate what is needed. We're mostly reading small files
103                     // so it shouldn't be too problematic.
104                     byte[] moreBuffer = new byte[read + avail];
105                     System.arraycopy(buffer, 0, moreBuffer, 0, read);
106                     buffer = moreBuffer;
107                 }
108 
109                 read += stream.read(buffer, read, avail);
110             }
111 
112             // Return a new stream encapsulating this buffer.
113             return new ByteArrayInputStream(buffer);
114         } catch (IOException e) {
115             throw new XmlPullParserException("Failed to read " + name, null, e);
116         }
117     }
118 }
119