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