Home | History | Annotate | Download | only in apicheck
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.apicheck;
     18 
     19 import org.xml.sax.*;
     20 import org.xml.sax.helpers.*;
     21 import java.io.*;
     22 import java.util.ArrayList;
     23 import java.util.Stack;
     24 
     25 public class ApiCheck {
     26         // parse out and consume the -whatever command line flags
     27         private static ArrayList<String[]> parseFlags(ArrayList<String> allArgs) {
     28             ArrayList<String[]> ret = new ArrayList<String[]>();
     29 
     30             int i;
     31             for (i = 0; i < allArgs.size(); i++) {
     32                 // flags with one value attached
     33                 String flag = allArgs.get(i);
     34                 if (flag.equals("-error")
     35                         || flag.equals("-warning")
     36                         || flag.equals("-hide")) {
     37                     String[] arg = new String[2];
     38                     arg[0] = flag;
     39                     arg[1] = allArgs.get(++i);
     40                     ret.add(arg);
     41                 } else {
     42                     // we've consumed all of the -whatever args, so we're done
     43                     break;
     44                 }
     45             }
     46 
     47             // i now points to the first non-flag arg; strip what came before
     48             for (; i > 0; i--) {
     49                 allArgs.remove(0);
     50             }
     51             return ret;
     52         }
     53 
     54         public static void main(String[] originalArgs) {
     55             // translate to an ArrayList<String> for munging
     56             ArrayList<String> args = new ArrayList<String>(originalArgs.length);
     57             for (String a: originalArgs) {
     58                 args.add(a);
     59             }
     60 
     61             ArrayList<String[]> flags = ApiCheck.parseFlags(args);
     62             for (String[] a: flags) {
     63                 if (a[0].equals("-error") || a[0].equals("-warning")
     64                         || a[0].equals("-hide")) {
     65                     try {
     66                         int level = -1;
     67                         if (a[0].equals("-error")) {
     68                             level = Errors.ERROR;
     69                         }
     70                         else if (a[0].equals("-warning")) {
     71                             level = Errors.WARNING;
     72                         }
     73                         else if (a[0].equals("-hide")) {
     74                             level = Errors.HIDDEN;
     75                         }
     76                         Errors.setErrorLevel(Integer.parseInt(a[1]), level);
     77                     }
     78                     catch (NumberFormatException e) {
     79                         System.err.println("Bad argument: " + a[0] + " " + a[1]);
     80                         System.exit(2);
     81                     }
     82                 }
     83             }
     84 
     85             ApiCheck acheck = new ApiCheck();
     86 
     87             ApiInfo oldApi = acheck.parseApi(args.get(0));
     88             ApiInfo newApi = acheck.parseApi(args.get(1));
     89 
     90             // only run the consistency check if we haven't had XML parse errors
     91             if (!Errors.hadError) {
     92                 oldApi.isConsistent(newApi);
     93             }
     94 
     95             Errors.printErrors();
     96             System.exit(Errors.hadError ? 1 : 0);
     97         }
     98 
     99     public ApiInfo parseApi(String xmlFile) {
    100         FileReader fileReader = null;
    101         try {
    102             XMLReader xmlreader = XMLReaderFactory.createXMLReader();
    103             MakeHandler handler = new MakeHandler();
    104             xmlreader.setContentHandler(handler);
    105             xmlreader.setErrorHandler(handler);
    106             fileReader = new FileReader(xmlFile);
    107             xmlreader.parse(new InputSource(fileReader));
    108             ApiInfo apiInfo = handler.getApi();
    109             apiInfo.resolveSuperclasses();
    110             return apiInfo;
    111         } catch (SAXParseException e) {
    112             Errors.error(Errors.PARSE_ERROR,
    113                     new SourcePositionInfo(xmlFile, e.getLineNumber(), 0),
    114                     e.getMessage());
    115         } catch (Exception e) {
    116             e.printStackTrace();
    117             Errors.error(Errors.PARSE_ERROR,
    118                     new SourcePositionInfo(xmlFile, 0, 0), e.getMessage());
    119         } finally {
    120             if (fileReader != null) {
    121                 try {
    122                     fileReader.close();
    123                 } catch (IOException ignored) {}
    124             }
    125         }
    126         return null;
    127     }
    128 
    129     private static class MakeHandler extends DefaultHandler {
    130 
    131             private ApiInfo mApi;
    132             private PackageInfo mCurrentPackage;
    133             private ClassInfo mCurrentClass;
    134             private AbstractMethodInfo mCurrentMethod;
    135             private Stack<ClassInfo> mClassScope = new Stack<ClassInfo>();
    136 
    137 
    138             public MakeHandler() {
    139                 super();
    140                 mApi = new ApiInfo();
    141             }
    142 
    143             @Override
    144             public void startElement(String uri, String localName, String qName,
    145                                      Attributes attributes) {
    146                 if (qName.equals("package")) {
    147                     mCurrentPackage = new PackageInfo(attributes.getValue("name"),
    148                             SourcePositionInfo.fromXml(attributes.getValue("source")));
    149                 } else if (qName.equals("class")
    150                         || qName.equals("interface")) {
    151                     // push the old outer scope for later recovery, then set
    152                     // up the new current class object
    153                     mClassScope.push(mCurrentClass);
    154                     mCurrentClass = new ClassInfo(attributes.getValue("name"),
    155                                                   mCurrentPackage,
    156                                                   attributes.getValue("extends") ,
    157                                                   qName.equals("interface"),
    158                                                   Boolean.valueOf(
    159                                                       attributes.getValue("abstract")),
    160                                                   Boolean.valueOf(
    161                                                       attributes.getValue("static")),
    162                                                   Boolean.valueOf(
    163                                                       attributes.getValue("final")),
    164                                                   attributes.getValue("deprecated"),
    165                                                   attributes.getValue("visibility"),
    166                                                   SourcePositionInfo.fromXml(attributes.getValue("source")),
    167                                                   mCurrentClass);
    168                 } else if (qName.equals("method")) {
    169                     mCurrentMethod = new MethodInfo(attributes.getValue("name"),
    170                                                     attributes.getValue("return") ,
    171                                                     Boolean.valueOf(
    172                                                         attributes.getValue("abstract")),
    173                                                     Boolean.valueOf(
    174                                                         attributes.getValue("native")),
    175                                                     Boolean.valueOf(
    176                                                         attributes.getValue("synchronized")),
    177                                                     Boolean.valueOf(
    178                                                         attributes.getValue("static")),
    179                                                     Boolean.valueOf(
    180                                                         attributes.getValue("final")),
    181                                                     attributes.getValue("deprecated"),
    182                                                     attributes.getValue("visibility"),
    183                                                     SourcePositionInfo.fromXml(attributes.getValue("source")),
    184                                                     mCurrentClass);
    185                 } else if (qName.equals("constructor")) {
    186                     mCurrentMethod = new ConstructorInfo(attributes.getValue("name"),
    187                                                          attributes.getValue("type") ,
    188                                                          Boolean.valueOf(
    189                                                              attributes.getValue("static")),
    190                                                          Boolean.valueOf(
    191                                                              attributes.getValue("final")),
    192                                                          attributes.getValue("deprecated"),
    193                                                          attributes.getValue("visibility"),
    194                                                          SourcePositionInfo.fromXml(attributes.getValue("source")),
    195                                                          mCurrentClass);
    196                 } else if (qName.equals("field")) {
    197                     FieldInfo fInfo = new FieldInfo(attributes.getValue("name"),
    198                                                     attributes.getValue("type") ,
    199                                                     Boolean.valueOf(
    200                                                         attributes.getValue("transient")),
    201                                                     Boolean.valueOf(
    202                                                         attributes.getValue("volatile")),
    203                                                     attributes.getValue("value"),
    204                                                     Boolean.valueOf(
    205                                                         attributes.getValue("static")),
    206                                                     Boolean.valueOf(
    207                                                         attributes.getValue("final")),
    208                                                     attributes.getValue("deprecated"),
    209                                                     attributes.getValue("visibility"),
    210                                                     SourcePositionInfo.fromXml(attributes.getValue("source")),
    211                                                     mCurrentClass);
    212                     mCurrentClass.addField(fInfo);
    213                 } else if (qName.equals("parameter")) {
    214                     mCurrentMethod.addParameter(new ParameterInfo(attributes.getValue("type"),
    215                                                                   attributes.getValue("name")));
    216                 } else if (qName.equals("exception")) {
    217                     mCurrentMethod.addException(attributes.getValue("type"));
    218                 } else if (qName.equals("implements")) {
    219                     mCurrentClass.addInterface(attributes.getValue("name"));
    220                 }
    221             }
    222 
    223             @Override
    224             public void endElement(String uri, String localName, String qName) {
    225                 if (qName.equals("method")) {
    226                     mCurrentClass.addMethod((MethodInfo) mCurrentMethod);
    227                 } else if (qName.equals("constructor")) {
    228                     mCurrentClass.addConstructor((ConstructorInfo) mCurrentMethod);
    229                 } else if (qName.equals("class")
    230                         || qName.equals("interface")) {
    231                     mCurrentPackage.addClass(mCurrentClass);
    232                     mCurrentClass = mClassScope.pop();
    233                 } else if (qName.equals("package")){
    234                     mApi.addPackage(mCurrentPackage);
    235                 }
    236             }
    237             public ApiInfo getApi() {
    238                 return mApi;
    239             }
    240         }
    241 }
    242