Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.hl7.fhir.utilities.xhtml.XhtmlComposer Maven / Gradle / Ivy
package org.hl7.fhir.utilities.xhtml;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.List;
import java.util.Set;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.xml.IXMLWriter;
import org.w3c.dom.Element;
public class XhtmlComposer {
protected static Set BLOCK_NAMES = Set.of("li", "ul", "ol", "tr", "td", "th", "div", "table");
public static final String XHTML_NS = "http://www.w3.org/1999/xhtml";
private boolean pretty;
private boolean xml;
private boolean autoLinks;
public static final boolean XML = true;
public static final boolean HTML = false;
public XhtmlComposer(boolean xml, boolean pretty) {
super();
this.pretty = pretty;
this.xml = xml;
}
public XhtmlComposer(boolean xml) {
super();
this.pretty = false;
this.xml = xml;
}
private Writer dst;
public String compose(XhtmlDocument doc) throws IOException {
if (!xml && !pretty) {
breakBlocksWithLines(doc);
}
StringWriter sdst = new StringWriter();
dst = sdst;
composeDoc(doc);
return sdst.toString();
}
private void breakBlocksWithLines(XhtmlNode node) {
if (node.hasChildren()) {
breakBlocksWithLines(node.getChildNodes());
}
}
private void breakBlocksWithLines(List list) {
for (int i = list.size() -1; i > 0; i--) {
XhtmlNode node = list.get(i);
if (node.getNodeType() == NodeType.Element && BLOCK_NAMES.contains(node.getName())) {
XhtmlNode prev = list.get(i-1);
if (prev.getNodeType() != NodeType.Text || prev.getContent() == null || !(prev.getContent().endsWith("\r") || prev.getValue().endsWith("\n"))) {
list.add(i, new XhtmlNode(NodeType.Text).setContent("\r\n"));
}
}
breakBlocksWithLines(node);
}
}
public String compose(XhtmlNode node) throws IOException {
if (!xml && !pretty) {
breakBlocksWithLines(node);
}
StringWriter sdst = new StringWriter();
dst = sdst;
writeNode("", node, false);
return sdst.toString();
}
public String compose(List nodes) throws IOException {
if (!xml && !pretty) {
breakBlocksWithLines(nodes);
}
StringWriter sdst = new StringWriter();
dst = sdst;
for (XhtmlNode node : nodes) {
writeNode("", node, false);
}
return sdst.toString();
}
public void compose(OutputStream stream, XhtmlDocument doc) throws IOException {
byte[] bom = new byte[] { (byte)0xEF, (byte)0xBB, (byte)0xBF };
stream.write(bom);
dst = new OutputStreamWriter(stream, "UTF-8");
composeDoc(doc);
dst.flush();
}
private void composeDoc(XhtmlDocument doc) throws IOException {
if (!xml) {
breakBlocksWithLines(doc);
}
// headers....
// dst.append("" + (pretty ? "\r\n" : ""));
for (XhtmlNode c : doc.getChildNodes()) {
writeNode(" ", c, false);
}
// dst.append("" + (pretty ? "\r\n" : ""));
}
private void writeNode(String indent, XhtmlNode node, boolean noPrettyOverride) throws IOException {
if (node.getNodeType() == NodeType.Comment) {
writeComment(indent, node, noPrettyOverride);
} else if (node.getNodeType() == NodeType.DocType) {
writeDocType(node);
} else if (node.getNodeType() == NodeType.Instruction) {
writeInstruction(node);
} else if (node.getNodeType() == NodeType.Element) {
writeElement(indent, node, noPrettyOverride);
} else if (node.getNodeType() == NodeType.Document) {
writeDocument(indent, node);
} else if (node.getNodeType() == NodeType.Text) {
writeText(node);
} else if (node.getNodeType() == null) {
throw new IOException("Null node type");
} else {
throw new IOException("Unknown node type: "+node.getNodeType().toString());
}
}
private boolean isValidUrlChar(char c) {
return Character.isAlphabetic(c) || Character.isDigit(c) || Utilities.existsInList(c, ';', ',', '/', '?', ':', '@', '&', '=', '+', '$', '-', '_', '.', '!', '~', '*', '\'', '(', ')');
}
private void writeText(XhtmlNode node) throws IOException {
String src = node.getContent();
int i = 0;
while (i < src.length()) {
int ci = src.codePointAt(i);
if (ci > 65535) {
dst.append("");
dst.append(Integer.toHexString(ci).toUpperCase());
dst.append(";");
i += Character.charCount(ci);
} else {
char c = (char) ci;
if (autoLinks && c == 'h' && Utilities.startsWithInList(src.substring(i), "http://", "https://")) {
int j = i;
while (i < src.length() && isValidUrlChar(src.charAt(i))) {
i++;
}
String url = src.substring(j, i);
if (url.endsWith(".") || url.endsWith(",")) {
i--;
url = url.substring(0, url.length()-1);
}
url = Utilities.escapeXml(url);
dst.append(""+ url +" ");
} else {
i++;
if (c == '&') {
dst.append("&");
} else if (c == '<') {
dst.append("<");
} else if (c == '>') {
dst.append(">");
} else if (xml) {
if (c == '"')
dst.append(""");
else
dst.append(c);
} else {
if (c == XhtmlNode.NBSP.charAt(0))
dst.append(" ");
else if (c == (char) 0xA7)
dst.append("§");
else if (c == (char) 169)
dst.append("©");
else if (c == (char) 8482)
dst.append("™");
else if (c == (char) 956)
dst.append("μ");
else if (c == (char) 174)
dst.append("®");
else
dst.append(c);
}
}
}
}
}
boolean isTwoCharUnicodeCodePoint(char c1, char c2) {
return false;
}
private void writeComment(String indent, XhtmlNode node, boolean noPrettyOverride) throws IOException {
dst.append(indent + "" + (pretty && !noPrettyOverride ? "\r\n" : ""));
}
private void writeDocType(XhtmlNode node) throws IOException {
dst.append("\r\n");
}
private void writeInstruction(XhtmlNode node) throws IOException {
dst.append("" + node.getContent() + "?>\r\n");
}
private String escapeHtml(String s) {
if (s == null || s.equals(""))
return null;
StringBuilder b = new StringBuilder();
for (char c : s.toCharArray())
if (c == '<')
b.append("<");
else if (c == '>')
b.append(">");
else if (c == '"')
b.append(""");
else if (c == '&')
b.append("&");
else
b.append(c);
return b.toString();
}
private String attributes(XhtmlNode node) {
StringBuilder s = new StringBuilder();
for (String n : node.getAttributes().keySet())
s.append(" " + n + "=\"" + escapeHtml(node.getAttributes().get(n)) + "\"");
return s.toString();
}
private void writeElement(String indent, XhtmlNode node, boolean noPrettyOverride) throws IOException {
if (!pretty || noPrettyOverride)
indent = "";
boolean concise = false;
if (!node.hasChildren()) {
if (this.xml) {
concise = true;
} else if (!(node.hasEmptyExpanded() && node.getEmptyExpanded()) &&
Utilities.existsInList(node.getName(), "area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "menuitem", "meta", "param", "source", "track", "wbr")) {
// In HTML5, only these elements can self-close
// https://developer.mozilla.org/en-US/docs/Glossary/Void_element
concise = true;
}
}
if (concise)
dst.append(indent + "<" + node.getName() + attributes(node) + "/>" + (pretty && !noPrettyOverride ? "\r\n" : ""));
else {
boolean act = node.allChildrenAreText();
if (act || !pretty || noPrettyOverride)
dst.append(indent + "<" + node.getName() + attributes(node)+">");
else
dst.append(indent + "<" + node.getName() + attributes(node) + ">\r\n");
if (node.getName() == "head" && node.getElement("meta") == null)
dst.append(indent + " " + (pretty && !noPrettyOverride ? "\r\n" : ""));
for (XhtmlNode c : node.getChildNodes())
writeNode(indent + " ", c, noPrettyOverride || node.isNoPretty());
if (act)
dst.append("" + node.getName() + ">" + (pretty && !noPrettyOverride ? "\r\n" : ""));
else if (node.getChildNodes().get(node.getChildNodes().size() - 1).getNodeType() == NodeType.Text)
dst.append((pretty && !noPrettyOverride ? "\r\n"+ indent : "") + "" + node.getName() + ">" + (pretty && !noPrettyOverride ? "\r\n" : ""));
else
dst.append(indent + "" + node.getName() + ">" + (pretty && !noPrettyOverride ? "\r\n" : ""));
}
}
private void writeDocument(String indent, XhtmlNode node) throws IOException {
indent = "";
for (XhtmlNode c : node.getChildNodes())
writeNode(indent, c, false);
}
public void compose(IXMLWriter xml, XhtmlNode node) throws IOException {
compose(xml, node, false);
}
public void compose(IXMLWriter xml, XhtmlNode node, boolean noPrettyOverride) throws IOException {
if (node.getNodeType() == NodeType.Comment)
xml.comment(node.getContent(), pretty && !noPrettyOverride);
else if (node.getNodeType() == NodeType.Element)
composeElement(xml, node, noPrettyOverride);
else if (node.getNodeType() == NodeType.Text)
xml.text(node.getContent());
else
throw new Error("Unhandled node type: "+node.getNodeType().toString());
}
private void composeElement(IXMLWriter xml, XhtmlNode node, boolean noPrettyOverride) throws IOException {
for (String n : node.getAttributes().keySet()) {
if (n.equals("xmlns"))
xml.setDefaultNamespace(node.getAttributes().get(n));
else if (n.startsWith("xmlns:"))
xml.namespace(n.substring(6), node.getAttributes().get(n));
else
xml.attribute(n, node.getAttributes().get(n));
}
xml.enter(XHTML_NS, node.getName());
for (XhtmlNode n : node.getChildNodes())
compose(xml, n, noPrettyOverride || node.isNoPretty());
xml.exit(XHTML_NS, node.getName());
}
public String composePlainText(XhtmlNode x) {
StringBuilder b = new StringBuilder();
composePlainText(x, b, false);
return b.toString().trim();
}
private boolean composePlainText(XhtmlNode x, StringBuilder b, boolean lastWS) {
if (x.getNodeType() == NodeType.Text) {
String s = x.getContent();
if (!lastWS & (s.startsWith(" ") || s.startsWith("\r") || s.startsWith("\n") || s.endsWith("\t"))) {
b.append(" ");
lastWS = true;
}
String st = s.trim().replace("\r", " ").replace("\n", " ").replace("\t", " ");
while (st.contains(" "))
st = st.replace(" ", " ");
if (!Utilities.noString(st)) {
b.append(st);
lastWS = false;
if (!lastWS & (s.endsWith(" ") || s.endsWith("\r") || s.endsWith("\n") || s.endsWith("\t"))) {
b.append(" ");
lastWS = true;
}
}
return lastWS;
} else if (x.getNodeType() == NodeType.Element) {
if (x.getName().equals("li")) {
b.append("* ");
lastWS = true;
}
for (XhtmlNode n : x.getChildNodes()) {
lastWS = composePlainText(n, b, lastWS);
}
if (x.getName().equals("p")) {
b.append("\r\n\r\n");
lastWS = true;
}
if (x.getName().equals("br") || x.getName().equals("li")) {
b.append("\r\n");
lastWS = true;
}
return lastWS;
} else
return lastWS;
}
public void compose(Element div, XhtmlNode x) {
for (XhtmlNode child : x.getChildNodes()) {
appendChild(div, child);
}
}
private void appendChild(Element e, XhtmlNode node) {
if (node.getNodeType() == NodeType.Comment)
e.appendChild(e.getOwnerDocument().createComment(node.getContent()));
else if (node.getNodeType() == NodeType.DocType)
throw new Error("not done yet");
else if (node.getNodeType() == NodeType.Instruction)
e.appendChild(e.getOwnerDocument().createProcessingInstruction("", node.getContent()));
else if (node.getNodeType() == NodeType.Text)
e.appendChild(e.getOwnerDocument().createTextNode(node.getContent()));
else if (node.getNodeType() == NodeType.Element) {
Element child = e.getOwnerDocument().createElementNS(XHTML_NS, node.getName());
e.appendChild(child);
for (String n : node.getAttributes().keySet()) {
child.setAttribute(n, node.getAttribute(n));
}
for (XhtmlNode c : node.getChildNodes()) {
appendChild(child, c);
}
} else
throw new Error("Unknown node type: "+node.getNodeType().toString());
}
public void compose(OutputStream stream, XhtmlNode x) throws IOException {
byte[] bom = new byte[] { (byte)0xEF, (byte)0xBB, (byte)0xBF };
stream.write(bom);
dst = new OutputStreamWriter(stream, "UTF-8");
dst.append(" \r\n");
writeNode("", x, false);
dst.append("\r\n");
dst.flush();
}
public void composeDocument(FileOutputStream f, XhtmlNode xhtml) throws IOException {
byte[] bom = new byte[] { (byte)0xEF, (byte)0xBB, (byte)0xBF };
f.write(bom);
dst = new OutputStreamWriter(f, "UTF-8");
writeNode("", xhtml, false);
dst.flush();
dst.close();
}
public String composeEx(XhtmlNode node) {
try {
return compose(node);
} catch (IOException e) {
throw new Error(e);
}
}
public String compose(XhtmlNodeList nodes) throws IOException {
StringWriter sdst = new StringWriter();
dst = sdst;
for (XhtmlNode node : nodes) {
writeNode("", node, false);
}
return sdst.toString();
}
public boolean isAutoLinks() {
return autoLinks;
}
public XhtmlComposer setAutoLinks(boolean autoLinks) {
this.autoLinks = autoLinks;
return this;
}
}