2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.apicheck;
20 import org.xml.sax.helpers.*;
22 import java.util.ArrayList;
23 import java.util.Stack;
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[]>();
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];
39 arg[1] = allArgs.get(++i);
42 // we've consumed all of the -whatever args, so we're done
47 // i now points to the first non-flag arg; strip what came before
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) {
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")) {
67 if (a[0].equals("-error")) {
70 else if (a[0].equals("-warning")) {
71 level = Errors.WARNING;
73 else if (a[0].equals("-hide")) {
74 level = Errors.HIDDEN;
76 Errors.setErrorLevel(Integer.parseInt(a[1]), level);
78 catch (NumberFormatException e) {
79 System.err.println("Bad argument: " + a[0] + " " + a[1]);
85 ApiCheck acheck = new ApiCheck();
87 ApiInfo oldApi = acheck.parseApi(args.get(0));
88 ApiInfo newApi = acheck.parseApi(args.get(1));
90 // only run the consistency check if we haven't had XML parse errors
91 if (!Errors.hadError) {
92 oldApi.isConsistent(newApi);
96 System.exit(Errors.hadError ? 1 : 0);
99 public ApiInfo parseApi(String xmlFile) {
100 FileReader fileReader = null;
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();
111 } catch (SAXParseException e) {
112 Errors.error(Errors.PARSE_ERROR,
113 new SourcePositionInfo(xmlFile, e.getLineNumber(), 0),
115 } catch (Exception e) {
117 Errors.error(Errors.PARSE_ERROR,
118 new SourcePositionInfo(xmlFile, 0, 0), e.getMessage());
120 if (fileReader != null) {
123 } catch (IOException ignored) {}
129 private static class MakeHandler extends DefaultHandler {
131 private ApiInfo mApi;
132 private PackageInfo mCurrentPackage;
133 private ClassInfo mCurrentClass;
134 private AbstractMethodInfo mCurrentMethod;
135 private Stack<ClassInfo> mClassScope = new Stack<ClassInfo>();
138 public MakeHandler() {
140 mApi = new ApiInfo();
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"),
156 attributes.getValue("extends") ,
157 qName.equals("interface"),
159 attributes.getValue("abstract")),
161 attributes.getValue("static")),
163 attributes.getValue("final")),
164 attributes.getValue("deprecated"),
165 attributes.getValue("visibility"),
166 SourcePositionInfo.fromXml(attributes.getValue("source")),
168 } else if (qName.equals("method")) {
169 mCurrentMethod = new MethodInfo(attributes.getValue("name"),
170 attributes.getValue("return") ,
172 attributes.getValue("abstract")),
174 attributes.getValue("native")),
176 attributes.getValue("synchronized")),
178 attributes.getValue("static")),
180 attributes.getValue("final")),
181 attributes.getValue("deprecated"),
182 attributes.getValue("visibility"),
183 SourcePositionInfo.fromXml(attributes.getValue("source")),
185 } else if (qName.equals("constructor")) {
186 mCurrentMethod = new ConstructorInfo(attributes.getValue("name"),
187 attributes.getValue("type") ,
189 attributes.getValue("static")),
191 attributes.getValue("final")),
192 attributes.getValue("deprecated"),
193 attributes.getValue("visibility"),
194 SourcePositionInfo.fromXml(attributes.getValue("source")),
196 } else if (qName.equals("field")) {
197 FieldInfo fInfo = new FieldInfo(attributes.getValue("name"),
198 attributes.getValue("type") ,
200 attributes.getValue("transient")),
202 attributes.getValue("volatile")),
203 attributes.getValue("value"),
205 attributes.getValue("static")),
207 attributes.getValue("final")),
208 attributes.getValue("deprecated"),
209 attributes.getValue("visibility"),
210 SourcePositionInfo.fromXml(attributes.getValue("source")),
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"));
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);
237 public ApiInfo getApi() {