| /* |
| * 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(); |
| } |
| } |
| } |