0001: /* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
0002: * This code is licensed under the GPL 2.0 license, availible at the root
0003: * application directory.
0004: */
0005: package org.vfny.geoserver.global;
0006:
0007: import java.io.File;
0008: import java.io.IOException;
0009: import java.lang.reflect.Method;
0010: import java.net.URI;
0011: import java.util.ArrayList;
0012: import java.util.Hashtable;
0013: import java.util.Iterator;
0014: import java.util.LinkedList;
0015: import java.util.List;
0016: import java.util.Map;
0017:
0018: import org.geoserver.feature.FeatureSourceUtils;
0019: import org.geoserver.feature.retype.RetypingDataStore;
0020: import org.geotools.data.DataSourceException;
0021: import org.geotools.data.DataStore;
0022: import org.geotools.data.FeatureSource;
0023: import org.geotools.factory.FactoryConfigurationError;
0024: import org.geotools.feature.AttributeType;
0025: import org.geotools.feature.FeatureType;
0026: import org.geotools.feature.FeatureTypeFactory;
0027: import org.geotools.feature.FeatureTypes;
0028: import org.geotools.feature.GeometryAttributeType;
0029: import org.geotools.feature.SchemaException;
0030: import org.geotools.feature.type.GeometricAttributeType;
0031: import org.geotools.geometry.jts.JTS;
0032: import org.geotools.geometry.jts.ReferencedEnvelope;
0033: import org.geotools.referencing.CRS;
0034: import org.geotools.styling.Style;
0035: import org.opengis.filter.Filter;
0036: import org.opengis.referencing.FactoryException;
0037: import org.opengis.referencing.NoSuchAuthorityCodeException;
0038: import org.opengis.referencing.crs.CoordinateReferenceSystem;
0039: import org.opengis.referencing.operation.MathTransform;
0040: import org.vfny.geoserver.global.dto.AttributeTypeInfoDTO;
0041: import org.vfny.geoserver.global.dto.DataTransferObjectFactory;
0042: import org.vfny.geoserver.global.dto.FeatureTypeInfoDTO;
0043: import org.vfny.geoserver.global.dto.LegendURLDTO;
0044: import org.w3c.dom.Attr;
0045: import org.w3c.dom.Element;
0046:
0047: import com.vividsolutions.jts.geom.Envelope;
0048: import com.vividsolutions.jts.geom.Geometry;
0049:
0050: /**
0051: * Represents a FeatureTypeInfo, its user config and autodefined information.
0052: * <p>
0053: * This class implements {@link org.geotools.catalog.Service} interface as a
0054: * link to a catalog.
0055: * </p>
0056: * @author Gabriel Rold?n
0057: * @author Chris Holmes
0058: * @author dzwiers
0059: * @author Charles Kolbowicz
0060: *
0061: * @version $Id: FeatureTypeInfo.java 8391 2008-02-13 16:44:23Z aaime $
0062: */
0063: public class FeatureTypeInfo extends GlobalLayerSupertype {
0064: /** hash table that takes a epsg# to its definition**/
0065: private static Hashtable SRSLookup = new Hashtable();
0066:
0067: /**
0068: * Force declared SRS
0069: */
0070: public static int FORCE = 0;
0071:
0072: /**
0073: * Reproject to declared SRS
0074: */
0075: public static int REPROJECT = 1;
0076:
0077: /**
0078: * Don't do anything, declared and actual are equal
0079: */
0080: public static int LEAVE = 2;
0081:
0082: /** Default constant */
0083: private static final int DEFAULT_NUM_DECIMALS = 8;
0084:
0085: /**
0086: * Id used to locate parent DataStoreInfo using Data Catalog.
0087: */
0088: private String dataStoreId;
0089:
0090: /**
0091: * Bounding box in Lat Long of the extent of this FeatureType.<p>Note
0092: * reprojection may be required to derive this value.</p>
0093: */
0094: private Envelope latLongBBox;
0095:
0096: /**
0097: * Bounding box in this FeatureType's native (or user declared) CRS.<p>Note
0098: * reprojection may be required to derive this value.</p>
0099: */
0100: private Envelope nativeBBox;
0101:
0102: /**
0103: * SRS number used to locate Coordidate Reference Systems
0104: * <p>
0105: * This will be used for reprojection and such like.
0106: * </p>
0107: */
0108: private int SRS;
0109:
0110: /**
0111: * List of AttributeTypeInfo representing the schema.xml information.
0112: * <p>
0113: * Used to define the order and manditoryness of FeatureType attributes
0114: * during query (re)construction.
0115: * </p>
0116: */
0117: private List schema;
0118:
0119: /** Name of elment that is an instance of schemaBase */
0120: private String schemaName;
0121:
0122: /** Base schema (usually NullType) defining manditory attribtues */
0123: private String schemaBase;
0124:
0125: /** typeName as defined by gt2 DataStore */
0126: String typeName;
0127:
0128: /**
0129: *
0130: */
0131: private String wmsPath;
0132:
0133: /**
0134: * Directory where featureType is loaded from.
0135: *
0136: * This may contain metadata files.
0137: */
0138: private String dirName;
0139:
0140: /**
0141: * Abstract used to describe FeatureType
0142: */
0143: private String _abstract;
0144:
0145: /**
0146: * List of keywords for Web Register Services
0147: */
0148: private List keywords;
0149:
0150: /**
0151: * List of keywords for Web Register Services
0152: */
0153: private List metadataLinks;
0154:
0155: /**
0156: * Number of decimals used in GML output.
0157: */
0158: private int numDecimals;
0159:
0160: /**
0161: * Magic query used to limit scope of this FeatureType.
0162: */
0163: private Filter definitionQuery = null;
0164:
0165: /**
0166: * Default style used to render this FeatureType with WMS
0167: */
0168: private String defaultStyle;
0169:
0170: /**
0171: * Other WMS Styles
0172: */
0173: private ArrayList styles;
0174:
0175: /**
0176: * Title of this FeatureType as presented to End-Users.
0177: * <p>
0178: * Think of this as the display name on the off chance that typeName
0179: * is considered ugly.
0180: * </p>
0181: */
0182: private String title;
0183:
0184: /**
0185: * ref to parent set of datastores.
0186: * <p>
0187: * This backpointer to our Catalog can be used to locate our DataStore
0188: * using the dataStoreId.
0189: * </p>
0190: */
0191: private Data data;
0192:
0193: /**
0194: * MetaData used by apps to squirel information away for a rainy day.
0195: */
0196: private Map meta;
0197:
0198: /**
0199: * AttributeTypeInfo by attribute name.
0200: *
0201: * <p>
0202: * This will be null unless populated by schema or DTO.
0203: * Even if the DTO provides one this list will be lazily
0204: * created - so use the accessors.
0205: * </p>
0206: */
0207: private String xmlSchemaFrag;
0208:
0209: /**
0210: * The real geotools2 featureType cached for sanity checks.
0211: * <p>
0212: * This will be lazily created so use the accessors
0213: * </p>
0214: */
0215: private FeatureType ft;
0216:
0217: // Modif C. Kolbowicz - 07/10/2004
0218: /**
0219: * Holds value of property legendURL.
0220: */
0221: private LegendURL legendURL;
0222:
0223: //-- Modif C. Kolbowicz - 07/10/2004
0224:
0225: /** Holds the location of the file that contains schema information. */
0226: private File schemaFile;
0227:
0228: /**
0229: * dont use this unless you know what you're doing. its for TemporaryFeatureTypeInfo.
0230: *
0231: */
0232: public FeatureTypeInfo() {
0233: }
0234:
0235: /**
0236: * This value is added the headers of generated maps, marking them as being both
0237: * "cache-able" and designating the time for which they are to remain valid.
0238: * The specific header added is "Cache-Control: max-age="
0239: */
0240: private String cacheMaxAge;
0241:
0242: /**
0243: * Should we be adding the CacheControl: max-age header to outgoing maps which include this layer?
0244: */
0245: private boolean cachingEnabled;
0246:
0247: /**
0248: * Either force or reproject (force is the only way if native data has no native SRS)
0249: */
0250: private int srsHandling;
0251:
0252: /**
0253: * Maximum number of features served for this feature type in wfs requests. 0 for no limit
0254: */
0255: private int maxFeatures;
0256:
0257: /**
0258: * The typename alias. If set the typename will be recognized by the alias only, the original
0259: * typename will be forgotten
0260: */
0261: private String alias;
0262:
0263: /**
0264: * FeatureTypeInfo constructor.
0265: *
0266: * <p>
0267: * Generates a new object from the data provided.
0268: * </p>
0269: *
0270: * @param dto FeatureTypeInfoDTO The data to populate this class with.
0271: * @param data Data a reference for future use to get at DataStoreInfo
0272: * instances
0273: *
0274: * @throws ConfigurationException
0275: */
0276: public FeatureTypeInfo(FeatureTypeInfoDTO dto, Data data)
0277: throws ConfigurationException {
0278: this .data = data;
0279: _abstract = dto.getAbstract();
0280: dataStoreId = dto.getDataStoreId();
0281: defaultStyle = dto.getDefaultStyle();
0282: styles = dto.getStyles();
0283:
0284: // Modif C. Kolbowicz - 07/10/2004
0285: if (dto.getLegendURL() != null) {
0286: legendURL = new LegendURL(dto.getLegendURL());
0287: } //-- Modif C. Kolbowicz - 07/10/2004
0288:
0289: definitionQuery = dto.getDefinitionQuery();
0290: dirName = dto.getDirName();
0291: keywords = dto.getKeywords();
0292: metadataLinks = dto.getMetadataLinks();
0293: latLongBBox = dto.getLatLongBBox();
0294: typeName = dto.getName();
0295: alias = dto.getAlias();
0296: wmsPath = dto.getWmsPath();
0297: numDecimals = dto.getNumDecimals();
0298:
0299: List tmp = dto.getSchemaAttributes();
0300: schema = new LinkedList();
0301:
0302: if ((tmp != null) && !tmp.isEmpty()) {
0303: Iterator i = tmp.iterator();
0304:
0305: while (i.hasNext())
0306: schema.add(new AttributeTypeInfo(
0307: (AttributeTypeInfoDTO) i.next()));
0308: }
0309:
0310: schemaBase = dto.getSchemaBase();
0311: schemaName = dto.getSchemaName();
0312: schemaFile = dto.getSchemaFile();
0313: SRS = dto.getSRS();
0314: srsHandling = dto.getSRSHandling();
0315: nativeBBox = dto.getNativeBBox();
0316: title = dto.getTitle();
0317:
0318: cacheMaxAge = dto.getCacheMaxAge();
0319: cachingEnabled = dto.isCachingEnabled();
0320:
0321: maxFeatures = dto.getMaxFeatures();
0322: }
0323:
0324: /**
0325: * toDTO purpose.
0326: *
0327: * <p>
0328: * This method is package visible only, and returns a reference to the
0329: * GeoServerDTO. This method is unsafe, and should only be used with
0330: * extreme caution.
0331: * </p>
0332: *
0333: * @return FeatureTypeInfoDTO the generated object
0334: */
0335: public Object toDTO() {
0336: FeatureTypeInfoDTO dto = new FeatureTypeInfoDTO();
0337: dto.setAbstract(_abstract);
0338: dto.setDataStoreId(dataStoreId);
0339: dto.setDefaultStyle(defaultStyle);
0340: dto.setStyles(styles);
0341:
0342: // Modif C. Kolbowicz - 07/10/2004
0343: if (legendURL != null) {
0344: dto.setLegendURL((LegendURLDTO) legendURL.toDTO());
0345: } //-- Modif C. Kolbowicz - 07/10/2004
0346:
0347: dto.setDefinitionQuery(definitionQuery);
0348: dto.setDirName(dirName);
0349: dto.setKeywords(keywords);
0350: dto.setMetadataLinks(metadataLinks);
0351: dto.setLatLongBBox(latLongBBox);
0352: dto.setNativeBBox(nativeBBox);
0353: dto.setName(typeName);
0354: dto.setAlias(typeName);
0355: dto.setWmsPath(wmsPath);
0356: dto.setNumDecimals(numDecimals);
0357:
0358: List tmp = new LinkedList();
0359: Iterator i = schema.iterator();
0360:
0361: while (i.hasNext()) {
0362: tmp.add(((AttributeTypeInfo) i.next()).toDTO());
0363: }
0364:
0365: dto.setSchemaAttributes(tmp);
0366: dto.setSchemaBase(schemaBase);
0367: dto.setSchemaName(getSchemaName());
0368: dto.setSRS(SRS);
0369: dto.setTitle(title);
0370:
0371: dto.setCacheMaxAge(cacheMaxAge);
0372: dto.setCachingEnabled(cachingEnabled);
0373:
0374: return dto;
0375: }
0376:
0377: /**
0378: * getNumDecimals purpose.
0379: *
0380: * <p>
0381: * The default number of decimals allowed in the data.
0382: * </p>
0383: *
0384: * @return int the default number of decimals allowed in the data.
0385: */
0386: public int getNumDecimals() {
0387: return numDecimals;
0388: }
0389:
0390: /**
0391: * getDataStore purpose.
0392: *
0393: * <p>
0394: * gets the string of the path to the schema file. This is set during
0395: * feature reading, the schema file should be in the same folder as the
0396: * feature type info, with the name schema.xml. This function does not
0397: * guarantee that the schema file actually exists, it just gives the
0398: * location where it _should_ be located.
0399: * </p>
0400: *
0401: * @return DataStoreInfo the requested DataStoreInfo if it was found.
0402: *
0403: * @see Data#getDataStoreInfo(String)
0404: */
0405: public DataStoreInfo getDataStoreInfo() {
0406: return data.getDataStoreInfo(dataStoreId);
0407: }
0408:
0409: /**
0410: * By now just return the default style to be able to declare it in
0411: * WMS capabilities, but all this stuff needs to be revisited since it seems
0412: * currently there is no way of retrieving all the styles declared for
0413: * a given FeatureType.
0414: *
0415: * @return the default Style for the FeatureType
0416: */
0417: public Style getDefaultStyle() {
0418: return data.getStyle(defaultStyle);
0419: }
0420:
0421: public ArrayList getStyles() {
0422: final ArrayList realStyles = new ArrayList();
0423: Iterator s_IT = styles.iterator();
0424:
0425: while (s_IT.hasNext())
0426: realStyles.add(data.getStyle((String) s_IT.next()));
0427:
0428: return realStyles;
0429: }
0430:
0431: /**
0432: * Indicates if this FeatureTypeInfo is enabled. For now just gets whether
0433: * the backing datastore is enabled.
0434: *
0435: * @return <tt>true</tt> if this FeatureTypeInfo is enabled.
0436: *
0437: * @task REVISIT: Consider adding more fine grained control to config
0438: * files, so users can indicate specifically if they want the
0439: * featureTypes enabled, instead of just relying on if the datastore
0440: * is. Jody here - this should be done on a service by service basis
0441: * WMS and WFS will need to decide for themselves on this one
0442: */
0443: public boolean isEnabled() {
0444: return (getDataStoreInfo() != null)
0445: && (getDataStoreInfo().isEnabled());
0446: }
0447:
0448: /**
0449: * Returns the XML prefix used for GML output of this FeatureType.
0450: *
0451: * <p>
0452: * Returns the namespace prefix for this FeatureTypeInfo.
0453: * </p>
0454: *
0455: * @return String the namespace prefix.
0456: */
0457: public String getPrefix() {
0458: return getDataStoreInfo().getNameSpace().getPrefix();
0459: }
0460:
0461: /**
0462: * Gets the namespace for this featureType.
0463: * <p>
0464: * This isn't _really_ necessary,
0465: * but I'm putting it in in case we change namespaces, letting
0466: * FeatureTypes set their own namespaces instead of being dependant on
0467: * datasources. This method will allow us to make that change more easily
0468: * in the future.
0469: *
0470: * @return NameSpaceInfo the namespace specified for the specified
0471: * DataStoreInfo (by ID)
0472: *
0473: * @throws IllegalStateException THrown when disabled.
0474: */
0475: public NameSpaceInfo getNameSpace() {
0476: if (!isEnabled()) {
0477: throw new IllegalStateException("This featureType is not "
0478: + "enabled");
0479: }
0480:
0481: return getDataStoreInfo().getNameSpace();
0482: }
0483:
0484: /**
0485: * Complete xml name (namespace:element> for this FeatureType.
0486: *
0487: * This is the full type name with namespace prefix.
0488: *
0489: * @return String the FeatureTypeInfo name - should be unique for the
0490: * parent Data instance.
0491: */
0492: public String getName() {
0493: if (alias == null)
0494: return getPrefix() + ":" + typeName;
0495: else
0496: return getPrefix() + ":" + alias;
0497: }
0498:
0499: /**
0500: * getFeatureSource purpose.
0501: *
0502: * <p>
0503: * Returns a real FeatureSource.
0504: * </p>
0505: *
0506: * @return FeatureSource the feature source represented by this info class
0507: *
0508: * @throws IOException when an error occurs.
0509: */
0510: public FeatureSource getFeatureSource() throws IOException {
0511: return getFeatureSource(false);
0512: }
0513:
0514: /**
0515: * If this layers has been setup to reproject data, skipReproject = true will
0516: * disable reprojection. This method is build especially for the rendering subsystem
0517: * that should be able to perform a full reprojection on its own, and do generalization
0518: * before reprojection (thus avoid to reproject all of the original coordinates)
0519: */
0520: public FeatureSource getFeatureSource(boolean skipReproject)
0521: throws IOException {
0522: if (!isEnabled() || (getDataStoreInfo().getDataStore() == null)) {
0523: throw new IOException("featureType: " + getName()
0524: + " does not have a properly configured "
0525: + "datastore");
0526: }
0527:
0528: FeatureSource realSource = getAliasedFeatureSource();
0529:
0530: // avoid reprojection if the calling code can do it better
0531: int localSrsHandling = srsHandling;
0532: if (srsHandling == REPROJECT && skipReproject)
0533: localSrsHandling = LEAVE;
0534:
0535: if (((schema == null) || schema.isEmpty())) { // &&
0536:
0537: //(ftc.getDefinitionQuery() == null || ftc.getDefinitionQuery().equals( Query.ALL ))){
0538: return realSource;
0539: } else {
0540: CoordinateReferenceSystem resultCrs = null;
0541: GeometryAttributeType gat = realSource.getSchema()
0542: .getDefaultGeometry();
0543: CoordinateReferenceSystem nativeCrs = gat != null ? gat
0544: .getCoordinateSystem() : null;
0545: if (localSrsHandling == LEAVE && nativeCrs != null)
0546: resultCrs = nativeCrs;
0547: else
0548: resultCrs = getSRS(SRS);
0549:
0550: // make sure we create the appropriate schema, with the right crs
0551: FeatureType schema = getFeatureType(realSource);
0552: try {
0553: if (schema.getDefaultGeometry() != null
0554: && !CRS.equalsIgnoreMetadata(resultCrs, schema
0555: .getDefaultGeometry()
0556: .getCoordinateSystem()))
0557: schema = FeatureTypes.transform(schema, resultCrs);
0558: } catch (Exception e) {
0559: throw new DataSourceException(
0560: "Problem forcing CRS onto feature type", e);
0561: }
0562:
0563: if (!implements Interface(realSource.getClass(),
0564: "org.geotools.data.VersioningFeatureSource")) {
0565: return GeoServerFeatureLocking.create(realSource,
0566: schema, getDefinitionQuery(), resultCrs,
0567: localSrsHandling);
0568: } else {
0569: // support versioning only if it is in the classpath, use reflection to invoke
0570: // methods so that we don't get a compile time dependency
0571: try {
0572: Class clazz = Class
0573: .forName("org.vfny.geoserver.global.GeoServerVersioningFeatureSource");
0574: Method m = clazz
0575: .getMethod(
0576: "create",
0577: new Class[] {
0578: Class
0579: .forName("org.geotools.data.VersioningFeatureSource"),
0580: FeatureType.class,
0581: Filter.class,
0582: CoordinateReferenceSystem.class,
0583: int.class });
0584:
0585: return (FeatureSource) m.invoke(null, new Object[] {
0586: realSource, schema, getDefinitionQuery(),
0587: resultCrs, new Integer(localSrsHandling) });
0588: } catch (Exception e) {
0589: throw new DataSourceException(
0590: "Creation of a versioning wrapper failed",
0591: e);
0592: }
0593:
0594: }
0595: }
0596: }
0597:
0598: /**
0599: * Returns the native feature source, eventually aliasing the name of the
0600: * feature type with the specified alias
0601: * @return
0602: * @throws IOException
0603: */
0604: private FeatureSource getAliasedFeatureSource() throws IOException {
0605: DataStore dataStore = data.getDataStoreInfo(dataStoreId)
0606: .getDataStore();
0607: FeatureSource fs;
0608: if (alias == null) {
0609: fs = dataStore.getFeatureSource(typeName);
0610: } else {
0611: fs = new RenamingDataStore(dataStore, typeName, alias)
0612: .getFeatureSource(alias);
0613: }
0614:
0615: return fs;
0616: }
0617:
0618: /**
0619: * JDK 4 compiler is behaving strangely, it's calling the super constructor
0620: * before setting the anonymous inner class relationship with the parent.
0621: * This leads to an NPE which does not occurr on trunk. So I had to roll
0622: * this class that initializes the fields before calling the parent constructor
0623: * (which in turn calls transformFeatureTypeName, and thus may cause an NPE since
0624: * name and renameTo are not initialized).
0625: * @author Administrator
0626: *
0627: */
0628: private static class RenamingDataStore extends RetypingDataStore {
0629: private String name;
0630: private String renameTo;
0631:
0632: public RenamingDataStore(DataStore wrapped, String typeName,
0633: String alias) throws IOException {
0634: this .name = typeName;
0635: this .renameTo = alias;
0636: init(wrapped);
0637: }
0638:
0639: protected String transformFeatureTypeName(String originalName) {
0640: if (!name.equals(originalName))
0641: return originalName;
0642: return renameTo;
0643: }
0644: }
0645:
0646: /**
0647: * Checks if a interface is implemented by looking at implemented interfaces using reflection
0648: * @param realSource
0649: * @param string
0650: * @return
0651: */
0652: private boolean implements Interface(Class clazz,
0653: String interfaceName) {
0654: if (clazz.getName().equals(interfaceName)) {
0655: return true;
0656: }
0657:
0658: final Class[] ifaces = clazz.getInterfaces();
0659:
0660: for (int i = 0; i < ifaces.length; i++) {
0661: if (ifaces[i].getName().equals(interfaceName)) {
0662: return true;
0663: } else if (implements Interface(ifaces[i], interfaceName)) {
0664: return true;
0665: }
0666: }
0667:
0668: if (clazz.getSuperclass() == null) {
0669: return false;
0670: } else {
0671: return implements Interface(clazz.getSuperclass(),
0672: interfaceName);
0673: }
0674: }
0675:
0676: /*public static FeatureSource reTypeSource(FeatureSource source,
0677: FeatureTypeInfoDTO ftc) throws SchemaException {
0678: AttributeType[] attributes = new AttributeType[ftc.getSchemaAttributes()
0679: .size()];
0680: List attributeDefinitions = ftc.getSchemaAttributes();
0681: int index = 0;
0682: FeatureType ft = source.getSchema();
0683: for (int i = 0; i < attributes.length; i++) {
0684: AttributeTypeInfoDTO attributeDTO = (AttributeTypeInfoDTO) ftc.getSchemaAttributes()
0685: .get(i);
0686: String xpath = attributeDTO.getName();
0687: attributes[i] = ft.getAttributeType(xpath);
0688: if (attributes[i] == null) {
0689: throw new NullPointerException("Error finding " + xpath
0690: + " specified in you schema.xml file for " + ftc.getName()
0691: + "FeatureType.");
0692: }
0693: }
0694: FeatureType myType = FeatureTypeFactory.newFeatureType(attributes,
0695: ftc.getName());
0696: return GeoServerFeatureLocking.create(source, myType,
0697: ftc.getDefinitionQuery());
0698: }*/
0699:
0700: /**
0701: * Returns the FeatureType's envelope in its native CRS (or user
0702: * declared CRS, if any).
0703: * <p>
0704: * Note the Envelope is cached in order to avoid a potential
0705: * performance penalty every time this value is requires (for example,
0706: * at every GetCapabilities request)
0707: * </p>
0708: *
0709: * @return Envelope of the feature source bounds.
0710: *
0711: * @throws IOException when an error occurs
0712: */
0713: public Envelope getBoundingBox() throws IOException {
0714: CoordinateReferenceSystem declaredCRS = getDeclaredCRS();
0715: CoordinateReferenceSystem nativeCRS = getNativeCRS();
0716: if ((nativeBBox == null) || nativeBBox.isNull()) {
0717: CoordinateReferenceSystem crs = srsHandling == LEAVE ? nativeCRS
0718: : declaredCRS;
0719: nativeBBox = getBoundingBox(crs);
0720: }
0721:
0722: if (!(nativeBBox instanceof ReferencedEnvelope)) {
0723: CoordinateReferenceSystem crs = srsHandling == LEAVE ? nativeCRS
0724: : declaredCRS;
0725: nativeBBox = new ReferencedEnvelope(nativeBBox, crs);
0726: }
0727:
0728: if (srsHandling == REPROJECT) {
0729: try {
0730: ReferencedEnvelope re = (ReferencedEnvelope) nativeBBox;
0731: nativeBBox = re.transform(declaredCRS, true);
0732: } catch (Exception e) {
0733: LOGGER.warning("Issues trying to transform native CRS");
0734: }
0735: }
0736:
0737: return nativeBBox;
0738: }
0739:
0740: private Envelope getBoundingBox(CoordinateReferenceSystem targetCrs)
0741: throws IOException {
0742: FeatureSource realSource = getAliasedFeatureSource();
0743: Envelope bbox = FeatureSourceUtils
0744: .getBoundingBoxEnvelope(realSource);
0745:
0746: // check if the original CRS is not the declared one
0747: GeometryAttributeType defaultGeometry = realSource.getSchema()
0748: .getDefaultGeometry();
0749: CoordinateReferenceSystem originalCRS = defaultGeometry
0750: .getCoordinateSystem();
0751:
0752: try {
0753: if (targetCrs != null
0754: && !CRS
0755: .equalsIgnoreMetadata(originalCRS,
0756: targetCrs)) {
0757: MathTransform xform = CRS.findMathTransform(
0758: originalCRS, targetCrs, true);
0759:
0760: // bbox = JTS.transform(bbox, null, xform, 10);
0761: if (bbox instanceof ReferencedEnvelope) {
0762: bbox = ((ReferencedEnvelope) bbox).transform(
0763: targetCrs, true, 10);
0764: } else {
0765: bbox = new ReferencedEnvelope(JTS.transform(bbox,
0766: null, xform, 10), targetCrs);
0767: }
0768: }
0769: } catch (Exception e) {
0770: LOGGER
0771: .severe("Could not turn the original envelope in one into the declared CRS for type "
0772: + getTypeName());
0773: LOGGER.severe("Original CRS is " + originalCRS);
0774: LOGGER.severe("Declared CRS is " + targetCrs);
0775: }
0776:
0777: return new ReferencedEnvelope(bbox, targetCrs);
0778: }
0779:
0780: /**
0781: * getDefinitionQuery purpose.
0782: *
0783: * <p>
0784: * Returns the definition query for this feature source
0785: * </p>
0786: *
0787: * @return Filter the definition query
0788: */
0789: public Filter getDefinitionQuery() {
0790: return definitionQuery;
0791: }
0792:
0793: /**
0794: * getLatLongBoundingBox purpose.
0795: *
0796: * <p>
0797: * The feature source lat/long bounds.
0798: * </p>
0799: *
0800: * @return Envelope the feature source lat/long bounds.
0801: *
0802: * @throws IOException when an error occurs
0803: */
0804: public Envelope getLatLongBoundingBox() throws IOException {
0805: if (latLongBBox == null) {
0806: latLongBBox = getBoundingBox(getSRS(4326));
0807: }
0808:
0809: return latLongBBox;
0810: }
0811:
0812: /**
0813: * getSRS purpose.
0814: *
0815: * <p>
0816: * Proprietary identifier number
0817: * </p>
0818: *
0819: * @return int the SRS number.
0820: */
0821: public String getSRS() {
0822: return SRS + "";
0823: }
0824:
0825: /**
0826: * Returns the declared CRS, that is, the CRS specified in the feature type
0827: * editor form
0828: */
0829: public CoordinateReferenceSystem getDeclaredCRS() {
0830: return getSRS(SRS);
0831: }
0832:
0833: public CoordinateReferenceSystem getNativeCRS() throws IOException {
0834: GeometryAttributeType dg = getDefaultGeometry();
0835:
0836: if (dg == null) {
0837: return null;
0838: }
0839:
0840: return dg.getCoordinateSystem();
0841: }
0842:
0843: /**
0844: * Returns the default geometry for this feature type
0845: * @return
0846: * @throws IOException if the layer is not properly configured
0847: */
0848: GeometryAttributeType getDefaultGeometry() throws IOException {
0849: if (getDataStoreInfo().getDataStore() == null) {
0850: throw new IOException("featureType: " + getName()
0851: + " does not have a properly configured "
0852: + "datastore");
0853: }
0854:
0855: FeatureSource realSource = getAliasedFeatureSource();
0856:
0857: return realSource.getSchema().getDefaultGeometry();
0858: }
0859:
0860: /**
0861: * If true, the layer does not have a default geometry
0862: * @return
0863: * @throws IOException
0864: */
0865: public boolean isGeometryless() throws IOException {
0866: return getDefaultGeometry() == null;
0867: }
0868:
0869: /**
0870: * Get XMLSchema for this FeatureType.
0871: *
0872: * <p>
0873: * Note this may require connection to the real geotools2 DataStore and as
0874: * such is subject to IOExceptions.
0875: * </p>
0876: *
0877: * <p>
0878: * You have been warned.
0879: * </p>
0880: *
0881: * @return XMLFragment
0882: *
0883: * @throws IOException DOCUMENT ME!
0884: */
0885:
0886: /* public synchronized String getXMLSchema(){
0887: if (xmlSchemaFrag == null) {
0888: StringWriter sw = new StringWriter();
0889: try {
0890: FeatureTypeInfoDTO dto = getGeneratedDTO();
0891: XMLConfigWriter.storeFeatureSchema(dto, sw);
0892: } catch (ConfigurationException e) {
0893: e.printStackTrace();
0894: } catch (IOException e) {
0895: e.printStackTrace();
0896: xmlSchemaFrag = null;
0897: }
0898: xmlSchemaFrag = sw.toString();
0899: try{
0900: sw.close();
0901: }catch(IOException e){}
0902: }
0903: return xmlSchemaFrag;
0904: }*/
0905:
0906: /**
0907: * Will return our delegate with all information filled out
0908: *
0909: * <p>
0910: * This is a hack because we cache our DTO delegate, this method combines
0911: * or ftc delegate with possibly generated schema information for use by
0912: * XMLConfigWriter among others.
0913: * </p>
0914: *
0915: * <p>
0916: * Call this method to receive a complete featureTypeInfoDTO that incldues
0917: * all schema information.
0918: * </p>
0919: *
0920: * @return
0921: *
0922: * @throws IOException DOCUMENT ME!
0923: */
0924: private synchronized FeatureTypeInfoDTO getGeneratedDTO()
0925: throws IOException {
0926: return DataTransferObjectFactory.create(dataStoreId,
0927: getFeatureType());
0928: }
0929:
0930: /**
0931: * getAttribute purpose.
0932: *
0933: * <p>
0934: * XLM helper method.
0935: * </p>
0936: *
0937: * @param elem The element to work on.
0938: * @param attName The attribute name to find
0939: * @param mandatory true is an exception is be thrown when the attr is not
0940: * found.
0941: *
0942: * @return String the Attr value
0943: *
0944: * @throws ConfigurationException thrown when an error occurs.
0945: */
0946: protected String getAttribute(Element elem, String attName,
0947: boolean mandatory) throws ConfigurationException {
0948: Attr att = elem.getAttributeNode(attName);
0949:
0950: String value = null;
0951:
0952: if (att != null) {
0953: value = att.getValue();
0954: }
0955:
0956: if (mandatory) {
0957: if (att == null) {
0958: throw new ConfigurationException("element "
0959: + elem.getNodeName()
0960: + " does not contains an attribute named "
0961: + attName);
0962: } else if ("".equals(value)) {
0963: throw new ConfigurationException("attribute " + attName
0964: + "in element " + elem.getNodeName()
0965: + " is empty");
0966: }
0967: }
0968:
0969: return value;
0970: }
0971:
0972: /*private FeatureType getSchema(String schema) throws ConfigurationException{
0973: try{
0974: return getSchema(loadConfig(new StringReader(schema)));
0975: }catch(IOException e){
0976: throw new ConfigurationException("",e);
0977: }
0978: }*/
0979:
0980: /**
0981: * loadConfig purpose.
0982: *
0983: * <p>
0984: * Parses the specified file into a DOM tree.
0985: * </p>
0986: *
0987: * @param fromSrId The file to parse int a DOM tree.
0988: * @param bbox DOCUMENT ME!
0989: *
0990: * @return the resulting DOM tree
0991: */
0992:
0993: /*public static Element loadConfig(Reader fis) throws ConfigurationException {
0994: try {
0995: InputSource in = new InputSource(fis);
0996: DocumentBuilderFactory dfactory = DocumentBuilderFactory
0997: .newInstance();
0998: /*set as optimizations and hacks for geoserver schema config files
0999: * @HACK should make documents ALL namespace friendly, and validated. Some documents are XML fragments.
1000: * @TODO change the following config for the parser and modify config files to avoid XML fragmentation.
1001: */
1002:
1003: /* dfactory.setNamespaceAware(false);
1004: dfactory.setValidating(false);
1005: dfactory.setIgnoringComments(true);
1006: dfactory.setCoalescing(true);
1007: dfactory.setIgnoringElementContentWhitespace(true);
1008: Document serviceDoc = dfactory.newDocumentBuilder().parse(in);
1009: Element configElem = serviceDoc.getDocumentElement();
1010: return configElem;
1011: } catch (IOException ioe) {
1012: String message = "problem reading file " + "due to: "
1013: + ioe.getMessage();
1014: LOGGER.warning(message);
1015: throw new ConfigurationException(message, ioe);
1016: } catch (ParserConfigurationException pce) {
1017: String message =
1018: "trouble with parser to read org.vfny.geoserver.config.org.vfny.geoserver.config.xml, make sure class"
1019: + "path is correct, reading file ";
1020: LOGGER.warning(message);
1021: throw new ConfigurationException(message, pce);
1022: } catch (SAXException saxe) {
1023: String message = "trouble parsing XML " + ": " + saxe.getMessage();
1024: LOGGER.warning(message);
1025: throw new ConfigurationException(message, saxe);
1026: }
1027: }*/
1028:
1029: /**
1030: * here we must make the transformation. Crhis: do you know how to do it? I
1031: * don't know. Ask martin or geotools devel. This will be better when
1032: * our geometries actually have their srs objects. And I think that we
1033: * may need some MS Access database, not sure, but I saw some stuff about
1034: * that on the list. Hopefully they'll do it all in java soon. I'm sorta
1035: * tempted to just have users define for now.
1036: *
1037: * @param fromSrId
1038: * @param bbox Envelope
1039: *
1040: * @return Envelope
1041: */
1042: private static Envelope getLatLongBBox(String fromSrId,
1043: Envelope bbox) {
1044: return bbox;
1045: }
1046:
1047: /**
1048: * Get abstract (description) of FeatureType.
1049: *
1050: * @return Short description of FeatureType
1051: */
1052: public String getAbstract() {
1053: return _abstract;
1054: }
1055:
1056: /**
1057: * Keywords describing content of FeatureType.
1058: *
1059: * <p>
1060: * Keywords are often used by Search engines or Catalog services.
1061: * </p>
1062: *
1063: * @return List the FeatureTypeInfo keywords
1064: */
1065: public List getKeywords() {
1066: return keywords;
1067: }
1068:
1069: /**
1070: * Metadata links providing metadata access for FeatureTypes.
1071: *
1072: * @return List the FeatureTypeInfo metadata links
1073: */
1074: public List getMetadataLinks() {
1075: return metadataLinks;
1076: }
1077:
1078: /**
1079: * getTitle purpose.
1080: *
1081: * <p>
1082: * returns the FeatureTypeInfo title
1083: * </p>
1084: *
1085: * @return String the FeatureTypeInfo title
1086: */
1087: public String getTitle() {
1088: return title;
1089: }
1090:
1091: /**
1092: * A valid schema name for this FeatureType.
1093: *
1094: * @return schemaName if provided or typeName+"_Type"
1095: */
1096: public String getSchemaName() {
1097: if (schemaName == null) {
1098: return getTypeName() + "_Type";
1099: }
1100:
1101: return schemaName;
1102: }
1103:
1104: /**
1105: * setSchemaName purpose.
1106: *
1107: * <p>
1108: * Description ...
1109: * </p>
1110: *
1111: * @param string
1112: */
1113: public void setSchemaName(String string) {
1114: schemaName = string;
1115: }
1116:
1117: /**
1118: * getSchemaName purpose.
1119: *
1120: * <p>
1121: * Description ...
1122: * </p>
1123: *
1124: * @return
1125: */
1126: public String getSchemaBase() {
1127: return schemaBase;
1128: }
1129:
1130: /**
1131: * setSchemaName purpose.
1132: *
1133: * <p>
1134: * Description ...
1135: * </p>
1136: *
1137: * @param string
1138: */
1139: public void setSchemaBase(String string) {
1140: schemaBase = string;
1141: }
1142:
1143: //
1144: // FeatureTypeMetaData Interface
1145: //
1146: /**
1147: * Access the name of this FeatureType.
1148: * <p>
1149: * This is the typeName as provided by the gt2 datastore, unless an alias
1150: * is set, in that case the alias is returned
1151: * </p>
1152: *
1153: * @return String getName()
1154: * @see org.geotools.data.FeatureTypeMetaData#getTypeName()
1155: */
1156: public String getTypeName() {
1157: return alias == null ? typeName : alias;
1158: }
1159:
1160: /**
1161: * Access the name of this SimpleFeatureType.
1162: * <p>
1163: * This is the typeName as provided by the gt2 datastore, even when an alias
1164: * is set
1165: * </p>
1166: *
1167: * @return String getName()
1168: * @see org.geotools.data.FeatureTypeMetaData#getTypeName()
1169: */
1170: public String getNativeTypeName() {
1171: return typeName;
1172: }
1173:
1174: /**
1175: * Access real geotools2 FeatureType.
1176: *
1177: * @return Schema information.
1178: *
1179: * @throws IOException
1180: *
1181: * @see org.geotools.data.FeatureTypeMetaData#getFeatureType()
1182: */
1183: public FeatureType getFeatureType() throws IOException {
1184: return getFeatureType(getFeatureSource());
1185: }
1186:
1187: /**
1188: * Fixes the data store feature type so that it has the right CRS (only in case they are missing)
1189: * and the requiered base attributes
1190: */
1191: private FeatureType getFeatureType(FeatureSource fs)
1192: throws IOException {
1193: if (ft == null) {
1194: int count = 0;
1195: ft = fs.getSchema();
1196: URI namespace = ft.getNamespace(); //DJB:: change to #getNamespace() due to API change
1197:
1198: String[] baseNames = DataTransferObjectFactory
1199: .getRequiredBaseAttributes(schemaBase);
1200: AttributeType[] attributes = new AttributeType[schema
1201: .size()
1202: + baseNames.length];
1203:
1204: if (attributes.length > 0) {
1205: int errors = 0;
1206:
1207: for (; count < baseNames.length; count++) {
1208: attributes[count - errors] = ft
1209: .getAttributeType(baseNames[count]);
1210:
1211: if (attributes[count - errors] == null) {
1212: // desired base attr is not availiable
1213: errors++;
1214: }
1215: }
1216:
1217: if (errors != 0) {
1218: //resize array;
1219: AttributeType[] tmp = new AttributeType[attributes.length
1220: - errors];
1221: count = count - errors;
1222:
1223: for (int i = 0; i < count; i++) {
1224: tmp[i] = attributes[i];
1225: }
1226:
1227: attributes = tmp;
1228: }
1229:
1230: for (Iterator i = schema.iterator(); i.hasNext();) {
1231: AttributeTypeInfo ati = (AttributeTypeInfo) i
1232: .next();
1233: String attName = ati.getName();
1234: attributes[count] = ft.getAttributeType(attName);
1235:
1236: // force the user specified CRS if the data has no CRS, or reproject it
1237: // if necessary
1238: if (Geometry.class
1239: .isAssignableFrom(attributes[count]
1240: .getType())) {
1241: GeometricAttributeType old = (GeometricAttributeType) attributes[count];
1242:
1243: try {
1244: if (old.getCoordinateSystem() == null) {
1245: attributes[count] = new GeometricAttributeType(
1246: old, getSRS(SRS));
1247: srsHandling = FORCE;
1248: } else if (srsHandling == REPROJECT
1249: || srsHandling == FORCE) {
1250: attributes[count] = new GeometricAttributeType(
1251: old, getSRS(SRS));
1252: }
1253: } catch (Exception e) {
1254: e.printStackTrace(); //DJB: this is okay to ignore since (a) it should never happen (b) we'll use the default one (crs=null)
1255: }
1256: }
1257:
1258: if (attributes[count] == null) {
1259: throw new IOException(
1260: "the FeatureType "
1261: + getName()
1262: + " does not contains the configured attribute "
1263: + attName
1264: + ". Check your schema configuration");
1265: }
1266:
1267: count++;
1268: }
1269:
1270: try {
1271: ft = FeatureTypeFactory.newFeatureType(attributes,
1272: getTypeName(), namespace);
1273: } catch (SchemaException ex) {
1274: } catch (FactoryConfigurationError ex) {
1275: }
1276: }
1277: }
1278:
1279: return ft;
1280: }
1281:
1282: /**
1283: * Implement getDataStoreMetaData.
1284: *
1285: * @return
1286: *
1287: * @see org.geotools.data.FeatureTypeMetaData#getDataStoreMetaData()
1288: */
1289: public DataStoreInfo getDataStoreMetaData() {
1290: return data.getDataStoreInfo(dataStoreId);
1291: }
1292:
1293: /**
1294: * FeatureType attributes names as a List.
1295: *
1296: * <p>
1297: * Convience method for accessing attribute names as a Collection. You may
1298: * use the names for AttributeTypeMetaData lookup or with the schema for
1299: * XPATH queries.
1300: * </p>
1301: *
1302: * @return List of attribute names
1303: *
1304: * @task REVISIT: This method sucks. It didn't do the same thing as
1305: * getAttributes, which it should have. I fixed the root problem of
1306: * why attribs.size() would equal 0. So the second half of this
1307: * method should probably be eliminated, as it should never be
1308: * called. But I don't want to break code right before a release -
1309: * ch.
1310: *
1311: * @see org.geotools.data.FeatureTypeMetaData#getAttributeNames()
1312: */
1313: public List getAttributeNames() {
1314: List attribs = schema;
1315:
1316: if (attribs.size() != 0) {
1317: List list = new ArrayList(attribs.size());
1318:
1319: for (Iterator i = attribs.iterator(); i.hasNext();) {
1320: AttributeTypeInfo at = (AttributeTypeInfo) i.next();
1321: list.add(at.getName());
1322: }
1323:
1324: return list;
1325: }
1326:
1327: List list = new ArrayList();
1328:
1329: try {
1330: FeatureType ftype = getFeatureType();
1331: AttributeType[] types = ftype.getAttributeTypes();
1332: list = new ArrayList(types.length);
1333:
1334: for (int i = 0; i < types.length; i++) {
1335: list.add(types[i].getName());
1336: }
1337: } catch (IOException e) {
1338: }
1339:
1340: return list;
1341: }
1342:
1343: /**
1344: * Returns a list of the attributeTypeInfo objects that make up this
1345: * FeatureType.
1346: *
1347: * @return list of attributeTypeInfo objects.
1348: */
1349: public List getAttributes() {
1350: return schema;
1351: }
1352:
1353: /**
1354: * Implement AttributeTypeMetaData.
1355: *
1356: * <p>
1357: * Description ...
1358: * </p>
1359: *
1360: * @param attributeName
1361: *
1362: * @return
1363: *
1364: * @see org.geotools.data.FeatureTypeMetaData#AttributeTypeMetaData(java.lang.String)
1365: */
1366: public synchronized AttributeTypeInfo AttributeTypeMetaData(
1367: String attributeName) {
1368: // WARNING: this method has not been updated to handle aliases
1369: AttributeTypeInfo info = null;
1370:
1371: if (schema != null) {
1372: for (Iterator i = schema.iterator(); i.hasNext();) {
1373: AttributeTypeInfoDTO dto = (AttributeTypeInfoDTO) i
1374: .next();
1375: info = new AttributeTypeInfo(dto);
1376: }
1377:
1378: DataStore dataStore = data.getDataStoreInfo(dataStoreId)
1379: .getDataStore();
1380:
1381: try {
1382: FeatureType ftype = dataStore.getSchema(typeName);
1383: info.sync(ftype.getAttributeType(attributeName));
1384: } catch (IOException e) {
1385: }
1386: } else {
1387: // will need to generate from Schema
1388: DataStore dataStore = data.getDataStoreInfo(dataStoreId)
1389: .getDataStore();
1390:
1391: try {
1392: FeatureType ftype = dataStore.getSchema(typeName);
1393: info = new AttributeTypeInfo(ftype
1394: .getAttributeType(attributeName));
1395: } catch (IOException e) {
1396: }
1397: }
1398:
1399: return info;
1400: }
1401:
1402: /**
1403: * Implement containsMetaData.
1404: *
1405: * @param key
1406: *
1407: * @return
1408: *
1409: * @see org.geotools.data.MetaData#containsMetaData(java.lang.String)
1410: */
1411: public boolean containsMetaData(String key) {
1412: return meta.containsKey(key);
1413: }
1414:
1415: /**
1416: * Implement putMetaData.
1417: *
1418: * @param key
1419: * @param value
1420: *
1421: * @see org.geotools.data.MetaData#putMetaData(java.lang.String,
1422: * java.lang.Object)
1423: */
1424: public void putMetaData(String key, Object value) {
1425: meta.put(key, value);
1426: }
1427:
1428: /**
1429: * Implement getMetaData.
1430: *
1431: * @param key
1432: *
1433: * @return
1434: *
1435: * @see org.geotools.data.MetaData#getMetaData(java.lang.String)
1436: */
1437: public Object getMetaData(String key) {
1438: return meta.get(key);
1439: }
1440:
1441: /**
1442: * getLegendURL purpose.
1443: *
1444: * <p>
1445: * returns the FeatureTypeInfo legendURL
1446: * </p>
1447: *
1448: * @return String the FeatureTypeInfo legendURL
1449: */
1450:
1451: // Modif C. Kolbowicz - 07/10/2004
1452: public LegendURL getLegendURL() {
1453: return this .legendURL;
1454: }
1455:
1456: //-- Modif C. Kolbowicz - 07/10/2004
1457:
1458: /**
1459: * Gets the schema.xml file associated with this FeatureType. This is set
1460: * during the reading of configuration, it is not persisted as an element
1461: * of the FeatureTypeInfoDTO, since it is just whether the schema.xml file
1462: * was persisted, and its location. If there is no schema.xml file then
1463: * this method will return a File object with the location where the schema
1464: * file would be located, but the file will return false for exists().
1465: */
1466: public File getSchemaFile() {
1467: return this .schemaFile;
1468: }
1469:
1470: /**
1471: * simple way of getting epsg #.
1472: * We cache them so that we dont have to keep reading the DB or the epsg.properties file.
1473: * I cannot image a system with more than a dozen CRSs in it...
1474: *
1475: * @param epsg
1476: * @return
1477: */
1478: private CoordinateReferenceSystem getSRS(int epsg) {
1479: CoordinateReferenceSystem result = (CoordinateReferenceSystem) SRSLookup
1480: .get(new Integer(epsg));
1481:
1482: if (result == null) {
1483: //make and add to hash
1484: try {
1485: result = CRS.decode("EPSG:" + epsg);
1486: SRSLookup.put(new Integer(epsg), result);
1487: } catch (NoSuchAuthorityCodeException e) {
1488: String msg = "Error looking up SRS for EPSG: " + epsg
1489: + ":" + e.getLocalizedMessage();
1490: LOGGER.warning(msg);
1491: } catch (FactoryException e) {
1492: String msg = "Error looking up SRS for EPSG: " + epsg
1493: + ":" + e.getLocalizedMessage();
1494: LOGGER.warning(msg);
1495: }
1496: }
1497:
1498: return result;
1499: }
1500:
1501: public String getDirName() {
1502: return dirName;
1503: }
1504:
1505: public String getWmsPath() {
1506: return wmsPath;
1507: }
1508:
1509: public void setWmsPath(String wmsPath) {
1510: this .wmsPath = wmsPath;
1511: }
1512:
1513: /**
1514: * This value is added the headers of generated maps, marking them as being both
1515: * "cache-able" and designating the time for which they are to remain valid.
1516: * The specific header added is "Cache-Control: max-age="
1517: * @return a string representing the number of seconds to be added to the "Cache-Control: max-age=" header
1518: */
1519: public String getCacheMaxAge() {
1520: return cacheMaxAge;
1521: }
1522:
1523: /**
1524: *
1525: * @param cacheMaxAge a string representing the number of seconds to be added to the "Cache-Control: max-age=" header
1526: */
1527: public void setCacheMaxAge(String cacheMaxAge) {
1528: this .cacheMaxAge = cacheMaxAge;
1529: }
1530:
1531: /**
1532: * Should we add the cache-control: max-age header to maps containing this layer?
1533: * @return true if we should, false if we should omit the header
1534: */
1535: public boolean isCachingEnabled() {
1536: return cachingEnabled;
1537: }
1538:
1539: /**
1540: * Sets whether we should add the cache-control: max-age header to maps containing this layer
1541: * @param cachingEnabled true if we should add the header, false if we should omit the header
1542: */
1543: public void setCachingEnabled(boolean cachingEnabled) {
1544: this .cachingEnabled = cachingEnabled;
1545: }
1546:
1547: /**
1548: * Returns the maximum number of features to be served by WFS GetFeature for this feature
1549: * type (or 0 for no limit)
1550: * @return
1551: */
1552: public int getMaxFeatures() {
1553: return maxFeatures;
1554: }
1555:
1556: public void setMaxFeatures(int maxFeatures) {
1557: this.maxFeatures = maxFeatures;
1558: }
1559: }
|