    001    // Copyright 2007 by Basil Vandegriend.  All rights reserved.
    002
    003    package com.basilv.envgen;
    004
    005    import java.util.HashMap;
    006    import java.util.Iterator;
    007    import java.util.Map;
    008
    009    import com.basilv.core.Assert;
    010
    011    import freemarker.template.TemplateHashModel;
    012    import freemarker.template.TemplateModel;
    013    import 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     */
    027    public 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    }