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
17package com.google.doclava.apicheck;
18
19import java.io.FileInputStream;
20import java.io.FileNotFoundException;
21import java.io.IOException;
22import java.io.InputStream;
23import java.io.PrintStream;
24import java.net.URL;
25import java.util.ArrayList;
26import java.util.Set;
27import java.util.Stack;
28
29import com.google.doclava.Errors;
30import com.google.doclava.Errors.ErrorMessage;
31import com.google.doclava.Stubs;
32
33public 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
107    try {
108      oldApi = parseApi(args.get(0));
109      newApi = parseApi(args.get(1));
110    } catch (ApiParseException e) {
111      e.printStackTrace();
112      System.err.println("Error parsing API");
113      return new Report(1, Errors.getErrors());
114    }
115
116    // only run the consistency check if we haven't had XML parse errors
117    if (!Errors.hadError) {
118      oldApi.isConsistent(newApi);
119    }
120
121    return new Report(Errors.hadError ? 1 : 0, Errors.getErrors());
122  }
123
124  public static ApiInfo parseApi(String filename) throws ApiParseException {
125    InputStream stream = null;
126    Throwable textParsingError = null;
127    // try it as our format
128    try {
129      stream = new FileInputStream(filename);
130    } catch (IOException e) {
131      throw new ApiParseException("Could not open file for parsing: " + filename, e);
132    }
133    try {
134      return ApiFile.parseApi(filename, stream);
135    } catch (ApiParseException ignored) {
136      textParsingError = ignored;
137      if (false) {
138        return null;
139      }
140    } finally {
141      try {
142        stream.close();
143      } catch (IOException ignored) {}
144    }
145    // try it as xml
146    try {
147      stream = new FileInputStream(filename);
148    } catch (IOException e) {
149      throw new ApiParseException("Could not open file for parsing: " + filename, e);
150    }
151    try {
152      return XmlApiFile.parseApi(stream);
153    } catch (ApiParseException ignored) {
154        System.out.println("Couldn't parse API file \"" + filename + "\"");
155        System.out.println("  ...as text: " + textParsingError.toString());
156        System.out.println("  ...as XML:  " + ignored.toString());
157        if (false) {
158          if (textParsingError != null) textParsingError.printStackTrace();
159          ignored.printStackTrace();
160          return null;
161        }
162    } finally {
163      try {
164        stream.close();
165      } catch (IOException ignored) {}
166    }
167    return null;
168  }
169
170  public ApiInfo parseApi(URL url) throws ApiParseException {
171    InputStream stream = null;
172    // try it as our format
173    try {
174      stream = url.openStream();
175    } catch (IOException e) {
176      throw new ApiParseException("Could not open stream for parsing: " + url, e);
177    }
178    try {
179      return ApiFile.parseApi(url.toString(), stream);
180    } catch (ApiParseException ignored) {
181    } finally {
182      try {
183        stream.close();
184      } catch (IOException ignored) {}
185    }
186    // try it as xml
187    try {
188      stream = url.openStream();
189    } catch (IOException e) {
190      throw new ApiParseException("Could not open stream for parsing: " + url, e);
191    }
192    try {
193      return XmlApiFile.parseApi(stream);
194    } finally {
195      try {
196        stream.close();
197      } catch (IOException ignored) {}
198    }
199  }
200
201  public class Report {
202    private int code;
203    private Set<ErrorMessage> errors;
204
205    private Report(int code, Set<ErrorMessage> errors) {
206      this.code = code;
207      this.errors = errors;
208    }
209
210    public int code() {
211      return code;
212    }
213
214    public Set<ErrorMessage> errors() {
215      return errors;
216    }
217  }
218
219  static int convertToApi(String src, String dst) {
220    ApiInfo api;
221    try {
222      api = parseApi(src);
223    } catch (ApiParseException e) {
224      e.printStackTrace();
225      System.err.println("Error parsing API: " + src);
226      return 1;
227    }
228
229    PrintStream apiWriter = null;
230    try {
231      apiWriter = new PrintStream(dst);
232    } catch (FileNotFoundException ex) {
233      System.err.println("can't open file: " + dst);
234    }
235
236    Stubs.writeApi(apiWriter, api.getPackages().values());
237
238    return 0;
239  }
240
241  static int convertToXml(String src, String dst) {
242    ApiInfo api;
243    try {
244      api = parseApi(src);
245    } catch (ApiParseException e) {
246      e.printStackTrace();
247      System.err.println("Error parsing API: " + src);
248      return 1;
249    }
250
251    PrintStream apiWriter = null;
252    try {
253      apiWriter = new PrintStream(dst);
254    } catch (FileNotFoundException ex) {
255      System.err.println("can't open file: " + dst);
256    }
257
258    Stubs.writeXml(apiWriter, api.getPackages().values());
259
260    return 0;
261  }
262
263}
264