Home | History | Annotate | Download | only in apicheck
      1 /*
      2  * Copyright (C) 2010 Google Inc.
      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.google.doclava.apicheck;
     18 
     19 import java.io.FileInputStream;
     20 import java.io.FileNotFoundException;
     21 import java.io.IOException;
     22 import java.io.InputStream;
     23 import java.io.PrintStream;
     24 import java.net.URL;
     25 import java.util.ArrayList;
     26 import java.util.Set;
     27 import java.util.Stack;
     28 
     29 import com.google.doclava.Errors;
     30 import com.google.doclava.Errors.ErrorMessage;
     31 import com.google.doclava.Stubs;
     32 
     33 public class ApiCheck {
     34   // parse out and consume the -whatever command line flags
     35   private static ArrayList<String[]> parseFlags(ArrayList<String> allArgs) {
     36     ArrayList<String[]> ret = new ArrayList<String[]>();
     37 
     38     int i;
     39     for (i = 0; i < allArgs.size(); i++) {
     40       // flags with one value attached
     41       String flag = allArgs.get(i);
     42       if (flag.equals("-error") || flag.equals("-warning") || flag.equals("-hide")) {
     43         String[] arg = new String[2];
     44         arg[0] = flag;
     45         arg[1] = allArgs.get(++i);
     46         ret.add(arg);
     47       } else {
     48         // we've consumed all of the -whatever args, so we're done
     49         break;
     50       }
     51     }
     52 
     53     // i now points to the first non-flag arg; strip what came before
     54     for (; i > 0; i--) {
     55       allArgs.remove(0);
     56     }
     57     return ret;
     58   }
     59 
     60   public static void main(String[] originalArgs) {
     61     if (originalArgs.length == 3 && "-convert".equals(originalArgs[0])) {
     62       System.exit(convertToApi(originalArgs[1], originalArgs[2]));
     63     } else if (originalArgs.length == 3 && "-convert2xml".equals(originalArgs[0])) {
     64       System.exit(convertToXml(originalArgs[1], originalArgs[2]));
     65     } else {
     66       ApiCheck acheck = new ApiCheck();
     67       Report report = acheck.checkApi(originalArgs);
     68 
     69       Errors.printErrors(report.errors());
     70       System.exit(report.code);
     71     }
     72   }
     73 
     74   /**
     75    * Compares two api xml files for consistency.
     76    */
     77   public Report checkApi(String[] originalArgs) {
     78     // translate to an ArrayList<String> for munging
     79     ArrayList<String> args = new ArrayList<String>(originalArgs.length);
     80     for (String a : originalArgs) {
     81       args.add(a);
     82     }
     83 
     84     ArrayList<String[]> flags = ApiCheck.parseFlags(args);
     85     for (String[] a : flags) {
     86       if (a[0].equals("-error") || a[0].equals("-warning") || a[0].equals("-hide")) {
     87         try {
     88           int level = -1;
     89           if (a[0].equals("-error")) {
     90             level = Errors.ERROR;
     91           } else if (a[0].equals("-warning")) {
     92             level = Errors.WARNING;
     93           } else if (a[0].equals("-hide")) {
     94             level = Errors.HIDDEN;
     95           }
     96           Errors.setErrorLevel(Integer.parseInt(a[1]), level);
     97         } catch (NumberFormatException e) {
     98           System.err.println("Bad argument: " + a[0] + " " + a[1]);
     99           return new Report(2, Errors.getErrors());
    100         }
    101       }
    102     }
    103 
    104     ApiInfo oldApi;
    105     ApiInfo newApi;
    106     ApiInfo oldRemovedApi;
    107     ApiInfo newRemovedApi;
    108 
    109     // commandline options look like:
    110     // [other optoins] old_api.txt new_api.txt old_removed_api.txt new_removed_api.txt
    111     try {
    112       oldApi = parseApi(args.get(0));
    113       newApi = parseApi(args.get(1));
    114       oldRemovedApi = parseApi(args.get(2));
    115       newRemovedApi = parseApi(args.get(3));
    116     } catch (ApiParseException e) {
    117       e.printStackTrace();
    118       System.err.println("Error parsing API");
    119       return new Report(1, Errors.getErrors());
    120     }
    121 
    122     // only run the consistency check if we haven't had XML parse errors
    123     if (!Errors.hadError) {
    124       oldApi.isConsistent(newApi);
    125     }
    126 
    127     if (!Errors.hadError) {
    128       oldRemovedApi.isConsistent(newRemovedApi);
    129     }
    130 
    131     return new Report(Errors.hadError ? 1 : 0, Errors.getErrors());
    132   }
    133 
    134   public static ApiInfo parseApi(String filename) throws ApiParseException {
    135     InputStream stream = null;
    136     Throwable textParsingError = null;
    137     // try it as our format
    138     try {
    139       stream = new FileInputStream(filename);
    140     } catch (IOException e) {
    141       throw new ApiParseException("Could not open file for parsing: " + filename, e);
    142     }
    143     try {
    144       return ApiFile.parseApi(filename, stream);
    145     } catch (ApiParseException ignored) {
    146       textParsingError = ignored;
    147       if (false) {
    148         return null;
    149       }
    150     } finally {
    151       try {
    152         stream.close();
    153       } catch (IOException ignored) {}
    154     }
    155     // try it as xml
    156     try {
    157       stream = new FileInputStream(filename);
    158     } catch (IOException e) {
    159       throw new ApiParseException("Could not open file for parsing: " + filename, e);
    160     }
    161     try {
    162       return XmlApiFile.parseApi(stream);
    163     } catch (ApiParseException ignored) {
    164         System.out.println("Couldn't parse API file \"" + filename + "\"");
    165         System.out.println("  ...as text: " + textParsingError.toString());
    166         System.out.println("  ...as XML:  " + ignored.toString());
    167         if (false) {
    168           if (textParsingError != null) textParsingError.printStackTrace();
    169           ignored.printStackTrace();
    170           return null;
    171         }
    172     } finally {
    173       try {
    174         stream.close();
    175       } catch (IOException ignored) {}
    176     }
    177     return null;
    178   }
    179 
    180   public ApiInfo parseApi(URL url) throws ApiParseException {
    181     InputStream stream = null;
    182     // try it as our format
    183     try {
    184       stream = url.openStream();
    185     } catch (IOException e) {
    186       throw new ApiParseException("Could not open stream for parsing: " + url, e);
    187     }
    188     try {
    189       return ApiFile.parseApi(url.toString(), stream);
    190     } catch (ApiParseException ignored) {
    191     } finally {
    192       try {
    193         stream.close();
    194       } catch (IOException ignored) {}
    195     }
    196     // try it as xml
    197     try {
    198       stream = url.openStream();
    199     } catch (IOException e) {
    200       throw new ApiParseException("Could not open stream for parsing: " + url, e);
    201     }
    202     try {
    203       return XmlApiFile.parseApi(stream);
    204     } finally {
    205       try {
    206         stream.close();
    207       } catch (IOException ignored) {}
    208     }
    209   }
    210 
    211   public class Report {
    212     private int code;
    213     private Set<ErrorMessage> errors;
    214 
    215     private Report(int code, Set<ErrorMessage> errors) {
    216       this.code = code;
    217       this.errors = errors;
    218     }
    219 
    220     public int code() {
    221       return code;
    222     }
    223 
    224     public Set<ErrorMessage> errors() {
    225       return errors;
    226     }
    227   }
    228 
    229   static int convertToApi(String src, String dst) {
    230     ApiInfo api;
    231     try {
    232       api = parseApi(src);
    233     } catch (ApiParseException e) {
    234       e.printStackTrace();
    235       System.err.println("Error parsing API: " + src);
    236       return 1;
    237     }
    238 
    239     PrintStream apiWriter = null;
    240     try {
    241       apiWriter = new PrintStream(dst);
    242     } catch (FileNotFoundException ex) {
    243       System.err.println("can't open file: " + dst);
    244     }
    245 
    246     Stubs.writeApi(apiWriter, api.getPackages().values());
    247 
    248     return 0;
    249   }
    250 
    251   static int convertToXml(String src, String dst) {
    252     ApiInfo api;
    253     try {
    254       api = parseApi(src);
    255     } catch (ApiParseException e) {
    256       e.printStackTrace();
    257       System.err.println("Error parsing API: " + src);
    258       return 1;
    259     }
    260 
    261     PrintStream apiWriter = null;
    262     try {
    263       apiWriter = new PrintStream(dst);
    264     } catch (FileNotFoundException ex) {
    265       System.err.println("can't open file: " + dst);
    266     }
    267 
    268     Stubs.writeXml(apiWriter, api.getPackages().values());
    269 
    270     return 0;
    271   }
    272 
    273 }
    274