1 package com.android.class2nonsdklist; 2 3 import com.android.annotationvisitor.AnnotationConsumer; 4 5 import java.io.File; 6 import java.io.FileNotFoundException; 7 import java.io.FileOutputStream; 8 import java.io.OutputStream; 9 import java.io.PrintStream; 10 import java.util.ArrayList; 11 import java.util.Comparator; 12 import java.util.HashMap; 13 import java.util.HashSet; 14 import java.util.List; 15 import java.util.Map; 16 import java.util.Set; 17 import java.util.stream.Collectors; 18 19 public class AnnotationPropertyWriter implements AnnotationConsumer { 20 21 private static final String SIGNATURE_COLUMN = "signature"; 22 private final PrintStream mOutput; 23 private final List<Map<String, String>> mContents; 24 private final Set<String> mColumns; 25 AnnotationPropertyWriter(String csvFile)26 public AnnotationPropertyWriter(String csvFile) throws FileNotFoundException { 27 mOutput = new PrintStream(new FileOutputStream(new File(csvFile))); 28 mContents = new ArrayList<>(); 29 mColumns = new HashSet<>(); 30 } 31 AnnotationPropertyWriter(OutputStream output)32 public AnnotationPropertyWriter(OutputStream output) { 33 mOutput = new PrintStream(output); 34 mContents = new ArrayList<>(); 35 mColumns = new HashSet<>(); 36 } 37 consume(String apiSignature, Map<String, String> annotationProperties, Set<String> parsedFlags)38 public void consume(String apiSignature, Map<String, String> annotationProperties, 39 Set<String> parsedFlags) { 40 // Clone properties map. 41 Map<String, String> contents = new HashMap<>(annotationProperties); 42 43 // Append the member signature. 44 contents.put(SIGNATURE_COLUMN, apiSignature); 45 46 // Store data. 47 mColumns.addAll(contents.keySet()); 48 mContents.add(contents); 49 } 50 escapeCsvColumn(String column)51 private static String escapeCsvColumn(String column) { 52 // Using '|' as a quote character, as in frameworks/base/tools/hiddenapi/merge_csv.py 53 // Escape '|' characters in the column, then wrap the column in '|' characters. 54 column = column.replace("|", "||"); 55 return "|" + column + "|"; 56 } 57 close()58 public void close() { 59 // Sort columns by name. 60 List<String> columns = new ArrayList<>(mColumns); 61 columns.sort(Comparator.naturalOrder()); 62 63 // Make sure that the signature column, if present, comes first. 64 if (columns.remove(SIGNATURE_COLUMN)) { 65 columns.add(0, SIGNATURE_COLUMN); 66 } 67 68 // Print header row. 69 mOutput.println(String.join(",", columns)); 70 71 // Sort rows by signature 72 mContents.sort(Comparator.comparing(r -> r.get(SIGNATURE_COLUMN))); 73 74 // Print contents of each row in column order. 75 for (Map<String, String> row : mContents) { 76 mOutput.println(columns.stream().map(column -> row.getOrDefault(column, "")) 77 .map(AnnotationPropertyWriter::escapeCsvColumn) 78 .collect(Collectors.joining(","))); 79 } 80 81 // Close output. 82 mOutput.close(); 83 } 84 } 85