/*
* Copyright 2001-2005 The Apache Software Foundation.
*
* 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 org.jvnet.maven.jellydoc;
import org.apache.maven.reporting.AbstractMavenReportRenderer;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.codehaus.doxia.sink.Sink;
import java.net.URL;
import java.util.List;
import java.util.Set;
import java.util.HashSet;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.ArrayList;
import java.io.StringWriter;
import net.java.textilej.parser.MarkupParser;
import net.java.textilej.parser.builder.HtmlDocumentBuilder;
import net.java.textilej.parser.markup.confluence.ConfluenceDialect;
/**
* Generates a Maven report from <tt>taglib.xml</tt>.
*
* @author Kohsuke Kawaguchi
*/
public class ReferenceRenderer extends AbstractMavenReportRenderer {
private final Document taglibXml;
private static final Comparator<Element> SORT_BY_NAME = new Comparator<Element>() {
public int compare(Element o1, Element o2) {
return o1.attributeValue("name").compareTo(o2.attributeValue("name"));
}
};
public ReferenceRenderer(Sink sink, URL taglibXml) throws DocumentException {
super(sink);
this.taglibXml = new SAXReader().read(taglibXml);
}
public String getTitle() {
return "Jelly Taglib references";
}
protected void renderBody() {
List<Element> libraries = sortByName((List<Element>) taglibXml.getRootElement().elements("library"));
paragraph("The following Jelly tag libraries are defined in this project.");
if(libraries.size()>1) {
startTable();
tableHeader(new String[]{"Namespace URI","Description"});
for (Element library : libraries) {
sink.tableRow();
sink.tableCell();
sink.rawText(String.format("<a href='#%s'>%s</a>",
library.attributeValue("prefix"),
library.attributeValue("uri")
));
sink.tableCell_();
docCell(library);
sink.tableRow_();
}
endTable();
}
for( Element library : libraries) {
String prefix = library.attributeValue("prefix");
anchor(prefix);
startSection(library.attributeValue("uri"));
doc(library);
paragraphHtml("This tag library is <a href='taglib-"+prefix+".xsd'>also available as an XML Schema</a>");
renderSummaryTable(library,prefix);
for( Element tag : sortByName((List<Element>)library.elements("tag")))
renderTagReference(prefix,tag);
endSection();
}
}
private void paragraphHtml(String rawText) {
sink.paragraph();
sink.rawText(rawText);
sink.paragraph_();
}
private void renderSummaryTable(Element library, String prefix) {
startTable();
tableHeader(new String[]{"Tag Name","Description"});
List<Element> tags = sortByName((List<Element>) library.elements("tag"));
for( Element tag : tags) {
sink.tableRow();
sink.tableCell();
String name = tag.attributeValue("name");
sink.rawText("<a href='#"+prefix+':'+ name+"'>"+name+"</a>");
sink.tableCell_();
docCell(tag);
sink.tableRow_();
}
endTable();
}
private List<Element> sortByName(List<Element> list) {
List<Element> tags = new ArrayList<Element>(list);
Collections.sort(tags,SORT_BY_NAME);
return tags;
}
/**
* Generates a documentation for one tag.
*/
private void renderTagReference(String taglibPrefix,Element tag) {
String name = tag.attributeValue("name");
anchor(taglibPrefix +':'+ name);
startSection(name);
doc(tag);
if(hasVisibleAttributes(tag)) {
startTable();
tableHeader(new String[]{"Attribute Name","Type","Description"});
for( Element att : sortByName((List<Element>)tag.elements("attribute")))
renderAttribute(att);
endTable();
}
// renders description of the body
if(tag.attributeValue("no-content","false").equals("true"))
paragraph("This tag does not accept any child elements/text.");
else {
Element body = tag.element("body");
if(body!=null) {
startSection("body");
doc(body);
endSection();
}
}
endSection();
}
private boolean hasVisibleAttributes(Element tag) {
for( Element att : (List<Element>)tag.elements("attribute")) {
String name = att.attributeValue("name");
if(!HIDDEN_ATTRIBUTES.contains(name))
return true;
}
return false;
}
private void anchor(String name) {
sink.anchor(name);
sink.anchor_();
}
/**
* Generates a documentation for one attribute.
*/
private void renderAttribute(Element att) {
String name = att.attributeValue("name");
if(HIDDEN_ATTRIBUTES.contains(name))
return; // defined in TagSupport.
sink.tableRow();
String suffix="";
if(att.attributeValue("use","optional").equals("required"))
suffix+=" (required)";
if(att.attributeValue("deprecated","false").equals("true"))
suffix+=" (deprecated)";
tableCell(name +suffix);
tableCell(att.attributeValue("type"));
docCell(att);
sink.tableRow_();
}
/**
* Renders the <doc> tag as raw text.
*/
private void doc(Element tag) {
sink.rawText(docXml(tag));
}
/**
* Renders the <doc> tag as table cell.
*/
private void docCell(Element tag) {
sink.tableCell();
sink.rawText(docXml(tag));
sink.tableCell_();
}
private String docXml(Element parent) {
Element doc = parent.element("doc");
// remove all javadoc tags that don't belong.
doc.content().removeAll(doc.elements("authortag"));
String xml = doc.getText();
StringWriter w = new StringWriter();
MarkupParser parser = new MarkupParser(new ConfluenceDialect());
HtmlDocumentBuilder builder = new HtmlDocumentBuilder(w) {
@Override
public void lineBreak() {
// no line break since IDEs usually don't wrap text.
}
};
builder.setEmitAsDocument(false);
parser.setBuilder(builder);
parser.parse(xml);
return w.toString();
}
private static final Set<String> HIDDEN_ATTRIBUTES = new HashSet<String>(Arrays.asList("escapeText","trim"));
}