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