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}