001: /* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
002: * This code is licensed under the GPL 2.0 license, availible at the root
003: * application directory.
004: */
005: package org.geoserver.template;
006:
007: import java.text.DateFormat;
008: import java.util.Date;
009: import java.util.HashMap;
010: import java.util.LinkedHashMap;
011: import java.util.Map;
012:
013: import org.geotools.feature.AttributeType;
014: import org.geotools.feature.Feature;
015: import org.geotools.feature.FeatureCollection;
016: import org.geotools.feature.FeatureType;
017: import org.geotools.feature.GeometryAttributeType;
018:
019: import com.vividsolutions.jts.geom.Geometry;
020:
021: import freemarker.ext.beans.BeansWrapper;
022: import freemarker.ext.beans.CollectionModel;
023: import freemarker.template.Configuration;
024: import freemarker.template.SimpleHash;
025: import freemarker.template.SimpleSequence;
026: import freemarker.template.TemplateModel;
027: import freemarker.template.TemplateModelException;
028:
029: /**
030: * Wraps a {@link Feature} in the freemarker {@link BeansWrapper} interface
031: * allowing a template to be directly applied to a {@link Feature} or
032: * {@link FeatureCollection}.
033: * <p>
034: * When a {@link FeatureCollection} is being processed by the template, it is
035: * available via the <code>$features</code> variable, which can be broken down into single features and attributes following this hierarchy:
036: * <ul>
037: * <li>features -> feature</li>
038: * <ul>
039: * <li>fid (String)</li>
040: * <li>typeName (String)</li>
041: * <li>attributes -> attribute</li>
042: * <ul>
043: * <li>value (String), a default String representation of the attribute value</li>
044: * <li>rawValue (Object), the actual attribute value if it's non null, the empty string otherwise</li>
045: * <li>name (String)</li>
046: * <li>type (String)</li>
047: * <li>isGeometry (Boolean)</li>
048: * </ul>
049: * </ul>
050: * </ul>
051: * Example of a template processing a feature collection which will print
052: * out the features id of every feature in the collection.
053: * <pre><code>
054: * <#list features as feature>
055: * FeatureId: ${feature.fid}
056: * </#list>
057: * </code></pre>
058: * </p>
059: * <p>
060: * To use this wrapper,use the {@link Configuration#setObjectWrapper(freemarker.template.ObjectWrapper)}
061: * method:
062: * <pre>
063: * <code>
064: * //features we want to apply template to
065: * FeatureCollection features = ...;
066: *
067: * //create the configuration and set the wrapper
068: * Configuration cfg = new Configuration();
069: * cfg.setObjectWrapper( new FeatureWrapper() );
070: *
071: * //get the template and go
072: * Template template = cfg.getTemplate( "foo.ftl" );
073: * template.process( features, System.out );
074: *
075: * </code>
076: * </pre>
077: * </p>
078: * </p>
079: * @author Justin Deoliveira, The Open Planning Project, jdeolive@openplans.org
080: * @author Andrea Aime, TOPP
081: *
082: */
083: public class FeatureWrapper extends BeansWrapper {
084: public FeatureWrapper() {
085: setSimpleMapWrapper(true);
086: }
087:
088: /**
089: * Returns a sensible String value for attributes so they are
090: * easily used by templates.
091: * <p>
092: * Special cases:
093: * <ul>
094: * <li>for Date values returns a default {@link DateFormat} representation</li>
095: * <li>for Boolean values returns "true" or "false"</li>
096: * <li>for null values returns an empty string</li>
097: * <li>for any other value returns its toString()</li>
098: * </ul>
099: * </p>
100: *
101: * @param o could be an instance of Date (a special case)
102: * @return the formated date as a String, or the object
103: */
104: protected String wrapValue(Object o) {
105: if (o == null) {
106: //nulls throw tempaltes off, use empty string
107: return "";
108: }
109: if (o instanceof Date) {
110: return DateFormat.getInstance().format((Date) o);
111: }
112: if (o instanceof Boolean) {
113: return ((Boolean) o).booleanValue() ? "true" : "false";
114: }
115: return String.valueOf(o);
116: }
117:
118: public TemplateModel wrap(Object object)
119: throws TemplateModelException {
120: //check for feature collection
121: if (object instanceof FeatureCollection) {
122: //create a model with just one variable called 'features'
123: SimpleHash map = new SimpleHash();
124: map.put("features", new CollectionModel(
125: (FeatureCollection) object, this ));
126: map.put("type", wrap(((FeatureCollection) object)
127: .getSchema()));
128:
129: return map;
130: } else if (object instanceof FeatureType) {
131: FeatureType ft = (FeatureType) object;
132:
133: // create a variable "attributes" which his a list of all the
134: // attributes, but at the same time, is a map keyed by name
135: Map attributeMap = new LinkedHashMap();
136: for (int i = 0; i < ft.getAttributeCount(); i++) {
137: AttributeType type = ft.getAttributeType(i);
138:
139: Map attribute = new HashMap();
140: attribute.put("name", type.getLocalName());
141: attribute.put("type", type.getBinding().getName());
142: attribute
143: .put(
144: "isGeometry",
145: Boolean
146: .valueOf(type instanceof GeometryAttributeType));
147:
148: attributeMap.put(type.getLocalName(), attribute);
149: }
150:
151: // build up the result, feature type is represented by its name an attributes
152: SimpleHash map = new SimpleHash();
153: map.put("attributes", new SequenceMapModel(attributeMap,
154: this ));
155: map.put("name", ft.getTypeName());
156: return map;
157: } else if (object instanceof Feature) {
158: Feature feature = (Feature) object;
159:
160: //create the model
161: SimpleHash map = new SimpleHash();
162:
163: //first add the feature id
164: map.put("fid", feature.getID());
165: map.put("typeName", feature.getFeatureType().getTypeName());
166:
167: //next add variables for each attribute, variable name = name of attribute
168: SimpleSequence attributes = new SimpleSequence();
169: Map attributeMap = new LinkedHashMap();
170:
171: for (int i = 0; i < feature.getNumberOfAttributes(); i++) {
172: AttributeType type = feature.getFeatureType()
173: .getAttributeType(i);
174:
175: Map attribute = new HashMap();
176: Object value = feature.getAttribute(i);
177: attribute.put("value", wrapValue(value));
178: if (value == null) {
179: //some special case checks
180: attribute.put("rawValue", "");
181: attribute.put("isGeometry",
182: Boolean
183: .valueOf(Geometry.class
184: .isAssignableFrom(type
185: .getBinding())));
186: } else {
187: attribute.put("rawValue", value);
188: attribute.put("isGeometry", Boolean
189: .valueOf(value instanceof Geometry));
190: }
191:
192: attribute.put("name", type.getName());
193: attribute.put("type", type.getType().getName());
194:
195: map.put(type.getName(), attribute);
196: attributeMap.put(type.getName(), attribute);
197: attributes.add(attribute);
198: }
199:
200: // create a variable "attributes" which his a list of all the
201: // attributes, but at the same time, is a map keyed by name
202: map.put("attributes", new SequenceMapModel(attributeMap,
203: this));
204:
205: return map;
206: }
207:
208: return super.wrap(object);
209: }
210: }
|