• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 Google LLC
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 package com.google.android.libraries.mobiledatadownload.file;
17 
18 import android.net.Uri;
19 import com.google.android.libraries.mobiledatadownload.file.common.internal.ForwardingOutputStream;
20 import com.google.android.libraries.mobiledatadownload.file.common.internal.Preconditions;
21 import com.google.android.libraries.mobiledatadownload.file.spi.Monitor;
22 import java.io.IOException;
23 import java.io.OutputStream;
24 import java.util.ArrayList;
25 import java.util.List;
26 import javax.annotation.Nullable;
27 
28 /** Stream that invokes output stream monitors. */
29 final class MonitorOutputStream extends ForwardingOutputStream {
30   private final List<Monitor.OutputMonitor> outputMonitors;
31 
MonitorOutputStream(OutputStream output, List<Monitor.OutputMonitor> outputMonitors)32   private MonitorOutputStream(OutputStream output, List<Monitor.OutputMonitor> outputMonitors) {
33     super(output);
34     this.outputMonitors = outputMonitors;
35     Preconditions.checkArgument(output != null, "Output was null");
36   }
37 
38   /**
39    * Wraps {@code out} with a new stream that orchestrates {@code monitors}, or returns null if this
40    * IO doesn't need to be monitored.
41    */
42   @Nullable
newInstanceForWrite( List<Monitor> monitors, Uri uri, OutputStream out)43   public static MonitorOutputStream newInstanceForWrite(
44       List<Monitor> monitors, Uri uri, OutputStream out) {
45     List<Monitor.OutputMonitor> outputMonitors = new ArrayList<>();
46     for (Monitor monitor : monitors) {
47       Monitor.OutputMonitor outputMonitor = monitor.monitorWrite(uri);
48       if (outputMonitor != null) {
49         outputMonitors.add(outputMonitor);
50       }
51     }
52     return !outputMonitors.isEmpty() ? new MonitorOutputStream(out, outputMonitors) : null;
53   }
54 
55   /**
56    * Wraps {@code out} with a new stream that orchestrates {@code monitors}, or returns null if this
57    * IO doesn't need to be monitored.
58    */
59   @Nullable
newInstanceForAppend( List<Monitor> monitors, Uri uri, OutputStream out)60   public static MonitorOutputStream newInstanceForAppend(
61       List<Monitor> monitors, Uri uri, OutputStream out) {
62     List<Monitor.OutputMonitor> outputMonitors = new ArrayList<>();
63     for (Monitor monitor : monitors) {
64       Monitor.OutputMonitor outputMonitor = monitor.monitorAppend(uri);
65       if (outputMonitor != null) {
66         outputMonitors.add(outputMonitor);
67       }
68     }
69     return !outputMonitors.isEmpty() ? new MonitorOutputStream(out, outputMonitors) : null;
70   }
71 
72   @Override
write(int b)73   public void write(int b) throws IOException {
74     out.write(b);
75     byte[] bs = new byte[] {(byte) b};
76     for (Monitor.OutputMonitor outputMonitor : outputMonitors) {
77       outputMonitor.bytesWritten(bs, 0, 1);
78     }
79   }
80 
81   @Override
write(byte[] b)82   public void write(byte[] b) throws IOException {
83     out.write(b);
84     for (Monitor.OutputMonitor outputMonitor : outputMonitors) {
85       outputMonitor.bytesWritten(b, 0, b.length);
86     }
87   }
88 
89   @Override
write(byte[] b, int off, int len)90   public void write(byte[] b, int off, int len) throws IOException {
91     out.write(b, off, len);
92     for (Monitor.OutputMonitor outputMonitor : outputMonitors) {
93       outputMonitor.bytesWritten(b, off, len);
94     }
95   }
96 
97   @Override
close()98   public void close() throws IOException {
99     for (Monitor.OutputMonitor outputMonitor : outputMonitors) {
100       try {
101         outputMonitor.close();
102       } catch (Throwable t) {
103         // Ignore.
104       }
105     }
106     super.close();
107   }
108 }
109