/* * Copyright (C) 2010 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.doclava.apicheck; import com.google.doclava.AnnotationInstanceInfo; import com.google.doclava.ClassInfo; import com.google.doclava.Converter; import com.google.doclava.FieldInfo; import com.google.doclava.MethodInfo; import com.google.doclava.PackageInfo; import com.google.doclava.ParameterInfo; import com.google.doclava.SourcePositionInfo; import com.google.doclava.TypeInfo; import com.sun.javadoc.ClassDoc; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.XMLReaderFactory; import java.io.InputStream; import java.util.ArrayList; import java.util.Stack; class XmlApiFile extends DefaultHandler { private ApiInfo mApi; private PackageInfo mCurrentPackage; private ClassInfo mCurrentClass; private AbstractMethodInfo mCurrentMethod; private Stack mClassScope = new Stack(); public static ApiInfo parseApi(InputStream xmlStream) throws ApiParseException { try { XMLReader xmlreader = XMLReaderFactory.createXMLReader(); XmlApiFile handler = new XmlApiFile(); xmlreader.setContentHandler(handler); xmlreader.setErrorHandler(handler); xmlreader.parse(new InputSource(xmlStream)); ApiInfo apiInfo = handler.getApi(); apiInfo.resolveSuperclasses(); apiInfo.resolveInterfaces(); return apiInfo; } catch (Exception e) { throw new ApiParseException("Error parsing API", e); } } private XmlApiFile() { super(); mApi = new ApiInfo(); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) { if (qName.equals("package")) { mCurrentPackage = new PackageInfo(attributes.getValue("name"), SourcePositionInfo.fromXml(attributes .getValue("source"))); } else if (qName.equals("class") || qName.equals("interface")) { // push the old outer scope for later recovery, then set // up the new current class object mClassScope.push(mCurrentClass); ClassDoc classDoc = null; String rawCommentText = ""; SourcePositionInfo position = SourcePositionInfo.fromXml(attributes.getValue("source")); String visibility = attributes.getValue("visibility"); boolean isPublic = "public".equals(visibility); boolean isProtected = "protected".equals(visibility); boolean isPrivate = "private".equals(visibility); boolean isPackagePrivate = !isPublic && !isPrivate && !isProtected; boolean isStatic = Boolean.valueOf(attributes.getValue("static")); boolean isInterface = qName.equals("interface"); boolean isAbstract = Boolean.valueOf(attributes.getValue("abstract")); boolean isOrdinaryClass = qName.equals("class"); boolean isException = false; // TODO: check hierarchy for java.lang.Exception boolean isError = false; // TODO: not sure. boolean isEnum = false; // TODO: not sure. boolean isAnnotation = false; // TODO: not sure. boolean isFinal = Boolean.valueOf(attributes.getValue("final")); boolean isIncluded = false; String name = attributes.getValue("name"); String qualifiedName = qualifiedName(mCurrentPackage.name(), name, mCurrentClass); String qualifiedTypeName = null; // TODO: not sure boolean isPrimitive = false; mCurrentClass = new ClassInfo(classDoc, rawCommentText, position, isPublic, isProtected, isPackagePrivate, isPrivate, isStatic, isInterface, isAbstract, isOrdinaryClass, isException, isError, isEnum, isAnnotation, isFinal, isIncluded, name, qualifiedName, qualifiedTypeName, isPrimitive); mCurrentClass.setDeprecated("deprecated".equals(attributes.getValue("deprecated"))); mCurrentClass.setContainingPackage(mCurrentPackage); String superclass = attributes.getValue("extends"); if (superclass == null && !isInterface && !"java.lang.Object".equals(qualifiedName)) { throw new AssertionError("no superclass known for class " + name); } // Resolve superclass after .xml completely parsed. mApi.mapClassToSuper(mCurrentClass, superclass); TypeInfo typeInfo = Converter.obtainTypeFromString(qualifiedName) ; mCurrentClass.setTypeInfo(typeInfo); mCurrentClass.setAnnotations(new ArrayList()); } else if (qName.equals("method")) { String rawCommentText = ""; ArrayList typeParameters = new ArrayList(); String name = attributes.getValue("name"); String signature = null; // TODO ClassInfo containingClass = mCurrentClass; ClassInfo realContainingClass = mCurrentClass; String visibility = attributes.getValue("visibility"); boolean isPublic = "public".equals(visibility); boolean isProtected = "protected".equals(visibility); boolean isPrivate = "private".equals(visibility); boolean isPackagePrivate = !isPublic && !isPrivate && !isProtected; boolean isFinal = Boolean.valueOf(attributes.getValue("final")); boolean isStatic = Boolean.valueOf(attributes.getValue("static")); boolean isSynthetic = false; // TODO boolean isAbstract = Boolean.valueOf(attributes.getValue("abstract")); boolean isSynchronized = Boolean.valueOf(attributes.getValue("synchronized")); boolean isNative = Boolean.valueOf(attributes.getValue("native")); boolean isAnnotationElement = false; // TODO String kind = qName; String flatSignature = null; // TODO MethodInfo overriddenMethod = null; // TODO TypeInfo returnType = Converter.obtainTypeFromString(attributes.getValue("return")); ArrayList parameters = new ArrayList(); ArrayList thrownExceptions = new ArrayList(); SourcePositionInfo position = SourcePositionInfo.fromXml(attributes.getValue("source")); ArrayList annotations = new ArrayList(); // TODO mCurrentMethod = new MethodInfo(rawCommentText, typeParameters, name, signature, containingClass, realContainingClass, isPublic, isProtected, isPackagePrivate, isPrivate, isFinal, isStatic, isSynthetic, isAbstract, isSynchronized, isNative, isAnnotationElement, kind, flatSignature, overriddenMethod, returnType, parameters, thrownExceptions, position, annotations); mCurrentMethod.setDeprecated("deprecated".equals(attributes.getValue("deprecated"))); } else if (qName.equals("constructor")) { final boolean pub = "public".equals(attributes.getValue("visibility")); final boolean prot = "protected".equals(attributes.getValue("visibility")); final boolean pkgpriv = "".equals(attributes.getValue("visibility")); mCurrentMethod = new MethodInfo(""/*rawCommentText*/, new ArrayList()/*typeParameters*/, attributes.getValue("name"), null/*signature*/, mCurrentClass, mCurrentClass, pub, prot, pkgpriv, false/*isPrivate*/, false/*isFinal*/, false/*isStatic*/, false/*isSynthetic*/, false/*isAbstract*/, false/*isSynthetic*/, false/*isNative*/, false /*isAnnotationElement*/, "constructor", null/*flatSignature*/, null/*overriddenMethod*/, mCurrentClass.asTypeInfo(), new ArrayList(), new ArrayList()/*thrownExceptions*/, SourcePositionInfo.fromXml(attributes.getValue("source")), new ArrayList()/*annotations*/); mCurrentMethod.setDeprecated("deprecated".equals(attributes.getValue("deprecated"))); } else if (qName.equals("field")) { String visibility = attributes.getValue("visibility"); boolean isPublic = visibility.equals("public"); boolean isProtected = visibility.equals("protected"); boolean isPrivate = visibility.equals("private"); boolean isPackagePrivate = visibility.equals(""); String typeName = attributes.getValue("type"); TypeInfo type = Converter.obtainTypeFromString(typeName); Object value; try { value = ApiFile.parseValue(typeName, attributes.getValue("value")); } catch (ApiParseException ex) { throw new RuntimeException(ex); } FieldInfo fInfo = new FieldInfo(attributes.getValue("name"), mCurrentClass, mCurrentClass, isPublic, isProtected, isPackagePrivate, isPrivate, Boolean.valueOf(attributes.getValue("final")), Boolean.valueOf(attributes.getValue("static")), Boolean.valueOf(attributes. getValue("transient")), Boolean.valueOf(attributes.getValue("volatile")), false, type, "", value, SourcePositionInfo.fromXml(attributes.getValue("source")), new ArrayList()); fInfo.setDeprecated("deprecated".equals(attributes.getValue("deprecated"))); mCurrentClass.addField(fInfo); } else if (qName.equals("parameter")) { String name = attributes.getValue("name"); String typeName = attributes.getValue("type"); TypeInfo type = Converter.obtainTypeFromString(typeName); boolean isVarArg = typeName.endsWith("..."); SourcePositionInfo position = null; mCurrentMethod.addParameter(new ParameterInfo(name, typeName, type, isVarArg, position)); mCurrentMethod.setVarargs(isVarArg); } else if (qName.equals("exception")) { mCurrentMethod.addException(attributes.getValue("type")); } else if (qName.equals("implements")) { // Resolve interfaces after .xml completely parsed. mApi.mapClassToInterface(mCurrentClass, attributes.getValue("name")); } } @Override public void endElement(String uri, String localName, String qName) { if (qName.equals("method")) { mCurrentClass.addMethod((MethodInfo) mCurrentMethod); } else if (qName.equals("constructor")) { mCurrentClass.addConstructor((MethodInfo) mCurrentMethod); } else if (qName.equals("class") || qName.equals("interface")) { mCurrentPackage.addClass(mCurrentClass); mCurrentClass = mClassScope.pop(); } else if (qName.equals("package")) { mApi.addPackage(mCurrentPackage); } } public ApiInfo getApi() { return mApi; } private String qualifiedName(String pkg, String className, ClassInfo parent) { String parentQName = (parent != null) ? (parent.qualifiedName() + ".") : ""; return pkg + "." + parentQName + className; } }