Line data Source code
1 : // Copyright (C) 2020 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.config;
16 :
17 : import static com.google.gerrit.server.config.ConfigUtil.loadSection;
18 : import static com.google.gerrit.server.config.ConfigUtil.skipField;
19 : import static com.google.gerrit.server.git.UserConfigSections.CHANGE_TABLE;
20 : import static com.google.gerrit.server.git.UserConfigSections.CHANGE_TABLE_COLUMN;
21 : import static com.google.gerrit.server.git.UserConfigSections.KEY_ID;
22 : import static com.google.gerrit.server.git.UserConfigSections.KEY_TARGET;
23 : import static com.google.gerrit.server.git.UserConfigSections.KEY_URL;
24 :
25 : import com.google.common.base.Strings;
26 : import com.google.common.collect.Lists;
27 : import com.google.common.flogger.FluentLogger;
28 : import com.google.gerrit.common.Nullable;
29 : import com.google.gerrit.extensions.client.DiffPreferencesInfo;
30 : import com.google.gerrit.extensions.client.EditPreferencesInfo;
31 : import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
32 : import com.google.gerrit.extensions.client.MenuItem;
33 : import com.google.gerrit.server.git.UserConfigSections;
34 : import java.lang.reflect.Field;
35 : import java.util.ArrayList;
36 : import java.util.List;
37 : import org.eclipse.jgit.errors.ConfigInvalidException;
38 : import org.eclipse.jgit.lib.Config;
39 :
40 : /** Helper to read default or user preferences from Git-style config files. */
41 : public class PreferencesParserUtil {
42 151 : private static final FluentLogger logger = FluentLogger.forEnclosingClass();
43 :
44 : private PreferencesParserUtil() {}
45 :
46 : /**
47 : * Returns a {@link GeneralPreferencesInfo} that is the result of parsing {@code defaultCfg} for
48 : * the server's default configs and {@code cfg} for the user's config. These configs are then
49 : * overlaid to inherit values (default -> user -> input (if provided).
50 : */
51 : public static GeneralPreferencesInfo parseGeneralPreferences(
52 : Config cfg, @Nullable Config defaultCfg, @Nullable GeneralPreferencesInfo input)
53 : throws ConfigInvalidException {
54 151 : GeneralPreferencesInfo r =
55 151 : loadSection(
56 : cfg,
57 : UserConfigSections.GENERAL,
58 : null,
59 : new GeneralPreferencesInfo(),
60 151 : defaultCfg != null
61 151 : ? parseDefaultGeneralPreferences(defaultCfg, input)
62 151 : : GeneralPreferencesInfo.defaults(),
63 : input);
64 151 : if (input != null) {
65 8 : r.changeTable = input.changeTable;
66 8 : r.my = input.my;
67 : } else {
68 151 : r.changeTable = parseChangeTableColumns(cfg, defaultCfg);
69 151 : r.my = parseMyMenus(cfg, defaultCfg);
70 : }
71 151 : return r;
72 : }
73 :
74 : /**
75 : * Returns a {@link GeneralPreferencesInfo} that is the result of parsing {@code defaultCfg} for
76 : * the server's default configs. These configs are then overlaid to inherit values (default ->
77 : * input (if provided).
78 : */
79 : public static GeneralPreferencesInfo parseDefaultGeneralPreferences(
80 : Config defaultCfg, GeneralPreferencesInfo input) throws ConfigInvalidException {
81 151 : GeneralPreferencesInfo allUserPrefs = new GeneralPreferencesInfo();
82 151 : loadSection(
83 : defaultCfg,
84 : UserConfigSections.GENERAL,
85 : null,
86 : allUserPrefs,
87 151 : GeneralPreferencesInfo.defaults(),
88 : input);
89 151 : return updateGeneralPreferencesDefaults(allUserPrefs);
90 : }
91 :
92 : /**
93 : * Returns a {@link DiffPreferencesInfo} that is the result of parsing {@code defaultCfg} for the
94 : * server's default configs and {@code cfg} for the user's config. These configs are then overlaid
95 : * to inherit values (default -> user -> input (if provided).
96 : */
97 : public static DiffPreferencesInfo parseDiffPreferences(
98 : Config cfg, @Nullable Config defaultCfg, @Nullable DiffPreferencesInfo input)
99 : throws ConfigInvalidException {
100 151 : return loadSection(
101 : cfg,
102 : UserConfigSections.DIFF,
103 : null,
104 : new DiffPreferencesInfo(),
105 151 : defaultCfg != null
106 151 : ? parseDefaultDiffPreferences(defaultCfg, input)
107 151 : : DiffPreferencesInfo.defaults(),
108 : input);
109 : }
110 :
111 : /**
112 : * Returns a {@link DiffPreferencesInfo} that is the result of parsing {@code defaultCfg} for the
113 : * server's default configs. These configs are then overlaid to inherit values (default -> input
114 : * (if provided).
115 : */
116 : public static DiffPreferencesInfo parseDefaultDiffPreferences(
117 : Config defaultCfg, DiffPreferencesInfo input) throws ConfigInvalidException {
118 151 : DiffPreferencesInfo allUserPrefs = new DiffPreferencesInfo();
119 151 : loadSection(
120 : defaultCfg,
121 : UserConfigSections.DIFF,
122 : null,
123 : allUserPrefs,
124 151 : DiffPreferencesInfo.defaults(),
125 : input);
126 151 : return updateDiffPreferencesDefaults(allUserPrefs);
127 : }
128 :
129 : /**
130 : * Returns a {@link EditPreferencesInfo} that is the result of parsing {@code defaultCfg} for the
131 : * server's default configs and {@code cfg} for the user's config. These configs are then overlaid
132 : * to inherit values (default -> user -> input (if provided).
133 : */
134 : public static EditPreferencesInfo parseEditPreferences(
135 : Config cfg, @Nullable Config defaultCfg, @Nullable EditPreferencesInfo input)
136 : throws ConfigInvalidException {
137 151 : return loadSection(
138 : cfg,
139 : UserConfigSections.EDIT,
140 : null,
141 : new EditPreferencesInfo(),
142 151 : defaultCfg != null
143 151 : ? parseDefaultEditPreferences(defaultCfg, input)
144 151 : : EditPreferencesInfo.defaults(),
145 : input);
146 : }
147 :
148 : /**
149 : * Returns a {@link EditPreferencesInfo} that is the result of parsing {@code defaultCfg} for the
150 : * server's default configs. These configs are then overlaid to inherit values (default -> input
151 : * (if provided).
152 : */
153 : public static EditPreferencesInfo parseDefaultEditPreferences(
154 : Config defaultCfg, EditPreferencesInfo input) throws ConfigInvalidException {
155 151 : EditPreferencesInfo allUserPrefs = new EditPreferencesInfo();
156 151 : loadSection(
157 : defaultCfg,
158 : UserConfigSections.EDIT,
159 : null,
160 : allUserPrefs,
161 151 : EditPreferencesInfo.defaults(),
162 : input);
163 151 : return updateEditPreferencesDefaults(allUserPrefs);
164 : }
165 :
166 : private static List<String> parseChangeTableColumns(Config cfg, @Nullable Config defaultCfg) {
167 151 : List<String> changeTable = changeTable(cfg);
168 151 : if (changeTable == null && defaultCfg != null) {
169 0 : changeTable = changeTable(defaultCfg);
170 : }
171 151 : return changeTable;
172 : }
173 :
174 : private static List<MenuItem> parseMyMenus(Config cfg, @Nullable Config defaultCfg) {
175 151 : List<MenuItem> my = my(cfg);
176 151 : if (my.isEmpty() && defaultCfg != null) {
177 151 : my = my(defaultCfg);
178 : }
179 151 : if (my.isEmpty()) {
180 151 : my.add(new MenuItem("Dashboard", "#/dashboard/self", null));
181 151 : my.add(new MenuItem("Draft Comments", "#/q/has:draft", null));
182 151 : my.add(new MenuItem("Edits", "#/q/has:edit", null));
183 151 : my.add(new MenuItem("Watched Changes", "#/q/is:watched+is:open", null));
184 151 : my.add(new MenuItem("Starred Changes", "#/q/is:starred", null));
185 151 : my.add(new MenuItem("All Visible Changes", "#/q/is:visible", null));
186 151 : my.add(new MenuItem("Groups", "#/settings/#Groups", null));
187 : }
188 151 : return my;
189 : }
190 :
191 : private static GeneralPreferencesInfo updateGeneralPreferencesDefaults(
192 : GeneralPreferencesInfo input) {
193 151 : GeneralPreferencesInfo result = GeneralPreferencesInfo.defaults();
194 : try {
195 151 : for (Field field : input.getClass().getDeclaredFields()) {
196 151 : if (skipField(field)) {
197 151 : continue;
198 : }
199 151 : Object newVal = field.get(input);
200 151 : if (newVal != null) {
201 151 : field.set(result, newVal);
202 : }
203 : }
204 0 : } catch (IllegalAccessException e) {
205 0 : logger.atSevere().withCause(e).log("Failed to apply default general preferences");
206 0 : return GeneralPreferencesInfo.defaults();
207 151 : }
208 151 : return result;
209 : }
210 :
211 : private static DiffPreferencesInfo updateDiffPreferencesDefaults(DiffPreferencesInfo input) {
212 151 : DiffPreferencesInfo result = DiffPreferencesInfo.defaults();
213 : try {
214 151 : for (Field field : input.getClass().getDeclaredFields()) {
215 151 : if (skipField(field)) {
216 151 : continue;
217 : }
218 151 : Object newVal = field.get(input);
219 151 : if (newVal != null) {
220 151 : field.set(result, newVal);
221 : }
222 : }
223 0 : } catch (IllegalAccessException e) {
224 0 : logger.atSevere().withCause(e).log("Failed to apply default diff preferences");
225 0 : return DiffPreferencesInfo.defaults();
226 151 : }
227 151 : return result;
228 : }
229 :
230 : private static EditPreferencesInfo updateEditPreferencesDefaults(EditPreferencesInfo input) {
231 151 : EditPreferencesInfo result = EditPreferencesInfo.defaults();
232 : try {
233 151 : for (Field field : input.getClass().getDeclaredFields()) {
234 151 : if (skipField(field)) {
235 151 : continue;
236 : }
237 151 : Object newVal = field.get(input);
238 151 : if (newVal != null) {
239 151 : field.set(result, newVal);
240 : }
241 : }
242 0 : } catch (IllegalAccessException e) {
243 0 : logger.atSevere().withCause(e).log("Failed to apply default edit preferences");
244 0 : return EditPreferencesInfo.defaults();
245 151 : }
246 151 : return result;
247 : }
248 :
249 : private static List<String> changeTable(Config cfg) {
250 151 : return Lists.newArrayList(cfg.getStringList(CHANGE_TABLE, null, CHANGE_TABLE_COLUMN));
251 : }
252 :
253 : private static List<MenuItem> my(Config cfg) {
254 151 : List<MenuItem> my = new ArrayList<>();
255 151 : for (String subsection : cfg.getSubsections(UserConfigSections.MY)) {
256 6 : String url = my(cfg, subsection, KEY_URL, "#/");
257 6 : String target = my(cfg, subsection, KEY_TARGET, url.startsWith("#") ? null : "_blank");
258 6 : my.add(new MenuItem(subsection, url, target, my(cfg, subsection, KEY_ID, null)));
259 6 : }
260 151 : return my;
261 : }
262 :
263 : private static String my(Config cfg, String subsection, String key, String defaultValue) {
264 6 : String val = cfg.getString(UserConfigSections.MY, subsection, key);
265 6 : return !Strings.isNullOrEmpty(val) ? val : defaultValue;
266 : }
267 : }
|