summaryrefslogtreecommitdiffstats
path: root/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectLevelConfig.java
blob: 2044db09bfeac0503e9230952d7f03c3c20c18ff (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// Copyright (C) 2013 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 static java.util.stream.Collectors.toList;

import com.google.common.collect.Iterables;
import com.google.common.collect.Streams;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.project.ProjectState;
import java.io.IOException;
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Stream;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Config;

/** Configuration file in the projects refs/meta/config branch. */
public class ProjectLevelConfig extends VersionedMetaData {
  private final String fileName;
  private final ProjectState project;
  private Config cfg;

  public ProjectLevelConfig(String fileName, ProjectState project) {
    this.fileName = fileName;
    this.project = project;
  }

  @Override
  protected String getRefName() {
    return RefNames.REFS_CONFIG;
  }

  @Override
  protected void onLoad() throws IOException, ConfigInvalidException {
    cfg = readConfig(fileName);
  }

  public Config get() {
    if (cfg == null) {
      cfg = new Config();
    }
    return cfg;
  }

  public Config getWithInheritance() {
    return getWithInheritance(false);
  }

  /**
   * Get a Config that includes the values from all parent projects.
   *
   * <p>Merging means that matching sections/subsection will be merged to include the values from
   * both parent and child config.
   *
   * <p>No merging means that matching sections/subsections in the child project will replace the
   * corresponding value from the parent.
   *
   * @param merge whether to merge parent values with child values or not.
   * @return a combined config.
   */
  public Config getWithInheritance(boolean merge) {
    Config cfgWithInheritance = new Config();
    try {
      cfgWithInheritance.fromText(get().toText());
    } catch (ConfigInvalidException e) {
      // cannot happen
    }
    ProjectState parent = Iterables.getFirst(project.parents(), null);
    if (parent != null) {
      Config parentCfg = parent.getConfig(fileName).getWithInheritance();
      for (String section : parentCfg.getSections()) {
        Set<String> allNames = get().getNames(section);
        for (String name : parentCfg.getNames(section)) {
          String[] parentValues = parentCfg.getStringList(section, null, name);
          if (!allNames.contains(name)) {
            cfgWithInheritance.setStringList(section, null, name, Arrays.asList(parentValues));
          } else if (merge) {
            cfgWithInheritance.setStringList(
                section,
                null,
                name,
                Stream.concat(
                        Arrays.stream(cfg.getStringList(section, null, name)),
                        Arrays.stream(parentValues))
                    .sorted()
                    .distinct()
                    .collect(toList()));
          }
        }

        for (String subsection : parentCfg.getSubsections(section)) {
          allNames = get().getNames(section, subsection);
          for (String name : parentCfg.getNames(section, subsection)) {
            String[] parentValues = parentCfg.getStringList(section, subsection, name);
            if (!allNames.contains(name)) {
              cfgWithInheritance.setStringList(
                  section, subsection, name, Arrays.asList(parentValues));
            } else if (merge) {
              cfgWithInheritance.setStringList(
                  section,
                  subsection,
                  name,
                  Streams.concat(
                          Arrays.stream(cfg.getStringList(section, subsection, name)),
                          Arrays.stream(parentValues))
                      .sorted()
                      .distinct()
                      .collect(toList()));
            }
          }
        }
      }
    }
    return cfgWithInheritance;
  }

  @Override
  protected boolean onSave(CommitBuilder commit) throws IOException, ConfigInvalidException {
    if (commit.getMessage() == null || "".equals(commit.getMessage())) {
      commit.setMessage("Updated configuration\n");
    }
    saveConfig(fileName, cfg);
    return true;
  }
}