/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.devtools.treeshaker;

import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

/** Give information about inheritance relationships between types. */
class TypeGraphBuilder {
  private final Collection<Type> types;
  private final Set<String> externalTypeReferences;
  private final Set<String> unknownMethodReferences;

  TypeGraphBuilder(LibraryInfo libraryInfo) {
    Map<String, Type> typesByName = new LinkedHashMap<>();
    externalTypeReferences = new HashSet<>();
    unknownMethodReferences = new HashSet<>();
    for (TypeInfo typeInfo : libraryInfo.getTypeList()) {
      Type type = Type.buildFrom(typeInfo, libraryInfo.getTypeMap(typeInfo.getTypeId()));
      typesByName.put(type.getName(), type);
    }
    // Build cross-references between types and members
    buildCrossReferences(libraryInfo, typesByName);
    types = typesByName.values();
  }

  Collection<Type> getTypes() {
    return types;
  }

  Collection<String> getExternalTypeReferences() {
    return externalTypeReferences;
  }

  Collection<String> getUnknownMethodReferences() {
    return unknownMethodReferences;
  }

  private void buildCrossReferences(LibraryInfo libraryInfo, Map<String, Type> typesByName) {
    for (TypeInfo typeInfo : libraryInfo.getTypeList()) {
      Type type = typesByName.get(libraryInfo.getTypeMap(typeInfo.getTypeId()));
      int extendsId = typeInfo.getExtendsType();
      String superClassName = libraryInfo.getTypeMap(extendsId);
      Type superClass = typesByName.get(superClassName);

      if (superClass != null) {
        superClass.addImmediateSubtype(type);
        type.setSuperClass(superClass);
      } else {
        externalTypeReferences.add(superClassName);
      }

      for (int implementsId : typeInfo.getImplementsTypeList()) {
        Type superInterface = typesByName.get(libraryInfo.getTypeMap(implementsId));
        if (superInterface == null) {
          externalTypeReferences.add(libraryInfo.getTypeMap(implementsId));
        } else {
          superInterface.addImmediateSubtype(type);
          type.addSuperInterface(superInterface);
        }
      }

      for (MemberInfo memberInfo : typeInfo.getMemberList()) {
        Member member = type.getMemberByName(memberInfo.getName());

        for (int referencedId : memberInfo.getReferencedTypesList()) {
          Type referencedType = typesByName.get(libraryInfo.getTypeMap(referencedId));
          if (referencedType == null) {
            externalTypeReferences.add(libraryInfo.getTypeMap(referencedId));
            continue;
          }
          member.addReferencedType(referencedType);
        }

        for (MethodInvocation methodInvocation : memberInfo.getInvokedMethodsList()) {
          Type enclosingType =
              typesByName.get(libraryInfo.getTypeMap(methodInvocation.getEnclosingType()));
          if (enclosingType != null) {
            Member referencedMember = enclosingType.getMemberByName(methodInvocation.getMethod());
            if (referencedMember != null) {
              member.addReferencedMember(referencedMember);
            } else {
              unknownMethodReferences.add(
                  enclosingType.getName() + "." + methodInvocation.getMethod());
            }
          } else {
            externalTypeReferences.add(libraryInfo.getTypeMap(methodInvocation.getEnclosingType()));
          }
        }
      }
    }
  }
}

@@ -2180,17 +2180,17 @@
 erClass 
-!
+=
 = null) 
@@ -2183,32 +2183,99 @@
 lass == null) %7B%0A
+        externalTypeReferences.add(superClassName);%0A      %7D else %7B%0A
         superCla
@@ -2355,75 +2355,8 @@
    %7D
- else %7B%0A        externalTypeReferences.add(superClassName);%0A      %7D
 %0A%0A  
@@ -2622,35 +2622,46 @@
 ));%0A        
-%7D else %7B%0A  
+  continue;%0A        %7D%0A
         supe
@@ -2702,26 +2702,24 @@
 e);%0A        
-  
 type.addSupe
@@ -2746,26 +2746,16 @@
 rface);%0A
-        %7D%0A
       %7D%0A
@@ -3498,17 +3498,17 @@
 ingType 
-!
+=
 = null) 
@@ -3525,63 +3525,57 @@
     
-Member referencedMember = enclosingType.getMemberByName
+externalTypeReferences.add(libraryInfo.getTypeMap
 (met
@@ -3583,39 +3583,47 @@
 odInvocation.get
-Method(
+EnclosingType()
 ));%0A            
@@ -3626,123 +3626,169 @@
     
-if (referencedMember != null) %7B%0A              member.addReferencedMember(referencedMember);%0A            %7D else
+continue;%0A          %7D%0A          Member referencedMember = enclosingType.getMemberByName(methodInvocation.getMethod());%0A          if (referencedMember == null)
  %7B%0A
-  
     
@@ -3824,18 +3824,16 @@
 es.add(%0A
-  
         
@@ -3907,33 +3907,41 @@
 ));%0A            
-%7D
+continue;
 %0A          %7D els
@@ -3932,31 +3932,24 @@
 %0A          %7D
- else %7B
 %0A           
@@ -3951,110 +3951,53 @@
     
-  externalTypeReferences.add(libraryInfo.getTypeMap(methodInvocation.getEnclosingType()));%0A          %7D
+member.addReferencedMember(referencedMember);
 %0A   

Clean up error logic in type graph builder to make the code consistent.