Author: aadamchik Date: Mon Dec 18 02:21:25 2006 New Revision: 488198 URL: http://svn.apache.org/viewvc?view=rev&rev=488198 Log: making a self-contained Confluence doc plugin
Added: incubator/cayenne/main/trunk/other/confluence-maven-plugin/.classpath incubator/cayenne/main/trunk/other/confluence-maven-plugin/.project incubator/cayenne/main/trunk/other/confluence-maven-plugin/src/main/java/org/apache/cayenne/other/plugin/confluence/DocGenerator.java incubator/cayenne/main/trunk/other/confluence-maven-plugin/src/main/java/org/apache/cayenne/other/plugin/confluence/DocPage.java incubator/cayenne/main/trunk/other/confluence-maven-plugin/src/main/java/org/apache/cayenne/other/plugin/confluence/DocPageRenderer.java incubator/cayenne/main/trunk/other/confluence-maven-plugin/src/main/resources/ incubator/cayenne/main/trunk/other/confluence-maven-plugin/src/main/resources/doctemplates/ incubator/cayenne/main/trunk/other/confluence-maven-plugin/src/main/resources/doctemplates/default.vm Modified: incubator/cayenne/main/trunk/other/confluence-maven-plugin/pom.xml incubator/cayenne/main/trunk/other/confluence-maven-plugin/src/main/java/org/apache/cayenne/other/plugin/confluence/ConfluenceExportMojo.java Added: incubator/cayenne/main/trunk/other/confluence-maven-plugin/.classpath URL: http://svn.apache.org/viewvc/incubator/cayenne/main/trunk/other/confluence-maven-plugin/.classpath?view=auto&rev=488198 ============================================================================== --- incubator/cayenne/main/trunk/other/confluence-maven-plugin/.classpath (added) +++ incubator/cayenne/main/trunk/other/confluence-maven-plugin/.classpath Mon Dec 18 02:21:25 2006 @@ -0,0 +1,7 @@ +<classpath> + <classpathentry kind="src" path="src/main/java" /> + <classpathentry kind="src" path="src/test/java" output="target/test-classes" /> + <classpathentry kind="output" path="target/classes" /> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER" /> + <classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER" /> +</classpath> \ No newline at end of file Added: incubator/cayenne/main/trunk/other/confluence-maven-plugin/.project URL: http://svn.apache.org/viewvc/incubator/cayenne/main/trunk/other/confluence-maven-plugin/.project?view=auto&rev=488198 ============================================================================== --- incubator/cayenne/main/trunk/other/confluence-maven-plugin/.project (added) +++ incubator/cayenne/main/trunk/other/confluence-maven-plugin/.project Mon Dec 18 02:21:25 2006 @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>confluence-maven-plugin</name> + <comment>Apache Cayenne is a powerful, full-featured Java Object + Relational Mapping framework currently in incubation.</comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.maven.ide.eclipse.maven2Builder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.jdt.core.javanature</nature> + <nature>org.maven.ide.eclipse.maven2Nature</nature> + </natures> +</projectDescription> Modified: incubator/cayenne/main/trunk/other/confluence-maven-plugin/pom.xml URL: http://svn.apache.org/viewvc/incubator/cayenne/main/trunk/other/confluence-maven-plugin/pom.xml?view=diff&rev=488198&r1=488197&r2=488198 ============================================================================== --- incubator/cayenne/main/trunk/other/confluence-maven-plugin/pom.xml (original) +++ incubator/cayenne/main/trunk/other/confluence-maven-plugin/pom.xml Mon Dec 18 02:21:25 2006 @@ -1,18 +1,18 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - Copyright 2006 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. + Copyright 2006 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. --> <project> <parent> @@ -27,23 +27,34 @@ <name>Cayenne Maven2 Plugins :: Confluence</name> <dependencies> <dependency> - <groupId>org.apache.cayenne.other</groupId> - <artifactId>cayenne-build-tools</artifactId> - <version>${pom.version}</version> - </dependency> - <dependency> <groupId>org.apache.maven</groupId> <artifactId>maven-archiver</artifactId> <version>2.2</version> </dependency> <dependency> - <groupId>commons-logging</groupId> - <artifactId>commons-logging</artifactId> - </dependency> - <dependency> <groupId>org.apache.maven</groupId> <artifactId>maven-plugin-api</artifactId> <version>2.0.4</version> + </dependency> + <dependency> + <groupId>com.atlassian.confluence</groupId> + <artifactId>confluence-soap</artifactId> + <version>2.0</version> + </dependency> + <dependency> + <groupId>axis</groupId> + <artifactId>axis</artifactId> + <version>1.4</version> + </dependency> + <dependency> + <groupId>velocity</groupId> + <artifactId>velocity</artifactId> + <version>1.4</version> + </dependency> + <dependency> + <groupId>org.apache.cayenne.core</groupId> + <artifactId>cayenne-jdk1.4</artifactId> + <version>3.0-incubating-SNAPSHOT</version> </dependency> </dependencies> </project> Modified: incubator/cayenne/main/trunk/other/confluence-maven-plugin/src/main/java/org/apache/cayenne/other/plugin/confluence/ConfluenceExportMojo.java URL: http://svn.apache.org/viewvc/incubator/cayenne/main/trunk/other/confluence-maven-plugin/src/main/java/org/apache/cayenne/other/plugin/confluence/ConfluenceExportMojo.java?view=diff&rev=488198&r1=488197&r2=488198 ============================================================================== --- incubator/cayenne/main/trunk/other/confluence-maven-plugin/src/main/java/org/apache/cayenne/other/plugin/confluence/ConfluenceExportMojo.java (original) +++ incubator/cayenne/main/trunk/other/confluence-maven-plugin/src/main/java/org/apache/cayenne/other/plugin/confluence/ConfluenceExportMojo.java Mon Dec 18 02:21:25 2006 @@ -20,49 +20,46 @@ import java.net.URL; -import org.apache.cayenne.tools.ant.docgen.DocGenerator; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; /** - * A goal to export confluence documentation + * A goal to export Confluence documentation. * * @author <a href="mailto:[EMAIL PROTECTED]">Bill Dudney</a> - * @version $Id$ - * + * * @goal export */ public class ConfluenceExportMojo extends AbstractMojo { /** * The directory to put the exported documentation into * - * @parameter expression="${project.build.directory}/${export.spaceName}" + * @parameter expression="${project.build.directory}/${confluence.spaceName}" */ private String outputDirectory; /** - * The velocity template to use - defaults to - * loading 'doctemplates/default.vm' from the classpath + * The velocity template to use - defaults to loading + * 'doctemplates/default.vm' from the classpath * * @parameter */ private String velocityTemplate; /** - * The root url to the confluence instance - * For example in Cayenne: http://cwiki.apache.org/confluence/ - * is the root URL and the space name is CAYDOC + * The root url to the Confluence instance For example in Cayenne: + * http://cwiki.apache.org/confluence/ is the base URL. * - * @parameter expression="${export.rootURL}" + * @parameter expression="${confluence.baseUrl}" * @required */ - private URL rootURL; + private URL baseUrl; /** * The name of the confluence space to export * - * @parameter expression="${export.spaceName}" + * @parameter expression="${confluence.spaceName}" * @required */ private String spaceName; @@ -70,40 +67,41 @@ /** * The page in the space to start with * - * @parameter expression="${export.startPage}" + * @parameter expression="${confluence.startPage}" * @required */ private String startPage; /** - * The username to log in as - define it on the commandline via the - * -Dconfluence.userName=user_name option - * or set the userName and password in your ~/.m2/settings.xml file - * like this; + * The username to log in as - define it on the commandline via the + * -Dconfluence.userName=user_name option or set the userName and password + * in your ~/.m2/settings.xml file like this; + * * <pre> - <profiles> - <profile> - <properties> - <property> - <name>confluence.userName</name> - <value>user name</value> - </property> - <property> - <name>confluence.password</name> - <value>password</value> - </property> - </properties> - <id>confluence</id> - </profile> - </profiles> - * </pre> + * <profiles> + * <profile> + * <properties> + * <property> + * <name>confluence.userName</name> + * <value>user name</value> + * </property> + * <property> + * <name>confluence.password</name> + * <value>password</value> + * </property> + * </properties> + * <id>confluence</id> + * </profile> + * </profiles> + * </pre> + * * @parameter expression="${confluence.userName}" * @required */ private String userName; /** - * The username to log in as - define it on the commandline via the + * The username to log in as - define it on the commandline via the * -Dconfluence.password=password option * * @parameter expression="${confluence.password}" @@ -112,28 +110,25 @@ private String password; /** - * where the actual work takes place + * Worker method. */ public void execute() throws MojoExecutionException, MojoFailureException { - getLog().info("Exporting space '" + spaceName + "' to " + outputDirectory); + getLog().info( + "Exporting space '" + spaceName + "' to " + outputDirectory); - try { - DocGenerator generator = new DocGenerator( - rootURL.toString(), - spaceName, - outputDirectory, - startPage, - userName, - password, - velocityTemplate); - - getLog().info("Confluence base URL '" + generator.getBaseUrl() + "'"); - generator.generateDocs(); - } - catch (Exception e) { - e.printStackTrace(); - throw new MojoExecutionException("Failed to export: " + spaceName + " from: " + rootURL, e); - } + try { + DocGenerator generator = new DocGenerator(baseUrl.toString(), + spaceName, outputDirectory, startPage, userName, password, + velocityTemplate); + + getLog().info( + "Confluence base URL '" + generator.getBaseUrl() + "'"); + generator.generateDocs(); + } catch (Exception e) { + e.printStackTrace(); + throw new MojoExecutionException("Failed to export: " + spaceName + + " from: " + baseUrl, e); + } } public String getOutputDirectory() { @@ -160,12 +155,12 @@ this.password = password; } - public URL getRootURL() { - return rootURL; + public URL getBaseUrl() { + return baseUrl; } - public void setRootURL(URL rootURL) { - this.rootURL = rootURL; + public void setBaseUrl(URL rootURL) { + this.baseUrl = rootURL; } public String getSpaceName() { @@ -191,5 +186,4 @@ public void setUserName(String userName) { this.userName = userName; } - } Added: incubator/cayenne/main/trunk/other/confluence-maven-plugin/src/main/java/org/apache/cayenne/other/plugin/confluence/DocGenerator.java URL: http://svn.apache.org/viewvc/incubator/cayenne/main/trunk/other/confluence-maven-plugin/src/main/java/org/apache/cayenne/other/plugin/confluence/DocGenerator.java?view=auto&rev=488198 ============================================================================== --- incubator/cayenne/main/trunk/other/confluence-maven-plugin/src/main/java/org/apache/cayenne/other/plugin/confluence/DocGenerator.java (added) +++ incubator/cayenne/main/trunk/other/confluence-maven-plugin/src/main/java/org/apache/cayenne/other/plugin/confluence/DocGenerator.java Mon Dec 18 02:21:25 2006 @@ -0,0 +1,207 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.cayenne.other.plugin.confluence; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.InputStream; +import java.net.URL; +import java.util.Iterator; + +import org.objectstyle.confluence.rpc.soap_axis.confluenceservice_v1.ConfluenceSoapService; +import org.objectstyle.confluence.rpc.soap_axis.confluenceservice_v1.ConfluenceSoapServiceProxy; + +import com.atlassian.confluence.rpc.soap.beans.RemoteAttachment; +import com.atlassian.confluence.rpc.soap.beans.RemotePage; +import com.atlassian.confluence.rpc.soap.beans.RemotePageSummary; + +/** + * Generates standalone documentation for Cayenne based on Confluence content. + * + * @author Cris Daniluk + */ +public class DocGenerator { + private static final String DEFAULT_TEMPLATE = "doctemplates/default.vm"; + + private static final String ENDPOINT_SUFFIX = "/rpc/soap-axis/confluenceservice-v1"; + + private String baseUrl; + + private String spaceKey; + + private String docBase; + + private String startPage; + + private String token; + + private ConfluenceSoapService service; + + private String username; + + private String password; + + private String template; + + private DocPageRenderer parser; + + public DocGenerator(String baseUrl, String spaceKey, String docBase, + String startPage, String username, String password, String template) { + + ConfluenceSoapServiceProxy service = new ConfluenceSoapServiceProxy(); + + // derive service URL from base URL + if (baseUrl != null) { + if (baseUrl.endsWith("/")) { + baseUrl = baseUrl.substring(0, baseUrl.length() - 1); + } + + String endpoint = baseUrl + ENDPOINT_SUFFIX; + service.setEndpoint(endpoint); + } + // service base URL from service default URL + else if (service.getEndpoint().endsWith(ENDPOINT_SUFFIX)) { + String endpoint = service.getEndpoint(); + baseUrl = endpoint.substring(0, endpoint.length() + - ENDPOINT_SUFFIX.length()); + } else { + throw new IllegalArgumentException( + "Null base url and invalid service URL"); + } + + this.baseUrl = baseUrl; + this.service = service; + this.spaceKey = spaceKey; + this.docBase = docBase; + this.startPage = startPage; + this.username = username; + this.password = password; + + if (template == null) { + this.template = DEFAULT_TEMPLATE; + } else { + this.template = template; + } + } + + public void generateDocs() throws Exception { + + login(); + + // only works for adminstrators + // String url = service.exportSite(token, true); + + // URL foo = new URL(url); + createPath(docBase); + + // Build a page hierarchy first.. + DocPage page = getPage(null, startPage); + + iterateChildren(page); + + // Now render the content nodes.. + renderPage(page, docBase); + + } + + protected void iterateChildren(DocPage parent) throws Exception { + + RemotePageSummary[] children = getChildren(parent); + for (int i = 0; i < children.length; i++) { + + DocPage child = getPage(parent, children[i].getTitle()); + parent.addChild(child); + iterateChildren(child); + + } + + } + + protected void renderPage(DocPage page, String basePath) throws Exception { + String currentPath = basePath + "/" + page.getTitle(); + + createPath(currentPath); + + FileWriter fw = new FileWriter(currentPath + "/index.html"); + parser.render(page, fw); + fw.close(); + + writeAttachments(currentPath, page); + + for (Iterator childIter = page.getChildren().iterator(); childIter + .hasNext();) { + renderPage((DocPage) childIter.next(), currentPath); + } + + } + + protected RemotePageSummary[] getChildren(DocPage page) throws Exception { + return service.getChildren(token, page.getId()); + } + + protected void writeAttachments(String basePath, DocPage page) + throws Exception { + RemoteAttachment[] attachments = service.getAttachments(token, page + .getId()); + + for (int j = 0; j < attachments.length; j++) { + + FileOutputStream fos = null; + try { + fos = new FileOutputStream(basePath + "/" + + attachments[j].getFileName()); + + fos.write(getAttachmentData(page, attachments[j])); + } finally { + fos.close(); + } + + } + } + + protected byte[] getAttachmentData(DocPage page, RemoteAttachment attachment) + throws Exception { + return service.getAttachmentData(token, page.getId(), attachment + .getFileName(), 0); + } + + protected void login() throws Exception { + token = service.login(username, password); + parser = new DocPageRenderer(service, baseUrl, token, spaceKey, + template); + } + + protected DocPage getPage(DocPage parentPage, String pageTitle) + throws Exception { + RemotePage page = service.getPage(token, spaceKey, pageTitle); + return new DocPage(parentPage, page.getTitle(), page.getId(), page + .getContent()); + } + + protected void createPath(String path) { + new File(path).mkdirs(); + + } + + public String getBaseUrl() { + return baseUrl; + } +} Added: incubator/cayenne/main/trunk/other/confluence-maven-plugin/src/main/java/org/apache/cayenne/other/plugin/confluence/DocPage.java URL: http://svn.apache.org/viewvc/incubator/cayenne/main/trunk/other/confluence-maven-plugin/src/main/java/org/apache/cayenne/other/plugin/confluence/DocPage.java?view=auto&rev=488198 ============================================================================== --- incubator/cayenne/main/trunk/other/confluence-maven-plugin/src/main/java/org/apache/cayenne/other/plugin/confluence/DocPage.java (added) +++ incubator/cayenne/main/trunk/other/confluence-maven-plugin/src/main/java/org/apache/cayenne/other/plugin/confluence/DocPage.java Mon Dec 18 02:21:25 2006 @@ -0,0 +1,209 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.cayenne.other.plugin.confluence; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Represents a TOC entry. This has a lot of tree-like search functions, but I + * did not find a tree implementation that I thought was worth using for this. + * + * @author Cris Daniluk + */ +public class DocPage { + + private static final Pattern orderingPattern = Pattern + .compile("\n?\\{excerpt(.*?)\\}"); + + private static final Map titleMap = new HashMap(); + + private String title = null; + + private long id; + + private String rawContent; + + private DocPage parentRef; + + private List children = null; + + private List ordering; + + private int depth; + + public static DocPage getPageByTitle(String title) { + return (DocPage) titleMap.get(title); + } + + public DocPage(DocPage parentRef, String title, long id, String rawContent) { + this.parentRef = parentRef; + this.title = title; + this.id = id; + this.rawContent = rawContent; + + titleMap.put(title, this); + + // Look for a page ordering... + Matcher matcher = orderingPattern.matcher(rawContent); + if (matcher.find()) { + int regionStart = matcher.end() + 1; + matcher.find(); + + ordering = Arrays.asList(rawContent.substring(regionStart, + matcher.start()).split("\n")); + + } + + if (parentRef == null) { + depth = 1; + } else if (ordering == null && parentRef.ordering != null) { + ordering = parentRef.ordering; + } + + children = new ArrayList(); + } + + public void addChild(DocPage child) { + child.depth = depth + 1; + children.add(child); + } + + public String getTitle() { + return title; + } + + public int getDepth() { + return depth; + } + + public List getChildren() { + // If an ordering is present, sort by it... + if (ordering != null) { + + Collections.sort(children, new Comparator() { + + public int compare(Object arg0, Object arg1) { + // we're the only one who modified this list, so we can + // trust it + // (and live with the consequences if we're wrong) + DocPage child0 = (DocPage) arg0; + DocPage child1 = (DocPage) arg1; + + if (child0.getTitle().equals(child1.getTitle())) { + return 0; + } else if (ordering.indexOf(child1.getTitle()) == -1) { + // if its not on the list, float it to the bottom + return 1; + } + if (ordering.indexOf(child0.getTitle()) < ordering + .indexOf(child1.getTitle())) { + return -1; + } else { + return 1; + } + } + + }); + } else { + + // no beanutils, so do this manually... + Collections.sort(children, new Comparator() { + + public int compare(Object arg0, Object arg1) { + DocPage child0 = (DocPage) arg0; + DocPage child1 = (DocPage) arg1; + return (child0.getTitle().compareTo(child1.getTitle())); + } + + }); + } + return Collections.unmodifiableList(children); + } + + public long getId() { + return id; + } + + public String getRawContent() { + return rawContent; + } + + public DocPage getParentRef() { + return parentRef; + } + + public DocPage findPageId(long searchId) { + + return findChild(this, searchId); + } + + public boolean hasDescendent(DocPage page) { + if (findChild(this, page.getId()) != null) { + return true; + } + return false; + } + + /** + * Get the "module" root. This returns the next-to-top element in the tree. + */ + public DocPage getRoot() { + DocPage base = this; + while (base.parentRef != null && base.parentRef.parentRef != null) { + base = base.parentRef; + } + return base; + } + + private DocPage findChild(DocPage page, long searchId) { + + if (page.getId() == searchId) { + return page; + } + Iterator pageIter = page.getChildren().iterator(); + while (pageIter.hasNext()) { + DocPage match = findChild((DocPage) pageIter.next(), searchId); + if (match != null) { + return match; + } + } + return null; + } + + public String getLinkPath() { + return buildLinkPath(this); + } + + private String buildLinkPath(DocPage page) { + if (page.getParentRef() == null) { + return page.getTitle(); + } + return buildLinkPath(page.getParentRef()) + "/" + page.getTitle(); + } +} Added: incubator/cayenne/main/trunk/other/confluence-maven-plugin/src/main/java/org/apache/cayenne/other/plugin/confluence/DocPageRenderer.java URL: http://svn.apache.org/viewvc/incubator/cayenne/main/trunk/other/confluence-maven-plugin/src/main/java/org/apache/cayenne/other/plugin/confluence/DocPageRenderer.java?view=auto&rev=488198 ============================================================================== --- incubator/cayenne/main/trunk/other/confluence-maven-plugin/src/main/java/org/apache/cayenne/other/plugin/confluence/DocPageRenderer.java (added) +++ incubator/cayenne/main/trunk/other/confluence-maven-plugin/src/main/java/org/apache/cayenne/other/plugin/confluence/DocPageRenderer.java Mon Dec 18 02:21:25 2006 @@ -0,0 +1,245 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.cayenne.other.plugin.confluence; + +import java.io.Writer; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.cayenne.CayenneRuntimeException; +import org.apache.cayenne.gen.ClassGeneratorResourceLoader; +import org.apache.velocity.Template; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.VelocityEngine; +import org.apache.velocity.runtime.RuntimeConstants; +import org.apache.velocity.runtime.log.NullLogSystem; +import org.objectstyle.confluence.rpc.soap_axis.confluenceservice_v1.ConfluenceSoapService; + +/** + * Extracts embedded links from Confluence documentation and converts them to + * local fs references + * + * @author Cris Daniluk + */ +public class DocPageRenderer { + + private static final String URL_PREFIX = "/confluence"; + + /** + * Only attachments within the page are supported right now. This could + * easily be adjusted to find attachments in external documents if + * necessary. + */ + private static final Pattern attachmentPattern = Pattern + .compile("(href|src)=\"" + URL_PREFIX + + "/download/attachments/(.*?)/(.*?)\""); + + /** + * When browsing the local filesystem, browsers like %20 (hex encoded) + * instead of + (legacy HTTP 0.9) for spaces. + */ + private static final Pattern spaceEncoderPattern = Pattern + .compile("href=\"(?!http://).*?\\+.*?\""); + + /** + * Not all images are supported - only the ones referenced by current docs. + */ + private static final Pattern confluenceImagePattern = Pattern + .compile("src=\"" + URL_PREFIX + "/images/icons/(.*?)\""); + + /** + * Take any confluence links to non-doc content and add the url + */ + private Pattern confluenceLinkPattern = Pattern.compile("href=\"(" + + URL_PREFIX + "/display/.*?)\""); + + private Pattern embeddedLinkPattern; + + private ConfluenceSoapService service; + + private String token; + + private String spaceKey; + + private String baseUrl; + + private VelocityContext velCtxt; + + private Template pageTemplate; + + public DocPageRenderer(ConfluenceSoapService service, String baseUrl, + String token, String spaceKey, String template) throws Exception { + + // Note that these regexps have a fairly narrow capture - since the HTML + // is + // machine-generated, + // we're kind of assuming it is well-formed + embeddedLinkPattern = Pattern.compile("href=\"" + URL_PREFIX + + "/display/" + spaceKey + "/(.*?)\""); + + this.service = service; + this.baseUrl = baseUrl; + this.token = token; + this.spaceKey = spaceKey; + + velCtxt = new VelocityContext(); + + initializeClassTemplate(template); + } + + private void initializeClassTemplate(String template) throws Exception { + VelocityEngine velocityEngine = new VelocityEngine(); + try { + + // use ClasspathResourceLoader for velocity templates lookup + // if Cayenne URL is not null, load resource from this URL + Properties props = new Properties(); + + // null logger that will prevent velocity.log from being generated + props.put(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, + NullLogSystem.class.getName()); + + props.put("resource.loader", "cayenne"); + + props.put("cayenne.resource.loader.class", + ClassGeneratorResourceLoader.class.getName()); + + velocityEngine.init(props); + } catch (Exception ex) { + throw new CayenneRuntimeException("Can't initialize Velocity", ex); + } + + pageTemplate = velocityEngine.getTemplate(template); + } + + public void render(DocPage page, Writer out) throws Exception { + + // Add the TOC, unless this is the top-level page + StringBuffer toc = new StringBuffer(); + if (page.getParentRef() != null) { + toc.append("<div id=\"cayenne_toc\">\n"); + + DocPage root = page.getRoot(); + + iterateChildren(toc, page, root); + toc.append("</div>\n"); + } + + // Figure out the level of nesting for relative links + String basePath = ""; + for (int i = 1; i <= page.getDepth(); i++) { + basePath += "../"; + } + + String renderedContent = null; + try { + renderedContent = service.renderContent(token, spaceKey, page + .getId(), page.getRawContent(), new HashMap(Collections + .singletonMap("style", "clean"))); + } catch (Throwable t) { + // could have hit a DOS prevention bit so + // sleep for 250ms and try again + Thread.sleep(250); + renderedContent = service.renderContent(token, spaceKey, page + .getId(), page.getRawContent(), new HashMap(Collections + .singletonMap("style", "clean"))); + } + // Replace cross-doc links + Matcher linkMatcher = embeddedLinkPattern.matcher(renderedContent); + StringBuffer replacementBuffer = new StringBuffer(); + while (linkMatcher.find()) { + DocPage destPage = DocPage.getPageByTitle(linkMatcher.group(1) + .replace('+', ' ')); + + // If we don't understand the link, just leave it alone to be safe + if (destPage == null) { + continue; + } + linkMatcher.appendReplacement(replacementBuffer, "href=\"" + + basePath + destPage.getLinkPath() + "/index.html\""); + } + linkMatcher.appendTail(replacementBuffer); + + renderedContent = replacementBuffer.toString(); + + // renderedContent = + // embeddedLinkPattern.matcher(renderedContent).replaceAll("href=\"$1/index.html\""); + + // Replace attachment links + renderedContent = attachmentPattern.matcher(renderedContent) + .replaceAll("$1=\"$3\""); + + // Convert confluence images to relative links + renderedContent = confluenceImagePattern.matcher(renderedContent) + .replaceAll("src=\"" + basePath + "images/$1\""); + + // Replace wiki links + renderedContent = confluenceLinkPattern.matcher(renderedContent) + .replaceAll("href=\"" + baseUrl + "$1\""); + + // Convert local links with + to %20 to make browsers happy (wtf?) + Matcher matcher = spaceEncoderPattern.matcher(renderedContent); + + replacementBuffer = new StringBuffer(); + while (matcher.find()) { + matcher.appendReplacement(replacementBuffer, matcher.group(0) + .replace("+", "%20")); + } + matcher.appendTail(replacementBuffer); + + renderedContent = replacementBuffer.toString(); + + velCtxt.put("page", page); + velCtxt.put("basePath", basePath); + velCtxt.put("pageContent", toc.toString() + renderedContent); + + pageTemplate.merge(velCtxt, out); + + } + + private void iterateChildren(StringBuffer toc, DocPage currentPage, + DocPage basePage) { + toc.append("<ul>\n"); + for (Iterator baseIter = basePage.getChildren().iterator(); baseIter + .hasNext();) { + + DocPage child = (DocPage) baseIter.next(); + + toc.append("<li>").append("<a href=\""); + for (int i = 1; i <= currentPage.getDepth(); i++) { + toc.append("../"); + } + toc.append(child.getLinkPath()).append("/index.html\">"); + toc.append(child.getTitle()).append("</a>"); + if (child.hasDescendent(currentPage)) { + // render children + iterateChildren(toc, currentPage, child); + } + + toc.append("</li>\n"); + } + + toc.append("</ul>\n"); + } +} Added: incubator/cayenne/main/trunk/other/confluence-maven-plugin/src/main/resources/doctemplates/default.vm URL: http://svn.apache.org/viewvc/incubator/cayenne/main/trunk/other/confluence-maven-plugin/src/main/resources/doctemplates/default.vm?view=auto&rev=488198 ============================================================================== --- incubator/cayenne/main/trunk/other/confluence-maven-plugin/src/main/resources/doctemplates/default.vm (added) +++ incubator/cayenne/main/trunk/other/confluence-maven-plugin/src/main/resources/doctemplates/default.vm Mon Dec 18 02:21:25 2006 @@ -0,0 +1,40 @@ +<!-- + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. +--> +<html> + <head> + <title>Apache Cayenne Documentation - ${page.title}</title> + <style type="text/css">@import "${basePath}style.css";</style> + </head> +<body> + <div class="header"> + <div style="float: left;"><a href="http://incubator.apache.org/cayenne/"><img src="${basePath}images/logo.gif" align="absmiddle" border="0"></a></div> + <span class="logoSpaceLink"><a href="${basePath}index.html">Cayenne User Documentation</a></span><br /> + <span class="pagetitle">${page.title}</span> + </div> +${pageContent} +</div> + <div class="clearer">.</div> + <div style="height: 12px; background-image: url('${basePath}images/border_bottom.gif'); background-repeat: repeat-x;"></div> + + <div class="smalltext copyright"> + Copyright ©2001-2006 Apache Software Foundation + </div> + +</body> +</html>