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 parseFlags(ArrayList<String> allArgs)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 main(String[] originalArgs)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 parseApi(String xmlFile)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 MakeHandler()138 public MakeHandler() { 139 super(); 140 mApi = new ApiInfo(); 141 } 142 143 @Override startElement(String uri, String localName, String qName, Attributes attributes)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 endElement(String uri, String localName, String qName)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 } getApi()237 public ApiInfo getApi() { 238 return mApi; 239 } 240 } 241 } 242