// Copyright (C) 2016 The Android Open Source Project
//
// 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
//
// http://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.gerrit.server.git;

import com.google.gerrit.common.Nullable;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.SubmoduleSubscription;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.git.MergeOpRepoManager.OpenRepo;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.util.RequestId;
import com.google.gerrit.server.util.SubmoduleSectionParser;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;

import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.BlobBasedConfig;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;

/**
 * Loads the .gitmodules file of the specified project/branch.
 * It can be queried which submodules this branch is subscribed to.
 */
public class GitModules {
  private static final Logger log = LoggerFactory.getLogger(GitModules.class);

  public interface Factory {
    GitModules create(Branch.NameKey project, MergeOpRepoManager m);
  }

  private static final String GIT_MODULES = ".gitmodules";

  private final RequestId submissionId;
  Set<SubmoduleSubscription> subscriptions;

  @AssistedInject
  GitModules(
      @CanonicalWebUrl @Nullable String canonicalWebUrl,
      @Assisted Branch.NameKey branch,
      @Assisted MergeOpRepoManager orm) throws IOException {
    this.submissionId = orm.getSubmissionId();
    Project.NameKey project = branch.getParentKey();
    logDebug("Loading .gitmodules of {} for project {}", branch, project);
    OpenRepo or;
    try {
      or = orm.openRepo(project);
    } catch (NoSuchProjectException e) {
      throw new IOException(e);
    }

    ObjectId id = or.repo.resolve(branch.get());
    if (id == null) {
      throw new IOException("Cannot open branch " + branch.get());
    }
    RevCommit commit = or.rw.parseCommit(id);

    TreeWalk tw = TreeWalk.forPath(or.repo, GIT_MODULES, commit.getTree());
    if (tw == null
        || (tw.getRawMode(0) & FileMode.TYPE_MASK) != FileMode.TYPE_FILE) {
      subscriptions = Collections.emptySet();
      logDebug("The .gitmodules file doesn't exist in " + branch);
      return;
    }
    BlobBasedConfig bbc;
    try {
      bbc = new BlobBasedConfig(null, or.repo, commit, GIT_MODULES);
    } catch (ConfigInvalidException e) {
      throw new IOException("Could not read .gitmodules of super project: " +
              branch.getParentKey(), e);
    }
    subscriptions = new SubmoduleSectionParser(bbc, canonicalWebUrl,
          branch).parseAllSections();
  }

  public Collection<SubmoduleSubscription> subscribedTo(Branch.NameKey src) {
    logDebug("Checking for a subscription of " + src);
    Collection<SubmoduleSubscription> ret = new ArrayList<>();
    for (SubmoduleSubscription s : subscriptions) {
      if (s.getSubmodule().equals(src)) {
        logDebug("Found " + s);
        ret.add(s);
      }
    }
    return ret;
  }

  private void logDebug(String msg, Object... args) {
    if (log.isDebugEnabled()) {
      log.debug(submissionId + msg, args);
    }
  }
}

@@ -2577,16 +2577,23 @@
 nRepo or
+ = null
 ;%0A    tr
@@ -2636,86 +2636,8 @@
 ;%0A  
-  %7D catch (NoSuchProjectException e) %7B%0A      throw new IOException(e);%0A    %7D%0A%0A
     
@@ -2681,24 +2681,26 @@
 get());%0A    
+  
 if (id == nu
@@ -2703,32 +2703,34 @@
 = null) %7B%0A      
+ 
+ 
 throw new IOExce
@@ -2774,26 +2774,30 @@
 get());%0A    
-%7D%0A
+  %7D%0A  
     RevCommi
@@ -2835,16 +2835,23 @@
 );%0A%0A    
+  try (
 TreeWalk
@@ -2855,16 +2855,26 @@
 alk tw =
+%0A         
  TreeWal
@@ -2922,18 +2922,24 @@
 tTree())
-;%0A
+) %7B%0A    
     if (
@@ -2957,16 +2957,20 @@
         
+   
+ 
 %7C%7C (tw.g
@@ -3025,24 +3025,28 @@
 YPE_FILE) %7B%0A
+    
       subscr
@@ -3081,24 +3081,28 @@
 et();%0A      
+    
 logDebug(%22Th
@@ -3152,24 +3152,28 @@
 nch);%0A      
+ 
+   
 return;%0A    
@@ -3168,26 +3168,40 @@
 return;%0A    
-%7D%0A
+    %7D%0A      %7D%0A  
     BlobBase
@@ -3213,16 +3213,18 @@
 ig bbc;%0A
+  
     try 
@@ -3231,16 +3231,18 @@
 %7B%0A      
+  
 bbc = ne
@@ -3300,16 +3300,18 @@
 S);%0A    
+  
 %7D catch 
@@ -3341,24 +3341,26 @@
  e) %7B%0A      
+ 
+ 
 throw new IO
@@ -3429,24 +3429,26 @@
             
+  
 branch.getPa
@@ -3462,26 +3462,30 @@
 (), e);%0A    
+ 
+ 
 %7D%0A
+  
     subscrip
@@ -3547,24 +3547,26 @@
 ,%0A          
+  
 branch).pars
@@ -3581,16 +3581,163 @@
 ions();%0A
+    %7D catch (NoSuchProjectException e) %7B%0A      throw new IOException(e);%0A    %7D finally %7B%0A      if (or != null) %7B%0A        or.close();%0A      %7D%0A    %7D%0A
   %7D%0A%0A  p

Fix resource leak flagged by infer