1 /* 2 * Copyright (C) 2024 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 package com.android.build.backportedfixes.common; 17 18 import static com.google.common.base.Preconditions.checkNotNull; 19 20 import com.android.build.backportedfixes.BackportedFix; 21 import com.android.build.backportedfixes.BackportedFixes; 22 23 import com.google.common.base.Throwables; 24 import com.google.common.collect.ImmutableList; 25 26 import java.io.File; 27 import java.io.FileInputStream; 28 import java.io.FileNotFoundException; 29 import java.io.IOException; 30 import java.io.InputStream; 31 import java.util.BitSet; 32 import java.util.Comparator; 33 import java.util.List; 34 import java.util.stream.Collector; 35 import java.util.stream.Collectors; 36 37 38 /** Static utilities for working with {@link BackportedFixes}. */ 39 public final class Parser { 40 41 /** Creates list of FileInputStreams for a list of files. */ getFileInputStreams(List<File> fixFiles)42 public static ImmutableList<FileInputStream> getFileInputStreams(List<File> fixFiles) throws 43 FileNotFoundException { 44 var streams = ImmutableList.<FileInputStream>builder(); 45 for (var f : fixFiles) { 46 streams.add(new FileInputStream(f)); 47 } 48 return streams.build(); 49 } 50 51 /** Converts a list of backported fix aliases into a long array representing a {@link BitSet} */ getBitSetArray(int[] aliases)52 public static long[] getBitSetArray(int[] aliases) { 53 BitSet bs = new BitSet(); 54 for (int a : aliases) { 55 bs.set(a); 56 } 57 return bs.toLongArray(); 58 } 59 60 /** 61 * Creates a {@link BackportedFixes} from a list of {@link BackportedFix} binary proto streams. 62 */ parseBackportedFixFiles(List<File> fixFiles)63 public static BackportedFixes parseBackportedFixFiles(List<File> fixFiles) 64 throws IOException { 65 try { 66 return fixFiles.stream().map(Parser::tunelFileInputStream) 67 .map(Parser::tunnelParse) 68 .sorted(Comparator.comparing(BackportedFix::getKnownIssue)) 69 .collect(fixCollector()); 70 71 } catch (TunnelException e) { 72 throw e.rethrow(FileNotFoundException.class, IOException.class); 73 } 74 } 75 76 fixCollector()77 private static Collector<BackportedFix, ?, BackportedFixes> fixCollector() { 78 return Collectors.collectingAndThen(Collectors.toList(), fixList -> { 79 var result = BackportedFixes.newBuilder(); 80 result.addAllFixes(fixList); 81 return result.build(); 82 }); 83 } 84 tunelFileInputStream(File file)85 private static FileInputStream tunelFileInputStream(File file) throws TunnelException { 86 try { 87 return new FileInputStream(file); 88 } catch (FileNotFoundException e) { 89 throw new TunnelException(e); 90 } 91 } 92 tunnelParse(InputStream s)93 private static BackportedFix tunnelParse(InputStream s) throws TunnelException { 94 try { 95 var fix = BackportedFix.parseFrom(s); 96 s.close(); 97 return fix; 98 } catch (IOException e) { 99 throw new TunnelException(e); 100 } 101 } 102 103 private static class TunnelException extends RuntimeException { TunnelException(Exception cause)104 TunnelException(Exception cause) { 105 super("If you see this TunnelException something went wrong. It should always be rethrown as the cause.", cause); 106 } 107 rethrow(Class<X> exceptionClazz)108 <X extends Exception> RuntimeException rethrow(Class<X> exceptionClazz) throws X { 109 checkNotNull(exceptionClazz); 110 Throwables.throwIfInstanceOf(getCause(), exceptionClazz); 111 throw exception( 112 getCause(), 113 "rethrow(%s) doesn't match underlying exception", exceptionClazz); 114 } 115 rethrow( Class<X1> exceptionClazz1, Class<X2> exceptionClazz2)116 public <X1 extends Exception, X2 extends Exception> RuntimeException rethrow( 117 Class<X1> exceptionClazz1, Class<X2> exceptionClazz2) throws X1, X2 { 118 checkNotNull(exceptionClazz1); 119 checkNotNull(exceptionClazz2); 120 Throwables.throwIfInstanceOf(getCause(), exceptionClazz1); 121 Throwables.throwIfInstanceOf(getCause(), exceptionClazz2); 122 throw exception( 123 getCause(), 124 "rethrow(%s, %s) doesn't match underlying exception", 125 exceptionClazz1, 126 exceptionClazz2); 127 } 128 exception( Throwable cause, String message, Object... formatArgs)129 private static ClassCastException exception( 130 Throwable cause, String message, Object... formatArgs) { 131 ClassCastException result = new ClassCastException(String.format(message, formatArgs)); 132 result.initCause(cause); 133 return result; 134 } 135 136 } 137 Parser()138 private Parser() { 139 } 140 } 141