/** * Copyright 2011-2012 Alexandre Dutra * * Licensed 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 fr.dutra.confluence2wordpress.core.toc; import static com.atlassian.confluence.content.render.xhtml.XhtmlConstants.*; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URLEncoder; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.xml.xpath.XPathExpressionException; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.atlassian.confluence.content.render.xhtml.HtmlElementIdCreator; import com.atlassian.confluence.renderer.PageContext; import fr.dutra.confluence2wordpress.core.author.Author; import fr.dutra.confluence2wordpress.util.StaxUtils; import fr.dutra.confluence2wordpress.util.XPathUtils; /** * @author Alexandre Dutra * */ public class TOCBuilder { private static final String AUTHOR = "author"; private static final String H1 = "h1"; private static final String H2 = "h2"; private static final String H3 = "h3"; private static final String H4 = "h4"; private static final String H5 = "h5"; private static final String H6 = "h6"; private static final String NAME = "name"; private static final String LOCAL_NAME_PARAMETER_XPATH = "*[local-name()='parameter']"; private static final String MAIN_XPATH = "//*[name()='h1' or name()='h2' or name()='h3' or name()='h4' or name()='h5' or name()='h6' or name()='ac:macro']"; private Heading current; private List<Heading> headings = new ArrayList<Heading>(); private Heading root; private PageContext pageContext; public List<Heading> getHeadings() { return headings; } public Heading buildTOC(String storage, PageContext pageContext) throws TOCException { this.pageContext = pageContext; this.root = new Heading(0); this.root.setLabel(pageContext.getPageTitle()); this.current = this.root; try { NodeList nodes = XPathUtils.evaluateXPathAsNodeList(StaxUtils.getReader(storage), MAIN_XPATH); for(int i = 0; i < nodes.getLength(); i++) { Node node = nodes.item(i); String name = node.getNodeName(); if(name.equals(H1)){ gotHeading(1, node); } else if(name.equals(H2)){ gotHeading(2, node); } else if(name.equals(H3)){ gotHeading(3, node); } else if(name.equals(H4)){ gotHeading(4, node); } else if(name.equals(H5)){ gotHeading(5, node); } else if(name.equals(H6)){ gotHeading(6, node); } else { String macroName = node.getAttributes().getNamedItemNS(CONFLUENCE_XHTML_NAMESPACE_URI, NAME).getTextContent(); if(AUTHOR.equals(macroName)){ gotAuthorMacro(node); } } } } catch (Exception e) { String message = "Could not parse storage"; if(e.getMessage() != null) { message += ": " + e.getMessage(); } if(e.getCause() != null && e.getCause().getMessage() != null) { message += ": " + e.getCause().getMessage(); } throw new TOCException(message, e); } return this.root; } private void gotHeading(int level, Node node) { Heading heading = new Heading(level); String body = node.getTextContent(); heading.setLabel(body); try { //AbstractPageLink.generateAnchor generates the final anchor //but escaped for XML storage. We need URLencode instead, //so we are basically doing exactly what generateAnchor does //less the XML escaping. //AbstractPageLink.generateAnchor(pageContext, body); String anchor = URLEncoder.encode(HtmlElementIdCreator.convertToIdHtml5(pageContext.getPageTitle() + "-" + body), "UTF-8"); heading.setAnchor(anchor); } catch (UnsupportedEncodingException e) { } Heading parent = findParentHeading(level); parent.addChild(heading); heading.setParent(parent); current = heading; } private Heading findParentHeading(int level) { Heading cur = current; while(cur != root && cur.getLevel() >= level) { cur = cur.getParent(); } // this was abandoned since we mainly use H3 as uppermost level headings // while(level - cur.getLevel() > 1) { // Heading h = new Heading(cur.getLevel() + 1); // h.setParent(cur); // cur.addChild(h); // cur = h; // } return cur; } private void gotAuthorMacro(Node authorNode) throws XPathExpressionException, MalformedURLException, URISyntaxException { Map<String, String> parameters = getAuthorMacroParameters(authorNode); Author author = Author.fromMacroParameters(parameters); current.setAuthor(author); } private Map<String, String> getAuthorMacroParameters(Node authorNode) throws XPathExpressionException { Map<String,String> parameters = new HashMap<String, String>(); NodeList parameterNodes = XPathUtils.evaluateXPathAsNodeList(authorNode, LOCAL_NAME_PARAMETER_XPATH); for(int i = 0; i < parameterNodes.getLength(); i++) { Node parameterNode = parameterNodes.item(i); String key = parameterNode.getAttributes().getNamedItemNS(CONFLUENCE_XHTML_NAMESPACE_URI, NAME).getTextContent(); String value = parameterNode.getTextContent(); parameters.put(key, value); } return parameters; } }