18190f4885b3eb34231877003a583116a0e82826eJeff Sharkey#!/usr/bin/env python
28190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
38190f4885b3eb34231877003a583116a0e82826eJeff Sharkey# Copyright (C) 2014 The Android Open Source Project
48190f4885b3eb34231877003a583116a0e82826eJeff Sharkey#
58190f4885b3eb34231877003a583116a0e82826eJeff Sharkey# Licensed under the Apache License, Version 2.0 (the 'License');
68190f4885b3eb34231877003a583116a0e82826eJeff Sharkey# you may not use this file except in compliance with the License.
78190f4885b3eb34231877003a583116a0e82826eJeff Sharkey# You may obtain a copy of the License at
88190f4885b3eb34231877003a583116a0e82826eJeff Sharkey#
98190f4885b3eb34231877003a583116a0e82826eJeff Sharkey#      http://www.apache.org/licenses/LICENSE-2.0
108190f4885b3eb34231877003a583116a0e82826eJeff Sharkey#
118190f4885b3eb34231877003a583116a0e82826eJeff Sharkey# Unless required by applicable law or agreed to in writing, software
128190f4885b3eb34231877003a583116a0e82826eJeff Sharkey# distributed under the License is distributed on an 'AS IS' BASIS,
138190f4885b3eb34231877003a583116a0e82826eJeff Sharkey# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
148190f4885b3eb34231877003a583116a0e82826eJeff Sharkey# See the License for the specific language governing permissions and
158190f4885b3eb34231877003a583116a0e82826eJeff Sharkey# limitations under the License.
168190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
178190f4885b3eb34231877003a583116a0e82826eJeff Sharkey"""
188190f4885b3eb34231877003a583116a0e82826eJeff SharkeyEnforces common Android public API design patterns.  It ignores lint messages from
198190f4885b3eb34231877003a583116a0e82826eJeff Sharkeya previous API level, if provided.
208190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
218190f4885b3eb34231877003a583116a0e82826eJeff SharkeyUsage: apilint.py current.txt
228190f4885b3eb34231877003a583116a0e82826eJeff SharkeyUsage: apilint.py current.txt previous.txt
231498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
241498f9c615395de17e11204b962d7d925e5f222dJeff SharkeyYou can also splice in blame details like this:
251498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey$ git blame api/current.txt -t -e > /tmp/currentblame.txt
261498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey$ apilint.py /tmp/currentblame.txt previous.txt --no-color
278190f4885b3eb34231877003a583116a0e82826eJeff Sharkey"""
288190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
291c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalfimport re, sys, collections, traceback, argparse
308190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
318190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
328190f4885b3eb34231877003a583116a0e82826eJeff SharkeyBLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
338190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
341c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam MetcalfALLOW_GOOGLE = False
351c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam MetcalfUSE_COLOR = True
361c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf
378190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef format(fg=None, bg=None, bright=False, bold=False, dim=False, reset=False):
388190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    # manually derived from http://en.wikipedia.org/wiki/ANSI_escape_code#Codes
391c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf    if not USE_COLOR: return ""
408190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    codes = []
418190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if reset: codes.append("0")
428190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    else:
438190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if not fg is None: codes.append("3%d" % (fg))
448190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if not bg is None:
458190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if not bright: codes.append("4%d" % (bg))
468190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            else: codes.append("10%d" % (bg))
478190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if bold: codes.append("1")
488190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        elif dim: codes.append("2")
498190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        else: codes.append("22")
508190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    return "\033[%sm" % (";".join(codes))
518190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
528190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
538190f4885b3eb34231877003a583116a0e82826eJeff Sharkeyclass Field():
54ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey    def __init__(self, clazz, line, raw, blame):
558190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.clazz = clazz
56ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        self.line = line
578190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.raw = raw.strip(" {;")
581498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        self.blame = blame
598190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
608190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        raw = raw.split()
618190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.split = list(raw)
628190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
638190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        for r in ["field", "volatile", "transient", "public", "protected", "static", "final", "deprecated"]:
648190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            while r in raw: raw.remove(r)
658190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
668190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.typ = raw[0]
678190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.name = raw[1].strip(";")
688190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if len(raw) >= 4 and raw[2] == "=":
698190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            self.value = raw[3].strip(';"')
708190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        else:
718190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            self.value = None
728190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
73037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        self.ident = self.raw.replace(" deprecated ", " ")
74037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
758190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    def __repr__(self):
768190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        return self.raw
778190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
788190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
798190f4885b3eb34231877003a583116a0e82826eJeff Sharkeyclass Method():
80ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey    def __init__(self, clazz, line, raw, blame):
818190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.clazz = clazz
82ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        self.line = line
838190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.raw = raw.strip(" {;")
841498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        self.blame = blame
851498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
861498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        # drop generics for now
871498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        raw = re.sub("<.+?>", "", raw)
888190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
898190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        raw = re.split("[\s(),;]+", raw)
908190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        for r in ["", ";"]:
918190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            while r in raw: raw.remove(r)
928190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.split = list(raw)
938190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
948190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        for r in ["method", "public", "protected", "static", "final", "deprecated", "abstract"]:
958190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            while r in raw: raw.remove(r)
968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
978190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.typ = raw[0]
988190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.name = raw[1]
998190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.args = []
1008190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        for r in raw[2:]:
1018190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if r == "throws": break
1028190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            self.args.append(r)
1038190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
104037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        # identity for compat purposes
105037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        ident = self.raw
106037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        ident = ident.replace(" deprecated ", " ")
107037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        ident = ident.replace(" synchronized ", " ")
108037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        ident = re.sub("<.+?>", "", ident)
109037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        if " throws " in ident:
110037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            ident = ident[:ident.index(" throws ")]
111037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        self.ident = ident
112037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1138190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    def __repr__(self):
1148190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        return self.raw
1158190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1168190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1178190f4885b3eb34231877003a583116a0e82826eJeff Sharkeyclass Class():
118ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey    def __init__(self, pkg, line, raw, blame):
1198190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.pkg = pkg
120ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        self.line = line
1218190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.raw = raw.strip(" {;")
1221498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        self.blame = blame
1238190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.ctors = []
1248190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.fields = []
1258190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.methods = []
1268190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1278190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        raw = raw.split()
1288190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.split = list(raw)
1298190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "class" in raw:
1308190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            self.fullname = raw[raw.index("class")+1]
1318190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        elif "interface" in raw:
1328190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            self.fullname = raw[raw.index("interface")+1]
133037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        else:
134037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            raise ValueError("Funky class type %s" % (self.raw))
135037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
136037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        if "extends" in raw:
137037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            self.extends = raw[raw.index("extends")+1]
138047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey            self.extends_path = self.extends.split(".")
139037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        else:
140037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            self.extends = None
141047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey            self.extends_path = []
1428190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1431498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        self.fullname = self.pkg.name + "." + self.fullname
144047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey        self.fullname_path = self.fullname.split(".")
145047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey
1461498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        self.name = self.fullname[self.fullname.rindex(".")+1:]
1471498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
1488190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    def __repr__(self):
1498190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        return self.raw
1508190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1518190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1528190f4885b3eb34231877003a583116a0e82826eJeff Sharkeyclass Package():
153ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey    def __init__(self, line, raw, blame):
154ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        self.line = line
1558190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.raw = raw.strip(" {;")
1561498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        self.blame = blame
1578190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1588190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        raw = raw.split()
1598190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.name = raw[raw.index("package")+1]
160047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey        self.name_path = self.name.split(".")
1618190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1628190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    def __repr__(self):
1638190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        return self.raw
1648190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1658190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
166a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkeydef _parse_stream(f, clazz_cb=None):
167ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey    line = 0
1681498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    api = {}
1698190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    pkg = None
1708190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    clazz = None
1711498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    blame = None
1721498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
1731498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    re_blame = re.compile("^([a-z0-9]{7,}) \(<([^>]+)>.+?\) (.+?)$")
174a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    for raw in f:
175ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        line += 1
176ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        raw = raw.rstrip()
177ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        match = re_blame.match(raw)
178ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        if match is not None:
179ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey            blame = match.groups()[0:2]
180ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey            raw = match.groups()[2]
181ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        else:
182ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey            blame = None
183ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey
184ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        if raw.startswith("package"):
185ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey            pkg = Package(line, raw, blame)
186ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        elif raw.startswith("  ") and raw.endswith("{"):
187a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey            # When provided with class callback, we treat as incremental
188a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey            # parse and don't build up entire API
189a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey            if clazz and clazz_cb:
190a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey                clazz_cb(clazz)
191ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey            clazz = Class(pkg, line, raw, blame)
192a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey            if not clazz_cb:
193a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey                api[clazz.fullname] = clazz
194ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        elif raw.startswith("    ctor"):
195ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey            clazz.ctors.append(Method(clazz, line, raw, blame))
196ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        elif raw.startswith("    method"):
197ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey            clazz.methods.append(Method(clazz, line, raw, blame))
198ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        elif raw.startswith("    field"):
199ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey            clazz.fields.append(Field(clazz, line, raw, blame))
200ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey
201a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    # Handle last trailing class
202a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    if clazz and clazz_cb:
203a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey        clazz_cb(clazz)
2048190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
205a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    return api
2068190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
207ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey
208ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkeyclass Failure():
2099f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    def __init__(self, sig, clazz, detail, error, rule, msg):
210ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        self.sig = sig
211ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        self.error = error
2129f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        self.rule = rule
213ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        self.msg = msg
214ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey
215ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        if error:
2169f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            self.head = "Error %s" % (rule) if rule else "Error"
2179f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            dump = "%s%s:%s %s" % (format(fg=RED, bg=BLACK, bold=True), self.head, format(reset=True), msg)
218ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        else:
2199f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            self.head = "Warning %s" % (rule) if rule else "Warning"
2209f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            dump = "%s%s:%s %s" % (format(fg=YELLOW, bg=BLACK, bold=True), self.head, format(reset=True), msg)
221ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey
222ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        self.line = clazz.line
223ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        blame = clazz.blame
224ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        if detail is not None:
225ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey            dump += "\n    in " + repr(detail)
226ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey            self.line = detail.line
227ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey            blame = detail.blame
228ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        dump += "\n    in " + repr(clazz)
229ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        dump += "\n    in " + repr(clazz.pkg)
230ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        dump += "\n    at line " + repr(self.line)
231ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        if blame is not None:
232ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey            dump += "\n    last modified by %s in %s" % (blame[1], blame[0])
233ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey
234ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        self.dump = dump
235ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey
236ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey    def __repr__(self):
237ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        return self.dump
2388190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2398190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2401498f9c615395de17e11204b962d7d925e5f222dJeff Sharkeyfailures = {}
241294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
2429f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef _fail(clazz, detail, error, rule, msg):
2438190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Records an API failure to be processed later."""
2448190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    global failures
2458190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2461498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    sig = "%s-%s-%s" % (clazz.fullname, repr(detail), msg)
2471498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    sig = sig.replace(" deprecated ", " ")
2481498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
2499f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    failures[sig] = Failure(sig, clazz, detail, error, rule, msg)
250ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey
2518190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2529f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef warn(clazz, detail, rule, msg):
2539f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    _fail(clazz, detail, False, rule, msg)
2548190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2559f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef error(clazz, detail, rule, msg):
2569f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    _fail(clazz, detail, True, rule, msg)
2578190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2588190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2598190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_constants(clazz):
2608190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """All static final constants must be FOO_NAME style."""
2611498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if re.match("android\.R\.[a-z]+", clazz.fullname): return
2628190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2638190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for f in clazz.fields:
2648190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "static" in f.split and "final" in f.split:
2658190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if re.match("[A-Z0-9_]+", f.name) is None:
26690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                error(clazz, f, "C2", "Constant field names must be FOO_NAME")
2678190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2688190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2698190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_enums(clazz):
2708190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Enums are bad, mmkay?"""
2718190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if "extends java.lang.Enum" in clazz.raw:
2729f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        error(clazz, None, "F5", "Enums are not allowed")
2738190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2748190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2758190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_class_names(clazz):
2768190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Try catching malformed class names like myMtp or MTPUser."""
2771498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if clazz.fullname.startswith("android.opengl"): return
2781498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if clazz.fullname.startswith("android.renderscript"): return
2791498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if re.match("android\.R\.[a-z]+", clazz.fullname): return
2801498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
2818190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if re.search("[A-Z]{2,}", clazz.name) is not None:
28290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey        warn(clazz, None, "S1", "Class names with acronyms should be Mtp not MTP")
2838190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if re.match("[^A-Z]", clazz.name):
2849f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        error(clazz, None, "S1", "Class must start with uppercase char")
2858190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2868190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2878190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_method_names(clazz):
2888190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Try catching malformed method names, like Foo() or getMTU()."""
2891498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if clazz.fullname.startswith("android.opengl"): return
2901498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if clazz.fullname.startswith("android.renderscript"): return
2911498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if clazz.fullname == "android.system.OsConstants": return
2928190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2938190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for m in clazz.methods:
2948190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if re.search("[A-Z]{2,}", m.name) is not None:
29590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            warn(clazz, m, "S1", "Method names with acronyms should be getMtu() instead of getMTU()")
2968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if re.match("[^a-z]", m.name):
2979f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            error(clazz, m, "S1", "Method name must start with lowercase char")
2988190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2998190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3008190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_callbacks(clazz):
3018190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify Callback classes.
3028190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    All callback classes must be abstract.
3038190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    All methods must follow onFoo() naming style."""
3041498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if clazz.fullname == "android.speech.tts.SynthesisCallback": return
3058190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3068190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if clazz.name.endswith("Callbacks"):
30790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey        error(clazz, None, "L1", "Callback class names should be singular")
3088190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if clazz.name.endswith("Observer"):
3099f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        warn(clazz, None, "L1", "Class should be named FooCallback")
3108190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3118190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if clazz.name.endswith("Callback"):
3128190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "interface" in clazz.split:
31390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            error(clazz, None, "CL3", "Callbacks must be abstract class to enable extension in future API levels")
3148190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3158190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        for m in clazz.methods:
3168190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if not re.match("on[A-Z][a-z]*", m.name):
3179f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                error(clazz, m, "L1", "Callback method names must be onFoo() style")
3188190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3198190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3208190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_listeners(clazz):
3218190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify Listener classes.
3228190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    All Listener classes must be interface.
3238190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    All methods must follow onFoo() naming style.
3248190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    If only a single method, it must match class name:
3258190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        interface OnFooListener { void onFoo() }"""
3268190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3278190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if clazz.name.endswith("Listener"):
3288190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if " abstract class " in clazz.raw:
32990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            error(clazz, None, "L1", "Listeners should be an interface, or otherwise renamed Callback")
3308190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3318190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        for m in clazz.methods:
3328190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if not re.match("on[A-Z][a-z]*", m.name):
3339f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                error(clazz, m, "L1", "Listener method names must be onFoo() style")
3348190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3358190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if len(clazz.methods) == 1 and clazz.name.startswith("On"):
3368190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            m = clazz.methods[0]
3378190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if (m.name + "Listener").lower() != clazz.name.lower():
33890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                error(clazz, m, "L1", "Single listener method name must match class name")
3398190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3408190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3418190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_actions(clazz):
3428190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify intent actions.
3438190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    All action names must be named ACTION_FOO.
3448190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    All action values must be scoped by package and match name:
3458190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        package android.foo {
3468190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            String ACTION_BAR = "android.foo.action.BAR";
3478190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        }"""
3488190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for f in clazz.fields:
3498190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if f.value is None: continue
3508190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if f.name.startswith("EXTRA_"): continue
3511498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        if f.name == "SERVICE_INTERFACE" or f.name == "PROVIDER_INTERFACE": continue
3528190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3538190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "static" in f.split and "final" in f.split and f.typ == "java.lang.String":
3548190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if "_ACTION" in f.name or "ACTION_" in f.name or ".action." in f.value.lower():
3558190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                if not f.name.startswith("ACTION_"):
3569f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                    error(clazz, f, "C3", "Intent action constant name must be ACTION_FOO")
3578190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                else:
3581498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                    if clazz.fullname == "android.content.Intent":
3598190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                        prefix = "android.intent.action"
3601498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                    elif clazz.fullname == "android.provider.Settings":
3618190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                        prefix = "android.settings"
3621498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                    elif clazz.fullname == "android.app.admin.DevicePolicyManager" or clazz.fullname == "android.app.admin.DeviceAdminReceiver":
3631498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                        prefix = "android.app.action"
3648190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    else:
3658190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                        prefix = clazz.pkg.name + ".action"
3668190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    expected = prefix + "." + f.name[7:]
3678190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    if f.value != expected:
3689f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                        error(clazz, f, "C4", "Inconsistent action value; expected %s" % (expected))
3698190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3708190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3718190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_extras(clazz):
3728190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify intent extras.
3738190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    All extra names must be named EXTRA_FOO.
3748190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    All extra values must be scoped by package and match name:
3758190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        package android.foo {
3768190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            String EXTRA_BAR = "android.foo.extra.BAR";
3778190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        }"""
3781498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if clazz.fullname == "android.app.Notification": return
3791498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if clazz.fullname == "android.appwidget.AppWidgetManager": return
3801498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
3818190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for f in clazz.fields:
3828190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if f.value is None: continue
3838190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if f.name.startswith("ACTION_"): continue
3848190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3858190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "static" in f.split and "final" in f.split and f.typ == "java.lang.String":
3868190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if "_EXTRA" in f.name or "EXTRA_" in f.name or ".extra" in f.value.lower():
3878190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                if not f.name.startswith("EXTRA_"):
3889f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                    error(clazz, f, "C3", "Intent extra must be EXTRA_FOO")
3898190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                else:
3901498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                    if clazz.pkg.name == "android.content" and clazz.name == "Intent":
3918190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                        prefix = "android.intent.extra"
3921498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                    elif clazz.pkg.name == "android.app.admin":
3931498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                        prefix = "android.app.extra"
3948190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    else:
3958190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                        prefix = clazz.pkg.name + ".extra"
3968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    expected = prefix + "." + f.name[6:]
3978190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    if f.value != expected:
3989f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                        error(clazz, f, "C4", "Inconsistent extra value; expected %s" % (expected))
3998190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4008190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4018190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_equals(clazz):
4028190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify that equals() and hashCode() must be overridden together."""
4038190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    methods = [ m.name for m in clazz.methods ]
4048190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    eq = "equals" in methods
4058190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    hc = "hashCode" in methods
4068190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if eq != hc:
4079f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        error(clazz, None, "M8", "Must override both equals and hashCode; missing one")
4088190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4098190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4108190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_parcelable(clazz):
4118190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify that Parcelable objects aren't hiding required bits."""
4128190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if "implements android.os.Parcelable" in clazz.raw:
4138190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        creator = [ i for i in clazz.fields if i.name == "CREATOR" ]
4148190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        write = [ i for i in clazz.methods if i.name == "writeToParcel" ]
4158190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        describe = [ i for i in clazz.methods if i.name == "describeContents" ]
4168190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4178190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if len(creator) == 0 or len(write) == 0 or len(describe) == 0:
4189f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            error(clazz, None, "FW3", "Parcelable requires CREATOR, writeToParcel, and describeContents; missing one")
4198190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4208190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4218190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_protected(clazz):
422b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey    """Verify that no protected methods or fields are allowed."""
4238190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for m in clazz.methods:
4248190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "protected" in m.split:
42590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            error(clazz, m, "M7", "Protected methods not allowed; must be public")
4268190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for f in clazz.fields:
4278190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "protected" in f.split:
42890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            error(clazz, f, "M7", "Protected fields not allowed; must be public")
4298190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4308190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4318190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_fields(clazz):
4328190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify that all exposed fields are final.
4338190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    Exposed fields must follow myName style.
4348190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    Catch internal mFoo objects being exposed."""
4351498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
4361498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    IGNORE_BARE_FIELDS = [
4371498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        "android.app.ActivityManager.RecentTaskInfo",
4381498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        "android.app.Notification",
4391498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        "android.content.pm.ActivityInfo",
4401498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        "android.content.pm.ApplicationInfo",
4411498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        "android.content.pm.FeatureGroupInfo",
4421498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        "android.content.pm.InstrumentationInfo",
4431498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        "android.content.pm.PackageInfo",
4441498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        "android.content.pm.PackageItemInfo",
4451498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        "android.os.Message",
4461498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        "android.system.StructPollfd",
4471498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    ]
4481498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
4498190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for f in clazz.fields:
4508190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if not "final" in f.split:
4511498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            if clazz.fullname in IGNORE_BARE_FIELDS:
4521498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                pass
4531498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            elif clazz.fullname.endswith("LayoutParams"):
4541498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                pass
4551498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            elif clazz.fullname.startswith("android.util.Mutable"):
4561498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                pass
4571498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            else:
4589f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                error(clazz, f, "F2", "Bare fields must be marked final, or add accessors if mutable")
4598190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4608190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if not "static" in f.split:
4618190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if not re.match("[a-z]([a-zA-Z]+)?", f.name):
46290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                error(clazz, f, "S1", "Non-static fields must be named using myField style")
4638190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4641498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        if re.match("[ms][A-Z]", f.name):
46590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            error(clazz, f, "F1", "Internal objects must not be exposed")
4668190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4671498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        if re.match("[A-Z_]+", f.name):
4681498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            if "static" not in f.split or "final" not in f.split:
4699f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                error(clazz, f, "C2", "Constants must be marked static final")
4701498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
4718190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4728190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_register(clazz):
4738190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify parity of registration methods.
4748190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    Callback objects use register/unregister methods.
4758190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    Listener objects use add/remove methods."""
4768190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    methods = [ m.name for m in clazz.methods ]
4778190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for m in clazz.methods:
4788190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "Callback" in m.raw:
4798190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if m.name.startswith("register"):
4808190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                other = "unregister" + m.name[8:]
4818190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                if other not in methods:
4829f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                    error(clazz, m, "L2", "Missing unregister method")
4838190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if m.name.startswith("unregister"):
4848190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                other = "register" + m.name[10:]
4858190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                if other not in methods:
4869f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                    error(clazz, m, "L2", "Missing register method")
4878190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4888190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if m.name.startswith("add") or m.name.startswith("remove"):
4899f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                error(clazz, m, "L3", "Callback methods should be named register/unregister")
4908190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4918190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "Listener" in m.raw:
4928190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if m.name.startswith("add"):
4938190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                other = "remove" + m.name[3:]
4948190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                if other not in methods:
4959f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                    error(clazz, m, "L2", "Missing remove method")
4968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if m.name.startswith("remove") and not m.name.startswith("removeAll"):
4978190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                other = "add" + m.name[6:]
4988190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                if other not in methods:
4999f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                    error(clazz, m, "L2", "Missing add method")
5008190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5018190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if m.name.startswith("register") or m.name.startswith("unregister"):
5029f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                error(clazz, m, "L3", "Listener methods should be named add/remove")
5038190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5048190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5058190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_sync(clazz):
5068190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify synchronized methods aren't exposed."""
5078190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for m in clazz.methods:
5088190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "synchronized" in m.split:
50990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            error(clazz, m, "M5", "Internal locks must not be exposed")
5108190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5118190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5128190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_intent_builder(clazz):
5138190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify that Intent builders are createFooIntent() style."""
5148190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if clazz.name == "Intent": return
5158190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5168190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for m in clazz.methods:
5178190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if m.typ == "android.content.Intent":
5188190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if m.name.startswith("create") and m.name.endswith("Intent"):
5198190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                pass
5208190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            else:
521539ea12810f6373eed926d07b856ab76a8ae8355Adam Powell                warn(clazz, m, "FW1", "Methods creating an Intent should be named createFooIntent()")
5228190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5238190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5248190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_helper_classes(clazz):
525294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    """Verify that helper classes are named consistently with what they extend.
526294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    All developer extendable methods should be named onFoo()."""
527294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    test_methods = False
5288190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if "extends android.app.Service" in clazz.raw:
529294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        test_methods = True
5308190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if not clazz.name.endswith("Service"):
5319f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            error(clazz, None, "CL4", "Inconsistent class name; should be FooService")
5321498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
5331498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        found = False
5341498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        for f in clazz.fields:
5351498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            if f.name == "SERVICE_INTERFACE":
5361498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                found = True
5371498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                if f.value != clazz.fullname:
5389f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                    error(clazz, f, "C4", "Inconsistent interface constant; expected %s" % (clazz.fullname))
5391498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
5408190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if "extends android.content.ContentProvider" in clazz.raw:
541294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        test_methods = True
5428190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if not clazz.name.endswith("Provider"):
5439f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            error(clazz, None, "CL4", "Inconsistent class name; should be FooProvider")
5441498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
5451498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        found = False
5461498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        for f in clazz.fields:
5471498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            if f.name == "PROVIDER_INTERFACE":
5481498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                found = True
5491498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                if f.value != clazz.fullname:
55090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                    error(clazz, f, "C4", "Inconsistent interface constant; expected %s" % (clazz.fullname))
5511498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
5528190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if "extends android.content.BroadcastReceiver" in clazz.raw:
553294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        test_methods = True
5548190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if not clazz.name.endswith("Receiver"):
5559f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            error(clazz, None, "CL4", "Inconsistent class name; should be FooReceiver")
5561498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
557294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    if "extends android.app.Activity" in clazz.raw:
558294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        test_methods = True
559294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        if not clazz.name.endswith("Activity"):
5609f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            error(clazz, None, "CL4", "Inconsistent class name; should be FooActivity")
561294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
562294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    if test_methods:
563294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        for m in clazz.methods:
564294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            if "final" in m.split: continue
565294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            if not re.match("on[A-Z]", m.name):
5661498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                if "abstract" in m.split:
567b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey                    warn(clazz, m, None, "Methods implemented by developers should be named onFoo()")
5681498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                else:
5699f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                    warn(clazz, m, None, "If implemented by developer, should be named onFoo(); otherwise consider marking final")
5708190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5718190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5728190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_builder(clazz):
5738190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify builder classes.
5748190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    Methods should return the builder to enable chaining."""
5758190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if " extends " in clazz.raw: return
5768190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if not clazz.name.endswith("Builder"): return
5778190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5788190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if clazz.name != "Builder":
5799f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        warn(clazz, None, None, "Builder should be defined as inner class")
5808190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5818190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    has_build = False
5828190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for m in clazz.methods:
5838190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if m.name == "build":
5848190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            has_build = True
5858190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            continue
5868190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5878190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if m.name.startswith("get"): continue
5888190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if m.name.startswith("clear"): continue
5898190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
590294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        if m.name.startswith("with"):
59190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            warn(clazz, m, None, "Builder methods names should use setFoo() style")
592294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
593294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        if m.name.startswith("set"):
594294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            if not m.typ.endswith(clazz.fullname):
59590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                warn(clazz, m, "M4", "Methods must return the builder object")
5968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5978190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if not has_build:
5989f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        warn(clazz, None, None, "Missing build() method")
5998190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
6008190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
6018190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_aidl(clazz):
6028190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Catch people exposing raw AIDL."""
603932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    if "extends android.os.Binder" in clazz.raw or "implements android.os.IInterface" in clazz.raw:
60490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey        error(clazz, None, None, "Raw AIDL interfaces must not be exposed")
6058190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
6068190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
607932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkeydef verify_internal(clazz):
608932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    """Catch people exposing internal classes."""
609932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    if clazz.pkg.name.startswith("com.android"):
61090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey        error(clazz, None, None, "Internal classes must not be exposed")
611932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey
612932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey
613932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkeydef verify_layering(clazz):
614932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    """Catch package layering violations.
615932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    For example, something in android.os depending on android.app."""
616932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    ranking = [
617932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        ["android.service","android.accessibilityservice","android.inputmethodservice","android.printservice","android.appwidget","android.webkit","android.preference","android.gesture","android.print"],
618932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.app",
619932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.widget",
620932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.view",
621932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.animation",
622932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.provider",
6231498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        ["android.content","android.graphics.drawable"],
624932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.database",
625932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.graphics",
626932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.text",
627932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.os",
628932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.util"
629932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    ]
630932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey
631932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    def rank(p):
632932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        for i in range(len(ranking)):
633932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey            if isinstance(ranking[i], list):
634932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey                for j in ranking[i]:
635932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey                    if p.startswith(j): return i
636932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey            else:
637932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey                if p.startswith(ranking[i]): return i
638932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey
639932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    cr = rank(clazz.pkg.name)
640932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    if cr is None: return
641932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey
642932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    for f in clazz.fields:
643932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        ir = rank(f.typ)
644932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        if ir and ir < cr:
6459f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            warn(clazz, f, "FW6", "Field type violates package layering")
646932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey
647932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    for m in clazz.methods:
648932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        ir = rank(m.typ)
649932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        if ir and ir < cr:
6509f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            warn(clazz, m, "FW6", "Method return type violates package layering")
651932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        for arg in m.args:
652932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey            ir = rank(arg)
653932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey            if ir and ir < cr:
6549f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                warn(clazz, m, "FW6", "Method argument type violates package layering")
655932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey
656932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey
657a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkeydef verify_boolean(clazz):
65890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    """Verifies that boolean accessors are named correctly.
65990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    For example, hasFoo() and setHasFoo()."""
6601498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
66190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    def is_get(m): return len(m.args) == 0 and m.typ == "boolean"
66290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    def is_set(m): return len(m.args) == 1 and m.args[0] == "boolean"
66390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey
66490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    gets = [ m for m in clazz.methods if is_get(m) ]
66590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    sets = [ m for m in clazz.methods if is_set(m) ]
6661498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
66790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    def error_if_exists(methods, trigger, expected, actual):
66890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey        for m in methods:
66990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            if m.name == actual:
67090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                error(clazz, m, "M6", "Symmetric method for %s must be named %s" % (trigger, expected))
6711498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
672294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    for m in clazz.methods:
67390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey        if is_get(m):
67490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            if re.match("is[A-Z]", m.name):
67590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                target = m.name[2:]
67690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                expected = "setIs" + target
67790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                error_if_exists(sets, m.name, expected, "setHas" + target)
67890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            elif re.match("has[A-Z]", m.name):
67990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                target = m.name[3:]
68090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                expected = "setHas" + target
68190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                error_if_exists(sets, m.name, expected, "setIs" + target)
68290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                error_if_exists(sets, m.name, expected, "set" + target)
68390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            elif re.match("get[A-Z]", m.name):
68490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                target = m.name[3:]
68590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                expected = "set" + target
68690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                error_if_exists(sets, m.name, expected, "setIs" + target)
68790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                error_if_exists(sets, m.name, expected, "setHas" + target)
68890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey
68990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey        if is_set(m):
69090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            if re.match("set[A-Z]", m.name):
69190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                target = m.name[3:]
69290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                expected = "get" + target
69390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                error_if_exists(sets, m.name, expected, "is" + target)
69490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                error_if_exists(sets, m.name, expected, "has" + target)
695294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
696294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
697294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkeydef verify_collections(clazz):
698294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    """Verifies that collection types are interfaces."""
6991498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if clazz.fullname == "android.os.Bundle": return
7001498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
701294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    bad = ["java.util.Vector", "java.util.LinkedList", "java.util.ArrayList", "java.util.Stack",
702294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey           "java.util.HashMap", "java.util.HashSet", "android.util.ArraySet", "android.util.ArrayMap"]
703294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    for m in clazz.methods:
7041498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        if m.typ in bad:
70590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            error(clazz, m, "CL2", "Return type is concrete collection; must be higher-level interface")
706294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        for arg in m.args:
7071498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            if arg in bad:
70890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                error(clazz, m, "CL2", "Argument is concrete collection; must be higher-level interface")
709294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
710294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
711294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkeydef verify_flags(clazz):
712294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    """Verifies that flags are non-overlapping."""
713294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    known = collections.defaultdict(int)
714294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    for f in clazz.fields:
715294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        if "FLAG_" in f.name:
716294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            try:
717294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey                val = int(f.value)
718294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            except:
719294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey                continue
720294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
721294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            scope = f.name[0:f.name.index("FLAG_")]
722294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            if val & known[scope]:
723b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey                warn(clazz, f, "C1", "Found overlapping flag constant value")
724294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            known[scope] |= val
725294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
726294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
7279f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef verify_exception(clazz):
7289f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    """Verifies that methods don't throw generic exceptions."""
7299f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    for m in clazz.methods:
7309f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        if "throws java.lang.Exception" in m.raw or "throws java.lang.Throwable" in m.raw or "throws java.lang.Error" in m.raw:
7319f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            error(clazz, m, "S1", "Methods must not throw generic exceptions")
7329f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
7339f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
7349f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef verify_google(clazz):
7359f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    """Verifies that APIs never reference Google."""
7369f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
7379f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    if re.search("google", clazz.raw, re.IGNORECASE):
7389f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        error(clazz, None, None, "Must never reference Google")
7399f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
7409f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    test = []
7419f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    test.extend(clazz.ctors)
7429f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    test.extend(clazz.fields)
7439f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    test.extend(clazz.methods)
7449f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
7459f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    for t in test:
7469f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        if re.search("google", t.raw, re.IGNORECASE):
7479f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            error(clazz, t, None, "Must never reference Google")
7489f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
7499f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
7509f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef verify_bitset(clazz):
7519f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    """Verifies that we avoid using heavy BitSet."""
7529f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
7539f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    for f in clazz.fields:
7549f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        if f.typ == "java.util.BitSet":
7559f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            error(clazz, f, None, "Field type must not be heavy BitSet")
7569f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
7579f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    for m in clazz.methods:
7589f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        if m.typ == "java.util.BitSet":
7599f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            error(clazz, m, None, "Return type must not be heavy BitSet")
7609f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        for arg in m.args:
7619f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            if arg == "java.util.BitSet":
7629f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                error(clazz, m, None, "Argument type must not be heavy BitSet")
7639f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
7649f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
7659f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef verify_manager(clazz):
7669f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    """Verifies that FooManager is only obtained from Context."""
7679f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
7689f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    if not clazz.name.endswith("Manager"): return
7699f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
7709f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    for c in clazz.ctors:
771047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey        error(clazz, c, None, "Managers must always be obtained from Context; no direct constructors")
7729f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
7739f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
7749f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef verify_boxed(clazz):
7759f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    """Verifies that methods avoid boxed primitives."""
7769f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
777b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey    boxed = ["java.lang.Number","java.lang.Byte","java.lang.Double","java.lang.Float","java.lang.Integer","java.lang.Long","java.lang.Short"]
7789f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
7799f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    for c in clazz.ctors:
7809f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        for arg in c.args:
7819f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            if arg in boxed:
78290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                error(clazz, c, "M11", "Must avoid boxed primitives")
7839f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
7849f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    for f in clazz.fields:
7859f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        if f.typ in boxed:
78690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            error(clazz, f, "M11", "Must avoid boxed primitives")
7879f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
7889f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    for m in clazz.methods:
7899f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        if m.typ in boxed:
79090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            error(clazz, m, "M11", "Must avoid boxed primitives")
7919f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        for arg in m.args:
7929f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            if arg in boxed:
79390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                error(clazz, m, "M11", "Must avoid boxed primitives")
7949f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
7959f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
7969f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef verify_static_utils(clazz):
7979f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    """Verifies that helper classes can't be constructed."""
7989f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    if clazz.fullname.startswith("android.opengl"): return
7999f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    if re.match("android\.R\.[a-z]+", clazz.fullname): return
8009f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
8019f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    if len(clazz.fields) > 0: return
8029f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    if len(clazz.methods) == 0: return
8039f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
8049f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    for m in clazz.methods:
8059f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        if "static" not in m.split:
8069f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            return
8079f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
8089f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    # At this point, we have no fields, and all methods are static
8099f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    if len(clazz.ctors) > 0:
8109f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        error(clazz, None, None, "Fully-static utility classes must not have constructor")
8119f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
8129f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
813b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkeydef verify_overload_args(clazz):
814b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey    """Verifies that method overloads add new arguments at the end."""
815b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey    if clazz.fullname.startswith("android.opengl"): return
816b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
817b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey    overloads = collections.defaultdict(list)
818b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey    for m in clazz.methods:
819b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        if "deprecated" in m.split: continue
820b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        overloads[m.name].append(m)
821b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
822a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    for name, methods in overloads.items():
823b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        if len(methods) <= 1: continue
824b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
825b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        # Look for arguments common across all overloads
826b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        def cluster(args):
827b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey            count = collections.defaultdict(int)
828b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey            res = set()
829b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey            for i in range(len(args)):
830b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey                a = args[i]
831b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey                res.add("%s#%d" % (a, count[a]))
832b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey                count[a] += 1
833b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey            return res
834b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
835b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        common_args = cluster(methods[0].args)
836b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        for m in methods:
837b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey            common_args = common_args & cluster(m.args)
838b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
839b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        if len(common_args) == 0: continue
840b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
841b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        # Require that all common arguments are present at start of signature
842b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        locked_sig = None
843b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        for m in methods:
844b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey            sig = m.args[0:len(common_args)]
845b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey            if not common_args.issubset(cluster(sig)):
846b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey                warn(clazz, m, "M2", "Expected common arguments [%s] at beginning of overloaded method" % (", ".join(common_args)))
847b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey            elif not locked_sig:
848b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey                locked_sig = sig
849b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey            elif locked_sig != sig:
850b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey                error(clazz, m, "M2", "Expected consistent argument ordering between overloads: %s..." % (", ".join(locked_sig)))
851b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
852b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
853b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkeydef verify_callback_handlers(clazz):
854b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey    """Verifies that methods adding listener/callback have overload
855b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey    for specifying delivery thread."""
856b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
857047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey    # Ignore UI packages which assume main thread
858b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey    skip = [
859047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey        "animation",
860047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey        "view",
861047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey        "graphics",
862047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey        "transition",
863047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey        "widget",
864047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey        "webkit",
865b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey    ]
866b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey    for s in skip:
867047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey        if s in clazz.pkg.name_path: return
868047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey        if s in clazz.extends_path: return
869047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey
870047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey    # Ignore UI classes which assume main thread
871047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey    if "app" in clazz.pkg.name_path or "app" in clazz.extends_path:
872047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey        for s in ["ActionBar","Dialog","Application","Activity","Fragment","Loader"]:
873047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey            if s in clazz.fullname: return
874047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey    if "content" in clazz.pkg.name_path or "content" in clazz.extends_path:
875047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey        for s in ["Loader"]:
876047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey            if s in clazz.fullname: return
877b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
878b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey    found = {}
879b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey    by_name = collections.defaultdict(list)
880b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey    for m in clazz.methods:
881b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        if m.name.startswith("unregister"): continue
882b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        if m.name.startswith("remove"): continue
883b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        if re.match("on[A-Z]+", m.name): continue
884b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
885b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        by_name[m.name].append(m)
886b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
887b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        for a in m.args:
888b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey            if a.endswith("Listener") or a.endswith("Callback") or a.endswith("Callbacks"):
889b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey                found[m.name] = m
890b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
891b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey    for f in found.values():
892b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        takes_handler = False
893b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        for m in by_name[f.name]:
894b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey            if "android.os.Handler" in m.args:
895b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey                takes_handler = True
896b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        if not takes_handler:
89790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            warn(clazz, f, "L1", "Registration methods should have overload that accepts delivery Handler")
898b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
899b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
900b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkeydef verify_context_first(clazz):
901b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey    """Verifies that methods accepting a Context keep it the first argument."""
90290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    examine = clazz.ctors + clazz.methods
90390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    for m in examine:
90490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey        if len(m.args) > 1 and m.args[0] != "android.content.Context":
905b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey            if "android.content.Context" in m.args[1:]:
906b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey                error(clazz, m, "M3", "Context is distinct, so it must be the first argument")
907b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
90890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey
90990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkeydef verify_listener_last(clazz):
91090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    """Verifies that methods accepting a Listener or Callback keep them as last arguments."""
91190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    examine = clazz.ctors + clazz.methods
91290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    for m in examine:
91390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey        if "Listener" in m.name or "Callback" in m.name: continue
91490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey        found = False
91590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey        for a in m.args:
91690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            if a.endswith("Callback") or a.endswith("Callbacks") or a.endswith("Listener"):
91790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                found = True
91890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            elif found and a != "android.os.Handler":
91990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                warn(clazz, m, "M3", "Listeners should always be at end of argument list")
92090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey
92190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey
92290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkeydef verify_resource_names(clazz):
92390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    """Verifies that resource names have consistent case."""
92490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    if not re.match("android\.R\.[a-z]+", clazz.fullname): return
92590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey
92690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    # Resources defined by files are foo_bar_baz
92790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    if clazz.name in ["anim","animator","color","dimen","drawable","interpolator","layout","transition","menu","mipmap","string","plurals","raw","xml"]:
92890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey        for f in clazz.fields:
92990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            if re.match("[a-z1-9_]+$", f.name): continue
93090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            error(clazz, f, None, "Expected resource name in this class to be foo_bar_baz style")
93190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey
93290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    # Resources defined inside files are fooBarBaz
93390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    if clazz.name in ["array","attr","id","bool","fraction","integer"]:
93490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey        for f in clazz.fields:
93590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            if re.match("config_[a-z][a-zA-Z1-9]*$", f.name): continue
93690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            if re.match("layout_[a-z][a-zA-Z1-9]*$", f.name): continue
93790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            if re.match("state_[a-z_]*$", f.name): continue
93890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey
93990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            if re.match("[a-z][a-zA-Z1-9]*$", f.name): continue
94090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            error(clazz, f, "C7", "Expected resource name in this class to be fooBarBaz style")
94190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey
94290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    # Styles are FooBar_Baz
94390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    if clazz.name in ["style"]:
94490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey        for f in clazz.fields:
94590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            if re.match("[A-Z][A-Za-z1-9]+(_[A-Z][A-Za-z1-9]+?)*$", f.name): continue
94690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            error(clazz, f, "C7", "Expected resource name in this class to be FooBar_Baz style")
947b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
948b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
949a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkeydef examine_clazz(clazz):
950a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    """Find all style issues in the given class."""
951a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    if clazz.pkg.name.startswith("java"): return
952a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    if clazz.pkg.name.startswith("junit"): return
953a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    if clazz.pkg.name.startswith("org.apache"): return
954a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    if clazz.pkg.name.startswith("org.xml"): return
955a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    if clazz.pkg.name.startswith("org.json"): return
956a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    if clazz.pkg.name.startswith("org.w3c"): return
957a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey
958a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_constants(clazz)
959a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_enums(clazz)
960a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_class_names(clazz)
961a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_method_names(clazz)
962a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_callbacks(clazz)
963a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_listeners(clazz)
964a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_actions(clazz)
965a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_extras(clazz)
966a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_equals(clazz)
967a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_parcelable(clazz)
968a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_protected(clazz)
969a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_fields(clazz)
970a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_register(clazz)
971a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_sync(clazz)
972a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_intent_builder(clazz)
973a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_helper_classes(clazz)
974a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_builder(clazz)
975a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_aidl(clazz)
976a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_internal(clazz)
977a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_layering(clazz)
978a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_boolean(clazz)
979a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_collections(clazz)
980a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_flags(clazz)
981a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_exception(clazz)
9821c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf    if not ALLOW_GOOGLE: verify_google(clazz)
983a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_bitset(clazz)
984a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_manager(clazz)
985a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_boxed(clazz)
986a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_static_utils(clazz)
987a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_overload_args(clazz)
988a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_callback_handlers(clazz)
989a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_context_first(clazz)
990a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_listener_last(clazz)
991a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_resource_names(clazz)
992a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey
993a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey
994a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkeydef examine_stream(stream):
995a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    """Find all style issues in the given API stream."""
9968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    global failures
997a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    failures = {}
998a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    _parse_stream(stream, examine_clazz)
999a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    return failures
1000a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey
10018190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1002a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkeydef examine_api(api):
1003a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    """Find all style issues in the given parsed API."""
1004a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    global failures
10051498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    failures = {}
10061498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    for key in sorted(api.keys()):
1007a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey        examine_clazz(api[key])
10088190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    return failures
10098190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
10108190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1011037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkeydef verify_compat(cur, prev):
1012037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    """Find any incompatible API changes between two levels."""
1013037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    global failures
1014037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1015037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    def class_exists(api, test):
1016037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        return test.fullname in api
1017037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1018037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    def ctor_exists(api, clazz, test):
1019037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        for m in clazz.ctors:
1020037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            if m.ident == test.ident: return True
1021037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        return False
1022037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1023037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    def all_methods(api, clazz):
1024037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        methods = list(clazz.methods)
1025037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        if clazz.extends is not None:
1026037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            methods.extend(all_methods(api, api[clazz.extends]))
1027037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        return methods
1028037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1029037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    def method_exists(api, clazz, test):
1030037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        methods = all_methods(api, clazz)
1031037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        for m in methods:
1032037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            if m.ident == test.ident: return True
1033037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        return False
1034037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1035037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    def field_exists(api, clazz, test):
1036037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        for f in clazz.fields:
1037037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            if f.ident == test.ident: return True
1038037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        return False
1039037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1040037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    failures = {}
1041037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    for key in sorted(prev.keys()):
1042037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        prev_clazz = prev[key]
1043037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1044037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        if not class_exists(cur, prev_clazz):
10459f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            error(prev_clazz, None, None, "Class removed or incompatible change")
1046037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            continue
1047037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1048037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        cur_clazz = cur[key]
1049037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1050037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        for test in prev_clazz.ctors:
1051037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            if not ctor_exists(cur, cur_clazz, test):
10529f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                error(prev_clazz, prev_ctor, None, "Constructor removed or incompatible change")
1053037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1054037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        methods = all_methods(prev, prev_clazz)
1055037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        for test in methods:
1056037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            if not method_exists(cur, cur_clazz, test):
10579f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                error(prev_clazz, test, None, "Method removed or incompatible change")
1058037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1059037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        for test in prev_clazz.fields:
1060037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            if not field_exists(cur, cur_clazz, test):
10619f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                error(prev_clazz, test, None, "Field removed or incompatible change")
1062037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1063037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    return failures
1064037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1065037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1066ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkeyif __name__ == "__main__":
10671c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf    parser = argparse.ArgumentParser(description="Enforces common Android public API design \
10681c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf            patterns. It ignores lint messages from a previous API level, if provided.")
10691c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf    parser.add_argument("current.txt", type=argparse.FileType('r'), help="current.txt")
10701c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf    parser.add_argument("previous.txt", nargs='?', type=argparse.FileType('r'), default=None,
10711c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf            help="previous.txt")
10721c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf    parser.add_argument("--no-color", action='store_const', const=True,
10731c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf            help="Disable terminal colors")
10741c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf    parser.add_argument("--allow-google", action='store_const', const=True,
10751c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf            help="Allow references to Google")
10761c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf    args = vars(parser.parse_args())
10771c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf
10781c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf    if args['no_color']:
10791c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf        USE_COLOR = False
10801c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf
10811c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf    if args['allow_google']:
10821c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf        ALLOW_GOOGLE = True
10831c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf
10841c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf    current_file = args['current.txt']
10851c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf    previous_file = args['previous.txt']
10861c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf
10871c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf    with current_file as f:
1088a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey        cur_fail = examine_stream(f)
10891c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf    if not previous_file is None:
10901c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf        with previous_file as f:
1091a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey            prev_fail = examine_stream(f)
10928190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1093ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        # ignore errors from previous API level
1094ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        for p in prev_fail:
1095ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey            if p in cur_fail:
1096ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey                del cur_fail[p]
10978190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1098a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey        """
1099a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey        # NOTE: disabled because of memory pressure
1100ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        # look for compatibility issues
1101ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        compat_fail = verify_compat(cur, prev)
1102037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1103ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        print "%s API compatibility issues %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True)))
1104ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        for f in sorted(compat_fail):
1105ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey            print compat_fail[f]
1106ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey            print
1107a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey        """
11088190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1109ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey    print "%s API style issues %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True)))
1110ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey    for f in sorted(cur_fail):
1111ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        print cur_fail[f]
1112ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        print
1113