001/* 002 * Copyright 2001-2013 Stephen Colebourne 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.joda.beans.integrate.freemarker; 017 018import org.joda.beans.Bean; 019import org.joda.beans.MetaProperty; 020 021import freemarker.ext.beans.BeanModel; 022import freemarker.ext.beans.BeansWrapper; 023import freemarker.template.AdapterTemplateModel; 024import freemarker.template.SimpleCollection; 025import freemarker.template.SimpleScalar; 026import freemarker.template.TemplateCollectionModel; 027import freemarker.template.TemplateHashModelEx; 028import freemarker.template.TemplateModel; 029import freemarker.template.TemplateModelException; 030import freemarker.template.WrappingTemplateModel; 031 032/** 033 * Template model converting a Joda-Bean to a Freemarker model. 034 * <p> 035 * Although this class is public, it should not normally be use directly. 036 * Follow the instructions in {@link FreemarkerObjectWrapper} to use this class. 037 */ 038public class FreemarkerTemplateModel 039 extends WrappingTemplateModel 040 implements TemplateHashModelEx, AdapterTemplateModel { 041 042 /** 043 * The bean being wrapped. 044 */ 045 private final Bean bean; 046 047 /** 048 * Creates an instance of the model. 049 * @param bean the bean being wrapped, not null 050 * @param wrapper the default wrapper for further wrapping, not null 051 */ 052 public FreemarkerTemplateModel(final Bean bean, final FreemarkerObjectWrapper wrapper) { 053 super(wrapper); 054 this.bean = bean; 055 } 056 057 //------------------------------------------------------------------------- 058 /** 059 * Gets the value for the specified key, wrapping the result in another model. 060 * @param key the property name, not null 061 * @return the model, null if not found 062 */ 063 @Override 064 public TemplateModel get(String key) throws TemplateModelException { 065 MetaProperty<?> metaProperty = bean.metaBean().metaPropertyMap().get(key); 066 if (metaProperty == null) { 067 // try standard approach via BeanModel for non-bean properties and methods 068 BeanModel model = new BeanModel(bean, (BeansWrapper) getObjectWrapper()); 069 TemplateModel result = model.get(key); 070 if (result instanceof SimpleScalar) { 071 // have to map empty string to null 072 String str = ((SimpleScalar) result).getAsString(); 073 if (str == null || str.isEmpty()) { 074 return null; 075 } 076 } 077 return result; 078 } 079 return wrap(metaProperty.get(bean)); 080 } 081 082 /** 083 * Checks if there are no properties. 084 * @return true if no properties 085 */ 086 @Override 087 public boolean isEmpty() { 088 return size() == 0; 089 } 090 091 /** 092 * Gets the number of properties. 093 * @return the number of properties 094 */ 095 @Override 096 public int size() { 097 return bean.metaBean().metaPropertyCount(); 098 } 099 100 /** 101 * Gets the full set of property names, allowing the bean to be accessed as a sequence. 102 * @return the property names, not null 103 */ 104 @Override 105 public TemplateCollectionModel keys() { 106 return new SimpleCollection(bean.propertyNames(), getObjectWrapper()); 107 } 108 109 /** 110 * Gets the full set of property values, allowing the bean to be accessed as a sequence. 111 * @return the wrapped property values, not null 112 */ 113 @Override 114 public TemplateCollectionModel values() { 115 return new SimpleCollection(bean.metaBean().createPropertyMap(bean).flatten().values(), getObjectWrapper()); 116 } 117 118 /** 119 * Unwraps the model, returning the bean. 120 * @param hint the class hint 121 * @return the underlying bean, not null 122 */ 123 @SuppressWarnings("rawtypes") 124 @Override 125 public Object getAdaptedObject(Class hint) { 126 return bean; 127 } 128 129}