LCOV - code coverage report
Current view: top level - server/project - ProjectLevelConfig.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 58 60 96.7 %
Date: 2022-11-19 15:00:39 Functions: 10 10 100.0 %

          Line data    Source code
       1             : // Copyright (C) 2013 The Android Open Source Project
       2             : //
       3             : // Licensed under the Apache License, Version 2.0 (the "License");
       4             : // you may not use this file except in compliance with the License.
       5             : // You may obtain a copy of the License at
       6             : //
       7             : // http://www.apache.org/licenses/LICENSE-2.0
       8             : //
       9             : // Unless required by applicable law or agreed to in writing, software
      10             : // distributed under the License is distributed on an "AS IS" BASIS,
      11             : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             : // See the License for the specific language governing permissions and
      13             : // limitations under the License.
      14             : 
      15             : package com.google.gerrit.server.project;
      16             : 
      17             : import static java.util.stream.Collectors.toList;
      18             : 
      19             : import com.google.common.collect.Streams;
      20             : import com.google.gerrit.entities.ImmutableConfig;
      21             : import com.google.gerrit.entities.RefNames;
      22             : import com.google.gerrit.server.git.meta.VersionedMetaData;
      23             : import java.io.IOException;
      24             : import java.util.Arrays;
      25             : import java.util.Set;
      26             : import java.util.stream.Stream;
      27             : import java.util.stream.StreamSupport;
      28             : import org.eclipse.jgit.annotations.Nullable;
      29             : import org.eclipse.jgit.errors.ConfigInvalidException;
      30             : import org.eclipse.jgit.lib.CommitBuilder;
      31             : import org.eclipse.jgit.lib.Config;
      32             : 
      33             : /** Configuration file in the projects refs/meta/config branch. */
      34             : public class ProjectLevelConfig {
      35             :   /**
      36             :    * This class is a low-level API that allows callers to read the config directly from a repository
      37             :    * and make updates to it.
      38             :    */
      39             :   public static class Bare extends VersionedMetaData {
      40             :     private final String fileName;
      41             :     @Nullable private Config cfg;
      42             : 
      43           7 :     public Bare(String fileName) {
      44           7 :       this.fileName = fileName;
      45           7 :       this.cfg = null;
      46           7 :     }
      47             : 
      48             :     public Config getConfig() {
      49           7 :       if (cfg == null) {
      50           0 :         cfg = new Config();
      51             :       }
      52           7 :       return cfg;
      53             :     }
      54             : 
      55             :     @Override
      56             :     protected String getRefName() {
      57           7 :       return RefNames.REFS_CONFIG;
      58             :     }
      59             : 
      60             :     @Override
      61             :     protected void onLoad() throws IOException, ConfigInvalidException {
      62           7 :       cfg = readConfig(fileName);
      63           7 :     }
      64             : 
      65             :     @Override
      66             :     protected boolean onSave(CommitBuilder commit) throws IOException {
      67           1 :       if (commit.getMessage() == null || "".equals(commit.getMessage())) {
      68           0 :         commit.setMessage("Updated configuration\n");
      69             :       }
      70           1 :       saveConfig(fileName, cfg);
      71           1 :       return true;
      72             :     }
      73             :   }
      74             : 
      75             :   private final String fileName;
      76             :   private final ProjectState project;
      77             :   private final ImmutableConfig immutableConfig;
      78             : 
      79             :   public ProjectLevelConfig(
      80           1 :       String fileName, ProjectState project, @Nullable ImmutableConfig immutableConfig) {
      81           1 :     this.fileName = fileName;
      82           1 :     this.project = project;
      83           1 :     this.immutableConfig = immutableConfig == null ? ImmutableConfig.EMPTY : immutableConfig;
      84           1 :   }
      85             : 
      86             :   public Config get() {
      87           1 :     return immutableConfig.mutableCopy();
      88             :   }
      89             : 
      90             :   public Config getWithInheritance() {
      91           1 :     return getWithInheritance(/* merge= */ false);
      92             :   }
      93             : 
      94             :   /**
      95             :    * Get a Config that includes the values from all parent projects.
      96             :    *
      97             :    * <p>Merging means that matching sections/subsection will be merged to include the values from
      98             :    * both parent and child config.
      99             :    *
     100             :    * <p>No merging means that matching sections/subsections in the child project will replace the
     101             :    * corresponding value from the parent.
     102             :    *
     103             :    * @param merge whether to merge parent values with child values or not.
     104             :    * @return a combined config.
     105             :    */
     106             :   public Config getWithInheritance(boolean merge) {
     107           1 :     Config cfg = new Config();
     108             :     // Traverse from All-Projects down to the current project
     109           1 :     StreamSupport.stream(project.treeInOrder().spliterator(), false)
     110           1 :         .forEach(
     111             :             parent -> {
     112           1 :               ImmutableConfig levelCfg = parent.getConfig(fileName).immutableConfig;
     113           1 :               for (String section : levelCfg.getSections()) {
     114           1 :                 Set<String> allNames = cfg.getNames(section);
     115           1 :                 for (String name : levelCfg.getNames(section)) {
     116           1 :                   String[] levelValues = levelCfg.getStringList(section, null, name);
     117           1 :                   if (allNames.contains(name) && merge) {
     118           1 :                     cfg.setStringList(
     119             :                         section,
     120             :                         null,
     121             :                         name,
     122           1 :                         Stream.concat(
     123           1 :                                 Arrays.stream(cfg.getStringList(section, null, name)),
     124           1 :                                 Arrays.stream(levelValues))
     125           1 :                             .sorted()
     126           1 :                             .distinct()
     127           1 :                             .collect(toList()));
     128             :                   } else {
     129           1 :                     cfg.setStringList(section, null, name, Arrays.asList(levelValues));
     130             :                   }
     131           1 :                 }
     132             : 
     133           1 :                 for (String subsection : levelCfg.getSubsections(section)) {
     134           1 :                   allNames = cfg.getNames(section, subsection);
     135             : 
     136           1 :                   Set<String> allNamesLevelCfg = levelCfg.getNames(section, subsection);
     137           1 :                   if (allNamesLevelCfg.isEmpty()) {
     138             :                     // Set empty subsection.
     139           1 :                     cfg.setString(section, subsection, null, null);
     140             :                   } else {
     141           1 :                     for (String name : allNamesLevelCfg) {
     142           1 :                       String[] levelValues = levelCfg.getStringList(section, subsection, name);
     143           1 :                       if (allNames.contains(name) && merge) {
     144           1 :                         cfg.setStringList(
     145             :                             section,
     146             :                             subsection,
     147             :                             name,
     148           1 :                             Streams.concat(
     149           1 :                                     Arrays.stream(cfg.getStringList(section, subsection, name)),
     150           1 :                                     Arrays.stream(levelValues))
     151           1 :                                 .sorted()
     152           1 :                                 .distinct()
     153           1 :                                 .collect(toList()));
     154             :                       } else {
     155           1 :                         cfg.setStringList(section, subsection, name, Arrays.asList(levelValues));
     156             :                       }
     157           1 :                     }
     158             :                   }
     159           1 :                 }
     160           1 :               }
     161           1 :             });
     162           1 :     return cfg;
     163             :   }
     164             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750