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