diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/links/LinkFactory.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/links/LinkFactory.java
index 261c2d53886..7ff0271a352 100644
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/links/LinkFactory.java
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/links/LinkFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,8 @@
 
 package jdk.javadoc.internal.doclets.toolkit.util.links;
 
+import java.util.Deque;
+import java.util.LinkedList;
 import java.util.List;
 
 import javax.lang.model.element.Element;
@@ -70,11 +72,8 @@ public abstract class LinkFactory {
      */
     public Content getLink(LinkInfo linkInfo) {
         if (linkInfo.type != null) {
-            SimpleTypeVisitor9<Content, LinkInfo> linkVisitor =
-                    new SimpleTypeVisitor9<Content, LinkInfo>() {
-
-                TypeMirror componentType = utils.getComponentType(linkInfo.type);
-                Content link = newContent();
+            return new SimpleTypeVisitor9<Content, LinkInfo>() {
+                final Content link = newContent();
 
                 // handles primitives, no types and error types
                 @Override
@@ -130,10 +129,7 @@ public abstract class LinkFactory {
                 public Content visitTypeVariable(TypeVariable type, LinkInfo linkInfo) {
                     link.add(getTypeAnnotationLinks(linkInfo));
                     linkInfo.isTypeBound = true;
-                    TypeVariable typevariable = (utils.isArrayType(type))
-                            ? (TypeVariable) componentType
-                            : type;
-                    Element owner = typevariable.asElement().getEnclosingElement();
+                    Element owner = type.asElement().getEnclosingElement();
                     if ((!linkInfo.excludeTypeParameterLinks) && utils.isTypeElement(owner)) {
                         linkInfo.typeElement = (TypeElement) owner;
                         Content label = newContent();
@@ -142,12 +138,12 @@ public abstract class LinkFactory {
                         link.add(getClassLink(linkInfo));
                     } else {
                         // No need to link method type parameters.
-                        link.add(utils.getTypeName(typevariable, false));
+                        link.add(utils.getTypeName(type, false));
                     }
 
                     if (!linkInfo.excludeTypeBounds) {
                         linkInfo.excludeTypeBounds = true;
-                        TypeParameterElement tpe = ((TypeParameterElement) typevariable.asElement());
+                        TypeParameterElement tpe = ((TypeParameterElement) type.asElement());
                         boolean more = false;
                         List<? extends TypeMirror> bounds = utils.getBounds(tpe);
                         for (TypeMirror bound : bounds) {
@@ -169,25 +165,53 @@ public abstract class LinkFactory {
 
                 @Override
                 public Content visitDeclared(DeclaredType type, LinkInfo linkInfo) {
-                    if (linkInfo.isTypeBound && linkInfo.excludeTypeBoundsLinks) {
-                        // Since we are excluding type parameter links, we should not
-                        // be linking to the type bound.
-                        link.add(utils.getTypeName(type, false));
-                        link.add(getTypeParameterLinks(linkInfo));
-                        return link;
-                    } else {
-                        link = newContent();
+                    // Handles like O<T>.I<S>
+                    Deque<DeclaredType> parameterizedChain = new LinkedList<>();
+                    var current = type;
+                    parameterizedChain.addFirst(current);
+                    while (true) {
+                        var enclosing = current.getEnclosingType();
+                        if (utils.isDeclaredType(enclosing)) {
+                            current = (DeclaredType) enclosing;
+                        } else {
+                            break;
+                        }
+                        parameterizedChain.addFirst(current);
+                    }
+
+                    boolean first = true;
+                    for (final var each : parameterizedChain) {
+                        final CharSequence nameSection;
+                        if (first) {
+                            first = false;
+                            nameSection = utils.getTypeName(each, false);
+                        } else {
+                            link.add(".");
+                            nameSection = utils.asTypeElement(each).getSimpleName();
+                        }
+
+                        linkInfo.type = each;
                         link.add(getTypeAnnotationLinks(linkInfo));
-                        linkInfo.typeElement = utils.asTypeElement(type);
-                        link.add(getClassLink(linkInfo));
-                        if (linkInfo.includeTypeAsSepLink) {
-                            link.add(getTypeParameterLinks(linkInfo, false));
+                        if (linkInfo.isTypeBound && linkInfo.excludeTypeBoundsLinks) {
+                            // Since we are excluding type parameter links, we should not
+                            // be linking to the type bound.
+                            link.add(nameSection);
+                        } else {
+                            linkInfo.label = newContent().add(nameSection);
+                            linkInfo.type = type; // link to the actual type than outer; restore later
+                            linkInfo.typeElement = utils.asTypeElement(each);
+                            link.add(getClassLink(linkInfo));
+                            linkInfo.type = each; // restore
+                            linkInfo.typeElement = null;
+                            linkInfo.label = null;
+                             // we already substituted in actual types
                         }
+                        link.add(getTypeParameterLinks(linkInfo));
                     }
+
                     return link;
                 }
-            };
-            return linkVisitor.visit(linkInfo.type, linkInfo);
+            }.visit(linkInfo.type, linkInfo);
         } else if (linkInfo.typeElement != null) {
             Content link = newContent();
             link.add(getClassLink(linkInfo));
@@ -219,8 +243,8 @@ public abstract class LinkFactory {
      * Returns links to the type parameters.
      *
      * @param linkInfo     the information about the link to construct
-     * @param isClassLabel true if this is a class label, or false if it is
-     *                     the type parameters portion of the link
+     * @param isClassLabel true if this is passed type arguments, or false if it is
+     *                     the type parameters' definition
      * @return the links to the type parameters
      */
     protected abstract Content getTypeParameterLinks(LinkInfo linkInfo, boolean isClassLabel);
