This page was generated using Webiyo. See its source code and unit tests.
|
org/webiyo/web/Link.javapackage org.webiyo.web; import org.w3c.dom.Document; import org.webiyo.xml.AttributeSink; import org.webiyo.xml.AttributeSource; import org.webiyo.xml.Xml; import org.webiyo.xml.dtds.Html; import org.xml.sax.InputSource; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.*; import java.util.regex.Pattern; public class Link implements AttributeSource { private static final Pattern ABSOLUTE_URL = Pattern.compile("[a-zA-Z][a-zA-Z0-9+\\.-]*:.*"); private final String protocol; private final String host; private final int port; private final List<String> pathItems; private Link(List<String> pathItems) { this(null, null, 0, pathItems); } public Link(String link) { if (ABSOLUTE_URL.matcher(link).matches()) { try { URL url = new URL(link); this.protocol = url.getProtocol(); this.host = url.getHost(); this.port = choosePort(protocol, url.getPort()); this.pathItems = makePathItems(url); } catch (MalformedURLException e) { throw new RuntimeException("can't parse URL: " + link, e); } } else { if (link.startsWith("/")) throw new IllegalArgumentException("site-relative url's not done yet"); this.protocol = null; this.host = null; this.port = 0; this.pathItems = makePathItems(link); } } public Link(URL url) { this.protocol = url.getProtocol(); this.host = url.getHost(); this.port = choosePort(protocol, url.getPort()); this.pathItems = makePathItems(url); } public Link(File file) { this(toURL(file)); } private Link(String protocol, String host, int port, List<String> pathItems) { this.protocol = protocol; this.host = host; this.port = port; this.pathItems = pathItems; } public Document fetchXml() throws IOException { if (!isAbsolute()) throw new UnsupportedOperationException("not an absolute link: " + toString()); return Xml.parse(new InputSource(toString())); } public boolean isAbsolute() { return protocol != null; } public void sendAttributes(AttributeSink sink) throws IOException { sink.putAttribute(Html.HREF.getName(), toString()); } public String getPath() { return makePath(pathItems); } public List<String> getPathItems() { return pathItems; } public Link getParent() { if (pathItems.size() == 0 || (pathItems.size() == 1 && pathItems.get(0).equals("/"))) return null; return new Link(protocol, host, port, pathItems.subList(0, pathItems.size() - 1)); } public boolean isDir() { return pathItems.size() == 0 || pathItems.get(pathItems.size() - 1).endsWith("/"); } public Link getDir() { return isDir() ? this : getParent(); } public Link findCommonAncestor(Link other) { if (!equals(protocol, other.protocol)) return null; if (!equals(host, other.host)) return null; if (!equals(port, other.port)) return null; List<String> result = new ArrayList<String>(); Iterator<String> it1 = getPathItems().iterator(); Iterator<String> it2 = other.getPathItems().iterator(); while (it1.hasNext() && it2.hasNext()) { String item1 = it1.next(); String item2 = it2.next(); if (!item1.equals(item2) || !item1.endsWith("/")) break; result.add(item1); } return new Link(protocol, host, port, result); } public Link makeRelativeTo(Link base) { Link ancestor = findCommonAncestor(base); if (ancestor == null) { throw new IllegalArgumentException("Base URL (" + base + ") and target URL (" + this + ") have no common ancestor"); } int backtrackCount = base.getDir().getPathSize() - ancestor.getPathSize(); List<String> result = new ArrayList<String>(); result.addAll(Collections.nCopies(backtrackCount, "../")); result.addAll(pathItems.subList(ancestor.getPathSize(), getPathSize())); return new Link(result); } public String getProtocol() { return protocol; } public String getHost() { return host; } public int getPort() { return port; } public String toString() { return host != null ? makeAbsoluteLink() : getPath(); } // end of public methods protected int getPathSize() { return getPathItems().size(); } // end of protected methods private String makeAbsoluteLink() { return protocol + "://" + host + (port == 80 ? "" : (":" + port)) + getPath(); } private static List<String> makePathItems(String path) { List<String> items; if (path.equals(".")) { items = Collections.emptyList(); } else { items = Collections.unmodifiableList(split(path)); } return items; } private static List<String> makePathItems(URL url) { String file = url.getFile(); return file.length() == 0 ? Arrays.asList("/") : makePathItems(file); } private static int choosePort(String protocol, int specifiedPort) { if (specifiedPort == -1 && protocol.equals("http")) { return 80; } else { return specifiedPort; } } private static boolean equals(Object first, Object second) { return first == null ? second == null : first.equals(second); } private static String toURL(File file) { try { return file.toURL().toString(); } catch (MalformedURLException e) { throw new IllegalArgumentException("can't convert file to URL: " + file, e); } } private static String makePath(List<String> items) { if (items.size() == 0) { return "."; } else { StringBuilder result = new StringBuilder(); for (String name : items) { result.append(name); } return result.toString(); } } private static List<String> split(String path) { List<String> result = new ArrayList<String>(); int here = 0; while (true) { int slashIndex = path.indexOf('/', here); if (slashIndex == -1) break; String item = path.substring(here, slashIndex + 1); if (item.equals("../")) { if (result.size() > 0 && !result.get(result.size() - 1).equals("../")) { result.remove(result.size() - 1); } else { result.add(item); } } else if (!item.equals("./")) { result.add(item); } here = slashIndex + 1; } String rest = path.substring(here); if (rest.equals(".")) { rest = ""; } if (rest.length() > 0) { result.add(rest); } return result; } } |