All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.geoserver.template.FeatureWrapper Maven / Gradle / Ivy

The newest version!
/* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
 * This code is licensed under the GPL 2.0 license, availible at the root
 * application directory.
 */
package org.geoserver.template;

import java.sql.Time;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.util.AbstractMap;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.geotools.data.DataUtilities;
import org.geotools.feature.FeatureCollection;
import org.geotools.util.MapEntry;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;

import com.vividsolutions.jts.geom.Geometry;

import freemarker.ext.beans.BeansWrapper;
import freemarker.ext.beans.CollectionModel;
import freemarker.ext.beans.IteratorModel;
import freemarker.template.Configuration;
import freemarker.template.SimpleHash;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;

/**
 * Wraps a {@link Feature} in the freemarker {@link BeansWrapper} interface
 * allowing a template to be directly applied to a {@link Feature} or
 * {@link FeatureCollection}.
 * 

* When a {@link FeatureCollection} is being processed by the template, it is * available via the $features variable, which can be broken down * into single features and attributes following this hierarchy: *

    *
  • features -> feature
  • *
      *
    • fid (String)
    • *
    • typeName (String)
    • *
    • attributes -> attribute
    • *
        *
      • value (String), a default String representation of the attribute value
      • *
      • rawValue (Object), the actual attribute value if it's non null, the * empty string otherwise
      • *
      • name (String)
      • *
      • type (String)
      • *
      • isGeometry (Boolean)
      • *
      *
    *
* Example of a template processing a feature collection which will print out * the features id of every feature in the collection. * *

 *  <#list features as feature>
 *  FeatureId: ${feature.fid}
 *  </#list>
 * 
* *

*

* To use this wrapper,use the * {@link Configuration#setObjectWrapper(freemarker.template.ObjectWrapper)} * method: * *

 *         
 *  //features we want to apply template to
 *  FeatureCollection features = ...;
 *  //create the configuration and set the wrapper
 *  Configuration cfg = new Configuration();
 *  cfg.setObjectWrapper( new FeatureWrapper() );
 *  //get the template and go
 *  Template template = cfg.getTemplate( "foo.ftl" );
 *  template.process( features, System.out );
 * 
 * 
* *

*

* * @author Justin Deoliveira, The Open Planning Project, [email protected] * @author Andrea Aime, TOPP * @author Gabriel Roldan, TOPP */ public class FeatureWrapper extends BeansWrapper { public FeatureWrapper() { setSimpleMapWrapper(true); } /** * Returns a sensible String value for attributes so they are easily used by * templates. *

* Special cases: *

    *
  • for Date values returns a default {@link DateFormat} representation
  • *
  • for Boolean values returns "true" or "false"
  • *
  • for null values returns an empty string
  • *
  • for any other value returns its toString()
  • *
*

* * @param o * could be an instance of Date (a special case) * @return the formated date as a String, or the object */ protected String wrapValue(Object o) { return valueToString(o); } /** * Returns a sensible String value for attributes so they are easily used by * templates. *

* Special cases: *

    *
  • for Date values returns a default {@link DateFormat} representation
  • *
  • for Boolean values returns "true" or "false"
  • *
  • for null values returns an empty string
  • *
  • for any other value returns its toString()
  • *
*

* * @param o * the object for which to return a String representation * suitable to be used as template content * @return the formated date as a String, or the object */ private static String valueToString(Object o) { if (o == null) { // nulls throw tempaltes off, use empty string return ""; } if (o instanceof Date) { if ( o instanceof Timestamp ) { return DateFormat.getDateTimeInstance().format((Date)o); } if ( o instanceof Time ) { return DateFormat.getTimeInstance().format((Date)o); } return DateFormat.getInstance().format((Date) o); } if (o instanceof Boolean) { return ((Boolean) o).booleanValue() ? "true" : "false"; } if (o instanceof Geometry) { return String.valueOf(o); } return String.valueOf(o); } public TemplateModel wrap(Object object) throws TemplateModelException { // check for feature collection if (object instanceof FeatureCollection) { // create a model with just one variable called 'features' SimpleHash map = new SimpleHash(); FeatureCollection featureCollection = (FeatureCollection) object; // this will load all the features into memory! //List features = DataUtilities.list( featureCollection ); //map.put("features", new CollectionModel( features, this)); // this has the risk of leaking memory (if anything goes wrong) // map.put("features", new IteratorModel( featureCollection.iterator(), this)); // this one is almost right; depends on finalizer to close iterator map.put("features", new FeatureCollectionModel( featureCollection, this ) ); map.put("type", wrap(((FeatureCollection) object).getSchema())); return map; } else if (object instanceof SimpleFeatureType) { SimpleFeatureType ft = (SimpleFeatureType) object; // create a variable "attributes" which his a list of all the // attributes, but at the same time, is a map keyed by name Map attributeMap = new LinkedHashMap(); for (int i = 0; i < ft.getAttributeCount(); i++) { AttributeDescriptor type = ft.getDescriptor(i); Map attribute = new HashMap(); attribute.put("name", type.getLocalName()); attribute.put("type", type.getType().getBinding().getName()); attribute.put("isGeometry", Boolean.valueOf(Geometry.class.isAssignableFrom(type.getType().getBinding()))); attributeMap.put(type.getLocalName(), attribute); } // build up the result, feature type is represented by its name an // attributes SimpleHash map = new SimpleHash(); map.put("attributes", new SequenceMapModel(attributeMap, this)); map.put("name", ft.getTypeName()); return map; } else if (object instanceof SimpleFeature) { SimpleFeature feature = (SimpleFeature) object; // create the model SimpleHash map = new SimpleHash(); // first add the feature id map.put("fid", feature.getID()); map.put("typeName", feature.getFeatureType().getTypeName()); // next create the Map representing the per attribute useful // properties for a template Map attributeMap = new FeatureAttributesMap(feature); map.putAll(attributeMap); // create a variable "attributes" which his a list of all the // attributes, but at the same time, is a map keyed by name map.put("attributes", new SequenceMapModel(attributeMap, this)); return map; } return super.wrap(object); } /** * Adapts a Feature to a java.util.Map, where the map keys are the feature * attribute names and the values other Map representing the Feature * name/value attributes. *

* A special purpose Map implementation is used in order to lazily return * the attribute properties, most notably the toString representation of * attribute values. *

* * @author Gabriel Roldan * @see AttributeMap */ private static class FeatureAttributesMap extends AbstractMap { private Set entrySet; private SimpleFeature feature; public FeatureAttributesMap(SimpleFeature feature) { this.feature = feature; } public Set entrySet() { if (entrySet == null) { entrySet = new LinkedHashSet(); final List types = feature.getFeatureType().getAttributeDescriptors(); final int attributeCount = types.size(); String attName; Map attributesMap; for (int i = 0; i < attributeCount; i++) { attName = types.get(i).getLocalName(); attributesMap = new AttributeMap(attName, feature); entrySet.add(new MapEntry(attName, attributesMap)); } } return entrySet; } } /** * Wraps a Feature as a * Map<String, Map<String, Object>>. *

* The Map keys are the wrapped feature's property names and the Map values * are Maps with appropriate key/value pairs for each feature attribute. *

*

* For instance, the value attribute Maps hold the following properties: *

    *
  • name: String holding the attribute name
  • *
  • type: String with the java class name bound to the attribute type
  • *
  • value: String representation of the attribute value suitable to be * used directly in a template expression. null values are * returned as the empty string, non String values as per * {@link FeatureWrapper#valueToString(Object)}
  • *
  • rawValue: the actual attribute value as it is in the Feature
  • *
  • isGeometry: Boolean indicating whether the attribute is of a * geometric type
  • *
*

* */ private static class AttributeMap extends AbstractMap { private final String attributeName; private final SimpleFeature feature; private Set entrySet; /** * Builds an "attribute map" as used in templates for the given * attribute of the given feature. * * @param attributeName * the name of the feature attribute this attribute map is * built for * @param feature * the feature where to lazily grab the attribute named * attributeName from */ public AttributeMap(final String attributeName, final SimpleFeature feature) { this.attributeName = attributeName; this.feature = feature; } /** * Override so asking for the hashCode does not implies traversing the * whole map and thus calling entrySet() prematurely */ public int hashCode() { return attributeName.hashCode(); } /** * Returns this map's entry set. An entry for each of the properties * mentioned in this class's javadoc is returned. Of special interest is * the entry for the "value" property, which is lazily * evaluated through the use of a {@link DeferredValueEntry} */ public Set entrySet() { if (entrySet == null) { entrySet = new LinkedHashSet(); final SimpleFeatureType featureType = feature.getFeatureType(); final AttributeDescriptor attributeType = featureType.getDescriptor(attributeName); final Object value = feature.getAttribute(attributeName); entrySet.add(new DeferredValueEntry("value", value)); entrySet.add(new MapEntry("name", attributeName)); entrySet.add(new MapEntry("type", attributeType.getType().getBinding().getName())); Object rawValue = value == null ? "" : value; boolean isGeometry = Geometry.class.isAssignableFrom(attributeType.getType().getBinding()); entrySet.add(new MapEntry("isGeometry", Boolean.valueOf(isGeometry))); entrySet.add(new MapEntry("rawValue", rawValue)); } return entrySet; } /** * A special purpose Map.Entry whose value is transformed to String on * demand, thus avoiding to hold both the actual value object and its * string value. * * @see FeatureWrapper#valueToString(Object) */ private static class DeferredValueEntry extends MapEntry { private static final long serialVersionUID = -3919798947862996744L; public DeferredValueEntry(String key, Object attribute) { super(key, attribute); } /** * Returns the value corresponding to this entry, as a String. */ public Object getValue() { Object actualValue = super.getValue(); String stringValue = FeatureWrapper.valueToString(actualValue); return stringValue; } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy