001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.portal.transformation;
018:
019: import java.io.IOException;
020: import java.util.ArrayList;
021: import java.util.Enumeration;
022: import java.util.List;
023:
024: import org.apache.cocoon.ProcessingException;
025: import org.apache.cocoon.environment.wrapper.RequestParameters;
026: import org.apache.cocoon.portal.LinkService;
027: import org.apache.cocoon.portal.coplet.CopletInstanceData;
028: import org.apache.cocoon.portal.event.impl.ChangeCopletInstanceAspectDataEvent;
029: import org.apache.cocoon.portal.event.impl.CopletJXPathEvent;
030: import org.apache.cocoon.portal.event.impl.JXPathEvent;
031: import org.apache.cocoon.xml.AttributesImpl;
032: import org.apache.cocoon.xml.XMLUtils;
033: import org.apache.commons.jxpath.JXPathContext;
034: import org.apache.excalibur.xml.sax.XMLizable;
035: import org.xml.sax.Attributes;
036: import org.xml.sax.SAXException;
037:
038: /**
039: * This transformer offers various functions for developing pipeline based coplets.
040: *
041: * Includes coplet instance data by using JXPath expressions.
042: * The transformer searches for tags <coplet:coplet xmlns:coplet="http://apache.org/cocoon/portal/coplet/1.0">.
043: * They must have an attribute "select" that contains a valid JXPath expression applying to the coplet instance data.<br><br>
044: *
045: * Example:<br><br>
046: *
047: * <pre><maxpageable xmlns:coplet="http://apache.org/cocoon/portal/coplet/1.0">
048: * <coplet:coplet select="copletData.maxpageable"/>
049: * </maxpageable><br></pre>
050: *
051: * The transformer will insert the boolean value specifying whether the coplet is
052: * maxpageable or not.<br>
053: * Please see also the documentation of superclass AbstractCopletTransformer for how
054: * the coplet instance data are acquired.
055: *
056: * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
057: * @author <a href="mailto:bluetkemeier@s-und-n.de">Björn Lütkemeier</a>
058: * @version CVS $Id: CopletTransformer.java 433543 2006-08-22 06:22:54Z crossley $
059: */
060: public class CopletTransformer extends AbstractCopletTransformer {
061:
062: /**
063: * The namespace URI to listen for.
064: */
065: public static final String NAMESPACE_URI = "http://apache.org/cocoon/portal/coplet/1.0";
066:
067: /**
068: * The XML element name to listen for.
069: */
070: public static final String COPLET_ELEM = "coplet";
071:
072: /**
073: * The attribute containing the JXPath expression.
074: */
075: public static final String SELECT_ATTR = "select";
076:
077: /**
078: * The XML element name to listen for.
079: */
080: public static final String LINK_ELEM = "link";
081:
082: /** Create a link containing several events */
083: public static final String LINKS_ELEM = "links";
084:
085: /** Create a link containing several events */
086: public static final String PARAMETER_ELEM = "parameter";
087:
088: /** The content for the links element */
089: public static final String CONTENT_ELEM = "content";
090:
091: /** Are we inside a links element? */
092: protected boolean insideLinks;
093:
094: /** The collected list of events */
095: protected List collectedEvents = new ArrayList();
096:
097: /** The content of the links */
098: protected XMLizable content;
099:
100: /**
101: * Creates new CopletTransformer.
102: */
103: public CopletTransformer() {
104: this .defaultNamespaceURI = NAMESPACE_URI;
105: }
106:
107: /* (non-Javadoc)
108: * @see org.apache.cocoon.transformation.AbstractSAXTransformer#setupTransforming()
109: */
110: public void setupTransforming() throws IOException,
111: ProcessingException, SAXException {
112: super .setupTransforming();
113: this .insideLinks = false;
114: this .content = null;
115: this .collectedEvents.clear();
116: }
117:
118: /**
119: * Overridden from superclass.
120: */
121: public void startTransformingElement(String uri, String name,
122: String raw, Attributes attr) throws ProcessingException,
123: IOException, SAXException {
124: if (name.equals(COPLET_ELEM)) {
125: String expression = attr.getValue(SELECT_ATTR);
126: if (expression == null) {
127: throw new ProcessingException("Attribute "
128: + SELECT_ATTR + " must be spcified.");
129: }
130:
131: final CopletInstanceData cid = this .getCopletInstanceData();
132:
133: final JXPathContext jxpathContext = JXPathContext
134: .newContext(cid);
135: final Object object = jxpathContext.getValue(expression);
136:
137: if (object != null) {
138: XMLUtils.valueOf(contentHandler, object);
139: }
140:
141: } else if (name.equals(LINK_ELEM)) {
142:
143: final LinkService linkService = this .portalService
144: .getComponentManager().getLinkService();
145: final String format = attr.getValue("format");
146: AttributesImpl newAttrs = new AttributesImpl();
147: newAttrs.setAttributes(attr);
148: newAttrs.removeAttribute("format");
149:
150: if (attr.getValue("href") != null) {
151: final CopletInstanceData cid = this
152: .getCopletInstanceData();
153: ChangeCopletInstanceAspectDataEvent event = new ChangeCopletInstanceAspectDataEvent(
154: cid, null, null);
155:
156: String value = linkService.getLinkURI(event);
157: if (value.indexOf('?') == -1) {
158: value = value + '?' + attr.getValue("href");
159: } else {
160: value = value + '&' + attr.getValue("href");
161: }
162: newAttrs.removeAttribute("href");
163: this .output(value, format, newAttrs);
164: } else {
165: final String path = attr.getValue("path");
166: final String value = attr.getValue("value");
167:
168: newAttrs.removeAttribute("path");
169: newAttrs.removeAttribute("value");
170:
171: JXPathEvent event = null;
172: if (attr.getValue("layout") != null) {
173: newAttrs.removeAttribute("layout");
174: final String layoutId = attr.getValue("layout");
175: Object layout = this .portalService
176: .getComponentManager().getProfileManager()
177: .getPortalLayout(null, layoutId);
178: if (layout != null) {
179: event = new JXPathEvent(layout, path, value);
180: }
181: } else {
182: String copletId = attr.getValue("coplet");
183: newAttrs.removeAttribute("coplet");
184: final CopletInstanceData cid = this
185: .getCopletInstanceData(copletId);
186: if (cid != null) {
187: event = new CopletJXPathEvent(cid, path, value);
188: }
189: }
190: if (this .insideLinks) {
191: if (event != null) {
192: this .collectedEvents.add(event);
193: }
194: } else {
195: final String href = linkService.getLinkURI(event);
196: this .output(href, format, newAttrs);
197: }
198: }
199: } else if (name.equals(PARAMETER_ELEM)) {
200: if (this .insideLinks) {
201: String href = attr.getValue("href");
202: if (href != null) {
203: final int pos = href.indexOf('?');
204: if (pos != -1) {
205: href = href.substring(pos + 1);
206: }
207: this .collectedEvents
208: .add(new LinkService.ParameterDescription(
209: href));
210: }
211: }
212: } else if (name.equals(LINKS_ELEM)) {
213: this .insideLinks = true;
214: final AttributesImpl newAttrs = new AttributesImpl();
215: newAttrs.setAttributes(attr);
216: newAttrs.removeAttribute("format");
217: this .stack.push(newAttrs);
218:
219: String format = attr.getValue("format");
220: if (format == null) {
221: format = "html-link";
222: }
223: this .stack.push(format);
224: } else if (name.equals(CONTENT_ELEM) && this .insideLinks) {
225: this .startSAXRecording();
226: } else {
227: super .startTransformingElement(uri, name, raw, attr);
228: }
229: }
230:
231: /**
232: * Overridden from superclass.
233: */
234: public void endTransformingElement(String uri, String name,
235: String raw) throws ProcessingException, IOException,
236: SAXException {
237: if (name.equals(LINK_ELEM)) {
238: if (!this .insideLinks) {
239: String elem = (String) this .stack.pop();
240: if (elem.length() > 0) {
241: this .sendEndElementEvent(elem);
242: }
243: }
244: } else if (name.equals(LINKS_ELEM)) {
245: this .insideLinks = false;
246: final String format = (String) this .stack.pop();
247: final LinkService linkService = this .portalService
248: .getComponentManager().getLinkService();
249: String href = linkService.getLinkURI(this .collectedEvents);
250:
251: AttributesImpl newAttrs = (AttributesImpl) this .stack.pop();
252: // test for alternate base url
253: final String baseURL = newAttrs.getValue("base-url");
254: if (baseURL != null) {
255: newAttrs.removeAttribute("base-url");
256: int pos = href.indexOf('?') + 1;
257: final char separator;
258: if (baseURL.indexOf('?') == -1) {
259: separator = '?';
260: } else {
261: separator = '&';
262: }
263: href = baseURL + separator + href.substring(pos);
264:
265: }
266: this .output(href, format, newAttrs);
267:
268: this .collectedEvents.clear();
269: if (this .content != null) {
270: this .content.toSAX(this .contentHandler);
271: this .content = null;
272: }
273: String elem = (String) this .stack.pop();
274: if (elem.length() > 0) {
275: this .sendEndElementEvent(elem);
276: }
277: } else if (name.equals(CONTENT_ELEM) && this .insideLinks) {
278: this .content = this .endSAXRecording();
279: } else if (!name.equals(COPLET_ELEM)
280: && !name.equals(PARAMETER_ELEM)) {
281: super .endTransformingElement(uri, name, raw);
282: }
283: }
284:
285: /**
286: * Output the link
287: */
288: protected void output(String uri, String format,
289: AttributesImpl newAttrs) throws SAXException {
290: if (format == null) {
291: // default
292: format = "html-link";
293: }
294:
295: if ("html-link".equals(format)) {
296: newAttrs.addCDATAAttribute("href", uri);
297: this .sendStartElementEvent("a", newAttrs);
298: this .stack.push("a");
299:
300: } else if ("html-form".equals(format)) {
301: boolean addParametersAsHiddenFields = false;
302: String parameters = null;
303: final String enctype = newAttrs.getValue("enctype");
304: if (enctype == null
305: || "application/x-www-form-urlencoded"
306: .equalsIgnoreCase(enctype)
307: || "multipart/form-data".equalsIgnoreCase(enctype)) {
308: final int pos = uri.indexOf('?');
309: if (pos != -1) {
310: parameters = uri.substring(pos + 1);
311: uri = uri.substring(0, pos);
312: addParametersAsHiddenFields = true;
313: }
314: }
315: newAttrs.addCDATAAttribute("action", uri);
316: this .sendStartElementEvent("form", newAttrs);
317: this .stack.push("form");
318: if (addParametersAsHiddenFields) {
319: // create hidden input fields
320: RequestParameters pars = new RequestParameters(
321: parameters);
322: Enumeration enumeration = pars.getParameterNames();
323: while (enumeration.hasMoreElements()) {
324: final String pName = (String) enumeration
325: .nextElement();
326: final String[] pValues = pars
327: .getParameterValues(pName);
328: for (int k = 0; k < pValues.length; k++) {
329: final String pValue = pValues[k];
330: AttributesImpl hiddenAttrs = new AttributesImpl();
331: hiddenAttrs.addCDATAAttribute("type", "hidden");
332: hiddenAttrs.addCDATAAttribute("name", pName);
333: hiddenAttrs.addCDATAAttribute("value", pValue);
334: this .startElement("", "input", "input",
335: hiddenAttrs);
336: this .endElement("", "input", "input");
337: }
338: }
339:
340: }
341: } else if ("text".equals(format)) {
342: this .sendTextEvent(uri);
343: this .stack.push("");
344: } else if ("parameters".equals(format)) {
345: final String value = uri.substring(uri.indexOf('?') + 1);
346: this .sendTextEvent(value);
347: this .stack.push("");
348: } else {
349: // own format
350: newAttrs.addCDATAAttribute("href", uri);
351: this .sendStartElementEvent("link", newAttrs);
352: this .stack.push("link");
353: }
354: }
355: }
|