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    }