1/*******************************************************************************
2 * Copyright (c) 2009, 2017 Mountainminds GmbH & Co. KG and Contributors
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 *    Marc R. Hoffmann - initial API and implementation
10 *
11 *******************************************************************************/
12package org.jacoco.core.test.validation;
13
14import java.io.BufferedReader;
15import java.io.File;
16import java.io.FileReader;
17import java.io.IOException;
18import java.io.Reader;
19import java.util.ArrayList;
20import java.util.Collections;
21import java.util.HashMap;
22import java.util.List;
23import java.util.Map;
24import java.util.NoSuchElementException;
25import java.util.regex.Matcher;
26import java.util.regex.Pattern;
27
28/**
29 * Reads a single source file and allows access to it through special probe
30 * comments in the following format <code>//$line-<i>tag</i>$.
31 */
32public class Source {
33
34	/**
35	 * Reads the source for the given type from the given source folder relative
36	 * to the working directory.
37	 *
38	 * @param srcFolder
39	 *            source folder
40	 * @param type
41	 *            type to load the source file for
42	 */
43	public static Source getSourceFor(final String srcFolder,
44			final Class<?> type) throws IOException {
45		File folder = new File(srcFolder);
46		File file = new File(folder, type.getName().replace('.', '/') + ".java");
47		return new Source(new FileReader(file));
48	}
49
50	private static final Pattern TAG_PATTERN = Pattern
51			.compile("\\$line-(.*)\\$");
52
53	private final List<String> lines = new ArrayList<String>();
54
55	private final Map<String, Integer> tags = new HashMap<String, Integer>();
56
57	/**
58	 * Reads a source file from the given reader.
59	 *
60	 * @param reader
61	 * @throws IOException
62	 */
63	public Source(final Reader reader) throws IOException {
64		final BufferedReader buffer = new BufferedReader(reader);
65		for (String l = buffer.readLine(); l != null; l = buffer.readLine()) {
66			addLine(l);
67		}
68		buffer.close();
69	}
70
71	private void addLine(final String l) {
72		lines.add(l);
73		final Matcher m = TAG_PATTERN.matcher(l);
74		if (m.find()) {
75			final String tag = m.group(1);
76			if (tags.put(tag, Integer.valueOf(lines.size())) != null) {
77				throw new IllegalArgumentException("Duplicate tag: " + tag);
78			}
79		}
80	}
81
82	/**
83	 * Returns all lines of the source file as a list.
84	 *
85	 * @return all lines of the source file
86	 */
87	public List<String> getLines() {
88		return Collections.unmodifiableList(lines);
89	}
90
91	/**
92	 * Returns the line with the given number
93	 *
94	 * @param nr
95	 *            line number (first line is 1)
96	 * @return line content
97	 */
98	public String getLine(int nr) {
99		return lines.get(nr - 1);
100	}
101
102	/**
103	 * Returns the line number with the given tag
104	 *
105	 * @param tag
106	 *            tag from a <code>//$line-<i>tag</i>$ marker
107	 * @return line number (first line is 1)
108	 * @throws NoSuchElementException
109	 *             if there is no such tag
110	 */
111	public int getLineNumber(String tag) throws NoSuchElementException {
112		final Integer nr = tags.get(tag);
113		if (nr == null) {
114			throw new NoSuchElementException("Unknown tag: " + tag);
115		}
116		return nr.intValue();
117	}
118
119}
120