/*
* 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.util.Stack;
import org.apache.cocoon.portal.LinkService;
import org.apache.cocoon.portal.coplet.CopletInstanceData;
import org.apache.cocoon.portal.event.impl.CopletLinkEvent;
import org.w3c.dom.DocumentFragment;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
/**
* This transformer searches for event descriptions in the XML.
* For each one an event is created and the event link is inserted into the XML
* instead of the description.<br><br>
*
* Example:<br><br>
*
* <pre><root xmlns:event="http://apache.org/cocoon/portal/eventlink/1.0">
* <event:event attribute="href">
* <a href="http://eventlinkexample"/>
* </event:event>
* <event:event element="uri">
* <link><uri>http://eventlinkexample</uri></link>
* </event:event>
* </root><br></pre>
*
* The transformer will create two CopletLinkEvents and insert corresponding links
* to them to the XML instead of "http://eventlinkexample". If such a link is pressed
* the corresponding CopletLinkEvent is sent to the Subscribers to be handled.<br>
* Please see also the documentation of superclass AbstractCopletTransformer for how
* the coplet instance data are acquired.
*
* @author <a href="mailto:bluetkemeier@s-und-n.de">Björn Lütkemeier</a>
* @version $Id$
*/
public class EventLinkTransformer extends AbstractCopletTransformer {
/**
* The namespace URI to listen for.
*/
public static final String NAMESPACE_URI = "http://apache.org/cocoon/portal/eventlink/1.0";
/**
* The XML element name to listen for.
*/
public static final String EVENT_ELEM = "event";
/**
* An attribute's name of EVENT_ELEMENT.
*/
public static final String ATTRIBUTE_ATTR = "attribute";
/**
* An attribute's name of EVENT_ELEMENT.
*/
public static final String ELEMENT_ATTR = "element";
/**
* Used to signal whether the transformer is inside an EVENT_ELEM tag.
*/
private boolean insideEvent = false;
/**
* The attribute defining the link inside an EVENT_ELEM tag.
*/
private String attributeName;
/**
* The element defining the link inside an EVENT_ELEM tag.
*/
private String elementName;
/**
* Used to store elements' attributes between startTransformingElement and endTransformingElement.
*/
private Stack attrStack = new Stack();
/**
* Overridden from superclass.
*/
public void recycle() {
this.insideEvent = false;
this.attributeName = null;
this.elementName = null;
this.attrStack.clear();
super.recycle();
}
/**
* Overridden from superclass.
*/
public void startElement(String uri, String name, String raw, Attributes attr)
throws SAXException {
if (uri.equals(NAMESPACE_URI) && name.equals(EVENT_ELEM)) {
if (this.insideEvent) {
throw new SAXException("Elements "+EVENT_ELEM+" must not be nested.");
}
this.insideEvent = true;
// get element or attribute name that contains links
this.attributeName = attr.getValue(ATTRIBUTE_ATTR);
this.elementName = attr.getValue(ELEMENT_ATTR);
// at least one of them must be set
if (this.attributeName == null && this.elementName == null) {
throw new SAXException("Element "+EVENT_ELEM+" must have one of attributes "+ATTRIBUTE_ATTR+" and "+ELEMENT_ATTR+".");
}
} else {
if (this.insideEvent) {
// store attributes for endTransformingElement
this.attrStack.push(new AttributesImpl(attr));
/* Record element content. In case of an element we asume, that no
* children exist but only text content, since the text content shall
* be the link. Therefore we do startTextRecording. Otherwise we
* record the whole subtree.
*/
if (this.elementName != null && name.equals(this.elementName)) {
this.startTextRecording();
} else {
this.startRecording();
}
} else {
super.startElement(uri, name, raw, attr);
}
}
}
/**
* Overridden from superclass.
*/
public void endElement(String uri, String name, String raw)
throws SAXException {
if (uri.equals(NAMESPACE_URI) && name.equals(EVENT_ELEM)) {
this.attributeName = null;
this.elementName = null;
this.insideEvent = false;
} else {
if (this.insideEvent) {
AttributesImpl attr = (AttributesImpl)this.attrStack.pop();
// process attribute that contains link
if (this.attributeName != null) {
int index = attr.getIndex(this.attributeName);
String link = attr.getValue(index);
// if attribute found that contains a link
if (link != null) {
CopletInstanceData cid = this.getCopletInstanceData(attr.getValue("coplet"));
LinkService linkService = this.portalService.getComponentManager().getLinkService();
// create event link
CopletLinkEvent event = new CopletLinkEvent(cid, link);
String eventLink = linkService.getLinkURI(event);
// insert event link
attr.setValue(index, eventLink);
}
}
String eventLink = null;
DocumentFragment fragment = null;
// process element that contains link
if (this.elementName != null && name.equals(this.elementName)) {
String link = this.endTextRecording();
CopletInstanceData cid = this.getCopletInstanceData();
LinkService linkService = this.portalService.getComponentManager().getLinkService();
// create event link
CopletLinkEvent event = new CopletLinkEvent(cid, link);
eventLink = linkService.getLinkURI(event);
} else {
fragment = this.endRecording();
}
// stream element
super.startElement(uri, name, raw, attr);
if (eventLink != null) {
// insert event link
super.characters(eventLink.toCharArray(), 0, eventLink.length());
} else if (fragment != null) {
super.sendEvents(fragment);
}
super.endElement(uri, name, raw);
} else {
super.endElement(uri, name, raw);
}
}
}
}