Home | History | Annotate | Download | only in apicheck
      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