svn commit: r634466 - in /lenya/trunk/src/modules-core/linking: config/sitemap/ java/src/org/apache/lenya/cms/cocoon/transformation/ java/src/org/apache/lenya/cms/linking/

[email protected]
Newsgroups gmane.comp.cms.lenya.cvs
Message-ID <[email protected]>
Author: andreas
Date: Thu Mar  6 15:55:37 2008
New Revision: 634466

URL: http://svn.apache.org/viewvc?rev=634466&view=rev
Log:
Refactoring: Extract uuid2url functionality from transformer to rewriter. Add message attribute for all link transformers.

Added:
    lenya/trunk/src/modules-core/linking/java/src/org/apache/lenya/cms/linking/UuidToUrlRewriter.java
Modified:
    lenya/trunk/src/modules-core/linking/config/sitemap/transformers.xmap
    lenya/trunk/src/modules-core/linking/java/src/org/apache/lenya/cms/cocoon/transformation/AbstractLinkTransformer.java
    lenya/trunk/src/modules-core/linking/java/src/org/apache/lenya/cms/cocoon/transformation/UuidToUrlTransformer.java

Modified: lenya/trunk/src/modules-core/linking/config/sitemap/transformers.xmap
URL: http://svn.apache.org/viewvc/lenya/trunk/src/modules-core/linking/config/sitemap/transformers.xmap?rev=634466&r1=634465&r2=634466&view=diff
==============================================================================
--- lenya/trunk/src/modules-core/linking/config/sitemap/transformers.xmap (original)
+++ lenya/trunk/src/modules-core/linking/config/sitemap/transformers.xmap Thu Mar  6 15:55:37 2008
@@ -29,6 +29,7 @@
     <transform namespace="http://www.w3.org/1999/xhtml" element="form" attribute="action"/>
     <transform namespace="http://www.w3.org/1999/xhtml" element="script" attribute="src"/>
     <transform namespace="http://www.w3.org/1999/xhtml" element="input" attribute="src"/>
+    <markBrokenLinks attribute="class" value="brokenlink" messageAttribute="title"/>
   </map:transformer>
     
   <map:transformer name="url2uuid" logger="lenya.sitemap.transformer.url2uuid"

Modified: lenya/trunk/src/modules-core/linking/java/src/org/apache/lenya/cms/cocoon/transformation/AbstractLinkTransformer.java
URL: http://svn.apache.org/viewvc/lenya/trunk/src/modules-core/linking/java/src/org/apache/lenya/cms/cocoon/transformation/AbstractLinkTransformer.java?rev=634466&r1=634465&r2=634466&view=diff
==============================================================================
--- lenya/trunk/src/modules-core/linking/java/src/org/apache/lenya/cms/cocoon/transformation/AbstractLinkTransformer.java (original)
+++ lenya/trunk/src/modules-core/linking/java/src/org/apache/lenya/cms/cocoon/transformation/AbstractLinkTransformer.java Thu Mar  6 15:55:37 2008
@@ -17,6 +17,7 @@
  */
 package org.apache.lenya.cms.cocoon.transformation;
 
+import java.io.IOException;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -27,9 +28,17 @@
 
 import org.apache.avalon.framework.configuration.Configuration;
 import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.cocoon.ProcessingException;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.environment.Request;
+import org.apache.cocoon.environment.SourceResolver;
 import org.apache.cocoon.transformation.AbstractSAXTransformer;
 import org.apache.lenya.ac.AccessControlException;
 import org.apache.lenya.cms.linking.LinkRewriter;
+import org.apache.lenya.cms.publication.Publication;
+import org.apache.lenya.cms.publication.URLInformation;
+import org.apache.lenya.util.ServletHelper;
 import org.xml.sax.Attributes;
 import org.xml.sax.SAXException;
 import org.xml.sax.helpers.AttributesImpl;
@@ -41,18 +50,29 @@
  * </p>
  * <p>
  * If the link rewriter returns <code>null</code> for a link, an attribute can be
- * added to the corresponding element.
+ * added to the corresponding element, with an optional message of the form "Broken link: ...".
  * </p>
  * <code><pre>
  *   &lt;map:transformer ... &gt;
  *     &lt;transform namespace=&quot;http://www.w3.org/1999/xhtml&quot; element=&quot;a&quot; attribute=&quot;href&quot;/&gt;
  *     &lt;transform namespace=&quot;...&quot; ... /&gt;
- *     &lt;markBrokenLinks attribute=&quot;...&quot; value=&quot;...&quot;/&gt;
+ *     &lt;markBrokenLinks attribute=&quot;...&quot; value=&quot;...&quot; messageAttribute=&quot;...&quot;/&gt;
  *   &lt;/map:transformer&gt;
  * </pre></code>
  */
 public abstract class AbstractLinkTransformer extends AbstractSAXTransformer {
     
+    private String area;
+    
+    public void setup(SourceResolver resolver, Map objectModel, String src, Parameters params)
+            throws ProcessingException, SAXException, IOException {
+        super.setup(resolver, objectModel, src, params);
+        Request request = ObjectModelHelper.getRequest(objectModel);
+        String webappUrl = ServletHelper.getWebappURI(request);
+        URLInformation url = new URLInformation(webappUrl);
+        this.area = url.getArea();
+    }
+
     /**
      * Set of supported local names for quick pre-checks.
      */
@@ -61,6 +81,7 @@
     private boolean markBrokenLinks;
     private String brokenLinkAttribute;
     private String brokenLinkValue;
+    private String brokenLinkMessageAttribute;
     
     public void configure(Configuration config) throws ConfigurationException {
         super.configure(config);
@@ -84,6 +105,10 @@
         if (brokenLinksConfig != null) {
             this.brokenLinkAttribute = brokenLinksConfig.getAttribute("attribute");
             this.brokenLinkValue = brokenLinksConfig.getAttribute("value");
+            String messageAttr = brokenLinksConfig.getAttribute("messageAttribute", null);
+            if (messageAttr != null) {
+                this.brokenLinkMessageAttribute = messageAttr;
+            }
             this.markBrokenLinks = true;
         }
         else {
@@ -225,7 +250,7 @@
             getLogger().debug(this.indent + "ignoreAElement: " + this.ignoreLinkElement);
         }
 
-        // use existsMatching to match up with ednElement
+        // use existsMatching to match up with endElement
         if (existsMatchingConfiguration(uri,name) && this.useIgnore) {
             if(this.ignoreLinkElement) { 
                 ignoreLinkElementStack.push(Boolean.TRUE);
@@ -260,7 +285,12 @@
                 setAttribute(newAttrs, config.attribute, rewrittenUrl);
             }
             else {
-                markBrokenLink(newAttrs, config.attribute);
+                if (this.area != null && this.area.equals(Publication.LIVE_AREA)) {
+                    this.ignoreLinkElement = true;
+                }
+                else {
+                    markBrokenLink(newAttrs, config.attribute, linkUrl);
+                }
             }
         }
     }
@@ -313,9 +343,10 @@
      * Marks a link element as broken and removes the attribute which contained the URL.
      * @param newAttrs The new attributes.
      * @param attrName The attribute name containing the URL which could not be rewritten.
+     * @param brokenLinkUri The broken link URI.
      * @throws AccessControlException when something went wrong.
      */
-    protected void markBrokenLink(AttributesImpl newAttrs, String attrName)
+    protected void markBrokenLink(AttributesImpl newAttrs, String attrName, String brokenLinkUri)
             throws AccessControlException {
         if (this.markBrokenLinks) {
             if (newAttrs.getIndex(this.brokenLinkAttribute) > -1) {
@@ -323,6 +354,15 @@
             }
             if (newAttrs.getIndex(attrName) > -1) {
                 newAttrs.setAttribute(newAttrs.getIndex(attrName), "", attrName, attrName, "CDATA", "");
+            }
+            String msgAttr = this.brokenLinkMessageAttribute;
+            if (msgAttr != null) {
+                int index = newAttrs.getIndex(msgAttr);
+                if (index > -1) {
+                    newAttrs.removeAttribute(index);
+                }
+                String msg = "Broken link: " + brokenLinkUri;
+                newAttrs.addAttribute("", msgAttr, msgAttr, "CDATA", msg);
             }
             newAttrs.addAttribute("", this.brokenLinkAttribute, this.brokenLinkAttribute, "CDATA", this.brokenLinkValue);
         }

Modified: lenya/trunk/src/modules-core/linking/java/src/org/apache/lenya/cms/cocoon/transformation/UuidToUrlTransformer.java
URL: http://svn.apache.org/viewvc/lenya/trunk/src/modules-core/linking/java/src/org/apache/lenya/cms/cocoon/transformation/UuidToUrlTransformer.java?rev=634466&r1=634465&r2=634466&view=diff
==============================================================================
--- lenya/trunk/src/modules-core/linking/java/src/org/apache/lenya/cms/cocoon/transformation/UuidToUrlTransformer.java (original)
+++ lenya/trunk/src/modules-core/linking/java/src/org/apache/lenya/cms/cocoon/transformation/UuidToUrlTransformer.java Thu Mar  6 15:55:37 2008
@@ -18,295 +18,53 @@
 package org.apache.lenya.cms.cocoon.transformation;
 
 import java.io.IOException;
-import java.io.Serializable;
-import java.net.MalformedURLException;
 import java.util.Map;
-import java.util.StringTokenizer;
 
 import org.apache.avalon.framework.activity.Disposable;
 import org.apache.avalon.framework.parameters.Parameters;
 import org.apache.cocoon.ProcessingException;
-import org.apache.cocoon.caching.CacheableProcessingComponent;
 import org.apache.cocoon.environment.ObjectModelHelper;
 import org.apache.cocoon.environment.Request;
 import org.apache.cocoon.environment.SourceResolver;
-import org.apache.excalibur.source.Source;
-import org.apache.excalibur.source.SourceValidity;
-import org.apache.lenya.ac.AccessControlException;
-import org.apache.lenya.cms.linking.Link;
 import org.apache.lenya.cms.linking.LinkResolver;
 import org.apache.lenya.cms.linking.LinkRewriter;
-import org.apache.lenya.cms.linking.LinkTarget;
-import org.apache.lenya.cms.publication.Area;
-import org.apache.lenya.cms.publication.Document;
+import org.apache.lenya.cms.linking.UuidToUrlRewriter;
 import org.apache.lenya.cms.publication.DocumentFactory;
 import org.apache.lenya.cms.publication.DocumentUtil;
-import org.apache.lenya.cms.publication.Publication;
-import org.apache.lenya.cms.publication.URLInformation;
-import org.apache.lenya.cms.repository.RepositoryUtil;
-import org.apache.lenya.cms.repository.Session;
-import org.apache.lenya.util.Query;
 import org.apache.lenya.util.ServletHelper;
 import org.xml.sax.SAXException;
-import org.xml.sax.helpers.AttributesImpl;
 
 /**
- * <p>
  * UUID to URL transformer.
- * </p>
- * 
- * <p>
- * This transformer is applied to an XHMTL document. It processes all links following the
- * {@link LinkResolver} syntax which are configured using <code>&lt;transform/&gt;</code> elements
- * (see below).
- * </p>
- * <p>
- * These links are resolved using the following rules:
- * </p>
- * <ul>
- * <li>The current area (obtained from the page envelope) is used.</li>
- * <li>A URL prefix is added depending on the proxy configuration of the publication.</li>
- * <li>If the target document does not exist and is in the authoring area, the href attribute is
- * removed and a class="brokenlink" attribute is added to the <code>&lt;a/&gt;</code> element.</li>
- * <li>If the target document does not exist and is in the live area, the <code>&lt;a/&gt;</code>
- * element is removed to disable the link.</li>
- * </ul>
- * 
- * <p>
- * You can add the query parameter <code>uuid2url.extension</code> to <code>lenya-document:</code>
- * URLs to set a specific link extension.
- * </p>
- * <p>
- * The resulting URLs are absolute web application URLs (without the servlet context path).
- * </p>
+ * @see AbstractLinkTransformer
+ * @see UuidToUrlRewriter
  * 
  * $Id: LinkRewritingTransformer.java,v 1.7 2004/03/16 11:12:16 gregor
  */
-public class UuidToUrlTransformer extends AbstractLinkTransformer implements Disposable, CacheableProcessingComponent {
-
-    protected static final String BROKEN_ATTRIB = "class";
-    protected static final String BROKEN_VALUE = "brokenlink";
-    protected static final String EXTENSION_PARAM = "uuid2url.extension";
+public class UuidToUrlTransformer extends AbstractLinkTransformer implements Disposable {
 
-    private String currentUrl;
-
-    private DocumentFactory factory;
+    private UuidToUrlRewriter rewriter;
     private LinkResolver linkResolver;
-    private Document currentDoc;
-    private String cacheKey;
-    private SourceValidity validity;
     
-    /**
-     * @see org.apache.cocoon.sitemap.SitemapModelComponent#setup(org.apache.cocoon.environment.SourceResolver,
-     *      java.util.Map, java.lang.String, org.apache.avalon.framework.parameters.Parameters)
-     */
-    public void setup(SourceResolver resolver, Map _objectModel, String _source,
-            Parameters _parameters) throws ProcessingException, SAXException, IOException {
-        super.setup(resolver, _objectModel, _source, _parameters);
-
-        Request _request = ObjectModelHelper.getRequest(_objectModel);
+    public void setup(SourceResolver resolver, Map objectModel, String source,
+            Parameters parameters) throws ProcessingException, SAXException, IOException {
+        super.setup(resolver, objectModel, source, parameters);
 
+        Request request = ObjectModelHelper.getRequest(objectModel);
         this.useIgnore = true;
-        Source source = null;
         try {
-            Session session = RepositoryUtil.getSession(this.manager, _request);
-            this.factory = DocumentUtil.createDocumentFactory(this.manager, session);
-            this.currentUrl = ServletHelper.getWebappURI(_request);
-            if (this.factory.isDocument(this.currentUrl)) {
-                this.currentDoc = this.factory.getFromURL(this.currentUrl);
-            }
+            DocumentFactory factory = DocumentUtil.getDocumentFactory(this.manager, request);
+            String currentUrl = ServletHelper.getWebappURI(request);
             this.linkResolver = (LinkResolver) this.manager.lookup(LinkResolver.ROLE);
+            this.rewriter = new UuidToUrlRewriter(currentUrl, this.linkResolver, factory);
+            
+            if (factory.isDocument(currentUrl)) {
+                this.rewriter.setCurrentDocument(factory.getFromURL(currentUrl));
+            }
             
-            URLInformation info = new URLInformation(this.currentUrl);
-            Publication pub = factory.getPublication(info.getPublicationId());
-            Area area = pub.getArea(info.getArea());
-            this.cacheKey = pub.getId() + ":" + area.getName();
-            source = resolver.resolveURI(area.getSite().getRepositoryNode().getSourceURI());
-            this.validity = source.getValidity();
         } catch (final Exception e) {
             throw new ProcessingException(e);
-        } finally {
-            if (source != null) {
-                resolver.release(source);
-            }
-        }
-    }
-
-    protected void handleLink(String linkUrl, AttributeConfiguration config, AttributesImpl newAttrs)
-            throws Exception {
-
-        URLInformation info = new URLInformation(this.currentUrl);
-        if (linkUrl.startsWith("lenya-document:")) {
-
-            String anchor = null;
-            String url = null;
-
-            int anchorIndex = linkUrl.indexOf("#");
-            if (anchorIndex > -1) {
-                url = linkUrl.substring(0, anchorIndex);
-                anchor = linkUrl.substring(anchorIndex + 1);
-            } else {
-                url = linkUrl;
-            }
-
-            StringTokenizer tokenizer = new StringTokenizer(url, "?");
-            String linkUri = tokenizer.nextToken();
-            String queryString = null;
-            String requiredExtension = null;
-            if (tokenizer.hasMoreTokens()) {
-                queryString = tokenizer.nextToken();
-                Query query = new Query(queryString);
-                requiredExtension = query.getValue(EXTENSION_PARAM);
-                query.removeValue(EXTENSION_PARAM);
-                queryString = query.toString();
-            }
-
-            LinkTarget target;
-            if (this.currentDoc != null) {
-                target = this.linkResolver.resolve(this.currentDoc, linkUri);
-            } else {
-                Link link = getAbsoluteLink(info, linkUri);
-                target = this.linkResolver.resolve(this.factory, link.getUri());
-            }
-
-            if (target.exists() && target.getDocument().hasLink()) {
-                Document targetDocument = target.getDocument();
-                String extension = getExtension(targetDocument, requiredExtension);
-                rewriteLink(newAttrs, config.attribute, targetDocument, anchor, queryString,
-                        extension);
-            } else if (info.getArea().equals(Publication.AUTHORING_AREA)) {
-                markBrokenLink(newAttrs, config.attribute, linkUrl);
-            } else {
-                this.ignoreLinkElement = true;
-            }
-        } else {
-            /*
-             * This is legacy code. It rewrites links to non-document images (in resources/shared).
-             * These images shouldn't be referenced in documents since this violates the separation
-             * between content and layout.
-             */
-            String prefix = "/" + info.getPublicationId() + "/";
-            if (linkUrl.startsWith(prefix)) {
-                String pubUrl = linkUrl.substring(prefix.length());
-                StringTokenizer tokenizer = new StringTokenizer(pubUrl, "/");
-                String area = tokenizer.nextToken();
-
-                // don't rewrite /{pub}/modules/...
-                if (area.equals(Publication.AUTHORING_AREA)) {
-                    String areaUrl = pubUrl.substring(area.length());
-                    String newUrl = prefix + area + areaUrl;
-                    setAttribute(newAttrs, config.attribute, newUrl);
-                }
-            }
-        }
-    }
-
-    /**
-     * The link is constructed from the linkUri string. If it lacks the area or publication ID
-     * information, these are obtained from the current URL information.
-     * @param info The current URL information.
-     * @param linkUri The link URI to use.
-     * @return A link.
-     * @throws MalformedURLException if the linkUri parameter is malformed.
-     */
-    protected Link getAbsoluteLink(URLInformation info, String linkUri)
-            throws MalformedURLException {
-        Link link = new Link(linkUri);
-        if (link.getPubId() == null) {
-            link.setPubId(info.getPublicationId());
-        }
-        if (link.getArea() == null) {
-            link.setArea(info.getArea());
-        }
-        return link;
-    }
-
-    /**
-     * Get the extension of a document. Caution: resolving the extension is expensive!
-     * @param targetDocument The document.
-     * @param requiredExtension The required extension.
-     * @return The required extension or, if it is null, the document's default extension.
-     */
-    protected String getExtension(Document targetDocument, String requiredExtension) {
-        String extension = requiredExtension != null ? requiredExtension : targetDocument.getExtension();
-        if (extension.length() > 0) {
-            extension = "." + extension;
         }
-        return extension;
-    }
-
-    /**
-     * Marks a <code>&lt;a/&gt;</code> element as broken and removes href attribute.
-     * 
-     * @param newAttrs The new attributes.
-     * @param attrName The attribute name.
-     * @param brokenUrl The broken link URI.
-     * @throws AccessControlException when something went wrong.
-     */
-    protected void markBrokenLink(AttributesImpl newAttrs, String attrName, String brokenUrl)
-            throws AccessControlException {
-        if (newAttrs.getIndex(BROKEN_ATTRIB) > -1)
-            newAttrs.removeAttribute(newAttrs.getIndex(BROKEN_ATTRIB));
-        if (newAttrs.getIndex("title") > -1)
-            newAttrs.removeAttribute(newAttrs.getIndex("title"));
-        if (newAttrs.getIndex(attrName) > -1)
-            newAttrs.setAttribute(newAttrs.getIndex(attrName), "", attrName, attrName, "CDATA", "");
-        String warning = "Broken Link: " + brokenUrl;
-        newAttrs.addAttribute("", "title", "title", "CDATA", warning);
-        newAttrs.addAttribute("", BROKEN_ATTRIB, BROKEN_ATTRIB, "CDATA", BROKEN_VALUE);
-    }
-
-    /**
-     * Rewrites a link.
-     * 
-     * @param newAttrs The new attributes.
-     * @param attributeName The name of the attribute to rewrite.
-     * @param targetDocument The target document.
-     * @param anchor The anchor (the string after the # character in the URL).
-     * @param queryString The query string without question mark.
-     * @param extension The extension to use.
-     * @throws AccessControlException when something went wrong.
-     */
-    protected void rewriteLink(AttributesImpl newAttrs, String attributeName,
-            Document targetDocument, String anchor, String queryString, String extension)
-            throws AccessControlException {
-
-        String webappUrl = targetDocument.getCanonicalWebappURL();
-
-        int lastDotIndex = webappUrl.lastIndexOf(".");
-        if (lastDotIndex > -1) {
-            webappUrl = webappUrl.substring(0, lastDotIndex) + extension;
-        }
-
-        if (anchor != null) {
-            webappUrl += "#" + anchor;
-        }
-
-        if (queryString != null && queryString.length() > 0) {
-            webappUrl += "?" + queryString;
-        }
-
-        if (getLogger().isDebugEnabled()) {
-            getLogger().debug(this.indent + "Rewriting URL to: [" + webappUrl + "]");
-        }
-
-        setAttribute(newAttrs, attributeName, webappUrl);
-    }
-
-    /**
-     * Sets the value of the href attribute.
-     * 
-     * @param attr The attributes.
-     * @param name The attribute name.
-     * @param value The value.
-     * @throws IllegalArgumentException if the href attribute is not contained in this attributes.
-     */
-    protected void setAttribute(AttributesImpl attr, String name, String value) {
-        int position = attr.getIndex(name);
-        if (position == -1) {
-            throw new IllegalArgumentException("The href attribute is not available!");
-        }
-        attr.setValue(position, value);
     }
 
     /**
@@ -318,30 +76,8 @@
         }
     }
 
-    /**
-     * @see org.apache.avalon.excalibur.pool.Recyclable#recycle()
-     */
-    public void recycle() {
-        this.ignoreLinkElement = false;
-        this.currentUrl = null;
-    }
-
     protected LinkRewriter getLinkRewriter() {
-        return null;
-    }
-
-    public Serializable getKey() {
-        if (this.cacheKey == null) {
-            throw new IllegalStateException("setup() was not called.");
-        }
-        return this.cacheKey;
-    }
-
-    public SourceValidity getValidity() {
-        if (this.validity == null) {
-            throw new IllegalStateException("setup() was not called.");
-        }
-        return this.validity;
+        return this.rewriter;
     }
 
 }

Added: lenya/trunk/src/modules-core/linking/java/src/org/apache/lenya/cms/linking/UuidToUrlRewriter.java
URL: http://svn.apache.org/viewvc/lenya/trunk/src/modules-core/linking/java/src/org/apache/lenya/cms/linking/UuidToUrlRewriter.java?rev=634466&view=auto
==============================================================================
--- lenya/trunk/src/modules-core/linking/java/src/org/apache/lenya/cms/linking/UuidToUrlRewriter.java (added)
+++ lenya/trunk/src/modules-core/linking/java/src/org/apache/lenya/cms/linking/UuidToUrlRewriter.java Thu Mar  6 15:55:37 2008
@@ -0,0 +1,241 @@
+/*
+ * 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.lenya.cms.linking;
+
+import java.net.MalformedURLException;
+import java.util.StringTokenizer;
+
+import org.apache.lenya.ac.AccessControlException;
+import org.apache.lenya.cms.publication.Document;
+import org.apache.lenya.cms.publication.DocumentFactory;
+import org.apache.lenya.cms.publication.Publication;
+import org.apache.lenya.cms.publication.URLInformation;
+import org.apache.lenya.util.Assert;
+import org.apache.lenya.util.Query;
+
+/**
+ * <p>
+ * Rewrite lenya-document: URLs to /{pub}/{area}/... URLs.
+ * </p>
+ * <p>
+ * To support legacy documents, image URLs starting with /{pub}/{area} are also
+ * handled.
+ * </p>
+ * <p>
+ * These links are resolved using the following rules:
+ * </p>
+ * <ul>
+ * <li>The current area (obtained from the page envelope) is used.</li>
+ * <li>A URL prefix is added depending on the proxy configuration of the
+ * publication.</li>
+ * <li>If the target document does not exist and is in the authoring area, the
+ * href attribute is removed and a class="brokenlink" attribute is added to the
+ * <code>&lt;a/&gt;</code> element.</li>
+ * <li>If the target document does not exist and is in the live area, the
+ * <code>&lt;a/&gt;</code> element is removed to disable the link.</li>
+ * </ul>
+ * <p>
+ * You can add the query parameter <code>uuid2url.extension</code> to
+ * <code>lenya-document:</code> URLs to set a specific link extension.
+ * </p>
+ * <p>
+ * The resulting URLs are absolute web application URLs (without the servlet
+ * context path).
+ * </p>
+ */
+public class UuidToUrlRewriter implements LinkRewriter {
+
+    protected static final String EXTENSION_PARAM = "uuid2url.extension";
+
+    private String currentUrl;
+    private Document currentDoc;
+    private LinkResolver linkResolver;
+    private DocumentFactory factory;
+
+    /**
+     * @param currentUrl The current request URL.
+     * @param linkResolver The link resolver to use.
+     * @param factory The document factory to use.
+     */
+    public UuidToUrlRewriter(String currentUrl, LinkResolver linkResolver, DocumentFactory factory) {
+        Assert.notNull("current URL", currentUrl);
+        Assert.notNull("link resolver", linkResolver);
+        Assert.notNull("document factory", factory);
+        this.currentUrl = currentUrl;
+        this.factory = factory;
+        this.linkResolver = linkResolver;
+    }
+
+    public void setCurrentDocument(Document doc) {
+        Assert.notNull("current document", doc);
+        this.currentDoc = doc;
+    }
+
+    protected static final String SCHEME = "lenya-document:";
+
+    public boolean matches(String url) {
+        return url.startsWith(SCHEME) || url.startsWith("/");
+    }
+
+    public String rewrite(String linkUrl) {
+
+        String rewrittenUrl = null;
+
+        URLInformation info = new URLInformation(this.currentUrl);
+        if (linkUrl.startsWith(SCHEME)) {
+
+            String anchor = null;
+            String url = null;
+
+            int anchorIndex = linkUrl.indexOf("#");
+            if (anchorIndex > -1) {
+                url = linkUrl.substring(0, anchorIndex);
+                anchor = linkUrl.substring(anchorIndex + 1);
+            } else {
+                url = linkUrl;
+            }
+
+            StringTokenizer tokenizer = new StringTokenizer(url, "?");
+            String linkUri = tokenizer.nextToken();
+            String queryString = null;
+            String requiredExtension = null;
+            if (tokenizer.hasMoreTokens()) {
+                queryString = tokenizer.nextToken();
+                Query query = new Query(queryString);
+                requiredExtension = query.getValue(EXTENSION_PARAM);
+                query.removeValue(EXTENSION_PARAM);
+                queryString = query.toString();
+            }
+
+            LinkTarget target;
+            try {
+                if (this.currentDoc != null) {
+                    target = this.linkResolver.resolve(this.currentDoc, linkUri);
+                } else {
+                    Link link = getAbsoluteLink(info, linkUri);
+                    target = this.linkResolver.resolve(this.factory, link.getUri());
+                }
+
+                if (target.exists() && target.getDocument().hasLink()) {
+                    Document targetDocument = target.getDocument();
+                    String extension = getExtension(targetDocument, requiredExtension);
+                    rewrittenUrl = getWebappUrl(targetDocument, anchor, queryString, extension);
+                } else {
+                    rewrittenUrl = null;
+                }
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        } else {
+            /*
+             * This is legacy code. It rewrites links to non-document images (in
+             * resources/shared). These images shouldn't be referenced in
+             * documents since this violates the separation between content and
+             * layout.
+             */
+            String prefix = "/" + info.getPublicationId() + "/";
+            if (linkUrl.startsWith(prefix)) {
+                String pubUrl = linkUrl.substring(prefix.length());
+                StringTokenizer tokenizer = new StringTokenizer(pubUrl, "/");
+                String area = tokenizer.nextToken();
+
+                // don't rewrite /{pub}/modules/...
+                if (area.equals(Publication.AUTHORING_AREA)) {
+                    String areaUrl = pubUrl.substring(area.length());
+                    rewrittenUrl = prefix + area + areaUrl;
+                }
+            }
+            if (rewrittenUrl == null) {
+                rewrittenUrl = linkUrl;
+            }
+        }
+        return rewrittenUrl;
+    }
+
+    /**
+     * Rewrites a link.
+     * 
+     * @param targetDocument The target document.
+     * @param anchor The anchor (the string after the # character in the URL).
+     * @param queryString The query string without question mark.
+     * @param extension The extension to use.
+     * @return a web application URL.
+     * @throws AccessControlException when something went wrong.
+     */
+    protected String getWebappUrl(Document targetDocument, String anchor, String queryString,
+            String extension) throws AccessControlException {
+
+        String webappUrl = targetDocument.getCanonicalWebappURL();
+
+        int lastDotIndex = webappUrl.lastIndexOf(".");
+        if (lastDotIndex > -1) {
+            webappUrl = webappUrl.substring(0, lastDotIndex) + extension;
+        }
+
+        if (anchor != null) {
+            webappUrl += "#" + anchor;
+        }
+
+        if (queryString != null && queryString.length() > 0) {
+            webappUrl += "?" + queryString;
+        }
+
+        return webappUrl;
+    }
+
+    /**
+     * The link is constructed from the linkUri string. If it lacks the area or
+     * publication ID information, these are obtained from the current URL
+     * information.
+     * 
+     * @param info The current URL information.
+     * @param linkUri The link URI to use.
+     * @return A link.
+     * @throws MalformedURLException if the linkUri parameter is malformed.
+     */
+    protected Link getAbsoluteLink(URLInformation info, String linkUri)
+            throws MalformedURLException {
+        Link link = new Link(linkUri);
+        if (link.getPubId() == null) {
+            link.setPubId(info.getPublicationId());
+        }
+        if (link.getArea() == null) {
+            link.setArea(info.getArea());
+        }
+        return link;
+    }
+
+    /**
+     * Get the extension of a document. Caution: resolving the extension is
+     * expensive!
+     * 
+     * @param targetDocument The document.
+     * @param requiredExtension The required extension.
+     * @return The required extension or, if it is null, the document's default
+     *         extension.
+     */
+    protected String getExtension(Document targetDocument, String requiredExtension) {
+        String extension = requiredExtension != null ? requiredExtension : targetDocument
+                .getExtension();
+        if (extension.length() > 0) {
+            extension = "." + extension;
+        }
+        return extension;
+    }
+
+}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.