Home | History | Annotate | Download | only in doclava
      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;
     18 
     19 import com.google.doclava.apicheck.ApiParseException;
     20 import java.net.URL;
     21 import java.util.ArrayList;
     22 import java.util.Arrays;
     23 import java.util.Collection;
     24 import java.util.HashMap;
     25 import java.util.List;
     26 import java.util.Map;
     27 
     28 /**
     29  * Cross-references documentation among different libraries. A FederationTagger
     30  * is populated with a list of {@link FederatedSite} objects which are linked
     31  * against when overlapping content is discovered.
     32  */
     33 public final class FederationTagger {
     34   private final Map<String, URL> federatedUrls = new HashMap<>();
     35   private final Map<String, String> federatedXmls = new HashMap<>();
     36   private final List<FederatedSite> federatedSites = new ArrayList<>();
     37 
     38   private boolean initialized = false;
     39 
     40   /**
     41    * Adds a Doclava documentation site for federation. Accepts the base URL of
     42    * the remote API.
     43    * <p>
     44    * If {@link #addSiteApi(String, String)} is not called, this will default to
     45    * reading the API from "/xml/current.xml" within the site's base URL.
     46    * <p>
     47    * <strong>Note:</strong> Must be called before calling tag() or get() methods.
     48    *
     49    * @param name internally-used name for federation site
     50    */
     51   public void addSiteUrl(String name, URL site) {
     52     if (initialized) {
     53       throw new IllegalStateException("Cannot add sites after calling tag() or get() methods.");
     54     }
     55     federatedUrls.put(name, site);
     56   }
     57 
     58   /**
     59    * Adds an explicit Doclava-generated API file for the specified site.
     60    * <p>
     61    * <strong>Note:</strong> Must be called before calling tag() or get() methods.
     62    *
     63    * @param name internally-used name for federation site (must match name used
     64    *             for {@link #addSiteUrl(String, URL)})
     65    * @param file path to a Doclava-generated API file
     66    */
     67   public void addSiteApi(String name, String file) {
     68     if (initialized) {
     69       throw new IllegalStateException("Cannot add sites after calling tag() or get() methods.");
     70     }
     71     federatedXmls.put(name, file);
     72   }
     73 
     74   public void tag(ClassInfo classDoc) {
     75     initialize();
     76     for (FederatedSite site : federatedSites) {
     77       applyFederation(site, Arrays.asList(classDoc));
     78     }
     79   }
     80 
     81   public void tagAll(Collection<ClassInfo> classDocs) {
     82     initialize();
     83     for (FederatedSite site : federatedSites) {
     84       applyFederation(site, classDocs);
     85     }
     86   }
     87 
     88   /**
     89    * Returns a non-{@code null} list of {@link FederatedSite} objects, one for
     90    * each unique {@code name} added using {@link #addSiteUrl(String, URL)}.
     91    */
     92   public List<FederatedSite> getSites() {
     93     initialize();
     94     return federatedSites;
     95   }
     96 
     97   private void initialize() {
     98     if (initialized) {
     99       return;
    100     }
    101 
    102     for (String name : federatedXmls.keySet()) {
    103       if (!federatedUrls.containsKey(name)) {
    104         Errors.error(Errors.NO_FEDERATION_DATA, (SourcePositionInfo) null,
    105             "Unknown documentation site for " + name);
    106       }
    107     }
    108 
    109     for (String name : federatedUrls.keySet()) {
    110       try {
    111         if (federatedXmls.containsKey(name)) {
    112           federatedSites.add(new FederatedSite(name, federatedUrls.get(name),
    113               federatedXmls.get(name)));
    114         } else {
    115           federatedSites.add(new FederatedSite(name, federatedUrls.get(name)));
    116         }
    117       } catch (ApiParseException e) {
    118         String error = "Could not add site for federation: " + name;
    119         if (e.getMessage() != null) {
    120           error += ": " + e.getMessage();
    121         }
    122         Errors.error(Errors.NO_FEDERATION_DATA, (SourcePositionInfo) null, error);
    123       }
    124     }
    125 
    126     initialized = true;
    127   }
    128 
    129   private void applyFederation(FederatedSite federationSource, Collection<ClassInfo> classDocs) {
    130     for (ClassInfo classDoc : classDocs) {
    131       PackageInfo packageSpec
    132           = federationSource.apiInfo().getPackages().get(classDoc.containingPackage().name());
    133 
    134       if (packageSpec == null) {
    135         continue;
    136       }
    137 
    138       ClassInfo classSpec = packageSpec.allClasses().get(classDoc.name());
    139 
    140       if (classSpec == null) {
    141         continue;
    142       }
    143 
    144       federateMethods(federationSource, classSpec, classDoc);
    145       federateConstructors(federationSource, classSpec, classDoc);
    146       federateFields(federationSource, classSpec, classDoc);
    147       federateClass(federationSource, classDoc);
    148       federatePackage(federationSource, classDoc.containingPackage());
    149     }
    150   }
    151 
    152   private void federateMethods(FederatedSite site, ClassInfo federatedClass, ClassInfo localClass) {
    153     for (MethodInfo method : localClass.methods()) {
    154       for (ClassInfo superclass : federatedClass.hierarchy()) {
    155         if (superclass.allMethods().containsKey(method.getHashableName())) {
    156           method.addFederatedReference(site);
    157           break;
    158         }
    159       }
    160     }
    161   }
    162 
    163   private void federateConstructors(FederatedSite site, ClassInfo federatedClass,
    164       ClassInfo localClass) {
    165     for (MethodInfo constructor : localClass.constructors()) {
    166       if (federatedClass.hasConstructor(constructor)) {
    167         constructor.addFederatedReference(site);
    168       }
    169     }
    170   }
    171 
    172   private void federateFields(FederatedSite site, ClassInfo federatedClass, ClassInfo localClass) {
    173     for (FieldInfo field : localClass.fields()) {
    174       if (federatedClass.allFields().containsKey(field.name())) {
    175         field.addFederatedReference(site);
    176       }
    177     }
    178   }
    179 
    180   private void federateClass(FederatedSite source, ClassInfo doc) {
    181     doc.addFederatedReference(source);
    182   }
    183 
    184   private void federatePackage(FederatedSite source, PackageInfo pkg) {
    185     pkg.addFederatedReference(source);
    186   }
    187 }
    188