/** * Records the given class name (internal name) and class file path as corresponding to a * typedef annotation * */ private void addTypeDef(String name, File file) { mAnnotationClassFiles.add(file); mAnnotationNames.add(name); String fileName = file.getName(); int index = fileName.lastIndexOf('$'); if (index != -1) { File parentFile = file.getParentFile(); assert parentFile != null : file; File container = new File(parentFile, fileName.substring(0, index) + ".class"); if (container.exists()) { mAnnotationOuterClassFiles.add(container); } else { Extractor.error("Warning: Could not find outer class " + container + " for typedef " + file); } } }
@NonNull public TypedefRemover setTypedefFile(@NonNull File file) { try { for (String line : Files.readLines(file, Charsets.UTF_8)) { if (line.startsWith("D ")) { String clz = line.substring(2).trim(); addTypeDef(clz); } } } catch (IOException e) { Extractor.error("Could not read " + file + ": " + e.getLocalizedMessage()); } return this; }
/** * Rewrites the outer classes containing the typedefs such that they no longer refer to * the (now removed) typedef annotation inner classes */ private void rewriteOuterClasses(@NonNull File classDir) { for (String relative : mAnnotationOuterClassFiles) { File file = new File(classDir, relative.replace('/', File.separatorChar)); if (!file.isFile()) { Extractor.error("Warning: Could not find outer class " + file + " for typedef"); continue; } byte[] bytes; try { bytes = Files.toByteArray(file); } catch (IOException e) { Extractor.error("Could not read " + file + ": " + e.getLocalizedMessage()); continue; } ClassReader reader = new ClassReader(bytes); byte[] rewritten = rewriteOuterClass(reader); try { Files.write(rewritten, file); } catch (IOException e) { Extractor.error("Could not write " + file + ": " + e.getLocalizedMessage()); //noinspection UnnecessaryContinue continue; } } }
public void mergeExisting(@NonNull File file) { if (file.isDirectory()) { File[] files = file.listFiles(); if (files != null) { for (File child : files) { mergeExisting(child); } } } else if (file.isFile()) { if (file.getPath().endsWith(DOT_JAR)) { mergeFromJar(file); } else if (file.getPath().endsWith(DOT_XML)) { try { String xml = Files.toString(file, Charsets.UTF_8); mergeAnnotationsXml(file.getPath(), xml); } catch (IOException e) { error("Aborting: I/O problem during transform: " + e.toString()); } } } }
public void mergeExisting(@NonNull File file) { if (file.isDirectory()) { File[] files = file.listFiles(); if (files != null) { for (File child : files) { mergeExisting(child); } } } else if (file.isFile()) { if (file.getPath().endsWith(DOT_JAR)) { mergeFromJar(file); } else if (file.getPath().endsWith(DOT_XML)) { try { String xml = Files.toString(file, Charsets.UTF_8); mergeAnnotationsXml(file.getPath(), xml); } catch (IOException e) { error("Aborting: I/O problem during transform: " + e.toString()); } } } }
private boolean writeKeepRules(@NonNull File proguardCfg) { if (!keepItems.isEmpty()) { try { Writer writer = new BufferedWriter(new FileWriter(proguardCfg)); try { Collections.sort(keepItems); for (Item item : keepItems) { writer.write(item.getKeepRule()); writer.write('\n'); } } finally { writer.close(); } } catch (IOException ioe) { error(ioe.toString()); return true; } // Now that we've handled these items, remove them from the list // such that we don't accidentally also emit them into the annotations.zip // file, where they are not needed for (Item item : keepItems) { removeItem(item.getQualifiedClassName(), item); } } else if (proguardCfg.exists()) { //noinspection ResultOfMethodCallIgnored proguardCfg.delete(); } return false; }
private void mergeAnnotationsXml(@NonNull String path, @NonNull String xml) { try { Document document = XmlUtils.parseDocument(xml, false); mergeDocument(document); } catch (Exception e) { String message = "Failed to merge " + path + ": " + e.toString(); if (e instanceof SAXParseException) { SAXParseException spe = (SAXParseException)e; message = "Line " + spe.getLineNumber() + ":" + spe.getColumnNumber() + ": " + message; } error(message); if (!(e instanceof IOException)) { e.printStackTrace(); } } }
private void mergeFromJar(@NonNull File jar) { // Reads in an existing annotations jar and merges in entries found there // with the annotations analyzed from source. JarInputStream zis = null; try { @SuppressWarnings("IOResourceOpenedButNotSafelyClosed") FileInputStream fis = new FileInputStream(jar); zis = new JarInputStream(fis); ZipEntry entry = zis.getNextEntry(); while (entry != null) { if (entry.getName().endsWith(".xml")) { byte[] bytes = ByteStreams.toByteArray(zis); String xml = new String(bytes, Charsets.UTF_8); mergeAnnotationsXml(jar.getPath() + ": " + entry, xml); } entry = zis.getNextEntry(); } } catch (IOException e) { error("Aborting: I/O problem during transform: " + e.toString()); } finally { //noinspection deprecation try { Closeables.close(zis, true /* swallowIOException */); } catch (IOException e) { // cannot happen } } }
private boolean writeKeepRules(@NonNull File proguardCfg) { if (!keepItems.isEmpty()) { try { try (Writer writer = new BufferedWriter(new FileWriter(proguardCfg))) { Collections.sort(keepItems); for (Item item : keepItems) { writer.write(item.getKeepRule()); writer.write('\n'); } } } catch (IOException ioe) { error(ioe.toString()); return true; } // Now that we've handled these items, remove them from the list // such that we don't accidentally also emit them into the annotations.zip // file, where they are not needed for (Item item : keepItems) { removeItem(item.getQualifiedClassName(), item); } } else if (proguardCfg.exists()) { //noinspection ResultOfMethodCallIgnored proguardCfg.delete(); } return false; }
private void mergeFromJar(@NonNull File jar) { // Reads in an existing annotations jar and merges in entries found there // with the annotations analyzed from source. JarInputStream zis = null; try { @SuppressWarnings("IOResourceOpenedButNotSafelyClosed") FileInputStream fis = new FileInputStream(jar); zis = new JarInputStream(fis); ZipEntry entry = zis.getNextEntry(); while (entry != null) { if (entry.getName().endsWith(".xml")) { byte[] bytes = ByteStreams.toByteArray(zis); String xml = new String(bytes, Charsets.UTF_8); mergeAnnotationsXml(jar.getPath() + ": " + entry, xml); } entry = zis.getNextEntry(); } } catch (IOException e) { error("Aborting: I/O problem during transform: " + e.toString()); } finally { //noinspection deprecation try { Closeables.close(zis, true /* swallowIOException */); } catch (IOException e) { // cannot happen } } }
/** * Performs the actual deletion (or display, if in dry-run mode) of the typedef annotation * files */ private void deleteAnnotationClasses(@NonNull File classDir) { for (String relative : mAnnotationClassFiles) { File file = new File(classDir, relative.replace('/', File.separatorChar)); if (!file.isFile()) { Extractor.error("Warning: Could not find class file " + file + " for typedef"); continue; } if (mVerbose) { if (mDryRun) { info("Would delete " + file); } else { info("Deleting " + file); } } if (!mDryRun) { boolean deleted = file.delete(); if (!deleted) { Extractor.warning("Could not delete " + file); } } } } }
/** * Filter the given file (given by a path). * * @param path the path within the jar file * @param input the contents of the file * @return a stream which provides the content of the file, which may be null (to delete/not * package the file), or the original input stream if the file should be packaged as is, or * possibly a different input stream with the file rewritten */ @Nullable public InputStream filter(@NonNull String path, @NonNull InputStream input) { if (mAnnotationClassFiles.contains(path)) { return null; } if (!mAnnotationOuterClassFiles.contains(path)) { return input; } // Transform try { ClassReader reader = new ClassReader(input); byte[] rewritten = rewriteOuterClass(reader); return new ByteArrayInputStream(rewritten); } catch (IOException ioe) { Extractor.error("Could not process " + path + ": " + ioe.getLocalizedMessage()); return input; } }