DateURLs.java
package edu.odu.cs.cowem.documents.urls;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.TemporalAccessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
/**
* Implements URL rewriting in course documents.
*
* [text](date:) where text is a date/time in ISO 8601 format
* YYYY-MM-DDThh:mm, is formatted so a span of class
* date with a more conventional format.
*
* No support at the moment for time zones. May add that later.
*
* @author zeil
*
*/
public class DateURLs implements SpecialURL {
/**
* For logging error messages.
*/
private static Logger logger
= LoggerFactory.getLogger(DateURLs.class);
/**
* Format used for rendering combined date-time items.
*/
private static final DateTimeFormatter DT_OUTPUT_FORMAT
= DateTimeFormatter.ofPattern("MM/dd/yyyy, h:mma z");
/**
* Format used for rendering combined dates.
*/
private static final DateTimeFormatter D_OUTPUT_FORMAT
= DateTimeFormatter.ofPattern("MM/dd/yyyy");
/**
* Format used for rendering combined times.
*/
private static final DateTimeFormatter T_OUTPUT_FORMAT
= DateTimeFormatter.ofPattern("h:mma");
/**
* Format used for rendering combined date-time attributes.
*/
private static final DateTimeFormatter DT_ATTR_FORMAT
= DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z");
/**
* Create a URL rewriter.
*
*/
public DateURLs() {
}
/**
* 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 ("date:".equals(url)) {
String rawDateTime = link.getTextContent();
String formattedDateTime = "";
String startTimeAttr = "";
String endTimeAttr = "";
ZoneId timeZone = ZoneId.systemDefault();
DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
try {
LocalDateTime dateTime = LocalDateTime.parse(rawDateTime,
formatter);
ZonedDateTime zdt = ZonedDateTime.of(dateTime, timeZone);
formattedDateTime = DT_OUTPUT_FORMAT.format(zdt);
startTimeAttr = DT_ATTR_FORMAT.format(zdt);
TemporalAccessor endDT = processEndDate(link, linkAttr);
if (endDT != null) {
if (endDT instanceof LocalDateTime) {
LocalDateTime ldt = (LocalDateTime) endDT;
LocalDate dt0 = dateTime.toLocalDate();
LocalDate dt1 = ldt.toLocalDate();
String formattedEndTime;
if (dt0.equals(dt1)) {
formattedEndTime = T_OUTPUT_FORMAT.format(ldt);
} else {
formattedEndTime = DT_OUTPUT_FORMAT.format(
ZonedDateTime.of(ldt, timeZone));
}
formattedDateTime = formattedDateTime + " - "
+ formattedEndTime;
endTimeAttr = DT_ATTR_FORMAT.format(
ZonedDateTime.of(ldt, timeZone));
} else if (endDT instanceof LocalDate) {
String formattedEndTime = D_OUTPUT_FORMAT.format(endDT);
formattedDateTime = formattedDateTime + " - "
+ formattedEndTime;
LocalDateTime eldt = ((LocalDate) endDT)
.atTime(23, 59, 59);
endTimeAttr = DT_ATTR_FORMAT.format(
ZonedDateTime.of(eldt, timeZone));
} else if (endDT instanceof LocalTime) {
String formattedEndTime = T_OUTPUT_FORMAT.format(endDT);
formattedDateTime = formattedDateTime + "-"
+ formattedEndTime;
LocalDateTime eldt = dateTime.toLocalDate()
.atTime((LocalTime)endDT);
endTimeAttr = DT_ATTR_FORMAT.format(
ZonedDateTime.of(eldt, timeZone));
} else {
logger.error("Obtained endDate of unhandled type "
+ endDT.getClass().getName());
}
} else {
LocalDateTime eldt = dateTime.plusMinutes(1);
endTimeAttr = DT_ATTR_FORMAT.format(
ZonedDateTime.of(eldt, timeZone));
}
} catch (DateTimeParseException ex) {
formattedDateTime = "";
}
if (formattedDateTime.length() == 0) {
formatter = DateTimeFormatter.ISO_LOCAL_DATE;
try {
LocalDate date = LocalDate.parse(rawDateTime,
formatter);
LocalDateTime dateTime = date.atTime(0,0,0);
ZonedDateTime zdt = ZonedDateTime.of(dateTime, timeZone);
formattedDateTime = D_OUTPUT_FORMAT.format(zdt);
startTimeAttr = DT_ATTR_FORMAT.format(zdt);
TemporalAccessor endDT = processEndDate(link, linkAttr);
if (endDT != null) {
if (endDT instanceof LocalDateTime) {
LocalDateTime ldt = (LocalDateTime) endDT;
String formattedEndTime =
D_OUTPUT_FORMAT.format(ldt);
formattedDateTime = formattedDateTime + " - "
+ formattedEndTime;
endTimeAttr = DT_ATTR_FORMAT.format(
ZonedDateTime.of(ldt, timeZone));
} else if (endDT instanceof LocalDate) {
String formattedEndTime =
D_OUTPUT_FORMAT.format(endDT);
formattedDateTime = formattedDateTime + " - "
+ formattedEndTime;
LocalDateTime eldt = ((LocalDate) endDT)
.atTime(23, 59, 59);
endTimeAttr = DT_ATTR_FORMAT.format(
ZonedDateTime.of(eldt, timeZone));
} else if (endDT instanceof LocalTime) {
logger.warn
("Cannot combine a pure starting date ("
+ formattedDateTime
+ ") with an ending time.");
LocalDateTime eldt = dateTime.toLocalDate()
.atTime((LocalTime)endDT);
endTimeAttr = DT_ATTR_FORMAT.format(
ZonedDateTime.of(eldt, timeZone));
} else {
logger.error("Obtained endDate of unhandled type "
+ endDT.getClass().getName());
}
} else {
LocalDateTime eldt = date.atTime(23, 59, 59);
endTimeAttr = DT_ATTR_FORMAT.format(
ZonedDateTime.of(eldt, timeZone));
}
} catch (DateTimeParseException ex) {
formattedDateTime = "";
}
}
if (formattedDateTime.length() == 0) {
formatter = DateTimeFormatter.ISO_LOCAL_TIME;
try {
LocalTime time = LocalTime.parse(rawDateTime,
formatter);
formattedDateTime = T_OUTPUT_FORMAT.format(time);
TemporalAccessor endDT = processEndDate(link, linkAttr);
if (endDT != null) {
if (endDT instanceof LocalDateTime) {
logger.warn
("Cannot combine a pure starting time ("
+ formattedDateTime
+ ") with an ending date.");
} else if (endDT instanceof LocalDate) {
logger.warn
("Cannot combine a pure starting time ("
+ formattedDateTime
+ ") with an ending date.");
} else if (endDT instanceof LocalTime) {
String formattedEndTime
= T_OUTPUT_FORMAT.format(endDT);
formattedDateTime = formattedDateTime + "-"
+ formattedEndTime;
} else {
logger.error("Obtained endDate of unhandled type "
+ endDT.getClass().getName());
}
}
} catch (DateTimeParseException ex) {
formattedDateTime = "";
}
}
if (formattedDateTime.length() > 0) {
Document doc = link.getOwnerDocument();
Element span = doc.createElement("span");
NamedNodeMap attrs = link.getAttributes();
for (int i = 0; i < attrs.getLength(); i++) {
Attr attr2 = (Attr) doc.importNode(attrs.item(i), true);
if (!attr2.getName().equals("href")) {
span.getAttributes().setNamedItem(attr2);
}
}
span.setAttribute("class", "date");
if (startTimeAttr.length() > 0) {
span.setAttribute("startsAt", startTimeAttr);
}
if (endTimeAttr.length() > 0) {
span.setAttribute("endsAt", endTimeAttr);
}
span.appendChild(doc.createTextNode(formattedDateTime));
link.getParentNode().replaceChild(span, link);
return true;
} else {
logger.warn("Could not interpret date: " + url);
}
}
return false;
}
/**
* Checks to see if an endDate: linking element follows the dateElement
* and, if so, attempts to rewrite the element.
*
* @param dateElement an element containing a date: URL
* @param linkAttr name of the attribute containing the URL
* @return Date/Time associated with the endDate item if found, null if
* no endDate: link was found
*/
private TemporalAccessor processEndDate(
final Element dateElement,
final String linkAttr) {
TemporalAccessor result = null;
Node n = dateElement.getNextSibling();
while (n != null) {
if ("a".equals(n.getNodeName())) {
String url = ((Element) n).getAttribute(linkAttr);
if ("endDate:".equals(url) || "enddate:".equals(url)) {
break;
}
}
n = n.getNextSibling();
}
if (n == null) {
return null;
}
Element link = (Element) n;
String url = link.getAttribute(linkAttr);
String rawDateTime = link.getTextContent();
DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
try {
result = LocalDateTime.parse(rawDateTime,
formatter);
} catch (DateTimeParseException ex) {
result = null;
}
if (result == null) {
formatter = DateTimeFormatter.ISO_LOCAL_DATE;
try {
result = LocalDate.parse(rawDateTime,
formatter);
} catch (DateTimeParseException ex) {
result = null;
}
}
if (result == null) {
formatter = DateTimeFormatter.ISO_LOCAL_TIME;
try {
result = LocalTime.parse(rawDateTime, formatter);
} catch (DateTimeParseException ex) {
result = null;
}
}
if (result != null) {
Document doc = link.getOwnerDocument();
Node parent = link.getParentNode();
parent.removeChild(link);
} else {
logger.warn("Could not interpret end date: " + url);
}
return result;
}
}