PageRenderTime 39ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/projects/geotools-9.2/modules/extension/app-schema/app-schema/src/main/java/org/geotools/data/complex/NestedAttributeMapping.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 475 lines | 266 code | 58 blank | 151 comment | 64 complexity | d5b52c613457a1b2db88edf635e16ddb MD5 | raw file
  1. /*
  2. * GeoTools - The Open Source Java GIS Toolkit
  3. * http://geotools.org
  4. *
  5. * (C) 2009-2011, Open Source Geospatial Foundation (OSGeo)
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation;
  10. * version 2.1 of the License.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. */
  17. package org.geotools.data.complex;
  18. import java.io.IOException;
  19. import java.util.ArrayList;
  20. import java.util.Collections;
  21. import java.util.HashSet;
  22. import java.util.List;
  23. import java.util.Map;
  24. import java.util.logging.Logger;
  25. import org.geotools.data.FeatureSource;
  26. import org.geotools.data.Query;
  27. import org.geotools.data.complex.filter.XPathUtil.StepList;
  28. import org.geotools.factory.Hints;
  29. import org.geotools.feature.FeatureCollection;
  30. import org.geotools.feature.FeatureIterator;
  31. import org.geotools.feature.Types;
  32. import org.geotools.filter.FilterFactoryImplNamespaceAware;
  33. import org.geotools.util.Converters;
  34. import org.opengis.feature.Feature;
  35. import org.opengis.feature.type.FeatureType;
  36. import org.opengis.feature.type.Name;
  37. import org.opengis.filter.Filter;
  38. import org.opengis.filter.FilterFactory;
  39. import org.opengis.filter.expression.Expression;
  40. import org.opengis.filter.expression.Function;
  41. import org.opengis.filter.expression.PropertyName;
  42. import org.opengis.filter.identity.FeatureId;
  43. import org.opengis.referencing.crs.CoordinateReferenceSystem;
  44. import org.xml.sax.helpers.NamespaceSupport;
  45. import java.util.Collections;
  46. /**
  47. * This class represents AttributeMapping for attributes that are nested inside another complex
  48. * attribute. The nested attributes would be features, or fake features, ie. complex attributes
  49. * which types are wrapped with NonFeatureTypeProxy instances. The purpose of this class is to store
  50. * nested built features so they can be retrieved when the parent feature is being built. Simple
  51. * features are also stored for caching if a filter involving these nested features is run.
  52. *
  53. * @author Rini Angreani (CSIRO Earth Science and Resource Engineering)
  54. *
  55. *
  56. *
  57. *
  58. * @source $URL$
  59. * http://svn.osgeo.org/geotools/trunk/modules/unsupported/app-schema/app-schema/src/main
  60. * /java/org/geotools/data/complex/NestedAttributeMapping.java $
  61. */
  62. public class NestedAttributeMapping extends AttributeMapping {
  63. private static final Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.geotools.data.complex");
  64. /**
  65. * Input feature source of the nested features
  66. */
  67. private FeatureSource<FeatureType, Feature> source;
  68. /**
  69. * Mapped feature source of the nested features
  70. */
  71. private FeatureSource<FeatureType, Feature> mappingSource;
  72. /**
  73. * Name of the nested features element
  74. */
  75. protected final Expression nestedFeatureType;
  76. /**
  77. * Target xpath that links to nested features
  78. */
  79. protected final StepList nestedTargetXPath;
  80. /**
  81. * Source expression of the nested features
  82. */
  83. private Expression nestedSourceExpression;
  84. /**
  85. * Filter factory
  86. */
  87. protected FilterFactory filterFac;
  88. private NamespaceSupport namespaces;
  89. /**
  90. * Id expression for the nested type.
  91. */
  92. private Expression nestedIdExpression;
  93. /**
  94. * true if the type is depending on a function value, i.e. could be a Function
  95. */
  96. private boolean isConditional;
  97. /**
  98. * Sole constructor
  99. *
  100. * @param idExpression
  101. * @param parentExpression
  102. * @param targetXPath
  103. * @param targetNodeInstance
  104. * @param isMultiValued
  105. * @param clientProperties
  106. * @param sourceElement
  107. * parent feature element type
  108. * @param sourcePath
  109. * XPath link to nested feature
  110. * @param parentSource
  111. * parent feature source
  112. * @throws IOException
  113. */
  114. public NestedAttributeMapping(Expression idExpression, Expression parentExpression,
  115. StepList targetXPath, boolean isMultiValued, Map<Name, Expression> clientProperties,
  116. Expression sourceElement, StepList sourcePath, NamespaceSupport namespaces)
  117. throws IOException {
  118. super(idExpression, parentExpression, null, targetXPath, null, isMultiValued, clientProperties);
  119. this.nestedTargetXPath = sourcePath;
  120. this.nestedFeatureType = sourceElement;
  121. this.filterFac = new FilterFactoryImplNamespaceAware(namespaces);
  122. this.namespaces = namespaces;
  123. this.isConditional = nestedFeatureType instanceof Function;
  124. }
  125. @Override
  126. /*
  127. * @see org.geotools.data.complex.AttributeMapping#isNestedAttribute()
  128. */
  129. public boolean isNestedAttribute() {
  130. return true;
  131. }
  132. /**
  133. * Get matching input features that are stored in this mapping using a supplied link value.
  134. *
  135. * @param foreignKeyValue
  136. * @return The matching input feature
  137. * @throws IOException
  138. * @throws IOException
  139. */
  140. public List<Feature> getInputFeatures(Object caller, Object foreignKeyValue,
  141. List<Object> idValues, Object feature, CoordinateReferenceSystem reprojection,
  142. List<PropertyName> selectedProperties, boolean includeMandatory) throws IOException {
  143. if (isSameSource()) {
  144. // if linkField is null, this method shouldn't be called because the mapping
  145. // should use the same table, and handles it differently
  146. throw new UnsupportedOperationException(
  147. "Link field is missing from feature chaining mapping!");
  148. }
  149. boolean isMultiple = false;
  150. if (source == null || isConditional) {
  151. // We can't initiate this in the constructor because the feature type mapping
  152. // might not be built yet.
  153. Object featureTypeName = getNestedFeatureType(feature);
  154. if (featureTypeName == null || !(featureTypeName instanceof Name)) {
  155. // this could be legitimate, for some null values polymorphism use case
  156. // or that it's set to be xlink:href
  157. return Collections.EMPTY_LIST;
  158. }
  159. FeatureTypeMapping featureTypeMapping = AppSchemaDataAccessRegistry
  160. .getMappingByName((Name) featureTypeName);
  161. if (featureTypeMapping == null) {
  162. LOGGER.info("FeatureTypeMapping for '" + featureTypeName + "' not found when evaluating filter!");
  163. return Collections.EMPTY_LIST;
  164. }
  165. nestedIdExpression = featureTypeMapping.getFeatureIdExpression();
  166. source = featureTypeMapping.getSource();
  167. if (source == null) {
  168. LOGGER.info("Feature source for '" + featureTypeName + "' not found when evaluating filter");
  169. return Collections.EMPTY_LIST;
  170. }
  171. // find source expression on nested features side
  172. List<AttributeMapping> mappings = featureTypeMapping
  173. .getAttributeMappingsIgnoreIndex(this.nestedTargetXPath);
  174. if (mappings.size() < 1) {
  175. throw new IllegalArgumentException("Mapping is missing for: '"
  176. + this.nestedTargetXPath + "'!");
  177. }
  178. AttributeMapping mapping = mappings.get(0);
  179. nestedSourceExpression = mapping.getSourceExpression();
  180. isMultiple = mapping.isMultiValued();
  181. }
  182. return getFilteredFeatures(foreignKeyValue, isMultiple);
  183. }
  184. /**
  185. * Run the query to get built features from a table based on a foreign key.
  186. *
  187. * @param foreignKeyValue
  188. * foreign key to filter by
  189. * @param isMultiple
  190. * true if the table is denormalised and multiple values are possible for the same id
  191. * @return list of built features
  192. * @throws IOException
  193. */
  194. private List<Feature> getFilteredFeatures(Object foreignKeyValue, boolean isMultiple) throws IOException {
  195. if (nestedSourceExpression == null) {
  196. return Collections.EMPTY_LIST;
  197. }
  198. ArrayList<Feature> matchingFeatures = new ArrayList<Feature>();
  199. Filter filter = filterFac.equals(this.nestedSourceExpression, filterFac
  200. .literal(foreignKeyValue));
  201. // get all the nested features based on the link values
  202. FeatureCollection<FeatureType, Feature> fCollection = source.getFeatures(filter);
  203. FeatureIterator<Feature> it = fCollection.features();
  204. Filter matchingIdFilter = null;
  205. if (nestedIdExpression.equals(Expression.NIL)) {
  206. HashSet<FeatureId> featureIds = new HashSet<FeatureId>();
  207. while (it.hasNext()) {
  208. Feature f = it.next();
  209. matchingFeatures.add(f);
  210. if (isMultiple && f.getIdentifier() != null) {
  211. featureIds.add(f.getIdentifier());
  212. }
  213. }
  214. // Find features of the same id from denormalised view
  215. if (!featureIds.isEmpty()) {
  216. matchingIdFilter = filterFac.id(featureIds);
  217. }
  218. } else {
  219. HashSet<String> featureIds = new HashSet<String>();
  220. while (it.hasNext()) {
  221. Feature f = it.next();
  222. matchingFeatures.add(f);
  223. if (isMultiple) {
  224. featureIds.add(Converters.convert(nestedIdExpression.evaluate(f), String.class));
  225. }
  226. }
  227. // Find features of the same id from denormalised view
  228. if (!featureIds.isEmpty()) {
  229. List<Filter> idFilters = new ArrayList<Filter>(featureIds.size());
  230. for (String id : featureIds) {
  231. idFilters.add(filterFac.equals(nestedIdExpression, filterFac.literal(id)));
  232. }
  233. matchingIdFilter = filterFac.or(idFilters);
  234. }
  235. }
  236. it.close();
  237. if (matchingIdFilter != null) {
  238. fCollection = source.getFeatures(matchingIdFilter);
  239. if (fCollection.size() > matchingFeatures.size()) {
  240. // there are rows of same id from denormalised view
  241. it = fCollection.features();
  242. matchingFeatures.clear();
  243. while (it.hasNext()) {
  244. matchingFeatures.add(it.next());
  245. }
  246. it.close();
  247. }
  248. }
  249. return matchingFeatures;
  250. }
  251. /**
  252. * Get matching input features that are stored in this mapping using a supplied link value.
  253. *
  254. * @param foreignKeyValue
  255. * @return The matching input feature
  256. * @throws IOException
  257. * @throws IOException
  258. */
  259. public List<Feature> getInputFeatures(Object foreignKeyValue, FeatureTypeMapping fMapping)
  260. throws IOException {
  261. if (isSameSource()) {
  262. // if linkField is null, this method shouldn't be called because the mapping
  263. // should use the same table, and handles it differently
  264. throw new UnsupportedOperationException(
  265. "Link field is missing from feature chaining mapping!");
  266. }
  267. boolean isMultiple = false;
  268. if (source == null || isConditional) {
  269. if (fMapping != null) {
  270. source = fMapping.getSource();
  271. if (source == null) {
  272. LOGGER.info("Feature source for '" + fMapping.getTargetFeature().getName()
  273. + "' not found when evaluating filter");
  274. return Collections.EMPTY_LIST;
  275. }
  276. nestedIdExpression = fMapping.getFeatureIdExpression();
  277. // find source expression on nested features side
  278. List<AttributeMapping> mappings = fMapping
  279. .getAttributeMappingsIgnoreIndex(this.nestedTargetXPath);
  280. if (mappings.size() < 1) {
  281. throw new IllegalArgumentException("Mapping is missing for: '"
  282. + this.nestedTargetXPath + "'!");
  283. }
  284. AttributeMapping mapping = mappings.get(0);
  285. nestedSourceExpression = mapping.getSourceExpression();
  286. isMultiple = mapping.isMultiValued();
  287. }
  288. }
  289. if (nestedSourceExpression == null) {
  290. return null;
  291. }
  292. return getFilteredFeatures(foreignKeyValue, isMultiple);
  293. }
  294. /**
  295. * Get the maching built features that are stored in this mapping using a supplied link value
  296. *
  297. * @param foreignKeyValue
  298. * @param reprojection
  299. * Reprojected CRS or null
  300. * @return The matching simple features
  301. * @throws IOException
  302. */
  303. public List<Feature> getFeatures(Object foreignKeyValue,
  304. CoordinateReferenceSystem reprojection, Feature feature) throws IOException{
  305. return getFeatures(null, foreignKeyValue, null, reprojection, feature, null, true);
  306. }
  307. /**
  308. * Get the maching built features that are stored in this mapping using a supplied link value
  309. *
  310. * @param foreignKeyValue
  311. * @param reprojection
  312. * Reprojected CRS or null
  313. * @param selectedProperties list of properties to get
  314. * @return The matching simple features
  315. * @throws IOException
  316. */
  317. public List<Feature> getFeatures(Object source, Object foreignKeyValue, List<Object> idValues,
  318. CoordinateReferenceSystem reprojection, Object feature, List<PropertyName> selectedProperties, boolean includeMandatory) throws IOException {
  319. if (foreignKeyValue == null) {
  320. return Collections.<Feature>emptyList();
  321. }
  322. if (isSameSource()) {
  323. // if linkField is null, this method shouldn't be called because the mapping
  324. // should use the same table, and handles it differently
  325. throw new UnsupportedOperationException(
  326. "Link field is missing from feature chaining mapping!");
  327. }
  328. FeatureSource<FeatureType, Feature> fSource = getMappingSource(feature);
  329. if (fSource == null) {
  330. return null;
  331. }
  332. Query query = new Query();
  333. query.setCoordinateSystemReproject(reprojection);
  334. Filter filter;
  335. PropertyName propertyName = filterFac.property(this.nestedTargetXPath.toString());
  336. filter = filterFac.equals(propertyName, filterFac.literal(foreignKeyValue));
  337. query.setFilter(filter);
  338. if (selectedProperties!=null) {
  339. selectedProperties = new ArrayList<PropertyName>(selectedProperties);
  340. selectedProperties.add(propertyName);
  341. }
  342. final Hints hints = new Hints();
  343. hints.put(Query.INCLUDE_MANDATORY_PROPS, includeMandatory);
  344. query.setHints(hints);
  345. query.setProperties(selectedProperties);
  346. ArrayList<Feature> matchingFeatures = new ArrayList<Feature>();
  347. // get all the mapped nested features based on the link values
  348. FeatureCollection<FeatureType, Feature> fCollection = fSource.getFeatures(query);
  349. if (fCollection instanceof MappingFeatureCollection) {
  350. FeatureIterator<Feature> iterator = fCollection.features();
  351. while (iterator.hasNext()) {
  352. matchingFeatures.add(iterator.next());
  353. }
  354. iterator.close();
  355. }
  356. return matchingFeatures;
  357. }
  358. protected FeatureSource<FeatureType, Feature> getMappingSource(Object feature)
  359. throws IOException {
  360. if (mappingSource == null || isConditional) {
  361. // initiate if null, or evaluate a new one if the targetElement is a function
  362. // which value depends on the feature
  363. Object featureTypeName = getNestedFeatureType(feature);
  364. if (featureTypeName == null || !(featureTypeName instanceof Name)) {
  365. return null;
  366. }
  367. // this cannot be set in the constructor since it might not exist yet
  368. mappingSource = DataAccessRegistry.getFeatureSource((Name) featureTypeName);
  369. }
  370. return mappingSource;
  371. }
  372. /**
  373. * @return the nested feature type name
  374. */
  375. public Object getNestedFeatureType(Object feature) {
  376. Object fTypeValue;
  377. if (isConditional) {
  378. if (feature == null) {
  379. throw new IllegalArgumentException("Feature parameter is required!");
  380. }
  381. fTypeValue = nestedFeatureType.evaluate(feature);
  382. if (fTypeValue == null) {
  383. // this could be legitimate, i.e. in polymorphism
  384. // to evaluate a function with a certain column value
  385. // if null, don't encode this element
  386. return null;
  387. }
  388. if (fTypeValue instanceof Hints) {
  389. return ((Hints) fTypeValue).get(ComplexFeatureConstants.STRING_KEY);
  390. }
  391. } else {
  392. fTypeValue = nestedFeatureType.toString();
  393. }
  394. return Types.degloseName(String.valueOf(fTypeValue), namespaces);
  395. }
  396. public boolean isConditional() {
  397. return this.isConditional;
  398. }
  399. public boolean isSameSource() {
  400. // if the linkField is null, we're meant to work out the nestedFeatureType from
  401. // the linkElement, which should contain a function. So the value could vary
  402. // feature per feature. But the linkElement would point to the same data source table
  403. // if the linkField is null.
  404. return this.nestedTargetXPath == null;
  405. }
  406. public FeatureTypeMapping getFeatureTypeMapping(Feature feature) throws IOException {
  407. FeatureSource<FeatureType, Feature> fSource = getMappingSource(feature);
  408. if (fSource == null) {
  409. return null;
  410. }
  411. return (fSource instanceof MappingFeatureSource) ? ((MappingFeatureSource) fSource)
  412. .getMapping() : null;
  413. }
  414. public NamespaceSupport getNamespaces() {
  415. return namespaces;
  416. }
  417. }