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.Collections;
     39 import java.util.List;
     40 import java.util.Stack;
     41 
     42 class 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