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     */
016    package org.joda.beans.integrate.freemarker;
017    
018    import org.joda.beans.Bean;
019    import org.joda.beans.MetaProperty;
020    
021    import freemarker.ext.beans.BeanModel;
022    import freemarker.ext.beans.BeansWrapper;
023    import freemarker.template.AdapterTemplateModel;
024    import freemarker.template.SimpleCollection;
025    import freemarker.template.SimpleScalar;
026    import freemarker.template.TemplateCollectionModel;
027    import freemarker.template.TemplateHashModelEx;
028    import freemarker.template.TemplateModel;
029    import freemarker.template.TemplateModelException;
030    import 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     */
038    public 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            _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    }