blob: 0a713d464c8e2104b68b07add5c7a28f8cf50379 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.hudson.subversion;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.netbeans.api.diff.StreamSource;
import org.netbeans.modules.hudson.api.ConnectionBuilder;
import org.netbeans.modules.hudson.api.HudsonJob;
import org.netbeans.modules.hudson.ui.api.HudsonSCMHelper;
import org.openide.filesystems.FileUtil;
import org.openide.util.NbBundle;
import org.openide.util.RequestProcessor;
import org.openide.windows.OutputEvent;
import org.openide.windows.OutputListener;
/**
* Creates diff hyperlink for one file delta.
* Currently relies on a RESTful interface provided by SVN over HTTP(S).
* Would better use {@link org.tigris.subversion.javahl} but this seems easier.
* @see <a href="http://subversion.tigris.org/webdav-usage.html">SVN repo URL layout</a>
*/
class SubversionHyperlink implements OutputListener {
private static final Logger LOG = Logger.getLogger(SubversionHyperlink.class.getName());
private final String module;
private final String path;
private final int startRev;
private final int endRev;
private final HudsonJob job;
/**
* Creates a new potential hyperlink
* @param module an HTTP(S) module path, which may need to be stripped down to repo root
* @param path a file path presumed to be relative to repo root
* @param startRev the starting revision to display, or 0 for nothing
* @param endRev the ending revision to display, or 0 for nothing
*/
SubversionHyperlink(String module, String path, int startRev, int endRev, HudsonJob job) {
this.module = module;
this.path = path;
this.startRev = startRev;
this.endRev = endRev;
this.job = job;
}
public @Override void outputLineAction(OutputEvent ev) {
HudsonSCMHelper.noteWillShowDiff(path);
RequestProcessor.getDefault().post(new Runnable() {
public @Override void run() {
String repo = findRepo(module);
if (repo == null) {
return;
}
try {
final StreamSource before = makeSource(repo, path, startRev);
final StreamSource after = makeSource(repo, path, endRev);
HudsonSCMHelper.showDiff(before, after, path);
} catch (IOException x) {
LOG.log(Level.INFO, null, x);
}
}
});
}
public @Override void outputLineSelected(OutputEvent ev) {
// XXX could focus diff window if open
}
public @Override void outputLineCleared(OutputEvent ev) {}
private static Set<String> knownRepos = new HashSet<String>();
/**
* Trial-and-error way to find the repository root.
* @param module {@code http://whatever/svnroot/somerepo/trunk/subdir}
* @return {@code http://whatever/svnroot/somerepo} or null
*/
private synchronized String findRepo(String module) {
for (String r : knownRepos) {
if (module.startsWith(r)) {
return r;
}
}
LOG.log(Level.FINER, "looking for repo from {0}", module);
STRIP: while (true) {
try {
InputStream is = new ConnectionBuilder().job(job).url(module).connection().getInputStream();
try {
BufferedReader r = new BufferedReader(new InputStreamReader(is));
String line;
while ((line = r.readLine()) != null) {
if (line.equals(" <li><a href=\"../\">..</a></li>")) { // NOI18N
module = module.replaceFirst("/[^/]+$", ""); // NOI18N
continue STRIP;
}
}
} finally {
is.close();
}
LOG.log(Level.FINER, " => {0}", module);
knownRepos.add(module);
return module;
} catch (IOException x) {
LOG.log(Level.FINE, "trying to find repo for " + module, x);
return null;
}
}
}
private StreamSource makeSource(String repo, String path, int rev) throws IOException {
Reader r;
if (rev == 0) {
r = new StringReader("");
} else {
InputStream is = new URL(repo + "/!svn/ver/" + rev + path).openStream(); // NOI18N
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
FileUtil.copy(is, baos);
r = new StringReader(baos.toString());
} finally {
is.close();
}
}
String mimeType = "text/plain"; // NOI18N // XXX use FileUtil.getMIMETypeExtensions
String name = path;
String title = NbBundle.getMessage(SubversionHyperlink.class, "SubversionHyperlink.title", path.replaceFirst(".+/", ""), rev);
return StreamSource.createSource(name, title, mimeType, r);
}
}