    001// Copyright 2007 by Basil Vandegriend.  All rights reserved.
    002
    003package com.basilv.envgen;
    004
    005import java.util.HashMap;
    006import java.util.Iterator;
    007import java.util.Map;
    008
    009import com.basilv.core.Assert;
    010
    011import freemarker.template.TemplateHashModel;
    012import freemarker.template.TemplateModel;
    013import freemarker.template.TemplateScalarModel;
    014
    015/**
    016 * Adapts a Map of property names and values to Freemarker's TemplateModel interface.
    017 * Properties of the form "foo.bar" need special handling, as FreeMarker considers a period
    018 * to be a dereference operator, and thus first looks up property "foo", and then in the result looks up
    019 * property "bar". So we store these properties in the same fashion.
    020 *
    021 * This class can represent both a simple string value for a given property, or a map of properties.
    022 * This allows it to store the properties "foo.bar=one" and "foo.bar.baz=two" as follows. The property "foo"
    023 * is mapped to instance #2 of this class. Instance #2 has property "bar", which maps to instance #3
    024 * that holds the string value "one" and holds the property "baz" which maps to instance #4 that just holds
    025 * the string value "two".
    026 */
    027public class TemplateMapModel implements TemplateHashModel, TemplateScalarModel
    028{
    029
    030        private Map map = new HashMap();
    031        private String stringValue;
    032
    033        private TemplateMapModel() {
    034        }
    035
    036        public TemplateMapModel(Map properties) {
    037                Assert.notNull("properties", properties);
    038
    039                for (Iterator i = properties.entrySet().iterator(); i.hasNext(); ) {
    040                        Map.Entry entry = (Map.Entry) i.next();
    041                        String key = (String) entry.getKey();
    042                        Assert.notNullOrEmpty("key", key);
    043                        put(key, (String)entry.getValue());
    044
    045                }
    046        }
    047
    048        public TemplateMapModel getAsTemplateMapModel(String key) {
    049                return (TemplateMapModel) map.get(key);
    050        }
    051
    052        public TemplateModel get(String key) {
    053                return getAsTemplateMapModel(key);
    054        }
    055
    056        public boolean isEmpty() {
    057                return map.isEmpty();
    058        }
    059
    060        public String getAsString() {
    061                return stringValue;
    062        }
    063
    064        private void put(String key, String value) {
    065                Assert.notNullOrEmpty("key", key);
    066
    067                if (key.indexOf(".") != -1) {
    068                        putDereferencingKey(key, value);
    069                } else {
    070                        putRegularKey(key, value);
    071                }
    072
    073        }
    074
    075        private void putDereferencingKey(String key, String value) {
    076                // Key is of the form foo.bar, which we want to split into the first 'foo' plus the remainder
    077                // The remainder may also contain a period, so this recursively calls put.
    078                String[] parts = key.split("\\.", 2);
    079                Assert.isTrue("Key [" + key + "] was not properly split two parts.", parts.length==2);
    080                String firstKey = parts[0];
    081                String remainingKey = parts[1];
    082
    083                TemplateMapModel modelForKey = getModelForKeyCreatingIfNotExists(firstKey);
    084                modelForKey.put(remainingKey, value);
    085        }
    086
    087        private void putRegularKey(String key, String value) {
    088                TemplateMapModel modelForKey = getModelForKeyCreatingIfNotExists(key);
    089                modelForKey.stringValue = value;
    090        }
    091
    092        private TemplateMapModel getModelForKeyCreatingIfNotExists(String key) {
    093
    094                TemplateMapModel modelForKey = (TemplateMapModel) map.get(key);
    095
    096                if (modelForKey == null) {
    097                        modelForKey = new TemplateMapModel();
    098                        map.put(key, modelForKey);
    099                }
    100                return modelForKey;
    101        }
    102
    103}