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 com.google.doclava.AnnotationInstanceInfo; 20import com.google.doclava.ClassInfo; 21import com.google.doclava.Converter; 22import com.google.doclava.FieldInfo; 23import com.google.doclava.MethodInfo; 24import com.google.doclava.PackageInfo; 25import com.google.doclava.ParameterInfo; 26import com.google.doclava.SourcePositionInfo; 27import com.google.doclava.TypeInfo; 28import com.sun.javadoc.ClassDoc; 29 30import org.xml.sax.Attributes; 31import org.xml.sax.InputSource; 32import org.xml.sax.XMLReader; 33import org.xml.sax.helpers.DefaultHandler; 34import org.xml.sax.helpers.XMLReaderFactory; 35 36import java.io.InputStream; 37import java.util.ArrayList; 38import java.util.Collections; 39import java.util.List; 40import java.util.Stack; 41 42class XmlApiFile extends DefaultHandler { 43 44 private ApiInfo mApi; 45 private PackageInfo mCurrentPackage; 46 private ClassInfo mCurrentClass; 47 private AbstractMethodInfo mCurrentMethod; 48 private Stack<ClassInfo> mClassScope = new Stack<ClassInfo>(); 49 50 public static ApiInfo parseApi(InputStream xmlStream) throws ApiParseException { 51 try { 52 XMLReader xmlreader = XMLReaderFactory.createXMLReader(); 53 XmlApiFile handler = new XmlApiFile(); 54 xmlreader.setContentHandler(handler); 55 xmlreader.setErrorHandler(handler); 56 xmlreader.parse(new InputSource(xmlStream)); 57 ApiInfo apiInfo = handler.getApi(); 58 apiInfo.resolveSuperclasses(); 59 apiInfo.resolveInterfaces(); 60 return apiInfo; 61 } catch (Exception e) { 62 throw new ApiParseException("Error parsing API", e); 63 } 64 } 65 66 private XmlApiFile() { 67 super(); 68 mApi = new ApiInfo(); 69 } 70 71 @Override 72 public void startElement(String uri, String localName, String qName, Attributes attributes) { 73 if (qName.equals("package")) { 74 mCurrentPackage = 75 new PackageInfo(attributes.getValue("name"), SourcePositionInfo.fromXml(attributes 76 .getValue("source"))); 77 } else if (qName.equals("class") || qName.equals("interface")) { 78 // push the old outer scope for later recovery, then set 79 // up the new current class object 80 mClassScope.push(mCurrentClass); 81 82 ClassDoc classDoc = null; 83 String rawCommentText = ""; 84 SourcePositionInfo position = SourcePositionInfo.fromXml(attributes.getValue("source")); 85 String visibility = attributes.getValue("visibility"); 86 boolean isPublic = "public".equals(visibility); 87 boolean isProtected = "protected".equals(visibility); 88 boolean isPrivate = "private".equals(visibility); 89 boolean isPackagePrivate = !isPublic && !isPrivate && !isProtected; 90 boolean isStatic = Boolean.valueOf(attributes.getValue("static")); 91 boolean isInterface = qName.equals("interface"); 92 boolean isAbstract = Boolean.valueOf(attributes.getValue("abstract")); 93 boolean isOrdinaryClass = qName.equals("class"); 94 boolean isException = false; // TODO: check hierarchy for java.lang.Exception 95 boolean isError = false; // TODO: not sure. 96 boolean isEnum = false; // TODO: not sure. 97 boolean isAnnotation = false; // TODO: not sure. 98 boolean isFinal = Boolean.valueOf(attributes.getValue("final")); 99 boolean isIncluded = false; 100 String name = attributes.getValue("name"); 101 String qualifiedName = qualifiedName(mCurrentPackage.name(), name, mCurrentClass); 102 String qualifiedTypeName = null; // TODO: not sure 103 boolean isPrimitive = false; 104 105 mCurrentClass = 106 new ClassInfo(classDoc, rawCommentText, position, isPublic, isProtected, 107 isPackagePrivate, isPrivate, isStatic, isInterface, isAbstract, isOrdinaryClass, 108 isException, isError, isEnum, isAnnotation, isFinal, isIncluded, name, qualifiedName, 109 qualifiedTypeName, isPrimitive); 110 111 mCurrentClass.setDeprecated("deprecated".equals(attributes.getValue("deprecated"))); 112 mCurrentClass.setContainingPackage(mCurrentPackage); 113 String superclass = attributes.getValue("extends"); 114 if (superclass == null && !isInterface && !"java.lang.Object".equals(qualifiedName)) { 115 throw new AssertionError("no superclass known for class " + name); 116 } 117 118 // Resolve superclass after .xml completely parsed. 119 mApi.mapClassToSuper(mCurrentClass, superclass); 120 121 TypeInfo typeInfo = Converter.obtainTypeFromString(qualifiedName) ; 122 mCurrentClass.setTypeInfo(typeInfo); 123 mCurrentClass.setAnnotations(new ArrayList<AnnotationInstanceInfo>()); 124 } else if (qName.equals("method")) { 125 String rawCommentText = ""; 126 ArrayList<TypeInfo> typeParameters = new ArrayList<TypeInfo>(); 127 String name = attributes.getValue("name"); 128 String signature = null; // TODO 129 ClassInfo containingClass = mCurrentClass; 130 ClassInfo realContainingClass = mCurrentClass; 131 String visibility = attributes.getValue("visibility"); 132 boolean isPublic = "public".equals(visibility); 133 boolean isProtected = "protected".equals(visibility); 134 boolean isPrivate = "private".equals(visibility); 135 boolean isPackagePrivate = !isPublic && !isPrivate && !isProtected; 136 boolean isFinal = Boolean.valueOf(attributes.getValue("final")); 137 boolean isStatic = Boolean.valueOf(attributes.getValue("static")); 138 boolean isSynthetic = false; // TODO 139 boolean isAbstract = Boolean.valueOf(attributes.getValue("abstract")); 140 boolean isSynchronized = Boolean.valueOf(attributes.getValue("synchronized")); 141 boolean isNative = Boolean.valueOf(attributes.getValue("native")); 142 boolean isDefault = Boolean.valueOf(attributes.getValue("default")); 143 boolean isAnnotationElement = false; // TODO 144 String kind = qName; 145 String flatSignature = null; // TODO 146 MethodInfo overriddenMethod = null; // TODO 147 TypeInfo returnType = Converter.obtainTypeFromString(attributes.getValue("return")); 148 ArrayList<ParameterInfo> parameters = new ArrayList<ParameterInfo>(); 149 ArrayList<ClassInfo> thrownExceptions = new ArrayList<ClassInfo>(); 150 SourcePositionInfo position = SourcePositionInfo.fromXml(attributes.getValue("source")); 151 ArrayList<AnnotationInstanceInfo> annotations = new ArrayList<AnnotationInstanceInfo>(); // TODO 152 153 mCurrentMethod = 154 new MethodInfo(rawCommentText, typeParameters, name, signature, containingClass, 155 realContainingClass, isPublic, isProtected, isPackagePrivate, isPrivate, isFinal, 156 isStatic, isSynthetic, isAbstract, isSynchronized, isNative, isDefault, 157 isAnnotationElement, kind, flatSignature, overriddenMethod, returnType, parameters, 158 thrownExceptions, position, annotations); 159 160 mCurrentMethod.setDeprecated("deprecated".equals(attributes.getValue("deprecated"))); 161 } else if (qName.equals("constructor")) { 162 final boolean pub = "public".equals(attributes.getValue("visibility")); 163 final boolean prot = "protected".equals(attributes.getValue("visibility")); 164 final boolean pkgpriv = "".equals(attributes.getValue("visibility")); 165 mCurrentMethod = 166 new MethodInfo(""/*rawCommentText*/, new ArrayList<TypeInfo>()/*typeParameters*/, 167 attributes.getValue("name"), null/*signature*/, mCurrentClass, mCurrentClass, 168 pub, prot, pkgpriv, false/*isPrivate*/, false/*isFinal*/, false/*isStatic*/, 169 false/*isSynthetic*/, false/*isAbstract*/, false/*isSynthetic*/, false/*isNative*/, 170 false/*isDefault*/, false/*isAnnotationElement*/, "constructor", 171 null/*flatSignature*/, null/*overriddenMethod*/, mCurrentClass.asTypeInfo(), 172 new ArrayList<ParameterInfo>(), new ArrayList<ClassInfo>()/*thrownExceptions*/, 173 SourcePositionInfo.fromXml(attributes.getValue("source")), 174 new ArrayList<AnnotationInstanceInfo>()/*annotations*/); 175 mCurrentMethod.setDeprecated("deprecated".equals(attributes.getValue("deprecated"))); 176 } else if (qName.equals("field")) { 177 String visibility = attributes.getValue("visibility"); 178 boolean isPublic = visibility.equals("public"); 179 boolean isProtected = visibility.equals("protected"); 180 boolean isPrivate = visibility.equals("private"); 181 boolean isPackagePrivate = visibility.equals(""); 182 String typeName = attributes.getValue("type"); 183 TypeInfo type = Converter.obtainTypeFromString(typeName); 184 185 Object value; 186 try { 187 value = ApiFile.parseValue(typeName, attributes.getValue("value")); 188 } catch (ApiParseException ex) { 189 throw new RuntimeException(ex); 190 } 191 192 FieldInfo fInfo = 193 new FieldInfo(attributes.getValue("name"), mCurrentClass, mCurrentClass, isPublic, 194 isProtected, isPackagePrivate, isPrivate, Boolean.valueOf(attributes.getValue("final")), 195 Boolean.valueOf(attributes.getValue("static")), Boolean.valueOf(attributes. 196 getValue("transient")), Boolean.valueOf(attributes.getValue("volatile")), false, 197 type, "", value, SourcePositionInfo.fromXml(attributes.getValue("source")), 198 new ArrayList<AnnotationInstanceInfo>()); 199 200 fInfo.setDeprecated("deprecated".equals(attributes.getValue("deprecated"))); 201 mCurrentClass.addField(fInfo); 202 } else if (qName.equals("parameter")) { 203 String name = attributes.getValue("name"); 204 String typeName = attributes.getValue("type"); 205 TypeInfo type = Converter.obtainTypeFromString(typeName); 206 boolean isVarArg = typeName.endsWith("..."); 207 SourcePositionInfo position = null; 208 List<AnnotationInstanceInfo> annotations = Collections.emptyList(); 209 210 mCurrentMethod.addParameter( 211 new ParameterInfo(name, typeName, type, isVarArg, position, annotations)); 212 mCurrentMethod.setVarargs(isVarArg); 213 } else if (qName.equals("exception")) { 214 mCurrentMethod.addException(attributes.getValue("type")); 215 } else if (qName.equals("implements")) { 216 // Resolve interfaces after .xml completely parsed. 217 mApi.mapClassToInterface(mCurrentClass, attributes.getValue("name")); 218 } 219 } 220 221 @Override 222 public void endElement(String uri, String localName, String qName) { 223 if (qName.equals("method")) { 224 mCurrentClass.addMethod((MethodInfo) mCurrentMethod); 225 } else if (qName.equals("constructor")) { 226 mCurrentClass.addConstructor((MethodInfo) mCurrentMethod); 227 } else if (qName.equals("class") || qName.equals("interface")) { 228 mCurrentPackage.addClass(mCurrentClass); 229 mCurrentClass = mClassScope.pop(); 230 } else if (qName.equals("package")) { 231 mApi.addPackage(mCurrentPackage); 232 } 233 } 234 235 public ApiInfo getApi() { 236 return mApi; 237 } 238 239 private String qualifiedName(String pkg, String className, ClassInfo parent) { 240 String parentQName = (parent != null) ? (parent.qualifiedName() + ".") : ""; 241 return pkg + "." + parentQName + className; 242 } 243} 244 245