/*
* Copyright 2000-2001,2004 The Apache Software Foundation.
*
* Licensed 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.jetspeed.portal.portlets.viewprocessor;
//standard java stuff
import java.io.Reader;
import java.io.StringReader;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.io.IOException;
//JAXP support
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
// Jetspeed api
import org.apache.jetspeed.cache.disk.JetspeedDiskCache;
import org.apache.jetspeed.capability.CapabilityMap;
import org.apache.jetspeed.capability.CapabilityMapFactory;
import org.apache.jetspeed.portal.Portlet;
import org.apache.jetspeed.portal.PortletException;
import org.apache.jetspeed.portal.portlets.GenericMVCContext;
import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
import org.apache.jetspeed.services.logging.JetspeedLogger;
import org.apache.jetspeed.services.rundata.JetspeedRunData;
import org.apache.jetspeed.util.JetspeedClearElement;
import org.apache.jetspeed.util.MimeType;
import org.apache.jetspeed.util.SimpleTransform;
import org.apache.jetspeed.xml.JetspeedXMLEntityResolver;
// Element Construction Set
import org.apache.ecs.ConcreteElement;
// Turbine api
import org.apache.turbine.util.RunData;
// XML stuff
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
* Simple ViewProcessor which does a basic XSLT transform with the stylesheet parameter
* and the given URL.
*
* @author tkuebler@cisco.com
* @version $Id: $
* @since 1.4b4
*/
public class XSLViewProcessor implements ViewProcessor
{
/**
* Static initialization of the logger for this class
*/
private static final JetspeedLogger logger = JetspeedLogFactoryService.getLogger(XSLViewProcessor.class.getName());
private static final String XMLDECL = "<?xml version=";
public static final String ERROR_NOT_VALID = "This does not appear to be an XML document";
public static final String INVALID_TYPE = "Unable to display for this browser";
protected Document document = null;
protected Hashtable stylesheets = null;
private Hashtable params = null;
/**
* This method loads the init parameters and
* parse the document tied to this portlet
*
* @param portlet
* @exception PortletException
*/
public void init(Portlet portlet)
throws PortletException
{
DocumentBuilder parser = null;
String url = null;
// load stylesheets available
stylesheets = new Hashtable();
params = new Hashtable();
Iterator i = portlet.getPortletConfig().getInitParameterNames();
while (i.hasNext())
{
String name = (String) i.next();
String base = MimeType.HTML.toString();
if (name.startsWith("stylesheet"))
{
int idx = -1;
if ((idx = name.indexOf(".")) > -1)
{
base = name.substring(idx + 1, name.length());
}
stylesheets.put(base, portlet.getPortletConfig().getInitParameter(name));
}
else
{
params.put(name.toLowerCase(), portlet.getPortletConfig().getInitParameter(name));
}
}
// read content, clean it, parse it and cache the DOM
try
{
final DocumentBuilderFactory docfactory = DocumentBuilderFactory.newInstance();
//Have it non-validating
docfactory.setValidating(false);
parser = docfactory.newDocumentBuilder();
parser.setEntityResolver(new JetspeedXMLEntityResolver());
url = portlet.getPortletConfig().getURL();
String content = JetspeedDiskCache.getInstance().getEntry(url).getData();
CapabilityMap xmap = CapabilityMapFactory.getCapabilityMap(CapabilityMapFactory.AGENT_XML);
// no cache yet // portlet.setContent( new JetspeedClearElement(content), xmap );
InputSource isrc = new InputSource(this.cleanse(content));
isrc.setSystemId(url);
isrc.setEncoding("UTF-8");
this.document = parser.parse(isrc);
}
catch (Throwable t)
{
String message = "XSLViewProcessor: Couldn't parse out XML document -> " + url;
logger.error(message, t);
throw new PortletException(t.getMessage());
}
}
/**
* This methods outputs the content of the portlet for a given
* request.
*
* @param context
* @return the content to be displayed to the user-agent
*/
public Object processView(GenericMVCContext context)
{
try
{
init((Portlet) context.get("portlet"));
}
catch (PortletException pe)
{
logger.error("XSLViewProcessor - error: " + pe.getMessage(), pe);
}
RunData data = (RunData) context.get("data");
CapabilityMap map = ((JetspeedRunData) data).getCapability();
String type = map.getPreferredType().toString();
ConcreteElement content = new JetspeedClearElement(INVALID_TYPE);
String stylesheet = (String) stylesheets.get(type);
if (stylesheet != null)
{
try
{
content = new JetspeedClearElement(SimpleTransform.transform(this.document, stylesheet, this.params));
// no caching yet // setContent( content, map );
}
catch (SAXException e)
{
logger.error("SAXException", e);
content = new JetspeedClearElement(e.getMessage());
}
}
else
{
content = new JetspeedClearElement("stylesheet not defined");
}
return content;
}
/**
* This portlet supports has many types as those
* it has stylesheets defined for in its parameters
*
* @param mimeType the MIME type queried
* @return true if the portlet knows how to display
* content for mimeType
* @see Portlet#supportsType
*/
public boolean supportsType(MimeType mimeType)
{
Enumeration en = stylesheets.keys();
while (en.hasMoreElements())
{
String type = (String) en.nextElement();
if (type.equals(mimeType.toString()))
{
return true;
}
}
return false;
}
/**
* Utility method for traversing the document parsed
* DOM tree and retrieving a Node by tagname
*
* @param start the parent node for the search
* @param name the tag name to be searched for
* @return the first child node of start whose tagname
* is name
*/
protected Node getNode(Node start, String name)
{
NodeList list = start.getChildNodes();
for (int i = 0; i < list.getLength(); ++i)
{
Node node = list.item(i);
if (node.getNodeName().equals(name))
{
return node;
}
}
return null;
}
/**
* Given a URL to some content, clean the content to Xerces can handle it
* better. Right now this involves:
* <ul>
* <li>
* If the document doesn't begin with "<?xml version=" truncate the
* content until this is the first line
* </li>
*
* </ul>
*
* @param content
* @return
* @exception IOException
*/
protected Reader cleanse(String content)
throws IOException
{
String filtered = null;
int start = content.indexOf(XMLDECL);
if (start <= 0)
{
filtered = content;
}
else
{
filtered = content.substring(start, content.length());
}
return new StringReader(filtered);
}
}