0001: //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/trunk/src/org/deegree/io/datastore/sql/AbstractRequestHandler.java $
0002: /*---------------- FILE HEADER ------------------------------------------
0003:
0004: This file is part of deegree.
0005: Copyright (C) 2001-2008 by:
0006: EXSE, Department of Geography, University of Bonn
0007: http://www.giub.uni-bonn.de/deegree/
0008: lat/lon GmbH
0009: http://www.lat-lon.de
0010:
0011: This library is free software; you can redistribute it and/or
0012: modify it under the terms of the GNU Lesser General Public
0013: License as published by the Free Software Foundation; either
0014: version 2.1 of the License, or (at your option) any later version.
0015:
0016: This library is distributed in the hope that it will be useful,
0017: but WITHOUT ANY WARRANTY; without even the implied warranty of
0018: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0019: Lesser General Public License for more details.
0020:
0021: You should have received a copy of the GNU Lesser General Public
0022: License along with this library; if not, write to the Free Software
0023: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0024:
0025: Contact:
0026:
0027: Andreas Poth
0028: lat/lon GmbH
0029: Aennchenstraße 19
0030: 53177 Bonn
0031: Germany
0032: E-Mail: poth@lat-lon.de
0033:
0034: Prof. Dr. Klaus Greve
0035: Department of Geography
0036: University of Bonn
0037: Meckenheimer Allee 166
0038: 53115 Bonn
0039: Germany
0040: E-Mail: greve@giub.uni-bonn.de
0041:
0042: ---------------------------------------------------------------------------*/
0043: package org.deegree.io.datastore.sql;
0044:
0045: import java.sql.Connection;
0046: import java.sql.PreparedStatement;
0047: import java.sql.ResultSet;
0048: import java.sql.SQLException;
0049: import java.util.ArrayList;
0050: import java.util.HashMap;
0051: import java.util.HashSet;
0052: import java.util.List;
0053: import java.util.Map;
0054: import java.util.Set;
0055:
0056: import org.deegree.framework.log.ILogger;
0057: import org.deegree.framework.log.LoggerFactory;
0058: import org.deegree.i18n.Messages;
0059: import org.deegree.io.datastore.DatastoreException;
0060: import org.deegree.io.datastore.FeatureId;
0061: import org.deegree.io.datastore.LockManager;
0062: import org.deegree.io.datastore.schema.MappedFeaturePropertyType;
0063: import org.deegree.io.datastore.schema.MappedFeatureType;
0064: import org.deegree.io.datastore.schema.MappedGMLId;
0065: import org.deegree.io.datastore.schema.MappedGMLSchema;
0066: import org.deegree.io.datastore.schema.MappedPropertyType;
0067: import org.deegree.io.datastore.schema.TableRelation;
0068: import org.deegree.io.datastore.schema.content.MappingField;
0069: import org.deegree.io.datastore.sql.wherebuilder.WhereBuilder;
0070: import org.deegree.model.feature.schema.FeatureType;
0071: import org.deegree.model.feature.schema.PropertyType;
0072: import org.deegree.model.filterencoding.Filter;
0073:
0074: /**
0075: * This abstract class implements some common SQL functionality needed by request handlers for SQL based datastores.
0076: *
0077: * @see QueryHandler
0078: *
0079: * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider </a>
0080: * @author last edited by: $Author: aschmitz $
0081: *
0082: * @version $Revision: 10299 $, $Date: 2008-02-26 00:42:07 -0800 (Tue, 26 Feb 2008) $
0083: */
0084: public class AbstractRequestHandler {
0085:
0086: private static final ILogger LOG = LoggerFactory
0087: .getLogger(AbstractRequestHandler.class);
0088:
0089: /**
0090: * Column used for disambiguation of feature properties that contain features that have more than one concrete type.
0091: */
0092: protected static final String FT_COLUMN = "featuretype";
0093:
0094: /**
0095: * Column prefix used for disambiguation of feature properties that contain features that have more than one
0096: * concrete type.
0097: */
0098: protected static final String FT_PREFIX = "FT_";
0099:
0100: protected AbstractSQLDatastore datastore;
0101:
0102: protected TableAliasGenerator aliasGenerator;
0103:
0104: protected Connection conn;
0105:
0106: /**
0107: * Creates a new instance of <code>AbstractRequestHandler</code> from the given parameters.
0108: *
0109: * @param ds
0110: * @param aliasGenerator
0111: * @param conn
0112: */
0113: public AbstractRequestHandler(AbstractSQLDatastore ds,
0114: TableAliasGenerator aliasGenerator, Connection conn) {
0115: this .datastore = ds;
0116: this .aliasGenerator = aliasGenerator;
0117: this .conn = conn;
0118: }
0119:
0120: /**
0121: * Determines the feature ids that are matched by the given filter.
0122: *
0123: * @param ft
0124: * non-abstract feature type
0125: * @param filter
0126: * constraints the feature instances
0127: * @return the feature ids that are matched by the given filter
0128: * @throws DatastoreException
0129: */
0130: public List<FeatureId> determineAffectedFIDs(MappedFeatureType ft,
0131: Filter filter) throws DatastoreException {
0132:
0133: assert !ft.isAbstract();
0134:
0135: TableAliasGenerator aliasGenerator = new TableAliasGenerator();
0136: VirtualContentProvider vcProvider = new VirtualContentProvider(
0137: filter, this .datastore, this .conn);
0138: WhereBuilder whereBuilder = this .datastore.getWhereBuilder(
0139: new MappedFeatureType[] { ft }, null, filter, null,
0140: aliasGenerator, vcProvider);
0141:
0142: // if no filter is given
0143: StatementBuffer query = buildInitialFIDSelect(ft, whereBuilder);
0144: LOG.logDebug("Determine affected feature id query: '" + query
0145: + "'");
0146:
0147: List<FeatureId> fids = null;
0148: PreparedStatement stmt = null;
0149: ResultSet rs = null;
0150: try {
0151: stmt = this .datastore.prepareStatement(conn, query);
0152: rs = stmt.executeQuery();
0153: fids = extractFeatureIds(rs, ft);
0154: } catch (SQLException e) {
0155: throw new DatastoreException(
0156: "Error while determining affected features of type: '"
0157: + ft.getName() + "': " + e.getMessage());
0158: } finally {
0159: try {
0160: if (rs != null) {
0161: try {
0162: rs.close();
0163: } catch (SQLException e) {
0164: LOG.logError("Error closing result set: '"
0165: + e.getMessage() + "'.", e);
0166: }
0167: }
0168: } finally {
0169: if (stmt != null) {
0170: try {
0171: stmt.close();
0172: } catch (SQLException e) {
0173: LOG.logError("Error closing statement: '"
0174: + e.getMessage() + "'.", e);
0175: }
0176: }
0177: }
0178: }
0179: return fids;
0180: }
0181:
0182: /**
0183: * Determines the feature ids that are matched by the given filter and that are either not locked or locked by the
0184: * specified lockId.
0185: *
0186: * @param ft
0187: * non-abstract feature type
0188: * @param filter
0189: * constraints the feature instances
0190: * @param lockId
0191: * optional id of associated lock (may be null)
0192: * @return the feature ids that are matched by the given filter
0193: * @throws DatastoreException
0194: */
0195: public List<FeatureId> determineAffectedAndModifiableFIDs(
0196: MappedFeatureType ft, Filter filter, String lockId)
0197: throws DatastoreException {
0198:
0199: List<FeatureId> affectedFids = determineAffectedFIDs(ft, filter);
0200: List<FeatureId> modifiableFids = new ArrayList<FeatureId>(
0201: affectedFids.size());
0202: for (FeatureId fid : affectedFids) {
0203: String lockedBy = LockManager.getInstance().getLockId(fid);
0204: if (lockedBy != null && !lockedBy.equals(lockId)) {
0205: String msg = Messages.getMessage(
0206: "DATASTORE_FEATURE_NOT_MODIFIABLE", fid,
0207: lockedBy);
0208: LOG.logInfo(msg);
0209: } else {
0210: modifiableFids.add(fid);
0211: }
0212: }
0213: return modifiableFids;
0214: }
0215:
0216: /**
0217: * Determines all complex properties and contained subfeature ids for a certain feature.
0218: *
0219: * @param fid
0220: * id of the feature
0221: * @return all complex properties and contained subfeature ids of the feature
0222: * @throws DatastoreException
0223: */
0224: public Map<MappedFeaturePropertyType, List<FeatureId>> determineSubFeatures(
0225: FeatureId fid) throws DatastoreException {
0226:
0227: LOG.logDebug("Determining sub features of feature '" + fid
0228: + "'...");
0229: Map<MappedFeaturePropertyType, List<FeatureId>> ptToSubFids = new HashMap<MappedFeaturePropertyType, List<FeatureId>>();
0230: PropertyType[] properties = fid.getFeatureType()
0231: .getProperties();
0232: for (PropertyType property : properties) {
0233: MappedPropertyType pt = (MappedPropertyType) property;
0234: if (pt instanceof MappedFeaturePropertyType) {
0235: LOG.logDebug("Complex property '" + pt.getName()
0236: + "'...");
0237: MappedFeaturePropertyType fPt = (MappedFeaturePropertyType) pt;
0238: List<FeatureId> subFids = determineSubFIDs(fid, fPt);
0239: ptToSubFids.put(fPt, subFids);
0240: }
0241: }
0242: return ptToSubFids;
0243: }
0244:
0245: /**
0246: * Determines the {@link FeatureId}s of the subfeatures contained in a specified feature's property.
0247: *
0248: * @param fid
0249: * id of the feature (for which the subfeatures will be determined)
0250: * @param pt
0251: * property type of the feature (that contains the subfeatures)
0252: * @return the matched subfeature's ids (with concrete feature types)
0253: * @throws DatastoreException
0254: */
0255: private List<FeatureId> determineSubFIDs(FeatureId fid,
0256: MappedFeaturePropertyType pt) throws DatastoreException {
0257:
0258: LOG.logDebug("Determining sub feature ids for feature: " + fid
0259: + " and property " + pt.getName());
0260:
0261: List<FeatureId> subFids = null;
0262: MappedFeatureType containedFt = pt.getFeatureTypeReference()
0263: .getFeatureType();
0264: MappedFeatureType[] concreteFts = containedFt
0265: .getConcreteSubstitutions();
0266: if (concreteFts.length > 1) {
0267: subFids = determineSubFIDs(fid, pt, concreteFts);
0268: } else {
0269: subFids = determineSubFIDs(fid, pt, containedFt);
0270: }
0271: return subFids;
0272: }
0273:
0274: /**
0275: * Determines all super features (as {@link FeatureId} instances) for a certain feature.
0276: *
0277: * @param fid
0278: * id of the feature
0279: * @return all super feature ids of the feature
0280: * @throws DatastoreException
0281: */
0282: public Set<FeatureId> determineSuperFeatures(FeatureId fid)
0283: throws DatastoreException {
0284:
0285: LOG.logDebug("Determining super features of feature "
0286: + fid.getAsString());
0287: Set<FeatureId> super Features = new HashSet<FeatureId>();
0288: MappedFeatureType subFt = fid.getFeatureType();
0289: Set<FeatureType> substitutableFts = subFt.getGMLSchema()
0290: .getSubstitutables(subFt);
0291: Set<MappedFeatureType> super Fts = determineSuperFeatureTypes(substitutableFts);
0292:
0293: for (MappedFeatureType super Ft : super Fts) {
0294: List<MappedFeaturePropertyType> featureProps = determineProperties(
0295: super Ft, subFt);
0296: for (MappedFeaturePropertyType featureProp : featureProps) {
0297: super Features.addAll(determineSuperFids(super Ft,
0298: featureProp, fid));
0299: }
0300: }
0301: return super Features;
0302: }
0303:
0304: /**
0305: * Determines all concrete feature types that can contain one or more of the given feature types inside a property.
0306: *
0307: * @param subFts
0308: * @return all concrete feature types that can contain the given feature type
0309: */
0310: private Set<MappedFeatureType> determineSuperFeatureTypes(
0311: Set<FeatureType> subFts) {
0312: Set<MappedFeatureType> super Fts = new HashSet<MappedFeatureType>();
0313: for (FeatureType subFt : subFts) {
0314: super Fts
0315: .addAll(determineSuperFeatureTypes((MappedFeatureType) subFt));
0316: }
0317: return super Fts;
0318: }
0319:
0320: /**
0321: * Determines all concrete feature types that can contain the given feature type inside a property.
0322: *
0323: * @param subFt
0324: * @return all concrete feature types that can contain the given feature type
0325: */
0326: private Set<MappedFeatureType> determineSuperFeatureTypes(
0327: MappedFeatureType subFt) {
0328: Set<MappedFeatureType> super Fts = new HashSet<MappedFeatureType>();
0329: MappedGMLSchema schema = subFt.getGMLSchema();
0330: FeatureType[] fts = schema.getFeatureTypes();
0331: for (int i = 0; i < fts.length; i++) {
0332: MappedFeatureType ft = (MappedFeatureType) fts[i];
0333: if (!ft.isAbstract()) {
0334: PropertyType[] properties = ft.getProperties();
0335: for (int j = 0; j < properties.length; j++) {
0336: MappedPropertyType property = (MappedPropertyType) properties[j];
0337: if (property instanceof MappedFeaturePropertyType) {
0338: MappedFeaturePropertyType ftProperty = (MappedFeaturePropertyType) property;
0339: if (ftProperty.getFeatureTypeReference()
0340: .getName().equals(subFt.getName())) {
0341: super Fts.add(ft);
0342: }
0343: }
0344: }
0345: }
0346:
0347: }
0348: return super Fts;
0349: }
0350:
0351: /**
0352: * Determines all {@link MappedFeaturePropertyType} instances that the super feature type has and which contain
0353: * features that may be substituted for features of the given sub feature type.
0354: *
0355: * @param superFt
0356: * @param subFt
0357: * @return corresponding property types
0358: */
0359: private List<MappedFeaturePropertyType> determineProperties(
0360: MappedFeatureType super Ft, MappedFeatureType subFt) {
0361: List<MappedFeaturePropertyType> featureProps = new ArrayList<MappedFeaturePropertyType>();
0362: PropertyType[] properties = super Ft.getProperties();
0363: for (PropertyType property : properties) {
0364: if (property instanceof MappedFeaturePropertyType) {
0365: MappedFeaturePropertyType featureProperty = (MappedFeaturePropertyType) property;
0366: MappedFeatureType containedFt = featureProperty
0367: .getFeatureTypeReference().getFeatureType();
0368: if (subFt.getGMLSchema().isValidSubstitution(
0369: containedFt, subFt)) {
0370: featureProps.add(featureProperty);
0371: }
0372: }
0373: }
0374: return featureProps;
0375: }
0376:
0377: /**
0378: * Determines all features (as {@link FeatureId}s) of the super feature type which contain the given feature
0379: * instance in the also specified property.
0380: *
0381: * @param superFt
0382: * @param featureProp
0383: * @param subFid
0384: * @return corresponding <code>DeleteNodes</code>
0385: */
0386: private List<FeatureId> determineSuperFids(
0387: MappedFeatureType super Ft,
0388: MappedFeaturePropertyType featureProp, FeatureId subFid)
0389: throws DatastoreException {
0390: this .aliasGenerator.reset();
0391: TableRelation[] relations = featureProp.getTableRelations();
0392:
0393: String super FtAlias = this .aliasGenerator.generateUniqueAlias();
0394: String[] joinTableAliases = this .aliasGenerator
0395: .generateUniqueAliases(relations.length);
0396: String subFtAlias = joinTableAliases[joinTableAliases.length - 1];
0397:
0398: StatementBuffer query = new StatementBuffer();
0399: query.append("SELECT DISTINCT ");
0400: appendFeatureIdColumns(super Ft, super FtAlias, query);
0401: query.append(" FROM ");
0402: query.append(super Ft.getTable());
0403: query.append(" ");
0404: query.append(super FtAlias);
0405: String fromAlias = super FtAlias;
0406: for (int i = 0; i < relations.length; i++) {
0407: String toAlias = joinTableAliases[i];
0408: query.append(" JOIN ");
0409: if (i == relations.length - 1) {
0410: query.append(subFid.getFeatureType().getTable());
0411: } else {
0412: query.append(relations[i].getToTable());
0413: }
0414: query.append(" ");
0415: query.append(toAlias);
0416: query.append(" ON ");
0417: appendJoinCondition(relations[i], fromAlias, toAlias, query);
0418: fromAlias = toAlias;
0419: }
0420:
0421: query.append(" WHERE ");
0422: MappedGMLId gmlId = subFid.getFidDefinition();
0423: MappingField[] idFields = gmlId.getIdFields();
0424: for (int i = 0; i < idFields.length; i++) {
0425: query.append(subFtAlias);
0426: query.append('.');
0427: query.append(idFields[i].getField());
0428: query.append("=?");
0429: query
0430: .addArgument(subFid.getValue(i), idFields[i]
0431: .getType());
0432: if (i != idFields.length - 1) {
0433: query.append(" AND ");
0434: }
0435: }
0436:
0437: List<FeatureId> fids = null;
0438: PreparedStatement stmt = null;
0439: ResultSet rs = null;
0440: try {
0441: stmt = this .datastore.prepareStatement(conn, query);
0442: LOG.logDebug("Performing: " + query);
0443: rs = stmt.executeQuery();
0444: fids = extractFeatureIds(rs, super Ft);
0445: } catch (SQLException e) {
0446: LOG.logInfo(e.getMessage(), e);
0447: throw new DatastoreException(
0448: "Error in determineSuperFeatures(): "
0449: + e.getMessage());
0450: } finally {
0451: try {
0452: if (rs != null) {
0453: try {
0454: rs.close();
0455: } catch (SQLException e) {
0456: LOG.logError("Error closing result set: '"
0457: + e.getMessage() + "'.", e);
0458: }
0459: }
0460: } finally {
0461: if (stmt != null) {
0462: try {
0463: stmt.close();
0464: } catch (SQLException e) {
0465: LOG.logError("Error closing statement: '"
0466: + e.getMessage() + "'.", e);
0467: }
0468: }
0469: }
0470: }
0471: return fids;
0472: }
0473:
0474: /**
0475: * Determines the {@link FeatureId}s of the subfeatures contained in the given feature property.
0476: *
0477: * @param fid
0478: * id of the feature
0479: * @param pt
0480: * table relation from the feature table to the subfeature table
0481: * @param concreteFt
0482: * concrete (non-abstract) type that is contained in the feature property
0483: * @return the <code>FeatureId</code> or null (if there is no such subfeature)
0484: * @throws DatastoreException
0485: */
0486: private List<FeatureId> determineSubFIDs(FeatureId fid,
0487: MappedFeaturePropertyType pt, MappedFeatureType concreteFt)
0488: throws DatastoreException {
0489:
0490: TableRelation[] relations = pt.getTableRelations();
0491:
0492: this .aliasGenerator.reset();
0493: String[] aliases = this .aliasGenerator
0494: .generateUniqueAliases(relations.length + 1);
0495:
0496: StatementBuffer query = new StatementBuffer();
0497: query.append("SELECT ");
0498: appendFeatureIdColumns(concreteFt, aliases[aliases.length - 1],
0499: query);
0500: query.append(" FROM ");
0501: query.append(relations[0].getFromTable());
0502: query.append(" ");
0503: query.append(aliases[0]);
0504:
0505: // append JOINs
0506: String fromAlias = aliases[0];
0507: for (int i = 0; i < relations.length; i++) {
0508: String toAlias = aliases[i + 1];
0509: query.append(" JOIN ");
0510: if (i == relations.length - 1) {
0511: query.append(concreteFt.getTable());
0512: } else {
0513: query.append(relations[i].getToTable());
0514: }
0515: query.append(" ");
0516: query.append(toAlias);
0517: query.append(" ON ");
0518: appendJoinCondition(relations[i], fromAlias, toAlias, query);
0519: fromAlias = toAlias;
0520: }
0521:
0522: query.append(" WHERE ");
0523: appendFeatureIdConstraint(query, fid, aliases[0]);
0524:
0525: List<FeatureId> subFids = null;
0526: PreparedStatement stmt = null;
0527: ResultSet rs = null;
0528: try {
0529: stmt = this .datastore.prepareStatement(conn, query);
0530: LOG.logDebug("Determining subfeature ids: " + query);
0531: rs = stmt.executeQuery();
0532: subFids = extractFeatureIds(rs, concreteFt);
0533: } catch (SQLException e) {
0534: LOG.logDebug(e.getMessage(), e);
0535: throw new DatastoreException(
0536: "Error in #determineSubFIDs(): " + e.getMessage());
0537: } finally {
0538: try {
0539: if (rs != null) {
0540: try {
0541: rs.close();
0542: } catch (SQLException e) {
0543: LOG.logError("Error closing result set: '"
0544: + e.getMessage() + "'.", e);
0545: }
0546: }
0547: } finally {
0548: if (stmt != null) {
0549: try {
0550: stmt.close();
0551: } catch (SQLException e) {
0552: LOG.logError("Error closing statement: '"
0553: + e.getMessage() + "'.", e);
0554: }
0555: }
0556: }
0557: }
0558: return subFids;
0559: }
0560:
0561: /**
0562: * Determines the feature ids of the subfeatures contained in the given feature property (that may contain features
0563: * of different concrete types).
0564: *
0565: * @param fid
0566: * id of the feature
0567: * @param pt
0568: * complex property that contains the subfeatures
0569: * @param concreteSubFts
0570: * all possible non-abstract feature types of the subfeatures
0571: * @return the ids of the subfeatures
0572: * @throws DatastoreException
0573: */
0574: private List<FeatureId> determineSubFIDs(FeatureId fid,
0575: MappedFeaturePropertyType pt,
0576: MappedFeatureType[] concreteSubFts)
0577: throws DatastoreException {
0578:
0579: List<FeatureId> subFids = null;
0580:
0581: TableRelation[] relations = pt.getTableRelations();
0582: LOG.logDebug("Determining sub feature ids for feature " + fid
0583: + ": relations.length: " + relations.length);
0584:
0585: switch (relations.length) {
0586: case 1: {
0587: // subfeature disambiguator in feature table (only zero or one subfeatures)
0588: MappedFeatureType concreteSubFt = determineSubFt(fid, pt,
0589: concreteSubFts);
0590: subFids = new ArrayList<FeatureId>(1);
0591: if (concreteSubFt != null) {
0592: FeatureId subFid = determineSubFID(fid, relations[0],
0593: concreteSubFt);
0594: if (subFid != null) {
0595: subFids.add(subFid);
0596: }
0597: }
0598: break;
0599: }
0600: case 2: {
0601: // subfeature disambiguator in join table (any number of subfeatures)
0602: subFids = determineSubFIDs(fid, pt, concreteSubFts,
0603: relations);
0604: break;
0605: }
0606: default: {
0607: String msg = Messages.getMessage(
0608: "DATASTORE_SUBFT_TOO_MANY_RELATIONS", fid
0609: .getFeatureType().getName(), pt.getName());
0610: throw new DatastoreException(msg);
0611: }
0612: }
0613: return subFids;
0614: }
0615:
0616: /**
0617: * Determine the concrete type of the subfeature that is stored in the specified property of a certain feature.
0618: * <p>
0619: * The relation to the sub feature table must be specified via a single step (join).
0620: *
0621: * @param fid
0622: * id of the feature for which the concrete subfeature type is needed
0623: * @param pt
0624: * property of the feature that contains the subfeature
0625: * @param concreteSubFts
0626: * concrete types that may be contained in the property
0627: * @return concrete type of the subfeature, or null if feature has no such property
0628: * @throws DatastoreException
0629: */
0630: private MappedFeatureType determineSubFt(FeatureId fid,
0631: MappedFeaturePropertyType pt,
0632: MappedFeatureType[] concreteSubFts)
0633: throws DatastoreException {
0634:
0635: assert (pt.getTableRelations().length == 1);
0636: TableRelation relation = pt.getTableRelations()[0];
0637:
0638: assert (relation.getFromFields().length == 1);
0639: String fkColumn = relation.getFromFields()[0].getField();
0640: String subFtColumn = FT_PREFIX + fkColumn;
0641:
0642: StatementBuffer query = new StatementBuffer();
0643: query.append("SELECT ");
0644: query.append(subFtColumn);
0645: query.append(" FROM ");
0646: query.append(fid.getFeatureType().getTable());
0647: query.append(" WHERE ");
0648: appendFeatureIdConstraint(query, fid);
0649:
0650: String localSubFtName = null;
0651: PreparedStatement stmt = null;
0652: ResultSet rs = null;
0653: try {
0654: stmt = this .datastore.prepareStatement(conn, query);
0655: LOG.logDebug("Determining concrete subfeature type: "
0656: + query);
0657: rs = stmt.executeQuery();
0658: rs.next();
0659: localSubFtName = rs.getString(1);
0660: } catch (SQLException e) {
0661: LOG.logDebug(e.getMessage(), e);
0662: throw new DatastoreException(
0663: "Error in determineConcreteSubFt() "
0664: + e.getMessage());
0665: } finally {
0666: try {
0667: if (rs != null) {
0668: try {
0669: rs.close();
0670: } catch (SQLException e) {
0671: LOG.logError("Error closing result set: '"
0672: + e.getMessage() + "'.", e);
0673: }
0674: }
0675: } finally {
0676: if (stmt != null) {
0677: try {
0678: stmt.close();
0679: } catch (SQLException e) {
0680: LOG.logError("Error closing statement: '"
0681: + e.getMessage() + "'.", e);
0682: }
0683: }
0684: }
0685: }
0686:
0687: MappedFeatureType concreteSubFt = null;
0688:
0689: if (localSubFtName != null) {
0690: for (MappedFeatureType type : concreteSubFts) {
0691: if (type.getName().getLocalName()
0692: .equals(localSubFtName)) {
0693: concreteSubFt = fid.getFeatureType().getGMLSchema()
0694: .getFeatureType(localSubFtName);
0695: break;
0696: }
0697: }
0698: if (concreteSubFt == null) {
0699: String msg = Messages.getMessage(
0700: "DATASTORE_FEATURE_TYPE_INFO_INCONSISTENT", pt
0701: .getName(), fid, subFtColumn,
0702: localSubFtName, pt.getFeatureTypeReference()
0703: .getName());
0704: throw new DatastoreException(msg);
0705: }
0706: }
0707:
0708: return concreteSubFt;
0709: }
0710:
0711: /**
0712: * Determines the {@link FeatureId} of the subfeature contained in the given feature property (if the feature has
0713: * such a subfeature).
0714: *
0715: * @param fid
0716: * id of the feature
0717: * @param relation
0718: * table relation from the feature table to the subfeature table
0719: * @param concreteFt
0720: * concrete (non-abstract) type that is contained in the feature property
0721: * @return the <code>FeatureId</code> or null (if there is no such subfeature)
0722: * @throws DatastoreException
0723: */
0724: private FeatureId determineSubFID(FeatureId fid,
0725: TableRelation relation, MappedFeatureType concreteFt)
0726: throws DatastoreException {
0727:
0728: this .aliasGenerator.reset();
0729: String fromAlias = this .aliasGenerator.generateUniqueAlias();
0730: String toAlias = this .aliasGenerator.generateUniqueAlias();
0731:
0732: StatementBuffer query = new StatementBuffer();
0733: query.append("SELECT ");
0734: appendFeatureIdColumns(concreteFt, toAlias, query);
0735: query.append(" FROM ");
0736: query.append(relation.getFromTable());
0737: query.append(" ");
0738: query.append(fromAlias);
0739: query.append(" JOIN ");
0740: query.append(concreteFt.getTable());
0741: query.append(" ");
0742: query.append(toAlias);
0743: query.append(" ON ");
0744: appendJoinCondition(relation, fromAlias, toAlias, query);
0745: query.append(" WHERE ");
0746: appendFeatureIdConstraint(query, fid, fromAlias);
0747:
0748: FeatureId subFid = null;
0749: PreparedStatement stmt = null;
0750: ResultSet rs = null;
0751: try {
0752: stmt = this .datastore.prepareStatement(conn, query);
0753: LOG.logDebug("Determining subfeature id: " + query);
0754: rs = stmt.executeQuery();
0755: if (rs.next()) {
0756: subFid = extractFeatureId(rs, concreteFt);
0757: }
0758: } catch (SQLException e) {
0759: LOG.logDebug(e.getMessage(), e);
0760: throw new DatastoreException(
0761: "Error in #determineSubFID(): " + e.getMessage());
0762: } finally {
0763: try {
0764: if (rs != null) {
0765: try {
0766: rs.close();
0767: } catch (SQLException e) {
0768: LOG.logError("Error closing result set: '"
0769: + e.getMessage() + "'.", e);
0770: }
0771: }
0772: } finally {
0773: if (stmt != null) {
0774: try {
0775: stmt.close();
0776: } catch (SQLException e) {
0777: LOG.logError("Error closing statement: '"
0778: + e.getMessage() + "'.", e);
0779: }
0780: }
0781: }
0782: }
0783: return subFid;
0784: }
0785:
0786: /**
0787: * Determines the feature ids of the subfeatures contained in the given feature property (that may contain features
0788: * of different concrete types and is connected via a join table with feature type disambiguation column).
0789: *
0790: * @param fid
0791: * @param pt
0792: * @return the matched subfeatures' ids
0793: * @throws DatastoreException
0794: */
0795: private List<FeatureId> determineSubFIDs(FeatureId fid,
0796: MappedFeaturePropertyType pt,
0797: MappedFeatureType[] concreteSubFts,
0798: TableRelation[] relations) throws DatastoreException {
0799: this .aliasGenerator.reset();
0800: String fromAlias = this .aliasGenerator.generateUniqueAlias();
0801: String jtAlias = this .aliasGenerator.generateUniqueAlias();
0802:
0803: StatementBuffer query = new StatementBuffer();
0804: query.append("SELECT ");
0805:
0806: // select feature type disambiguation column and from fields of second table relation
0807: appendQualifiedColumn(query, jtAlias, FT_COLUMN);
0808: MappingField[] fromFields = relations[1].getFromFields();
0809: for (int i = 0; i < fromFields.length; i++) {
0810: query.append(',');
0811: appendQualifiedColumn(query, jtAlias, fromFields[i]
0812: .getField());
0813: }
0814:
0815: query.append(" FROM ");
0816: query.append(relations[0].getFromTable());
0817: query.append(" ");
0818: query.append(fromAlias);
0819: query.append(" JOIN ");
0820: query.append(relations[0].getToTable());
0821: query.append(" ");
0822: query.append(jtAlias);
0823: query.append(" ON ");
0824: appendJoinCondition(relations[0], fromAlias, jtAlias, query);
0825: query.append(" WHERE ");
0826: appendFeatureIdConstraint(query, fid, fromAlias);
0827:
0828: List<FeatureId> subFids = new ArrayList<FeatureId>();
0829: PreparedStatement stmt = null;
0830: ResultSet rs = null;
0831: try {
0832: stmt = this .datastore.prepareStatement(conn, query);
0833: LOG
0834: .logDebug("Determining concrete subfeature types and join keys: "
0835: + query);
0836: rs = stmt.executeQuery();
0837: Object[] keyComponents = new Object[relations[1]
0838: .getFromFields().length];
0839: while (rs.next()) {
0840: String localSubFtName = rs.getString(1);
0841: for (int i = 0; i < keyComponents.length; i++) {
0842: keyComponents[i] = rs.getObject(i + 2);
0843: }
0844: MappedFeatureType concreteSubFt = null;
0845: for (MappedFeatureType type : concreteSubFts) {
0846: if (type.getName().getLocalName().equals(
0847: localSubFtName)) {
0848: concreteSubFt = fid.getFeatureType()
0849: .getGMLSchema().getFeatureType(
0850: localSubFtName);
0851: break;
0852: }
0853: }
0854: if (concreteSubFt == null) {
0855: String msg = Messages.getMessage(
0856: "DATASTORE_FEATURE_TYPE_INFO_INCONSISTENT",
0857: pt.getName(), fid, FT_COLUMN,
0858: localSubFtName, pt
0859: .getFeatureTypeReference()
0860: .getName());
0861: throw new DatastoreException(msg);
0862: }
0863:
0864: subFids.add(determineSubFID(concreteSubFt,
0865: relations[1], keyComponents));
0866: }
0867: } catch (SQLException e) {
0868: LOG.logDebug(e.getMessage(), e);
0869: throw new DatastoreException(
0870: "Error in #determineSubFIDs(): " + e.getMessage());
0871: } finally {
0872: try {
0873: if (rs != null) {
0874: try {
0875: rs.close();
0876: } catch (SQLException e) {
0877: LOG.logError("Error closing result set: '"
0878: + e.getMessage() + "'.", e);
0879: }
0880: }
0881: } finally {
0882: if (stmt != null) {
0883: try {
0884: stmt.close();
0885: } catch (SQLException e) {
0886: LOG.logError("Error closing statement: '"
0887: + e.getMessage() + "'.", e);
0888: }
0889: }
0890: }
0891: }
0892: return subFids;
0893: }
0894:
0895: /**
0896: * Determines the {@link FeatureId} of the subfeature referenced by the given {@link TableRelation}.
0897: *
0898: * @param concreteFt
0899: * conrete (non-abstract) type that is contained in the feature property
0900: * @param relation
0901: * table relation from the join table to the subfeature table
0902: * @param keyComponents
0903: * @return the <code>FeatureId</code> or null (if there is no such subfeature)
0904: * @throws DatastoreException
0905: */
0906: private FeatureId determineSubFID(MappedFeatureType concreteSubFt,
0907: TableRelation relation, Object[] keyComponents)
0908: throws DatastoreException {
0909: this .aliasGenerator.reset();
0910: String fromAlias = this .aliasGenerator.generateUniqueAlias();
0911: String toAlias = this .aliasGenerator.generateUniqueAlias();
0912:
0913: StatementBuffer query = new StatementBuffer();
0914: query.append("SELECT ");
0915: appendFeatureIdColumns(concreteSubFt, toAlias, query);
0916: query.append(" FROM ");
0917: query.append(relation.getFromTable());
0918: query.append(" ");
0919: query.append(fromAlias);
0920: query.append(" JOIN ");
0921: query.append(concreteSubFt.getTable());
0922: query.append(" ");
0923: query.append(toAlias);
0924: query.append(" ON ");
0925: appendJoinCondition(relation, fromAlias, toAlias, query);
0926: query.append(" WHERE ");
0927: for (int i = 0; i < keyComponents.length; i++) {
0928: appendQualifiedColumn(query, fromAlias, relation
0929: .getFromFields()[i].getField());
0930: query.append("=?");
0931: query.addArgument(keyComponents[i], relation
0932: .getFromFields()[i].getType());
0933: if (i != keyComponents.length - 1) {
0934: query.append(" AND ");
0935: }
0936: }
0937: FeatureId subFid = null;
0938: PreparedStatement stmt = null;
0939: ResultSet rs = null;
0940: try {
0941: stmt = this .datastore.prepareStatement(conn, query);
0942: LOG.logDebug("Determining subfeature id: " + query);
0943: rs = stmt.executeQuery();
0944: if (rs.next()) {
0945: subFid = extractFeatureId(rs, concreteSubFt);
0946: }
0947: } catch (SQLException e) {
0948: LOG.logDebug(e.getMessage(), e);
0949: throw new DatastoreException(
0950: "Error in #determineSubFID(): " + e.getMessage());
0951: } finally {
0952: try {
0953: if (rs != null) {
0954: try {
0955: rs.close();
0956: } catch (SQLException e) {
0957: LOG.logError("Error closing result set: '"
0958: + e.getMessage() + "'.", e);
0959: }
0960: }
0961: } finally {
0962: if (stmt != null) {
0963: try {
0964: stmt.close();
0965: } catch (SQLException e) {
0966: LOG.logError("Error closing statement: '"
0967: + e.getMessage() + "'.", e);
0968: }
0969: }
0970: }
0971: }
0972: return subFid;
0973: }
0974:
0975: /**
0976: * Builds the initial SELECT statement that retrieves the feature ids that are matched by the given
0977: * <code>WhereBuilder</code>.
0978: * <p>
0979: * The statement is structured like this:
0980: * <ul>
0981: * <li><code>SELECT</code></li>
0982: * <li>comma-separated list of qualified fid fields</li>
0983: * <li><code>FROM</code></li>
0984: * <li>comma-separated list of tables and their aliases (this is needed to constrain the paths to selected
0985: * XPath-PropertyNames)</li>
0986: * <li><code>WHERE</code></li>
0987: * <li>SQL representation of the Filter expression</li>
0988: * </ul>
0989: *
0990: * @param rootFt
0991: * @param whereBuilder
0992: * @return initial SELECT statement to retrieve the feature ids
0993: * @throws DatastoreException
0994: */
0995: private StatementBuffer buildInitialFIDSelect(
0996: MappedFeatureType rootFt, WhereBuilder whereBuilder)
0997: throws DatastoreException {
0998:
0999: String tableAlias = whereBuilder.getRootTableAlias(0);
1000: StatementBuffer query = new StatementBuffer();
1001: query.append("SELECT ");
1002: appendFeatureIdColumns(rootFt, tableAlias, query);
1003: query.append(" FROM ");
1004: whereBuilder.appendJoinTableList(query);
1005: whereBuilder.appendWhereCondition(query);
1006: return query;
1007: }
1008:
1009: /**
1010: * Appends the alias qualified columns that make up the feature id to the given query.
1011: *
1012: * @param featureType
1013: * @param tableAlias
1014: * @param query
1015: */
1016: protected void appendFeatureIdColumns(
1017: MappedFeatureType featureType, String tableAlias,
1018: StatementBuffer query) {
1019: MappingField[] fidFields = featureType.getGMLId().getIdFields();
1020: for (int i = 0; i < fidFields.length; i++) {
1021: query.append(tableAlias);
1022: query.append('.');
1023: query.append(fidFields[i].getField());
1024: if (i != fidFields.length - 1) {
1025: query.append(',');
1026: }
1027: }
1028: }
1029:
1030: /**
1031: * Extracts the FeatureId in the current row of the given {@link ResultSet}.
1032: *
1033: * @param rs
1034: * @param ft
1035: * feature type (may not be abstract)
1036: * @return feature ids
1037: * @throws SQLException
1038: * @throws DatastoreException
1039: */
1040: protected FeatureId extractFeatureId(ResultSet rs,
1041: MappedFeatureType ft) throws SQLException,
1042: DatastoreException {
1043:
1044: MappedGMLId gmlId = ft.getGMLId();
1045: MappingField[] idFields = gmlId.getIdFields();
1046:
1047: Object[] idValues = new Object[idFields.length];
1048: for (int i = 0; i < idValues.length; i++) {
1049: Object idValue = rs.getObject(i + 1);
1050: if (idValue == null) {
1051: String msg = Messages.getMessage(
1052: "DATASTORE_FEATURE_ID_NULL", ft.getTable(), ft
1053: .getName(), idFields[i].getField());
1054: throw new DatastoreException(msg);
1055: }
1056: idValues[i] = idValue;
1057: }
1058:
1059: return new FeatureId(ft, idValues);
1060: }
1061:
1062: /**
1063: * Extracts the feature ids in the given {@link ResultSet} as a List of FeatureIds.
1064: * <p>
1065: * If the given feature type is abstract, it is expected that the first column of the result set contains the local
1066: * name of the feature type.
1067: *
1068: * @param rs
1069: * @param ft
1070: * feature type (may be abstract)
1071: * @return feature ids
1072: * @throws SQLException
1073: * @throws DatastoreException
1074: */
1075: protected List<FeatureId> extractFeatureIds(ResultSet rs,
1076: MappedFeatureType ft) throws SQLException,
1077: DatastoreException {
1078: List<FeatureId> featureIdList = new ArrayList<FeatureId>();
1079: MappedGMLId gmlId = ft.getGMLId();
1080: MappingField[] idFields = gmlId.getIdFields();
1081:
1082: boolean needsDisambiguation = ft.hasSeveralImplementations();
1083:
1084: while (rs.next()) {
1085: int offset = 1;
1086: if (needsDisambiguation) {
1087: String localFtName = rs.getString(1);
1088: ft = ft.getGMLSchema().getFeatureType(localFtName);
1089: gmlId = ft.getGMLId();
1090: idFields = gmlId.getIdFields();
1091: offset = 2;
1092: }
1093: Object[] idValues = new Object[idFields.length];
1094: for (int i = 0; i < idValues.length; i++) {
1095: Object idValue = rs.getObject(i + offset);
1096: if (idValue == null) {
1097: String msg = Messages.getMessage(
1098: "DATASTORE_FEATURE_ID_NULL", ft.getTable(),
1099: ft.getName(), idFields[i].getField());
1100: throw new DatastoreException(msg);
1101: }
1102: idValues[i] = idValue;
1103: }
1104: featureIdList.add(new FeatureId(ft, idValues));
1105: }
1106: return featureIdList;
1107: }
1108:
1109: protected void appendJoins(TableRelation[] tableRelation,
1110: String fromAlias, String[] toAliases, StatementBuffer query) {
1111: for (int i = 0; i < toAliases.length; i++) {
1112: String toAlias = toAliases[i];
1113: appendJoin(tableRelation[i], fromAlias, toAlias, query);
1114: fromAlias = toAlias;
1115: }
1116: }
1117:
1118: private void appendJoin(TableRelation tableRelation,
1119: String fromAlias, String toAlias, StatementBuffer query) {
1120: query.append(" JOIN ");
1121: query.append(tableRelation.getToTable());
1122: query.append(" ");
1123: query.append(toAlias);
1124: query.append(" ON ");
1125: appendJoinCondition(tableRelation, fromAlias, toAlias, query);
1126: }
1127:
1128: protected void appendJoinCondition(TableRelation tableRelation,
1129: String fromAlias, String toAlias, StatementBuffer query) {
1130:
1131: MappingField[] fromFields = tableRelation.getFromFields();
1132: MappingField[] toFields = tableRelation.getToFields();
1133: for (int i = 0; i < fromFields.length; i++) {
1134: query.append(toAlias);
1135: query.append(".");
1136: query.append(toFields[i].getField());
1137: query.append("=");
1138: query.append(fromAlias);
1139: query.append(".");
1140: query.append(fromFields[i].getField());
1141: if (i != fromFields.length - 1) {
1142: query.append(" AND ");
1143: }
1144: }
1145: }
1146:
1147: protected void appendFeatureIdConstraint(StatementBuffer query,
1148: FeatureId fid) {
1149: MappingField[] idFields = fid.getFidDefinition().getIdFields();
1150: for (int i = 0; i < idFields.length; i++) {
1151: query.append(idFields[i].getField());
1152: query.append("=?");
1153: query.addArgument(fid.getValue(i), idFields[i].getType());
1154: if (i < idFields.length - 1) {
1155: query.append(" AND ");
1156: }
1157: }
1158: }
1159:
1160: protected void appendFeatureIdConstraint(StatementBuffer query,
1161: FeatureId fid, String tableAlias) {
1162: MappingField[] idFields = fid.getFidDefinition().getIdFields();
1163: for (int i = 0; i < idFields.length; i++) {
1164: query.append(tableAlias);
1165: query.append('.');
1166: query.append(idFields[i].getField());
1167: query.append("=?");
1168: query.addArgument(fid.getValue(i), idFields[i].getType());
1169: if (i < idFields.length - 1) {
1170: query.append(" AND ");
1171: }
1172: }
1173: }
1174:
1175: /**
1176: * Appends the specified columns as a comma-separated list to the given query.
1177: *
1178: * @param query
1179: * StatementBuffer that the list is appended to
1180: * @param columns
1181: * array of column names
1182: */
1183: public void appendColumnsList(StatementBuffer query,
1184: String[] columns) {
1185: for (int i = 0; i < columns.length; i++) {
1186: if (columns[i].indexOf('$') != -1) {
1187: // function call
1188: String column = columns[i];
1189: column = column.replaceAll("\\$\\.", "");
1190: query.append(column);
1191:
1192: } else {
1193: query.append(columns[i]);
1194: }
1195:
1196: if (i != columns.length - 1) {
1197: query.append(',');
1198: }
1199: }
1200: }
1201:
1202: /**
1203: * Appends the specified columns as alias-qualified, comma-separated list to the given query.
1204: *
1205: * @param query
1206: * StatementBuffer that the list is appended to
1207: * @param tableAlias
1208: * alias to use as qualifier (alias.field)
1209: * @param columns
1210: * array of column names
1211: */
1212: public void appendQualifiedColumnsList(StatementBuffer query,
1213: String tableAlias, String[] columns) {
1214: for (int i = 0; i < columns.length; i++) {
1215: appendQualifiedColumn(query, tableAlias, columns[i]);
1216: if (i != columns.length - 1) {
1217: query.append(',');
1218: }
1219: }
1220: }
1221:
1222: /**
1223: * Appends the specified column to the given query.
1224: *
1225: * @param query
1226: * StatementBuffer that the list is appended to
1227: * @param tableAlias
1228: * alias to use as qualifier (alias.field)
1229: * @param column
1230: * column name
1231: */
1232: public void appendQualifiedColumn(StatementBuffer query,
1233: String tableAlias, String column) {
1234: query.append(tableAlias);
1235: query.append('.');
1236: query.append(column);
1237: }
1238: }
|