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