DocURLs.java
package edu.odu.cs.cowem.documents.urls;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import edu.odu.cs.cowem.documents.MarkdownDocument;
import edu.odu.cs.cowem.documents.WebsiteProject;
/**
* Implements URL rewriting in course documents.
*
* doc:foo where foo contains no '/' or '.' characters and matches a
* document set name in a group DIR
* baseURL/DIR/foo/index.html
* doc:foo where foo contains one or more '/' or '.' characters and
* matches a file (presumably a secondary document or listing)
* in a group DIR,
* baseURL/DIR/foo.html
* docex:foo same as doc:foo, except during epub package generation when such
* links are ignored when choosing files to include in the e-book
* (useful for excluding "private" documents such as assignments)
*
* @author zeil
*
*/
public class DocURLs implements SpecialURL {
/**
* For logging error messages.
*/
private static Logger logger
= LoggerFactory.getLogger(DocURLs.class);
/**
* Context in which to determine relative URLs/paths.
*/
private WebsiteProject project;
/**
* directory containing the document source.
*/
private File sourceDirectory;
/**
* Create a URL rewriter.
*
* @param sourceDirectory0 directory containing the document source
* @param project0 context info on document set locations
*/
public DocURLs(final File sourceDirectory0, final WebsiteProject project0) {
sourceDirectory = sourceDirectory0;
project = project0;
}
private class DocumentSpecification {
public String documentName;
public String anchor;
}
/**
* Checks to see if a linking element (a or img) uses a special
* protocol label and, if so, attempts to rewrite the element.
*
* @param link an element containing a URL
* @param linkAttr name of the attribute containing the URL
* @return true if the element has been rewritten.
*/
@Override
public final boolean applyTo(final Element link, final String linkAttr) {
String url = link.getAttribute(linkAttr);
if (url.startsWith("doc:") || url.startsWith("docex:")) {
DocumentSpecification docSpec = parseDocumentSpecification(url);
String documentName = docSpec.documentName;
//logger.warn("Document spec is " + documentSpec);
if (documentName.contains("/")) {
processRelativeSpecification(link, linkAttr, url, docSpec);
} else if (documentName.contains(":")) {
// External site reference
int k = documentName.indexOf(':');
String siteName = documentName.substring(0, k);
documentName = documentName.substring(k+1);
String anchor = docSpec.anchor;
if (anchor.startsWith("#")) {
anchor = anchor.substring(1);
}
String siteURL = project.getExternalSite(siteName);
if (siteURL != null) {
if (!siteURL.endsWith("/")) {
siteURL = siteURL + "/";
}
siteURL = siteURL + "index.html?doc=" + documentName
+ "&anchor=" + anchor;
link.setAttribute(linkAttr, siteURL);
link.setAttribute("target", siteName);
} else {
logger.warn(
"Could not find external site for URL shorthand: @"
+ linkAttr + "=" + url);
}
} else if (documentName.endsWith(".mmd")) {
File targetFile = project.documentSource(documentName);
File targetDocSet = project.documentSetLocation(documentName);
if (targetFile != null) {
Path relative = project.relativePathToDocumentSet(
sourceDirectory,
documentName);
relative = relative.resolve(documentName);
String newLink = relative.toString().replace("\\","/")
+ ".html" + docSpec.anchor;
link.setAttribute(linkAttr, newLink);
String groupName = getGroupName(targetDocSet);
String targetAttr = project.getCourseName() + "_" + groupName;
link.setAttribute("target", targetAttr);
if (url.startsWith("doc:")) {
link.setAttribute("class", "doc");
}
attemptTBDReplacement(link, documentName);
} else {
logger.warn(
"Could not find a replacement for URL shorthand: @"
+ linkAttr + "=" + url);
}
} else {
String documentSetName = documentName;
//logger.warn("Hunting for document set name " + documentSpec);
File targetFile = project.documentSetLocation(documentSetName);
if (targetFile != null) {
Path relative = project.relativePathToDocumentSet(
sourceDirectory,
documentSetName);
String newLink = relative.resolve("index.html").toString().replace("\\","/")
+ docSpec.anchor;
link.setAttribute(linkAttr, newLink);
String groupName = getGroupName(targetFile);
String targetAttr = project.getCourseName() + "_" + groupName;
link.setAttribute("target", targetAttr);
if (url.startsWith("doc:")) {
link.setAttribute("class", "doc");
}
attemptTBDReplacement(link, documentSetName);
} else {
logger.warn(
"Could not find a replacement for URL shorthand: @"
+ linkAttr + "=" + url);
}
}
}
return false;
}
private String getGroupName(File sourceDirectory) {
return sourceDirectory.getParentFile().getName();
}
private void processRelativeSpecification(final Element link, final String linkAttr, String url,
DocumentSpecification docSpec) {
String documentName = docSpec.documentName;
int k = documentName.indexOf('/');
String documentSetName = documentName.substring(0, k);
String continuation = documentName.substring(0, k + 1);
File targetFile = project.documentSetLocation(documentSetName);
if (targetFile != null) {
Path relative = project.relativePathToDocumentSet(
sourceDirectory,
documentSetName);
relative = relative.resolve(continuation);
String newLink = relative.toString().replace("\\","/") + ".html"
+ docSpec.anchor;
link.setAttribute(linkAttr, newLink);
if (url.startsWith("doc:")) {
link.setAttribute("class", "doc");
}
attemptTBDReplacement(link, continuation);
} else {
logger.warn(
"Could not find a replacement for URL shorthand: @"
+ linkAttr + "=" + url);
}
}
private DocumentSpecification parseDocumentSpecification(String documentSpecStr) {
DocumentSpecification docSpec = new DocumentSpecification();
int dividerPos = documentSpecStr.indexOf(':');
docSpec.documentName = documentSpecStr.substring(dividerPos + 1);
int anchorPos = docSpec.documentName.indexOf('#');
docSpec.anchor = "";
if (anchorPos >= 0) {
docSpec.anchor = docSpec.documentName.substring(anchorPos);
docSpec.documentName = docSpec.documentName.substring(0, anchorPos);
}
return docSpec;
}
/**
* Checks to see if the entire text of an element is "TBD".
* If so, attempts to replace that text by the Title metadata field
* from the source document.
*
* @param element link element to examine
* @param documentName name of document
*/
private void attemptTBDReplacement(final Element element,
final String documentName) {
String title = project.getDocumentTitle(documentName);
String textContent = element.getTextContent().trim();
if ("TBD".equals(textContent) && title != null && title.length() > 0) {
while (element.hasChildNodes()) {
element.removeChild(element.getFirstChild());
}
Node newTitle = element.getOwnerDocument()
.createTextNode(title);
element.appendChild(newTitle);
}
}
}