blob: e04e79ebaf624a7bd679f76cd87aaa9419805e35 [file] [log] [blame]
/*
* Copyright 2009 Niclas Hedhman.
*
* 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.zest.sample.rental.web;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import org.apache.zest.api.Qi4j;
import org.apache.zest.api.composite.Composite;
import org.apache.zest.api.concern.Concerns;
import org.apache.zest.api.injection.scope.Uses;
import org.apache.zest.api.mixin.Mixins;
import org.apache.zest.api.service.ServiceComposite;
import org.apache.zest.api.service.ServiceDescriptor;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import static org.apache.zest.functional.Iterables.first;
@Concerns( PageUowManagement.class )
@Mixins( { Page.MountPointMixin.class, Page.DefaultPageRenderMixin.class } )
public interface Page
extends ServiceComposite
{
String XHTML = "http://www.w3.org/1999/xhtml";
String QI = "http://www.qi4j.org/ns/2009/quikit";
/**
* Returns the URL where the Page is mounted.
*
* @return the URL mountpoint.
*/
String mountPoint();
void render( QuikitContext context )
throws RenderException;
abstract class DefaultPageRenderMixin
implements Page
{
public void render( QuikitContext context )
throws RenderException
{
Document dom = context.dom();
Element htmlElement = dom.getDocumentElement();
Element bodyElement = (Element) htmlElement.getElementsByTagNameNS( Page.XHTML, "body" ).item( 0 );
parseElement( context, bodyElement, htmlElement );
}
private void parseElement( QuikitContext context, Element element, Element parent )
throws RenderException
{
String method = element.getAttributeNS( Page.QI, "method" );
if( method.length() > 0 )
{
context.setDynamic( method, element, parent );
execute( context, element, parent );
return;
}
NodeList nodes = element.getChildNodes();
for( int i = 0; i < nodes.getLength(); i++ )
{
Node node = nodes.item( i );
if( node instanceof Element )
{
parseElement( context, (Element) node, element );
}
}
}
private void execute( QuikitContext context, Element element, Element parent )
throws RenderException
{
Class<? extends Composite> compositeType = (Class<Composite>) first( Qi4j.FUNCTION_DESCRIPTOR_FOR
.map( context.page() )
.types() );
try
{
Method method = findMethod( context.methodName(), compositeType );
Object result = method.invoke( context.page(), context );
if( result instanceof String )
{
element.setTextContent( (String) result );
return;
}
if( result instanceof Node )
{
parent.replaceChild( (Node) result, element );
return;
}
if( result instanceof List )
{
for( Node node : (List<Node>) result )
{
element.appendChild( node );
}
return;
}
if( result.getClass().isArray() )
{
Class type = result.getClass().getComponentType();
if( Node.class.isAssignableFrom( type ) )
{
for( Node node : (Node[]) result )
{
element.appendChild( node );
}
}
return;
}
// TODO: Future!!!
// if( result instanceof EntityComposite )
// {
// Locate HTML template for reault.type()
// If not present, use EntityComposite.html
// repeat rendering.
// Need mechanism to pass the "reference"
// }
element.setTextContent( result.toString() );
}
catch( NoSuchMethodException e )
{
String message = "Method '" + context.methodName() + "' does not exist in " + compositeType.getSimpleName();
throw new RenderException( message, e );
}
catch( IllegalAccessException e )
{
String message = "Method '" + context.methodName() + "' is not public in " + compositeType.getSimpleName();
throw new RenderException( message, e );
}
catch( InvocationTargetException e )
{
if( e.getTargetException() instanceof RenderException )
{
throw ( (RenderException) e.getTargetException() );
}
throw new RenderException( "Method '" + context.methodName() + "' threw an exception.", e );
}
}
private Method findMethod( String methodName, Class<? extends Composite> compositeType )
throws NoSuchMethodException
{
// TODO: Add caching since locating the methods and the throwing of exceptions are expensive.
Method method = compositeType.getMethod( methodName, QuikitContext.class );
method.setAccessible( true );
return method;
}
}
abstract class MountPointMixin
implements Page
{
@Uses
ServiceDescriptor descriptor;
public String mountPoint()
{
return descriptor.metaInfo( PageMetaInfo.class ).mountPoint();
}
}
}