1 /*
2  * Copyright (C) 2016 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.tools.build.apkzlib.zip.utils;
18 
19 import com.google.common.hash.HashCode;
20 import com.google.common.hash.HashFunction;
21 import com.google.common.io.ByteProcessor;
22 import com.google.common.io.ByteSink;
23 import com.google.common.io.ByteSource;
24 import com.google.common.io.CharSource;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.OutputStream;
28 import java.nio.charset.Charset;
29 import javax.annotation.Nonnull;
30 import javax.annotation.Nullable;
31 
32 /**
33  * Closeable byte source that delegates to another byte source.
34  */
35 public class CloseableDelegateByteSource extends CloseableByteSource {
36 
37     /**
38      * The byte source we delegate all operations to. {@code null} if disposed.
39      */
40     @Nullable
41     private ByteSource inner;
42 
43     /**
44      * Size of the byte source. This is the same as {@code inner.size()} (when {@code inner}
45      * is not {@code null}), but we keep it separate to avoid calling {@code inner.size()}
46      * because it might throw {@code IOException}.
47      */
48     private final long mSize;
49 
50     /**
51      * Creates a new byte source.
52      *
53      * @param inner the inner byte source
54      * @param size the size of the source
55      */
CloseableDelegateByteSource(@onnull ByteSource inner, long size)56     public CloseableDelegateByteSource(@Nonnull ByteSource inner, long size) {
57         this.inner = inner;
58         mSize = size;
59     }
60 
61     /**
62      * Obtains the inner byte source. Will throw an exception if the inner by byte source has
63      * been disposed of.
64      *
65      * @return the inner byte source
66      */
67     @Nonnull
get()68     private synchronized ByteSource get() {
69         if (inner == null) {
70             throw new ByteSourceDisposedException();
71         }
72 
73         return inner;
74     }
75 
76     /**
77      * Mark the byte source as disposed.
78      */
79     @Override
innerClose()80     protected synchronized void innerClose() throws IOException {
81         if (inner == null) {
82             return;
83         }
84 
85         inner = null;
86     }
87 
88     /**
89      * Obtains the size of this byte source. Equivalent to {@link #size()} but not throwing
90      * {@code IOException}.
91      *
92      * @return the size of the byte source
93      */
sizeNoException()94     public long sizeNoException() {
95         return mSize;
96     }
97 
98     @Override
asCharSource(Charset charset)99     public CharSource asCharSource(Charset charset) {
100         return get().asCharSource(charset);
101     }
102 
103     @Override
openBufferedStream()104     public InputStream openBufferedStream() throws IOException {
105         return get().openBufferedStream();
106     }
107 
108     @Override
slice(long offset, long length)109     public ByteSource slice(long offset, long length) {
110         return get().slice(offset, length);
111     }
112 
113     @Override
isEmpty()114     public boolean isEmpty() throws IOException {
115         return get().isEmpty();
116     }
117 
118     @Override
size()119     public long size() throws IOException {
120         return get().size();
121     }
122 
123     @Override
copyTo(@onnull OutputStream output)124     public long copyTo(@Nonnull OutputStream output) throws IOException {
125         return get().copyTo(output);
126     }
127 
128     @Override
copyTo(@onnull ByteSink sink)129     public long copyTo(@Nonnull ByteSink sink) throws IOException {
130         return get().copyTo(sink);
131     }
132 
133     @Override
read()134     public byte[] read() throws IOException {
135         return get().read();
136     }
137 
138     @Override
read(@onnull ByteProcessor<T> processor)139     public <T> T read(@Nonnull ByteProcessor<T> processor) throws IOException {
140         return get().read(processor);
141     }
142 
143     @Override
hash(HashFunction hashFunction)144     public HashCode hash(HashFunction hashFunction) throws IOException {
145         return get().hash(hashFunction);
146     }
147 
148     @Override
contentEquals(@onnull ByteSource other)149     public boolean contentEquals(@Nonnull ByteSource other) throws IOException {
150         return get().contentEquals(other);
151     }
152 
153     @Override
openStream()154     public InputStream openStream() throws IOException {
155         return get().openStream();
156     }
157 
158     /**
159      * Exception thrown when trying to use a byte source that has been disposed.
160      */
161     private static class ByteSourceDisposedException extends RuntimeException {
162 
163         /**
164          * Creates a new exception.
165          */
ByteSourceDisposedException()166         private ByteSourceDisposedException() {
167             super("Byte source was created by a ByteTracker and is now disposed. If you see "
168                     + "this message, then there is a bug.");
169         }
170     }
171 }
172