| /* |
| * 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.cocoon.portal.transformation; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Enumeration; |
| import java.util.List; |
| |
| import org.apache.cocoon.ProcessingException; |
| import org.apache.cocoon.environment.wrapper.RequestParameters; |
| import org.apache.cocoon.portal.LinkService; |
| import org.apache.cocoon.portal.coplet.CopletInstanceData; |
| import org.apache.cocoon.portal.event.impl.ChangeCopletInstanceAspectDataEvent; |
| import org.apache.cocoon.portal.event.impl.CopletJXPathEvent; |
| import org.apache.cocoon.portal.event.impl.JXPathEvent; |
| import org.apache.cocoon.xml.AttributesImpl; |
| import org.apache.cocoon.xml.XMLUtils; |
| import org.apache.commons.jxpath.JXPathContext; |
| import org.apache.excalibur.xml.sax.XMLizable; |
| import org.xml.sax.Attributes; |
| import org.xml.sax.SAXException; |
| |
| /** |
| * This transformer offers various functions for developing pipeline based coplets. |
| * |
| * Includes coplet instance data by using JXPath expressions. |
| * The transformer searches for tags <coplet:coplet xmlns:coplet="http://apache.org/cocoon/portal/coplet/1.0">. |
| * They must have an attribute "select" that contains a valid JXPath expression applying to the coplet instance data.<br><br> |
| * |
| * Example:<br><br> |
| * |
| * <pre><maxpageable xmlns:coplet="http://apache.org/cocoon/portal/coplet/1.0"> |
| * <coplet:coplet select="copletData.maxpageable"/> |
| * </maxpageable><br></pre> |
| * |
| * The transformer will insert the boolean value specifying whether the coplet is |
| * maxpageable or not.<br> |
| * Please see also the documentation of superclass AbstractCopletTransformer for how |
| * the coplet instance data are acquired. |
| * |
| * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a> |
| * @author <a href="mailto:bluetkemeier@s-und-n.de">Björn Lütkemeier</a> |
| * @version CVS $Id$ |
| */ |
| public class CopletTransformer |
| extends AbstractCopletTransformer { |
| |
| /** |
| * The namespace URI to listen for. |
| */ |
| public static final String NAMESPACE_URI = "http://apache.org/cocoon/portal/coplet/1.0"; |
| |
| /** |
| * The XML element name to listen for. |
| */ |
| public static final String COPLET_ELEM = "coplet"; |
| |
| /** |
| * The attribute containing the JXPath expression. |
| */ |
| public static final String SELECT_ATTR = "select"; |
| |
| |
| /** |
| * The XML element name to listen for. |
| */ |
| public static final String LINK_ELEM = "link"; |
| |
| /** Create a link containing several events */ |
| public static final String LINKS_ELEM = "links"; |
| |
| /** Create a link containing several events */ |
| public static final String PARAMETER_ELEM = "parameter"; |
| |
| /** The content for the links element */ |
| public static final String CONTENT_ELEM = "content"; |
| |
| /** Are we inside a links element? */ |
| protected boolean insideLinks; |
| |
| /** The collected list of events */ |
| protected List collectedEvents = new ArrayList(); |
| |
| /** The content of the links */ |
| protected XMLizable content; |
| |
| /** |
| * Creates new CopletTransformer. |
| */ |
| public CopletTransformer() { |
| this.defaultNamespaceURI = NAMESPACE_URI; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.cocoon.transformation.AbstractSAXTransformer#setupTransforming() |
| */ |
| public void setupTransforming() |
| throws IOException, ProcessingException, SAXException { |
| super.setupTransforming(); |
| this.insideLinks = false; |
| this.content = null; |
| this.collectedEvents.clear(); |
| } |
| |
| /** |
| * Overridden from superclass. |
| */ |
| public void startTransformingElement(String uri, String name, String raw, Attributes attr) |
| throws ProcessingException, IOException, SAXException { |
| if (name.equals(COPLET_ELEM)) { |
| String expression = attr.getValue(SELECT_ATTR); |
| if (expression == null) { |
| throw new ProcessingException("Attribute "+SELECT_ATTR+" must be spcified."); |
| } |
| |
| final CopletInstanceData cid = this.getCopletInstanceData(); |
| |
| final JXPathContext jxpathContext = JXPathContext.newContext( cid ); |
| final Object object = jxpathContext.getValue(expression); |
| |
| if (object != null) { |
| XMLUtils.valueOf(contentHandler, object); |
| } |
| |
| } else if (name.equals(LINK_ELEM)) { |
| |
| final LinkService linkService = this.portalService.getComponentManager().getLinkService(); |
| final String format = attr.getValue("format"); |
| AttributesImpl newAttrs = new AttributesImpl(); |
| newAttrs.setAttributes(attr); |
| newAttrs.removeAttribute("format"); |
| |
| if ( attr.getValue("href") != null ) { |
| final CopletInstanceData cid = this.getCopletInstanceData(); |
| ChangeCopletInstanceAspectDataEvent event = new ChangeCopletInstanceAspectDataEvent(cid, null, null); |
| |
| String value = linkService.getLinkURI(event); |
| if (value.indexOf('?') == -1) { |
| value = value + '?' + attr.getValue("href"); |
| } else { |
| value = value + '&' + attr.getValue("href"); |
| } |
| newAttrs.removeAttribute("href"); |
| this.output(value, format, newAttrs ); |
| } else { |
| final String path = attr.getValue("path"); |
| final String value = attr.getValue("value"); |
| |
| newAttrs.removeAttribute("path"); |
| newAttrs.removeAttribute("value"); |
| |
| JXPathEvent event = null; |
| if ( attr.getValue("layout") != null ) { |
| newAttrs.removeAttribute("layout"); |
| final String layoutId = attr.getValue("layout"); |
| Object layout = this.portalService.getComponentManager().getProfileManager().getPortalLayout(null, layoutId); |
| if ( layout != null ) { |
| event = new JXPathEvent(layout, path, value); |
| } |
| } else { |
| String copletId = attr.getValue("coplet"); |
| newAttrs.removeAttribute("coplet"); |
| final CopletInstanceData cid = this.getCopletInstanceData(copletId); |
| if ( cid != null ) { |
| event = new CopletJXPathEvent(cid, path, value); |
| } |
| } |
| if ( this.insideLinks ) { |
| if ( event != null ) { |
| this.collectedEvents.add(event); |
| } |
| } else { |
| final String href = linkService.getLinkURI(event); |
| this.output(href, format, newAttrs ); |
| } |
| } |
| } else if (name.equals(PARAMETER_ELEM)) { |
| if (this.insideLinks) { |
| String href = attr.getValue("href"); |
| if ( href != null ) { |
| final int pos = href.indexOf('?'); |
| if ( pos != -1 ) { |
| href = href.substring(pos+1); |
| } |
| this.collectedEvents.add(new LinkService.ParameterDescription(href)); |
| } |
| } |
| } else if (name.equals(LINKS_ELEM) ) { |
| this.insideLinks = true; |
| final AttributesImpl newAttrs = new AttributesImpl(); |
| newAttrs.setAttributes(attr); |
| newAttrs.removeAttribute("format"); |
| this.stack.push(newAttrs); |
| |
| String format = attr.getValue("format"); |
| if ( format == null ) { |
| format = "html-link"; |
| } |
| this.stack.push(format); |
| } else if ( name.equals(CONTENT_ELEM) && this.insideLinks ) { |
| this.startSAXRecording(); |
| } else { |
| super.startTransformingElement(uri, name, raw, attr); |
| } |
| } |
| |
| /** |
| * Overridden from superclass. |
| */ |
| public void endTransformingElement(String uri, String name, String raw) |
| throws ProcessingException, IOException, SAXException { |
| if ( name.equals(LINK_ELEM) ) { |
| if ( !this.insideLinks ) { |
| String elem = (String)this.stack.pop(); |
| if ( elem.length() > 0 ) { |
| this.sendEndElementEvent(elem); |
| } |
| } |
| } else if ( name.equals(LINKS_ELEM) ) { |
| this.insideLinks = false; |
| final String format = (String)this.stack.pop(); |
| final LinkService linkService = this.portalService.getComponentManager().getLinkService(); |
| String href = linkService.getLinkURI(this.collectedEvents); |
| |
| AttributesImpl newAttrs = (AttributesImpl)this.stack.pop(); |
| // test for alternate base url |
| final String baseURL = newAttrs.getValue("base-url"); |
| if ( baseURL != null ) { |
| newAttrs.removeAttribute("base-url"); |
| int pos = href.indexOf('?') + 1; |
| final char separator; |
| if ( baseURL.indexOf('?') == -1 ) { |
| separator = '?'; |
| } else { |
| separator = '&'; |
| } |
| href = baseURL + separator + href.substring(pos); |
| |
| } |
| this.output(href, format, newAttrs ); |
| |
| this.collectedEvents.clear(); |
| if ( this.content != null ) { |
| this.content.toSAX(this.contentHandler); |
| this.content = null; |
| } |
| String elem = (String)this.stack.pop(); |
| if ( elem.length() > 0 ) { |
| this.sendEndElementEvent(elem); |
| } |
| } else if ( name.equals(CONTENT_ELEM) && this.insideLinks ) { |
| this.content = this.endSAXRecording(); |
| } else if (!name.equals(COPLET_ELEM) && !name.equals(PARAMETER_ELEM)) { |
| super.endTransformingElement(uri, name, raw); |
| } |
| } |
| |
| /** |
| * Output the link |
| */ |
| protected void output(String uri, String format, AttributesImpl newAttrs) |
| throws SAXException { |
| if ( format == null ) { |
| // default |
| format = "html-link"; |
| } |
| |
| if ( "html-link".equals(format) ) { |
| newAttrs.addCDATAAttribute("href", uri); |
| this.sendStartElementEvent("a", newAttrs); |
| this.stack.push("a"); |
| |
| } else if ( "html-form".equals(format) ) { |
| boolean addParametersAsHiddenFields = false; |
| String parameters = null; |
| final String enctype = newAttrs.getValue("enctype"); |
| if ( enctype== null |
| || "application/x-www-form-urlencoded".equalsIgnoreCase(enctype) |
| || "multipart/form-data".equalsIgnoreCase(enctype) ) { |
| final int pos = uri.indexOf('?'); |
| if ( pos != -1 ) { |
| parameters = uri.substring(pos+1); |
| uri = uri.substring(0, pos); |
| addParametersAsHiddenFields = true; |
| } |
| } |
| newAttrs.addCDATAAttribute("action", uri); |
| this.sendStartElementEvent("form", newAttrs); |
| this.stack.push("form"); |
| if ( addParametersAsHiddenFields ) { |
| // create hidden input fields |
| RequestParameters pars = new RequestParameters(parameters); |
| Enumeration enumeration = pars.getParameterNames(); |
| while ( enumeration.hasMoreElements() ) { |
| final String pName = (String)enumeration.nextElement(); |
| final String[] pValues = pars.getParameterValues(pName); |
| for(int k=0; k<pValues.length; k++) { |
| final String pValue = pValues[k]; |
| AttributesImpl hiddenAttrs = new AttributesImpl(); |
| hiddenAttrs.addCDATAAttribute("type", "hidden"); |
| hiddenAttrs.addCDATAAttribute("name", pName); |
| hiddenAttrs.addCDATAAttribute("value", pValue); |
| this.startElement("", "input", "input", hiddenAttrs); |
| this.endElement("", "input", "input"); |
| } |
| } |
| |
| } |
| } else if ( "text".equals(format) ) { |
| this.sendTextEvent(uri); |
| this.stack.push(""); |
| } else if ( "parameters".equals(format) ) { |
| final String value = uri.substring(uri.indexOf('?')+1); |
| this.sendTextEvent(value); |
| this.stack.push(""); |
| } else { |
| // own format |
| newAttrs.addCDATAAttribute("href", uri); |
| this.sendStartElementEvent("link", newAttrs); |
| this.stack.push("link"); |
| } |
| } |
| } |