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.Map;
021:
022: import org.apache.avalon.framework.parameters.Parameters;
023: import org.apache.cocoon.ProcessingException;
024: import org.apache.cocoon.environment.SourceResolver;
025: import org.apache.cocoon.portal.coplet.CopletInstanceData;
026: import org.apache.cocoon.xml.AttributesImpl;
027: import org.apache.excalibur.source.SourceUtil;
028: import org.xml.sax.Attributes;
029: import org.xml.sax.SAXException;
030:
031: /**
032: * This transformer transforms html actions into events.
033: * The transformer listens for the element a and form. Links
034: * that only contain an anchor are ignored.
035: * In addition if a link has the attribute "external" with the value
036: * "true", the link is also ignored.
037: *
038: * TODO: Support target attribute
039: *
040: * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
041: * @version CVS $Id: HTMLEventLinkTransformer.java 433543 2006-08-22 06:22:54Z crossley $
042: */
043: public class HTMLEventLinkTransformer extends AbstractCopletTransformer {
044:
045: /** The temporary attribute used to store the uri */
046: protected String attributeName;
047:
048: /** The jxpath for the attribute */
049: protected String jxPath;
050:
051: /**
052: * @see org.apache.cocoon.sitemap.SitemapModelComponent#setup(org.apache.cocoon.environment.SourceResolver, java.util.Map, java.lang.String, org.apache.avalon.framework.parameters.Parameters)
053: */
054: public void setup(SourceResolver resolver, Map objectModel,
055: String src, Parameters par) throws ProcessingException,
056: SAXException, IOException {
057: super .setup(resolver, objectModel, src, par);
058: this .attributeName = par.getParameter("attribute-name",
059: "application-uri");
060: this .jxPath = "temporaryAttributes/" + this .attributeName;
061: }
062:
063: /**
064: * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
065: */
066: public void startElement(String uri, String name, String raw,
067: Attributes attr) throws SAXException {
068: boolean processed = false;
069: if ("a".equals(name)) {
070: final AttributesImpl a = this .getMutableAttributes(attr);
071: attr = a;
072: boolean convert = false;
073: final boolean isRemoteAnchor = this .isRemoteAnchor(attr);
074: if (isRemoteAnchor) {
075: convert = !this .isExternalLink(a);
076: }
077: this .stack.push(convert ? Boolean.TRUE : Boolean.FALSE);
078: if (convert) {
079: this .createAnchorEvent(attr);
080: processed = true;
081: }
082: } else if ("form".equals(name)) {
083: final AttributesImpl a = this .getMutableAttributes(attr);
084: attr = a;
085: boolean convert = !this .isExternalForm(a);
086: this .stack.push(convert ? Boolean.TRUE : Boolean.FALSE);
087: if (convert) {
088: this .createFormEvent(attr);
089: processed = true;
090: }
091: }
092: if (!processed) {
093: super .startElement(uri, name, raw, attr);
094: }
095: }
096:
097: /**
098: * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
099: */
100: public void endElement(String uri, String name, String raw)
101: throws SAXException {
102: boolean processed = false;
103: if ("a".equals(name)) {
104: final Boolean converted = (Boolean) this .stack.pop();
105: if (converted.booleanValue()) {
106: this .xmlConsumer.endElement(
107: CopletTransformer.NAMESPACE_URI,
108: CopletTransformer.LINK_ELEM, "coplet:"
109: + CopletTransformer.LINK_ELEM);
110: this .xmlConsumer.endPrefixMapping("coplet");
111: processed = true;
112: }
113: } else if ("form".equals(name)) {
114: final Boolean converted = (Boolean) this .stack.pop();
115: if (converted.booleanValue()) {
116: this .xmlConsumer.endElement(
117: CopletTransformer.NAMESPACE_URI,
118: CopletTransformer.LINK_ELEM, "coplet:"
119: + CopletTransformer.LINK_ELEM);
120: this .xmlConsumer.endPrefixMapping("coplet");
121: processed = true;
122: }
123: }
124: if (!processed) {
125: super .endElement(uri, name, raw);
126: }
127: }
128:
129: protected void createAnchorEvent(Attributes attributes)
130: throws SAXException {
131: AttributesImpl newAttributes = new AttributesImpl(attributes);
132: newAttributes.removeAttribute("href");
133: newAttributes.removeAttribute("external");
134: String link = attributes.getValue("href");
135:
136: CopletInstanceData cid = this .getCopletInstanceData();
137: link = this .getLink((String) cid
138: .getTemporaryAttribute(this .attributeName), link);
139:
140: newAttributes.addCDATAAttribute("path", this .jxPath);
141: newAttributes.addCDATAAttribute("value", link);
142: newAttributes.addCDATAAttribute("coplet", cid.getId());
143: newAttributes.addCDATAAttribute("format", "html-link");
144: this .xmlConsumer.startPrefixMapping("coplet",
145: CopletTransformer.NAMESPACE_URI);
146: this .xmlConsumer.startElement(CopletTransformer.NAMESPACE_URI,
147: CopletTransformer.LINK_ELEM, "coplet:"
148: + CopletTransformer.LINK_ELEM, newAttributes);
149: }
150:
151: protected void createFormEvent(Attributes attributes)
152: throws SAXException {
153: AttributesImpl newAttributes = new AttributesImpl(attributes);
154: newAttributes.removeAttribute("action");
155: String link = attributes.getValue("action");
156:
157: CopletInstanceData cid = this .getCopletInstanceData();
158: link = this .getLink((String) cid
159: .getTemporaryAttribute(this .attributeName), link);
160:
161: newAttributes.addCDATAAttribute("path", this .jxPath);
162: newAttributes.addCDATAAttribute("value", link);
163: newAttributes.addCDATAAttribute("coplet", cid.getId());
164: newAttributes.addCDATAAttribute("format", "html-form");
165: if (newAttributes.getIndex("method") == -1) {
166: newAttributes.addCDATAAttribute("method", "POST");
167: }
168:
169: this .xmlConsumer.startPrefixMapping("coplet",
170: CopletTransformer.NAMESPACE_URI);
171: this .xmlConsumer.startElement(CopletTransformer.NAMESPACE_URI,
172: CopletTransformer.LINK_ELEM, "coplet:"
173: + CopletTransformer.LINK_ELEM, newAttributes);
174: }
175:
176: protected String getLink(String base, String link) {
177: final String v = SourceUtil.absolutize(base, link);
178: return v;
179: }
180:
181: /**
182: * Determine if the element is an url and if the url points to some
183: * remote source.
184: *
185: * @param attributes the attributes of the element
186: * @return true if the href url is an anchor pointing to a remote source
187: */
188: protected boolean isRemoteAnchor(Attributes attributes) {
189: String link = attributes.getValue("href");
190:
191: // no empty link to current document
192: if (link != null && link.trim().length() > 0) {
193: // check reference to document fragment
194: if (!link.trim().startsWith("#")) {
195: return true;
196: }
197: }
198:
199: return false;
200: }
201:
202: /**
203: * Is this link an external link?
204: * A link in an external application is not transformed
205: * if there is an attribute external="true" in the link-element
206: * or if the link starts with "mailto:" or "javascript:".
207: *
208: * @param attributes attributes of the node
209: * @return true if the attribute 'external' is 'true'
210: */
211: private boolean isExternalLink(AttributesImpl attributes) {
212: final String external = attributes.getValue("external");
213: // remote attribute
214: if (external != null) {
215: attributes.removeAttribute("external");
216: }
217: // links to external documents will be not transformed to portal links
218: if (external != null && external.trim().length() > 0
219: && external.trim().toLowerCase().equals("true")) {
220: return true;
221: }
222: final String link = attributes.getValue("href");
223: if (link != null
224: && (link.startsWith("mailto:") || link
225: .startsWith("javascript:"))) {
226: return true;
227: }
228: return false;
229: }
230:
231: /**
232: * Does this form contain an external action?
233: * A form is not transformed if there is an attribute
234: * external="true" in the form action or if the action
235: * starts with "javascript:".
236: *
237: * @param attributes attributes of the node
238: * @return True if the action is external.
239: */
240: private boolean isExternalForm(AttributesImpl attributes) {
241: final String external = attributes.getValue("external");
242: // remote attribute
243: if (external != null) {
244: attributes.removeAttribute("external");
245: }
246: // links to external documents will be not transformed to portal links
247: if (external != null && external.trim().length() > 0
248: && external.trim().toLowerCase().equals("true")) {
249: return true;
250: }
251: final String link = attributes.getValue("action");
252: if (link != null && link.startsWith("javascript:")) {
253: return true;
254: }
255: return false;
256: }
257: }
|