LookupAwareRelativePathResolver.java

1
package fr.sii.ogham.core.resource.path;
2
3
import java.nio.file.Path;
4
import java.nio.file.Paths;
5
import java.util.ArrayList;
6
import java.util.List;
7
import java.util.Map;
8
import java.util.Map.Entry;
9
10
import fr.sii.ogham.core.util.ResourceUtils;
11
12
/**
13
 * Resolve the given path against the base path by using the following rules:
14
 * 
15
 * <p>
16
 * If the <b>relative path</b> is not absolute (doesn't start with /) and the
17
 * <b>base path</b> ends with / then the <b>relative path</b> is <b>appended</b>
18
 * to the <b>base path</b>.
19
 * </p>
20
 * <p>
21
 * If the <b>relative path</b> is not absolute (doesn't start with /) and the
22
 * <b>base path</b> doesn't end with / then the <b>relative path</b>
23
 * <b>replaces</b> the last part of the <b>base path</b>.
24
 * </p>
25
 * <p>
26
 * If the <b>relative path</b> is absolute (starts with /) then the <b>relative
27
 * path</b> is is used.
28
 * </p>
29
 * <p>
30
 * If the <b>relative path</b> has lookup prefix (like "classpath:") then this
31
 * lookup prefix is used.
32
 * </p>
33
 * <p>
34
 * If the <b>relative path</b> has no lookup prefix then the lookup prefix of
35
 * <b>base path</b> is used (if any).
36
 * </p>
37
 * <p>
38
 * If the <b>relative path</b> has different lookup prefix than <b>base path</b>
39
 * lookup then relative path is considered like an absolute path.
40
 * </p>
41
 * 
42
 * <table>
43
 * <caption>Exemple of results</caption> <thead>
44
 * <tr>
45
 * <th>description</th>
46
 * <th>base path</th>
47
 * <th>relative path</th>
48
 * <th>result</th>
49
 * </tr>
50
 * </thead> <tbody>
51
 * <tr>
52
 * <td>resolve as child</td>
53
 * <td>classpath:/template/</td>
54
 * <td>images/foo.png</td>
55
 * <td>classpath:/template/images/foo.png</td>
56
 * </tr>
57
 * <tr>
58
 * <td>resolve as sibling</td>
59
 * <td>classpath:/template/register.html</td>
60
 * <td>images/foo.png</td>
61
 * <td>classpath:/template/images/foo.png</td>
62
 * </tr>
63
 * <tr>
64
 * <td>resolve as sibling</td>
65
 * <td>classpath:/template</td>
66
 * <td>images/foo.png</td>
67
 * <td>classpath:/images/foo.png</td>
68
 * </tr>
69
 * <tr>
70
 * <td>absolute path</td>
71
 * <td>/template/</td>
72
 * <td>/images/foo.png</td>
73
 * <td>/images/foo.png</td>
74
 * </tr>
75
 * <tr>
76
 * <td>absolute path (keep lookup)</td>
77
 * <td>classpath:/template/</td>
78
 * <td>/images/foo.png</td>
79
 * <td>classpath:/images/foo.png</td>
80
 * </tr>
81
 * <tr>
82
 * <td>different lookups</td>
83
 * <td>classpath:/template/</td>
84
 * <td>file:images/foo.png</td>
85
 * <td>file:images/foo.png</td>
86
 * </tr>
87
 * </tbody>
88
 * </table>
89
 * 
90
 * <p>
91
 * This implementation that requires the list of known lookups.
92
 * </p>
93
 * 
94
 * @author Aurélien Baudet
95
 *
96
 */
97
public class LookupAwareRelativePathResolver implements RelativePathResolver {
98
	private final List<LookupMetadata> lookupsMeta;
99
100
	/**
101
	 * Initializes with the list of known lookups (indexed by type).
102
	 * 
103
	 * @param lookupsIndexedByType
104
	 *            the lookups indexed by type
105
	 */
106
	public LookupAwareRelativePathResolver(Map<String, List<String>> lookupsIndexedByType) {
107
		super();
108
		this.lookupsMeta = new ArrayList<>();
109
		for (Entry<String, List<String>> entry : lookupsIndexedByType.entrySet()) {
110
			for (String lookup : entry.getValue()) {
111 1 1. <init> : negated conditional → RUN_ERROR
				if (!lookup.isEmpty()) {
112
					lookupsMeta.add(new LookupMetadata(lookup, entry.getKey()));
113
				}
114
			}
115
		}
116
	}
117
118
	@Override
119
	public RelativePath resolve(ResourcePath source, ResourcePath relativePath) {
120 1 1. resolve : replaced return value with null for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver::resolve → RUN_ERROR
		return new RelativePath(source, relativePath, getMergedPath(source, relativePath));
121
	}
122
123
	@Override
124
	public RelativePath resolve(ResourcePath source, String relativePath) {
125 1 1. resolve : replaced return value with null for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver::resolve → RUN_ERROR
		return resolve(source, new UnresolvedPath(relativePath));
126
	}
127
128
	private String getMergedPath(ResourcePath source, ResourcePath relativePath) {
129
		String relativeLookup = getLookup(relativePath.getOriginalPath());
130
		String sourceLookup = getLookup(source.getOriginalPath());
131
		// not the same lookup
132
		// => not the same system to load
133
		// => not relative
134 2 1. getMergedPath : negated conditional → RUN_ERROR
2. getMergedPath : negated conditional → RUN_ERROR
		if (relativeLookup != null && !isSameLookupType(relativeLookup, sourceLookup)) {
135 1 1. getMergedPath : replaced return value with "" for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver::getMergedPath → RUN_ERROR
			return relativePath.getOriginalPath();
136
		}
137
		// if path points to a directory
138
		// => append the relative path
139
		// if path points to a file
140
		// => replace file by the relative path
141 1 1. getMergedPath : negated conditional → RUN_ERROR
		Path merged = Paths.get(withoutLookup(source instanceof ResolvedPath ? ((ResolvedPath) source).getResolvedPath() : source.getOriginalPath()));
142 1 1. getMergedPath : negated conditional → RUN_ERROR
		if (isDirectory(source.getOriginalPath())) {
143
			merged = merged.resolve(withoutLookup(relativePath.getOriginalPath()));
144
		} else {
145
			merged = merged.resolveSibling(withoutLookup(relativePath.getOriginalPath()));
146
		}
147 1 1. getMergedPath : negated conditional → RUN_ERROR
		if (relativeLookup != null) {
148 1 1. getMergedPath : replaced return value with "" for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver::getMergedPath → RUN_ERROR
			return relativeLookup + ResourceUtils.toResourcePath(merged);
149
		}
150 1 1. getMergedPath : negated conditional → RUN_ERROR
		if (sourceLookup != null) {
151 1 1. getMergedPath : replaced return value with "" for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver::getMergedPath → RUN_ERROR
			return sourceLookup + ResourceUtils.toResourcePath(merged);
152
		}
153 1 1. getMergedPath : replaced return value with "" for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver::getMergedPath → RUN_ERROR
		return ResourceUtils.toResourcePath(merged);
154
	}
155
156
	private boolean isSameLookupType(String relativeLookup, String sourceLookup) {
157
		LookupMetadata relativeMeta = getLookupMeta(relativeLookup);
158 1 1. isSameLookupType : negated conditional → RUN_ERROR
		if (relativeMeta == null) {
159 1 1. isSameLookupType : replaced boolean return with true for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver::isSameLookupType → NO_COVERAGE
			return false;
160
		}
161 2 1. isSameLookupType : replaced boolean return with false for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver::isSameLookupType → RUN_ERROR
2. isSameLookupType : replaced boolean return with true for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver::isSameLookupType → RUN_ERROR
		return relativeMeta.isSameType(getLookupMeta(sourceLookup));
162
	}
163
164
	private LookupMetadata getLookupMeta(String lookup) {
165
		for (LookupMetadata meta : lookupsMeta) {
166 1 1. getLookupMeta : negated conditional → RUN_ERROR
			if (meta.getLookup().equals(lookup)) {
167 1 1. getLookupMeta : replaced return value with null for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver::getLookupMeta → RUN_ERROR
				return meta;
168
			}
169
		}
170
		return null;
171
	}
172
173
	private static boolean isDirectory(String path) {
174 2 1. isDirectory : replaced boolean return with true for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver::isDirectory → RUN_ERROR
2. isDirectory : replaced boolean return with false for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver::isDirectory → RUN_ERROR
		return path.endsWith("/");
175
	}
176
177
	private String withoutLookup(String path) {
178
		for (LookupMetadata meta : lookupsMeta) {
179 2 1. withoutLookup : negated conditional → RUN_ERROR
2. withoutLookup : negated conditional → RUN_ERROR
			if (!meta.getLookup().isEmpty() && path.startsWith(meta.getLookup())) {
180 1 1. withoutLookup : replaced return value with "" for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver::withoutLookup → RUN_ERROR
				return path.substring(meta.getLookup().length());
181
			}
182
		}
183 1 1. withoutLookup : replaced return value with "" for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver::withoutLookup → RUN_ERROR
		return path;
184
	}
185
186
	private String getLookup(String path) {
187
		for (LookupMetadata meta : lookupsMeta) {
188 2 1. getLookup : negated conditional → RUN_ERROR
2. getLookup : negated conditional → RUN_ERROR
			if (!meta.getLookup().isEmpty() && path.startsWith(meta.getLookup())) {
189 1 1. getLookup : replaced return value with "" for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver::getLookup → RUN_ERROR
				return meta.getLookup();
190
			}
191
		}
192 1 1. getLookup : replaced return value with "" for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver::getLookup → RUN_ERROR
		return null;
193
	}
194
195
	private static class LookupMetadata {
196
		private final String lookup;
197
		private final String type;
198
199
		public LookupMetadata(String lookup, String type) {
200
			super();
201
			this.lookup = lookup;
202
			this.type = type;
203
		}
204
205
		public boolean isSameType(LookupMetadata lookupMeta) {
206 1 1. isSameType : negated conditional → RUN_ERROR
			if (lookupMeta == null) {
207 1 1. isSameType : replaced boolean return with true for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver$LookupMetadata::isSameType → RUN_ERROR
				return false;
208
			}
209 2 1. isSameType : replaced boolean return with false for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver$LookupMetadata::isSameType → RUN_ERROR
2. isSameType : replaced boolean return with true for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver$LookupMetadata::isSameType → RUN_ERROR
			return type.equals(lookupMeta.getType());
210
		}
211
212
		public String getLookup() {
213 1 1. getLookup : replaced return value with "" for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver$LookupMetadata::getLookup → RUN_ERROR
			return lookup;
214
		}
215
216
		public String getType() {
217 1 1. getType : replaced return value with "" for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver$LookupMetadata::getType → RUN_ERROR
			return type;
218
		}
219
	}
220
}

Mutations

111

1.1
Location : <init>
Killed by :
negated conditional → RUN_ERROR

120

1.1
Location : resolve
Killed by :
replaced return value with null for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver::resolve → RUN_ERROR

125

1.1
Location : resolve
Killed by :
replaced return value with null for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver::resolve → RUN_ERROR

134

1.1
Location : getMergedPath
Killed by :
negated conditional → RUN_ERROR

2.2
Location : getMergedPath
Killed by :
negated conditional → RUN_ERROR

135

1.1
Location : getMergedPath
Killed by :
replaced return value with "" for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver::getMergedPath → RUN_ERROR

141

1.1
Location : getMergedPath
Killed by :
negated conditional → RUN_ERROR

142

1.1
Location : getMergedPath
Killed by :
negated conditional → RUN_ERROR

147

1.1
Location : getMergedPath
Killed by :
negated conditional → RUN_ERROR

148

1.1
Location : getMergedPath
Killed by :
replaced return value with "" for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver::getMergedPath → RUN_ERROR

150

1.1
Location : getMergedPath
Killed by :
negated conditional → RUN_ERROR

151

1.1
Location : getMergedPath
Killed by :
replaced return value with "" for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver::getMergedPath → RUN_ERROR

153

1.1
Location : getMergedPath
Killed by :
replaced return value with "" for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver::getMergedPath → RUN_ERROR

158

1.1
Location : isSameLookupType
Killed by :
negated conditional → RUN_ERROR

159

1.1
Location : isSameLookupType
Killed by :
replaced boolean return with true for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver::isSameLookupType → NO_COVERAGE

161

1.1
Location : isSameLookupType
Killed by :
replaced boolean return with false for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver::isSameLookupType → RUN_ERROR

2.2
Location : isSameLookupType
Killed by :
replaced boolean return with true for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver::isSameLookupType → RUN_ERROR

166

1.1
Location : getLookupMeta
Killed by :
negated conditional → RUN_ERROR

167

1.1
Location : getLookupMeta
Killed by :
replaced return value with null for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver::getLookupMeta → RUN_ERROR

174

1.1
Location : isDirectory
Killed by :
replaced boolean return with true for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver::isDirectory → RUN_ERROR

2.2
Location : isDirectory
Killed by :
replaced boolean return with false for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver::isDirectory → RUN_ERROR

179

1.1
Location : withoutLookup
Killed by :
negated conditional → RUN_ERROR

2.2
Location : withoutLookup
Killed by :
negated conditional → RUN_ERROR

180

1.1
Location : withoutLookup
Killed by :
replaced return value with "" for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver::withoutLookup → RUN_ERROR

183

1.1
Location : withoutLookup
Killed by :
replaced return value with "" for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver::withoutLookup → RUN_ERROR

188

1.1
Location : getLookup
Killed by :
negated conditional → RUN_ERROR

2.2
Location : getLookup
Killed by :
negated conditional → RUN_ERROR

189

1.1
Location : getLookup
Killed by :
replaced return value with "" for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver::getLookup → RUN_ERROR

192

1.1
Location : getLookup
Killed by :
replaced return value with "" for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver::getLookup → RUN_ERROR

206

1.1
Location : isSameType
Killed by :
negated conditional → RUN_ERROR

207

1.1
Location : isSameType
Killed by :
replaced boolean return with true for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver$LookupMetadata::isSameType → RUN_ERROR

209

1.1
Location : isSameType
Killed by :
replaced boolean return with false for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver$LookupMetadata::isSameType → RUN_ERROR

2.2
Location : isSameType
Killed by :
replaced boolean return with true for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver$LookupMetadata::isSameType → RUN_ERROR

213

1.1
Location : getLookup
Killed by :
replaced return value with "" for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver$LookupMetadata::getLookup → RUN_ERROR

217

1.1
Location : getType
Killed by :
replaced return value with "" for fr/sii/ogham/core/resource/path/LookupAwareRelativePathResolver$LookupMetadata::getType → RUN_ERROR

Active mutators

Tests examined


Report generated by PIT 1.13.1