package com.gitblit;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.function.Function;

/**
 * Simple class with the only purpose to save the realm file (users.conf) in
 * a fast efficient manner. The JGit Config classes used previously caused
 * a massive CPU hog if the users file got bigger than about 30000 lines.
 *
 * @author Ingo Lafrenz
 *
 */
public class StoredUserConfig {

	private final File realmFileCopy;
	private SortedMap<String, Section> sections = new TreeMap<>();

	public StoredUserConfig(File realmFileCopy) {
		this.realmFileCopy = realmFileCopy;
	}

	public void setString(final String section, final String subsection, String name, String value) {
		Section s = sections.computeIfAbsent(generateKey(section, subsection), new Function<String, Section>() {
			@Override
			public Section apply(String k) {
				return new Section(section, subsection);
			}
		});
		s.addEntry(name, value);
	}

	public void setBoolean(String section, String subsection, String name, boolean value) {
		setString(section, subsection, name, String.valueOf(value));
	}

	public void setStringList(String section, String subsection, String name, List<String> list) {
		for (String value : list) {
			setString(section, subsection, name, value);
		}
	}

	public void save() throws IOException {
		try (FileWriter fileWriter = new FileWriter(realmFileCopy);
				PrintWriter printWriter = new PrintWriter(fileWriter);) {
			for (Map.Entry<String,Section> entry : sections.entrySet()) {
				writeSection(printWriter, entry.getKey(), entry.getValue());
			}
		}
	}

	private static void writeSection(PrintWriter printWriter, String key, Section section) {
		printWriter.printf("[%s \"%s\"]\n", section.getName(), section.getSubSection());
		for (Entry entry : section.getEntries().values()) {
			writeEntry(printWriter, entry.getKey(), entry.getValue());
		}
	}

	private static void writeEntry(PrintWriter printWriter, String key, String value) {
		printWriter.printf("\t%s = %s\n", key, escape(value));
	}

	private static String escape(String value) {
		String fixedValue = '#' == value.charAt(0) ? "\"" + value + "\"" : value;
		fixedValue = fixedValue.replace("\\", "\\\\");
		return fixedValue;
	}

	private static String generateKey(String key, String subKey) {
		return "k:" + key + "s:" + subKey;
	}

	private static class Section {
		private final String name;
		private final String subSection;
		private final SortedMap<String, Entry> entries = new TreeMap<>();

		public Section(String name, String subSection) {
			this.name = name;
			this.subSection = subSection;
		}

		public void addEntry(final String key, final String value) {
			entries.put(generateKey(key, value), new Entry(key, value));
		}

		public String getName() {
			return name;
		}

		public String getSubSection() {
			return subSection;
		}

		public SortedMap<String, Entry> getEntries() {
			return entries;
		}

		@Override
		public int hashCode() {
			return Objects.hash(name, subSection);
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			Section other = (Section) obj;
			return Objects.equals(name, other.name) && Objects.equals(subSection, other.subSection);
		}

		@Override
		public String toString() {
			return String.format("Section [name=%s, subSection=%s]", name, subSection);
		}

	}

	private static class Entry {
		private final String key;
		private final String value;

		public Entry(String key, String value) {
			this.key = key;
			this.value = value;
		}

		public String getKey() {
			return key;
		}

		public String getValue() {
			return value;
		}

		@Override
		public int hashCode() {
			return Objects.hash(key, value);
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			Entry other = (Entry) obj;
			return Objects.equals(key, other.key) && Objects.equals(value, other.value);
		}

		@Override
		public String toString() {
			return String.format("Entry [key=%s, value=%s]", key, value);
		}

	}

}

@@ -1,8 +1,618 @@
+/*%0A * Copyright 2021 gitblit.com, Ingo Lafrenz%0A *%0A * Licensed under the Apache License, Version 2.0 (the %22License%22);%0A * you may not use this file except in compliance with the License.%0A * You may obtain a copy of the License at%0A *%0A *     http://www.apache.org/licenses/LICENSE-2.0%0A *%0A * Unless required by applicable law or agreed to in writing, software%0A * distributed under the License is distributed on an %22AS IS%22 BASIS,%0A * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.%0A * See the License for the specific language governing permissions and%0A * limitations under the License.%0A */%0A%0A
 package 
@@ -857,44 +857,8 @@
 Map;
-%0Aimport java.util.function.Function;
 %0A%0A/*
@@ -1447,171 +1447,112 @@
 %0A%09%09S
-ection s = sections.computeIfAbsent(generateKey(section, subsection), new Function%3CString, Section%3E() %7B%0A%09%09%09@Override%0A%09%09%09public Section apply(String k) %7B%0A%09%09%09%09return
+tring key = generateKey(section, subsection);%0A%09%09Section s = sections.get(key);%0A%09%09if (s == null) %7B%0A%09%09%09s =
  new
@@ -1589,15 +1589,33 @@
 %0A%09%09%09
-%7D
+sections.put(key, s);
 %0A%09%09%7D
-);
 %0A%09%09s

Make CPU hog fix Java 7 compatible