Lines 45-50
Link Here
|
45 |
package org.netbeans.modules.hudson.spi; |
45 |
package org.netbeans.modules.hudson.spi; |
46 |
|
46 |
|
47 |
import java.net.URI; |
47 |
import java.net.URI; |
|
|
48 |
import java.util.Arrays; |
49 |
import java.util.Collection; |
48 |
import java.util.HashMap; |
50 |
import java.util.HashMap; |
49 |
import java.util.Map; |
51 |
import java.util.Map; |
50 |
import java.util.logging.Level; |
52 |
import java.util.logging.Level; |
Lines 54-59
Link Here
|
54 |
import org.netbeans.api.annotations.common.CheckForNull; |
56 |
import org.netbeans.api.annotations.common.CheckForNull; |
55 |
import org.netbeans.api.project.Project; |
57 |
import org.netbeans.api.project.Project; |
56 |
import org.netbeans.api.project.ui.OpenProjects; |
58 |
import org.netbeans.api.project.ui.OpenProjects; |
|
|
59 |
import org.netbeans.modules.hudson.api.HudsonFolder; |
57 |
import org.netbeans.modules.hudson.api.HudsonInstance; |
60 |
import org.netbeans.modules.hudson.api.HudsonInstance; |
58 |
import org.netbeans.modules.hudson.api.HudsonJob; |
61 |
import org.netbeans.modules.hudson.api.HudsonJob; |
59 |
import org.netbeans.modules.hudson.impl.HudsonManagerImpl; |
62 |
import org.netbeans.modules.hudson.impl.HudsonManagerImpl; |
Lines 144-151
Link Here
|
144 |
*/ |
147 |
*/ |
145 |
public static final class Association { |
148 |
public static final class Association { |
146 |
|
149 |
|
147 |
private final String serverURL; |
150 |
private final String jobURL; |
148 |
private final String jobName; |
151 |
/** Server URL (first), sequence of folders, job name (last). */ |
|
|
152 |
private final String[] jobPath; |
149 |
|
153 |
|
150 |
/** |
154 |
/** |
151 |
* Creates an association. |
155 |
* Creates an association. |
Lines 164-171
Link Here
|
164 |
if (jobName != null && jobName.indexOf('/') != -1) { |
168 |
if (jobName != null && jobName.indexOf('/') != -1) { |
165 |
throw new IllegalArgumentException("No slashes permitted in job name: " + jobName); |
169 |
throw new IllegalArgumentException("No slashes permitted in job name: " + jobName); |
166 |
} |
170 |
} |
167 |
this.serverURL = serverURL; |
171 |
this.jobURL = getStandardJobUrl(serverURL, jobName); |
168 |
this.jobName = jobName; |
172 |
this.jobPath = new String[]{serverURL, jobName}; |
|
|
173 |
} |
174 |
|
175 |
private static String getStandardJobUrl(String serverURL, String jobName) { |
176 |
return jobName != null |
177 |
? serverURL + "job/" + Utilities.uriEncode(jobName) + "/" // NOI18N |
178 |
: serverURL; |
179 |
} |
180 |
|
181 |
/** |
182 |
* Creates an association. |
183 |
* |
184 |
* @param job URL |
185 |
* @throws IllegalArgumentException if parameter has invalid syntax |
186 |
* @since hudson/1.32 |
187 |
*/ |
188 |
public Association(String jobURL) throws IllegalArgumentException { |
189 |
URI.create(jobURL); // check syntax |
190 |
if (!jobURL.endsWith("/")) { // NOI18N |
191 |
throw new IllegalArgumentException(jobURL + " must end in a slash"); // NOI18N |
192 |
} |
193 |
this.jobURL = jobURL; |
194 |
this.jobPath = extractJobPath(jobURL); |
195 |
} |
196 |
|
197 |
private static String[] extractJobPath(String jobURL) throws IllegalArgumentException { |
198 |
Matcher m = Pattern.compile("(https?://.+?/)((?:job/(?:[^/]+)/)*)").matcher(jobURL); // NOI18N |
199 |
if (!m.matches()) { |
200 |
throw new IllegalArgumentException("Cannot extract job path: " + jobURL); //NOI18N |
201 |
} else { |
202 |
String rawPath = m.group(2); |
203 |
if (rawPath == null || rawPath.isEmpty()) { |
204 |
return new String[]{m.group(1)}; |
205 |
} else { |
206 |
String[] elements = rawPath.split("/"); |
207 |
assert elements.length > 0 && (elements.length % 2) == 0; |
208 |
String[] result = new String[(elements.length / 2) + 1]; |
209 |
result[0] = m.group(1); // server URL |
210 |
for (int i = 0; i < elements.length; i++) { |
211 |
String element = elements[i]; |
212 |
if (i % 2 == 0) { |
213 |
assert "job".equals(element); |
214 |
} else { |
215 |
String decoded = Utilities.uriDecode(element); |
216 |
if (decoded.trim().isEmpty()) { |
217 |
throw new IllegalArgumentException("Empty job name: " + jobURL); //NOI18N |
218 |
} |
219 |
result[(i / 2) + 1] = decoded; |
220 |
} |
221 |
} |
222 |
return result; |
223 |
} |
224 |
} |
169 |
} |
225 |
} |
170 |
|
226 |
|
171 |
/** |
227 |
/** |
Lines 174-194
Link Here
|
174 |
* @return an association with the same server URL and job name |
230 |
* @return an association with the same server URL and job name |
175 |
*/ |
231 |
*/ |
176 |
public static Association forJob(HudsonJob job) { |
232 |
public static Association forJob(HudsonJob job) { |
177 |
return new Association(job.getInstance().getUrl(), job.getName()); |
233 |
return new Association(job.getUrl()); |
178 |
} |
234 |
} |
179 |
|
235 |
|
180 |
/** |
236 |
/** |
181 |
* @return the root URL of the server ending in slash, e.g. {@code http://deadlock.netbeans.org/hudson/} |
237 |
* @return the root URL of the server ending in slash, e.g. {@code http://deadlock.netbeans.org/hudson/} |
182 |
*/ |
238 |
*/ |
183 |
public String getServerUrl() { |
239 |
public String getServerUrl() { |
184 |
return serverURL; |
240 |
return jobPath[0]; |
185 |
} |
241 |
} |
186 |
|
242 |
|
187 |
/** |
243 |
/** |
188 |
* @return the code name of the job on that server; may be null |
244 |
* @return the code name of the job on that server; may be null |
189 |
*/ |
245 |
*/ |
190 |
public String getJobName() { |
246 |
public String getJobName() { |
191 |
return jobName; |
247 |
return jobPath.length > 1 ? jobPath[jobPath.length - 1] : null; |
|
|
248 |
} |
249 |
|
250 |
/** |
251 |
* Get copy of job path. Not private - called from tests. |
252 |
*/ |
253 |
String[] getJobPath() { |
254 |
return Arrays.copyOf(jobPath, jobPath.length); |
192 |
} |
255 |
} |
193 |
|
256 |
|
194 |
/** |
257 |
/** |
Lines 196-210
Link Here
|
196 |
* @return a job with the name {@link #getJobName} on the server with the same {@link #getServerUrl}, or null |
259 |
* @return a job with the name {@link #getJobName} on the server with the same {@link #getServerUrl}, or null |
197 |
*/ |
260 |
*/ |
198 |
public @CheckForNull HudsonJob getJob() { |
261 |
public @CheckForNull HudsonJob getJob() { |
199 |
if (jobName == null) { |
262 |
if (jobPath == null || jobPath.length < 2) { // no job name |
200 |
return null; |
263 |
return null; |
201 |
} |
264 |
} |
202 |
HudsonInstance instance = HudsonManagerImpl.getDefault().getInstance(serverURL); |
265 |
HudsonInstance instance = HudsonManagerImpl.getDefault().getInstance(jobPath[0]); |
203 |
if (instance == null) { |
266 |
if (instance == null) { |
204 |
return null; |
267 |
return null; |
205 |
} |
268 |
} |
206 |
for (HudsonJob job : instance.getJobs()) { |
269 |
if (jobPath.length == 2) { |
207 |
if (job.getName().equals(jobName)) { |
270 |
return findJobByName(instance.getJobs(), jobPath[1]); |
|
|
271 |
} else { |
272 |
HudsonFolder lastFolder = null; |
273 |
for (int i = 1; i < jobPath.length; i++) { |
274 |
String name = jobPath[i]; |
275 |
if (i == 1) { |
276 |
lastFolder = findFolderByName(instance.getFolders(), name); |
277 |
} else if (i < jobPath.length - 1 && lastFolder != null) { |
278 |
lastFolder = findFolderByName(lastFolder.getFolders(), name); |
279 |
} else if (lastFolder != null) { |
280 |
return findJobByName(lastFolder.getJobs(), name); |
281 |
} |
282 |
} |
283 |
} |
284 |
return null; |
285 |
} |
286 |
|
287 |
/** |
288 |
* Find a folder of specified name in a collection of folders. |
289 |
* |
290 |
* @return The folder with name {@code name}, or null if not found. |
291 |
*/ |
292 |
private HudsonFolder findFolderByName(Collection<HudsonFolder> folders, |
293 |
String name) { |
294 |
for (HudsonFolder folder : folders) { |
295 |
if (name.equals(folder.getName())) { |
296 |
return folder; |
297 |
} |
298 |
} |
299 |
return null; |
300 |
} |
301 |
|
302 |
/** |
303 |
* Find a job of specified name in a collection of jobs. |
304 |
* |
305 |
* @return The job with name {@code name}, or null if not found. |
306 |
*/ |
307 |
private HudsonJob findJobByName(Collection<HudsonJob> jobs, |
308 |
String name) { |
309 |
for (HudsonJob job : jobs) { |
310 |
if (name.equals(job.getName())) { |
208 |
return job; |
311 |
return job; |
209 |
} |
312 |
} |
210 |
} |
313 |
} |
Lines 226-232
Link Here
|
226 |
* URL of either job or server root. |
329 |
* URL of either job or server root. |
227 |
*/ |
330 |
*/ |
228 |
public @Override String toString() { |
331 |
public @Override String toString() { |
229 |
return jobName != null ? serverURL + "job/" + Utilities.uriEncode(jobName) + "/" : serverURL; // NOI18N |
332 |
return jobURL; |
230 |
} |
333 |
} |
231 |
|
334 |
|
232 |
/** |
335 |
/** |
Lines 234-246
Link Here
|
234 |
* @return an association based on parsing a Hudson job or root URL, or null |
337 |
* @return an association based on parsing a Hudson job or root URL, or null |
235 |
*/ |
338 |
*/ |
236 |
public static Association fromString(String s) { |
339 |
public static Association fromString(String s) { |
237 |
Matcher m = Pattern.compile("(https?://.+?/)(?:job/([^/]+)/?)?").matcher(s); // NOI18N |
|
|
238 |
if (!m.matches()) { |
239 |
return null; |
240 |
} |
241 |
String jobNameRaw = m.group(2); |
242 |
try { |
340 |
try { |
243 |
return new Association(m.group(1), jobNameRaw != null ? Utilities.uriDecode(jobNameRaw) : null); |
341 |
return new Association(s); |
244 |
} catch (IllegalArgumentException x) { |
342 |
} catch (IllegalArgumentException x) { |
245 |
Logger.getLogger(ProjectHudsonProvider.class.getName()).log(Level.WARNING, "Bad URL: {0}", s); |
343 |
Logger.getLogger(ProjectHudsonProvider.class.getName()).log(Level.WARNING, "Bad URL: {0}", s); |
246 |
return null; |
344 |
return null; |