View Javadoc
1   /*
2    * Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com>
3    * Copyright (C) 2009, Constantine Plotnikov <constantine.plotnikov@gmail.com>
4    * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
5    * Copyright (C) 2008-2012, Google Inc.
6    * Copyright (C) 2009, JetBrains s.r.o.
7    * Copyright (C) 2007-2008, Robin Rosenberg <robin.rosenberg@dewire.com>
8    * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
9    * Copyright (C) 2008, Thad Hughes <thadh@thad.corp.google.com> and others
10   *
11   * This program and the accompanying materials are made available under the
12   * terms of the Eclipse Distribution License v. 1.0 which is available at
13   * https://www.eclipse.org/org/documents/edl-v10.php.
14   *
15   * SPDX-License-Identifier: BSD-3-Clause
16   */
17  
18  package org.eclipse.jgit.lib;
19  
20  import static org.eclipse.jgit.util.StringUtils.compareIgnoreCase;
21  import static org.eclipse.jgit.util.StringUtils.compareWithCase;
22  import static org.eclipse.jgit.util.StringUtils.toLowerCase;
23  
24  import java.util.AbstractSet;
25  import java.util.ArrayList;
26  import java.util.Collections;
27  import java.util.Comparator;
28  import java.util.HashMap;
29  import java.util.Iterator;
30  import java.util.LinkedHashMap;
31  import java.util.LinkedHashSet;
32  import java.util.List;
33  import java.util.Map;
34  import java.util.Set;
35  import java.util.concurrent.ConcurrentHashMap;
36  
37  import org.eclipse.jgit.util.StringUtils;
38  
39  class ConfigSnapshot {
40  	final List<ConfigLine> entryList;
41  	final Map<Object, Object> cache;
42  	final ConfigSnapshot baseState;
43  	volatile List<ConfigLine> sorted;
44  	volatile SectionNames names;
45  
46  	ConfigSnapshot(List<ConfigLine> entries, ConfigSnapshot base) {
47  		entryList = entries;
48  		cache = new ConcurrentHashMap<>(16, 0.75f, 1);
49  		baseState = base;
50  	}
51  
52  	Set<String> getSections() {
53  		return names().sections;
54  	}
55  
56  	Set<String> getSubsections(String section) {
57  		Map<String, Set<String>> m = names().subsections;
58  		Set<String> r = m.get(section);
59  		if (r == null)
60  			r = m.get(toLowerCase(section));
61  		if (r == null)
62  			return Collections.emptySet();
63  		return Collections.unmodifiableSet(r);
64  	}
65  
66  	Set<String> getNames(String section, String subsection) {
67  		return getNames(section, subsection, false);
68  	}
69  
70  	Set<String> getNames(String section, String subsection, boolean recursive) {
71  		Map<String, String> m = getNamesInternal(section, subsection, recursive);
72  		return new CaseFoldingSet(m);
73  	}
74  
75  	private Map<String, String> getNamesInternal(String section,
76  			String subsection, boolean recursive) {
77  		List<ConfigLine> s = sorted();
78  		int idx = find(s, section, subsection, ""); //$NON-NLS-1$
79  		if (idx < 0)
80  			idx = -(idx + 1);
81  
82  		Map<String, String> m = new LinkedHashMap<>();
83  		while (idx < s.size()) {
84  			ConfigLine e = s.get(idx++);
85  			if (!e.match(section, subsection))
86  				break;
87  			if (e.name == null)
88  				continue;
89  			String l = toLowerCase(e.name);
90  			if (!m.containsKey(l))
91  				m.put(l, e.name);
92  		}
93  		if (recursive && baseState != null)
94  			m.putAll(baseState.getNamesInternal(section, subsection, recursive));
95  		return m;
96  	}
97  
98  	String[] get(String section, String subsection, String name) {
99  		List<ConfigLine> s = sorted();
100 		int idx = find(s, section, subsection, name);
101 		if (idx < 0)
102 			return null;
103 		int end = end(s, idx, section, subsection, name);
104 		String[] r = new String[end - idx];
105 		for (int i = 0; idx < end;)
106 			r[i++] = s.get(idx++).value;
107 		return r;
108 	}
109 
110 	private int find(List<ConfigLine> s, String s1, String s2, String name) {
111 		int low = 0;
112 		int high = s.size();
113 		while (low < high) {
114 			int mid = (low + high) >>> 1;
115 			ConfigLine e = s.get(mid);
116 			int cmp = compare2(
117 					s1, s2, name,
118 					e.section, e.subsection, e.name);
119 			if (cmp < 0)
120 				high = mid;
121 			else if (cmp == 0)
122 				return first(s, mid, s1, s2, name);
123 			else
124 				low = mid + 1;
125 		}
126 		return -(low + 1);
127 	}
128 
129 	private int first(List<ConfigLine> s, int i, String s1, String s2, String n) {
130 		while (0 < i) {
131 			if (s.get(i - 1).match(s1, s2, n))
132 				i--;
133 			else
134 				return i;
135 		}
136 		return i;
137 	}
138 
139 	private int end(List<ConfigLine> s, int i, String s1, String s2, String n) {
140 		while (i < s.size()) {
141 			if (s.get(i).match(s1, s2, n))
142 				i++;
143 			else
144 				return i;
145 		}
146 		return i;
147 	}
148 
149 	private List<ConfigLine> sorted() {
150 		List<ConfigLine> r = sorted;
151 		if (r == null)
152 			sorted = r = sort(entryList);
153 		return r;
154 	}
155 
156 	private static List<ConfigLine> sort(List<ConfigLine> in) {
157 		List<ConfigLine> sorted = new ArrayList<>(in.size());
158 		for (ConfigLine line : in) {
159 			if (line.section != null && line.name != null)
160 				sorted.add(line);
161 		}
162 		Collections.sort(sorted, new LineComparator());
163 		return sorted;
164 	}
165 
166 	private static int compare2(
167 			String aSection, String aSubsection, String aName,
168 			String bSection, String bSubsection, String bName) {
169 		int c = compareIgnoreCase(aSection, bSection);
170 		if (c != 0)
171 			return c;
172 
173 		if (aSubsection == null && bSubsection != null)
174 			return -1;
175 		if (aSubsection != null && bSubsection == null)
176 			return 1;
177 		if (aSubsection != null) {
178 			c = compareWithCase(aSubsection, bSubsection);
179 			if (c != 0)
180 				return c;
181 		}
182 
183 		return compareIgnoreCase(aName, bName);
184 	}
185 
186 	private static class LineComparator implements Comparator<ConfigLine> {
187 		@Override
188 		public int compare(ConfigLinef="../../../../org/eclipse/jgit/lib/ConfigLine.html#ConfigLine">ConfigLine a, ConfigLine b) {
189 			return compare2(
190 					a.section, a.subsection, a.name,
191 					b.section, b.subsection, b.name);
192 		}
193 	}
194 
195 	private SectionNames names() {
196 		SectionNames n = names;
197 		if (n == null)
198 			names = n = new SectionNames(this);
199 		return n;
200 	}
201 
202 	private static class SectionNames {
203 		final CaseFoldingSet sections;
204 		final Map<String, Set<String>> subsections;
205 
206 		SectionNames(ConfigSnapshot cfg) {
207 			Map<String, String> sec = new LinkedHashMap<>();
208 			Map<String, Set<String>> sub = new HashMap<>();
209 			while (cfg != null) {
210 				for (ConfigLine e : cfg.entryList) {
211 					if (e.section == null)
212 						continue;
213 
214 					String l1 = toLowerCase(e.section);
215 					if (!sec.containsKey(l1))
216 						sec.put(l1, e.section);
217 
218 					if (e.subsection == null)
219 						continue;
220 
221 					Set<String> m = sub.get(l1);
222 					if (m == null) {
223 						m = new LinkedHashSet<>();
224 						sub.put(l1, m);
225 					}
226 					m.add(e.subsection);
227 				}
228 				cfg = cfg.baseState;
229 			}
230 
231 			sections = new CaseFoldingSet(sec);
232 			subsections = sub;
233 		}
234 	}
235 
236 	private static class CaseFoldingSet extends AbstractSet<String> {
237 		private final Map<String, String> names;
238 
239 		CaseFoldingSet(Map<String, String> names) {
240 			this.names = names;
241 		}
242 
243 		@Override
244 		public boolean contains(Object needle) {
245 			if (needle instanceof String) {
246 				String n = (String) needle;
247 				return names.containsKey(n)
248 						|| names.containsKey(StringUtils.toLowerCase(n));
249 			}
250 			return false;
251 		}
252 
253 		@Override
254 		public Iterator<String> iterator() {
255 			final Iterator<String> i = names.values().iterator();
256 			return new Iterator<String>() {
257 				@Override
258 				public boolean hasNext() {
259 					return i.hasNext();
260 				}
261 
262 				@Override
263 				public String next() {
264 					return i.next();
265 				}
266 
267 				@Override
268 				public void remove() {
269 					throw new UnsupportedOperationException();
270 				}
271 			};
272 		}
273 
274 		@Override
275 		public int size() {
276 			return names.size();
277 		}
278 	}
279 }