private void mergeField(Element item, String containingClass, String fieldName) { if (apiFilter != null && !hasHistoricData(item) && !apiFilter.hasField(containingClass, fieldName)) { if (isListIgnored()) { info("Skipping imported element because it is not part of the API file: " + containingClass + "#" + fieldName); } filteredCount++; } else { FieldItem fieldItem = new FieldItem(containingClass, ClassKind.CLASS, fieldName, null); Item existing = findItem(containingClass, fieldItem); if (existing != null) { mergedCount += mergeAnnotations(item, existing); } else { addItemUnconditionally(containingClass, fieldItem); mergedCount += addAnnotations(item, fieldItem); } } }
private int mergeAnnotations(Element itemElement, Item item) { int count = 0; loop: for (Element annotationElement : getChildren(itemElement)) { if (!isRelevantAnnotation(annotationElement)) { continue; AnnotationData annotation = createAnnotation(annotationElement); if (annotation == null) { continue; boolean haveNotNull = false; for (AnnotationData existing : item.annotations) { if (isNonNull(existing.name)) { haveNotNull = true; if (isNullable(existing.name)) { haveNullable = true; if (isNonNull(annotation.name) && haveNullable || isNullable(annotation.name) && haveNotNull) { warning("Found both @Nullable and @NonNull after import for " + item); 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()); } } } }
private int addAnnotations(Element itemElement, Item item) { int count = 0; for (Element annotationElement : getChildren(itemElement)) { if (!isRelevantAnnotation(annotationElement)) { continue; } AnnotationData annotation = createAnnotation(annotationElement); item.annotations.add(annotation); count++; } return count; }
private void mergeField(Element item, String containingClass, String fieldName) { if (apiFilter != null && !apiFilter.hasField(containingClass, fieldName)) { if (isListIgnored()) { info("Skipping imported element because it is not part of the API file: " + containingClass + "#" + fieldName); } filteredCount++; } else { FieldItem fieldItem = new FieldItem(containingClass, ClassKind.CLASS, fieldName, null); Item existing = findItem(containingClass, fieldItem); if (existing != null) { mergedCount += mergeAnnotations(item, existing); } else { addItem(containingClass, fieldItem); mergedCount += addAnnotations(item, fieldItem); } } }
Extractor extractor = new Extractor(database, rmTypeDefs, verbose, !skipClassRetention, true); extractor.setListIgnored(listFiltered); extractor.extractFromProjectSource(units); extractor.mergeExisting(jar); extractor.export(output, proguard); extractor.removeTypedefClasses();
@Nullable private AnnotationData createAnnotation(@NonNull Annotation annotation) { String fqn = getFqn(annotation); if (fqn == null) { return null; recordStats(fqn); return new AnnotationData(SUPPORT_NULLABLE); recordStats(fqn); return new AnnotationData(SUPPORT_NOTNULL); recordStats(fqn); return new AnnotationData(fqn); } else if (fqn.startsWith(ANDROID_ANNOTATIONS_PREFIX)) { fqn.substring(ANDROID_ANNOTATIONS_PREFIX.length()); if (!includeClassRetentionAnnotations && !hasSourceRetention(resAnnotation, null)) { return null; recordStats(resAnnotation); return new AnnotationData(resAnnotation); } else if (isRelevantFrameworkAnnotation(fqn)) { fqn.substring(ANDROID_ANNOTATIONS_PREFIX.length()); if (!includeClassRetentionAnnotations && !hasSourceRetention(supportAnnotation, null)) { return null;
private boolean isRelevantAnnotation(Element annotationElement) { AnnotationData annotation = createAnnotation(annotationElement); if (annotation == null) { // Unsupported annotation in import return false; } if (isNullable(annotation.name) || isNonNull(annotation.name) || annotation.name.startsWith(ANDROID_ANNOTATIONS_PREFIX) || annotation.name.startsWith(SUPPORT_ANNOTATIONS_PREFIX)) { return true; } else if (annotation.name.equals(IDEA_CONTRACT)) { return true; } else if (annotation.name.equals(IDEA_NON_NLS)) { return false; } else { if (!ignoredAnnotations.contains(annotation.name)) { ignoredAnnotations.add(annotation.name); if (isListIgnored()) { info("(Ignoring merge annotation " + annotation.name + ")"); } } } return false; }
sb.append('{'); for (String fqn : Splitter.on(',').omitEmptyStrings().trimResults().split(value)) { fqn = unescapeXml(fqn); if (fqn.startsWith("\"")) { continue; } else if (isListIgnored()) { info("Skipping constant from typedef because it is not part of the SDK: " + fqn); return escapeXml(sb.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 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 } } }
Document document = checkDocument(pkg, xml, false); if (document == null) { error("Could not parse XML document back in for entry " + name + ": invalid XML?\n\"\"\"\n" + xml + "\n\"\"\"\n"); return false; error(ioe.toString()); return false;
private void addAnnotations(@Nullable Annotation[] annotations, @NonNull Item item) { if (annotations != null) { for (Annotation annotation : annotations) { if (isRelevantAnnotation(annotation)) { AnnotationData annotationData = createAnnotation(annotation); if (annotationData != null) { if (annotationData.name.equals(SUPPORT_KEEP)) { // Put keep rules in a different place; we don't want to write // these out into the external annotations database, they go // into a special proguard file keepItems.add(item); } else { item.annotations.add(annotationData); } } } } } }
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(); } } }
String fqn = getFqn(typeDef); if (fqn != null && (fqn.equals(INT_DEF_ANNOTATION) || fqn.equals(ANDROID_INT_DEF) || fqn.equals(ANDROID_STRING_DEF))) { AnnotationData a = createAnnotation(typeDef); if (a != null) { types.put(typeName, a);
/** * 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); } } } } }
/** * 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); } } }
public void extractFromProjectSource(Collection<CompilationUnitDeclaration> units) { TypedefCollector collector = new TypedefCollector(units, false /*requireHide*/, true /*requireSourceRetention*/); typedefs = collector.getTypedefs(); typedefClasses = collector.getNonPublicTypedefClasses(); for (CompilationUnitDeclaration unit : units) { analyze(unit); } }
Extractor extractor = new Extractor( database, classDir, false /*includeClassRetentionAnnotations*/, false /*sortAnnotations*/); extractor.extractFromProjectSource(parsedUnits); if (mergeJars != null) { for (File jar : mergeJars) { extractor.mergeExisting(jar); extractor.export(output, proguard); if (typedefFile != null) { extractor.writeTypedefFile(typedefFile); } else { extractor.removeTypedefClasses();
private void mergeMethodOrParameter(Element item, Matcher matcher, String containingClass, String methodName, String type, boolean constructor, String parameters) { parameters = fixParameterString(parameters); if (isListIgnored()) { info("Skipping imported element because it is not part of the API file: " + containingClass + "#" + methodName + "(" + parameters + ")"); ParameterItem parameterItem = new ParameterItem(containingClass, ClassKind.CLASS, type, methodName, parameters, constructor, argNum); Item existing = findItem(containingClass, parameterItem); mergedCount += mergeAnnotations(item, existing); } else { addItem(containingClass, parameterItem); mergedCount += addAnnotations(item, parameterItem); Item existing = findItem(containingClass, methodItem); if (existing != null) { mergedCount += mergeAnnotations(item, existing); } else { addItem(containingClass, methodItem); mergedCount += addAnnotations(item, methodItem);