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>
10   * and other copyright owners as documented in the project's IP log.
11   *
12   * This program and the accompanying materials are made available
13   * under the terms of the Eclipse Distribution License v1.0 which
14   * accompanies this distribution, is reproduced below, and is
15   * available at http://www.eclipse.org/org/documents/edl-v10.php
16   *
17   * All rights reserved.
18   *
19   * Redistribution and use in source and binary forms, with or
20   * without modification, are permitted provided that the following
21   * conditions are met:
22   *
23   * - Redistributions of source code must retain the above copyright
24   *   notice, this list of conditions and the following disclaimer.
25   *
26   * - Redistributions in binary form must reproduce the above
27   *   copyright notice, this list of conditions and the following
28   *   disclaimer in the documentation and/or other materials provided
29   *   with the distribution.
30   *
31   * - Neither the name of the Eclipse Foundation, Inc. nor the
32   *   names of its contributors may be used to endorse or promote
33   *   products derived from this software without specific prior
34   *   written permission.
35   *
36   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
37   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
38   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
40   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
43   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
44   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
45   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
46   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
47   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
48   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
49   */
50  
51  package org.eclipse.jgit.lib;
52  
53  import static org.eclipse.jgit.util.StringUtils.compareIgnoreCase;
54  import static org.eclipse.jgit.util.StringUtils.compareWithCase;
55  import static org.eclipse.jgit.util.StringUtils.toLowerCase;
56  
57  import java.util.AbstractSet;
58  import java.util.ArrayList;
59  import java.util.Collections;
60  import java.util.Comparator;
61  import java.util.HashMap;
62  import java.util.Iterator;
63  import java.util.LinkedHashMap;
64  import java.util.LinkedHashSet;
65  import java.util.List;
66  import java.util.Map;
67  import java.util.Set;
68  import java.util.concurrent.ConcurrentHashMap;
69  
70  import org.eclipse.jgit.util.StringUtils;
71  
72  class ConfigSnapshot {
73  	final List<ConfigLine> entryList;
74  	final Map<Object, Object> cache;
75  	final ConfigSnapshot baseState;
76  	volatile List<ConfigLine> sorted;
77  	volatile SectionNames names;
78  
79  	ConfigSnapshot(List<ConfigLine> entries, ConfigSnapshot base) {
80  		entryList = entries;
81  		cache = new ConcurrentHashMap<>(16, 0.75f, 1);
82  		baseState = base;
83  	}
84  
85  	Set<String> getSections() {
86  		return names().sections;
87  	}
88  
89  	Set<String> getSubsections(String section) {
90  		Map<String, Set<String>> m = names().subsections;
91  		Set<String> r = m.get(section);
92  		if (r == null)
93  			r = m.get(toLowerCase(section));
94  		if (r == null)
95  			return Collections.emptySet();
96  		return Collections.unmodifiableSet(r);
97  	}
98  
99  	Set<String> getNames(String section, String subsection) {
100 		return getNames(section, subsection, false);
101 	}
102 
103 	Set<String> getNames(String section, String subsection, boolean recursive) {
104 		Map<String, String> m = getNamesInternal(section, subsection, recursive);
105 		return new CaseFoldingSet(m);
106 	}
107 
108 	private Map<String, String> getNamesInternal(String section,
109 			String subsection, boolean recursive) {
110 		List<ConfigLine> s = sorted();
111 		int idx = find(s, section, subsection, ""); //$NON-NLS-1$
112 		if (idx < 0)
113 			idx = -(idx + 1);
114 
115 		Map<String, String> m = new LinkedHashMap<>();
116 		while (idx < s.size()) {
117 			ConfigLine e = s.get(idx++);
118 			if (!e.match(section, subsection))
119 				break;
120 			if (e.name == null)
121 				continue;
122 			String l = toLowerCase(e.name);
123 			if (!m.containsKey(l))
124 				m.put(l, e.name);
125 		}
126 		if (recursive && baseState != null)
127 			m.putAll(baseState.getNamesInternal(section, subsection, recursive));
128 		return m;
129 	}
130 
131 	String[] get(String section, String subsection, String name) {
132 		List<ConfigLine> s = sorted();
133 		int idx = find(s, section, subsection, name);
134 		if (idx < 0)
135 			return null;
136 		int end = end(s, idx, section, subsection, name);
137 		String[] r = new String[end - idx];
138 		for (int i = 0; idx < end;)
139 			r[i++] = s.get(idx++).value;
140 		return r;
141 	}
142 
143 	private int find(List<ConfigLine> s, String s1, String s2, String name) {
144 		int low = 0;
145 		int high = s.size();
146 		while (low < high) {
147 			int mid = (low + high) >>> 1;
148 			ConfigLine e = s.get(mid);
149 			int cmp = compare2(
150 					s1, s2, name,
151 					e.section, e.subsection, e.name);
152 			if (cmp < 0)
153 				high = mid;
154 			else if (cmp == 0)
155 				return first(s, mid, s1, s2, name);
156 			else
157 				low = mid + 1;
158 		}
159 		return -(low + 1);
160 	}
161 
162 	private int first(List<ConfigLine> s, int i, String s1, String s2, String n) {
163 		while (0 < i) {
164 			if (s.get(i - 1).match(s1, s2, n))
165 				i--;
166 			else
167 				return i;
168 		}
169 		return i;
170 	}
171 
172 	private int end(List<ConfigLine> s, int i, String s1, String s2, String n) {
173 		while (i < s.size()) {
174 			if (s.get(i).match(s1, s2, n))
175 				i++;
176 			else
177 				return i;
178 		}
179 		return i;
180 	}
181 
182 	private List<ConfigLine> sorted() {
183 		List<ConfigLine> r = sorted;
184 		if (r == null)
185 			sorted = r = sort(entryList);
186 		return r;
187 	}
188 
189 	private static List<ConfigLine> sort(List<ConfigLine> in) {
190 		List<ConfigLine> sorted = new ArrayList<>(in.size());
191 		for (ConfigLine line : in) {
192 			if (line.section != null && line.name != null)
193 				sorted.add(line);
194 		}
195 		Collections.sort(sorted, new LineComparator());
196 		return sorted;
197 	}
198 
199 	private static int compare2(
200 			String aSection, String aSubsection, String aName,
201 			String bSection, String bSubsection, String bName) {
202 		int c = compareIgnoreCase(aSection, bSection);
203 		if (c != 0)
204 			return c;
205 
206 		if (aSubsection == null && bSubsection != null)
207 			return -1;
208 		if (aSubsection != null && bSubsection == null)
209 			return 1;
210 		if (aSubsection != null) {
211 			c = compareWithCase(aSubsection, bSubsection);
212 			if (c != 0)
213 				return c;
214 		}
215 
216 		return compareIgnoreCase(aName, bName);
217 	}
218 
219 	private static class LineComparator implements Comparator<ConfigLine> {
220 		@Override
221 		public int compare(ConfigLine a, ConfigLine b) {
222 			return compare2(
223 					a.section, a.subsection, a.name,
224 					b.section, b.subsection, b.name);
225 		}
226 	}
227 
228 	private SectionNames names() {
229 		SectionNames n = names;
230 		if (n == null)
231 			names = n = new SectionNames(this);
232 		return n;
233 	}
234 
235 	private static class SectionNames {
236 		final CaseFoldingSet sections;
237 		final Map<String, Set<String>> subsections;
238 
239 		SectionNames(ConfigSnapshot cfg) {
240 			Map<String, String> sec = new LinkedHashMap<>();
241 			Map<String, Set<String>> sub = new HashMap<>();
242 			while (cfg != null) {
243 				for (ConfigLine e : cfg.entryList) {
244 					if (e.section == null)
245 						continue;
246 
247 					String l1 = toLowerCase(e.section);
248 					if (!sec.containsKey(l1))
249 						sec.put(l1, e.section);
250 
251 					if (e.subsection == null)
252 						continue;
253 
254 					Set<String> m = sub.get(l1);
255 					if (m == null) {
256 						m = new LinkedHashSet<>();
257 						sub.put(l1, m);
258 					}
259 					m.add(e.subsection);
260 				}
261 				cfg = cfg.baseState;
262 			}
263 
264 			sections = new CaseFoldingSet(sec);
265 			subsections = sub;
266 		}
267 	}
268 
269 	private static class CaseFoldingSet extends AbstractSet<String> {
270 		private final Map<String, String> names;
271 
272 		CaseFoldingSet(Map<String, String> names) {
273 			this.names = names;
274 		}
275 
276 		@Override
277 		public boolean contains(Object needle) {
278 			if (needle instanceof String) {
279 				String n = (String) needle;
280 				return names.containsKey(n)
281 						|| names.containsKey(StringUtils.toLowerCase(n));
282 			}
283 			return false;
284 		}
285 
286 		@Override
287 		public Iterator<String> iterator() {
288 			final Iterator<String> i = names.values().iterator();
289 			return new Iterator<String>() {
290 				@Override
291 				public boolean hasNext() {
292 					return i.hasNext();
293 				}
294 
295 				@Override
296 				public String next() {
297 					return i.next();
298 				}
299 
300 				@Override
301 				public void remove() {
302 					throw new UnsupportedOperationException();
303 				}
304 			};
305 		}
306 
307 		@Override
308 		public int size() {
309 			return names.size();
310 		}
311 	}
312 }