PageRenderTime 41ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/projects/geotools-9.2/modules/extension/app-schema/app-schema/src/main/java/org/geotools/data/joining/JoiningNestedAttributeMapping.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 429 lines | 264 code | 62 blank | 103 comment | 64 complexity | 79080e6a1a7885741f418eb6d8352cac MD5 | raw file
  1. /*
  2. * GeoTools - The Open Source Java GIS Toolkit
  3. * http://geotools.org
  4. *
  5. * (C) 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.joining;
  18. import java.io.IOException;
  19. import java.util.ArrayList;
  20. import java.util.HashMap;
  21. import java.util.List;
  22. import java.util.Map;
  23. import org.geotools.data.FeatureSource;
  24. import org.geotools.data.Query;
  25. import org.geotools.data.complex.AppSchemaDataAccessRegistry;
  26. import org.geotools.data.complex.AttributeMapping;
  27. import org.geotools.data.complex.DataAccessMappingFeatureIterator;
  28. import org.geotools.data.complex.DataAccessRegistry;
  29. import org.geotools.data.complex.FeatureTypeMapping;
  30. import org.geotools.data.complex.MappingFeatureCollection;
  31. import org.geotools.data.complex.NestedAttributeMapping;
  32. import org.geotools.data.complex.filter.XPathUtil.StepList;
  33. import org.geotools.factory.Hints;
  34. import org.geotools.feature.FeatureCollection;
  35. import org.geotools.feature.FeatureIterator;
  36. import org.geotools.jdbc.JoiningJDBCFeatureSource;
  37. import org.opengis.feature.Feature;
  38. import org.opengis.feature.type.Name;
  39. import org.opengis.filter.expression.Expression;
  40. import org.opengis.filter.expression.PropertyName;
  41. import org.opengis.referencing.crs.CoordinateReferenceSystem;
  42. import org.xml.sax.helpers.NamespaceSupport;
  43. /**
  44. * Nested attribute mapping used for joining system
  45. *
  46. * @author Niels Charlier (Curtin University of Technology)
  47. *
  48. *
  49. * @source $URL$
  50. */
  51. public class JoiningNestedAttributeMapping extends NestedAttributeMapping {
  52. /**
  53. * Instance that holds temporary data for going through the features, for each 'caller' (any
  54. * object going through the features) there is one.
  55. */
  56. protected static class Instance {
  57. public static class Skip {
  58. List<Object> idValues;
  59. public Skip(List<Object> idValues) {
  60. this.idValues = idValues;
  61. }
  62. }
  63. public Map<Name, DataAccessMappingFeatureIterator> featureIterators = new HashMap<Name, DataAccessMappingFeatureIterator>();
  64. public Map<Name, Expression> nestedSourceExpressions = new HashMap<Name, Expression>();
  65. public List<Skip> skipped = new ArrayList<Skip>();
  66. public Query baseTableQuery;
  67. }
  68. /**
  69. * The instances.
  70. */
  71. protected Map<Object, Instance> instances = new HashMap<Object, Instance>();
  72. /**
  73. * Constructor
  74. *
  75. * @param idExpression
  76. * @param parentExpression
  77. * @param targetXPath
  78. * @param isMultiValued
  79. * @param clientProperties
  80. * @param sourceElement
  81. * @param sourcePath
  82. * @param namespaces
  83. * @throws IOException
  84. */
  85. public JoiningNestedAttributeMapping(Expression idExpression, Expression parentExpression,
  86. StepList targetXPath, boolean isMultiValued, Map<Name, Expression> clientProperties,
  87. Expression sourceElement, StepList sourcePath, NamespaceSupport namespaces)
  88. throws IOException {
  89. super(idExpression, parentExpression, targetXPath, isMultiValued, clientProperties,
  90. sourceElement, sourcePath, namespaces);
  91. }
  92. public List<Feature> getInputFeatures(Object foreignKeyValue, FeatureTypeMapping fMapping) {
  93. throw new UnsupportedOperationException(
  94. "Internal error: Not Allowed to run this method for Joining Nested Attribute Mapping!");
  95. }
  96. /**
  97. * Initialise a new iterator (for polymorphism, there could be multiple per instance)
  98. *
  99. * @param instance
  100. * @param featureTypeName
  101. * @param reprojection
  102. * @param selectedProperties
  103. * @param includeMandatory
  104. * @return
  105. * @throws IOException
  106. */
  107. public DataAccessMappingFeatureIterator initSourceFeatures(Instance instance,
  108. Name featureTypeName, CoordinateReferenceSystem reprojection,
  109. List<PropertyName> selectedProperties, boolean includeMandatory) throws IOException {
  110. JoiningQuery query = new JoiningQuery();
  111. query.setCoordinateSystemReproject(reprojection);
  112. FeatureTypeMapping fMapping = AppSchemaDataAccessRegistry.getMappingByName(featureTypeName);
  113. AttributeMapping mapping = fMapping
  114. .getAttributeMapping(this.nestedTargetXPath);
  115. if (mapping == null) {
  116. throw new IllegalArgumentException("Mapping is missing for: '" + this.nestedTargetXPath
  117. + "'!");
  118. }
  119. Expression nestedSourceExpression = mapping.getSourceExpression();
  120. List<JoiningQuery.QueryJoin> joins = new ArrayList<JoiningQuery.QueryJoin>();
  121. if (instance.baseTableQuery instanceof JoiningQuery) {
  122. if (((JoiningQuery) instance.baseTableQuery).getQueryJoins() != null) {
  123. joins.addAll(((JoiningQuery) instance.baseTableQuery).getQueryJoins());
  124. }
  125. }
  126. JoiningQuery.QueryJoin join = new JoiningQuery.QueryJoin();
  127. join.setForeignKeyName(sourceExpression);
  128. join.setJoiningKeyName(nestedSourceExpression);
  129. join.setJoiningTypeName(instance.baseTableQuery.getTypeName());
  130. join.setSortBy(instance.baseTableQuery.getSortBy()); // incorporate order
  131. joins.add(0, join);
  132. query.setQueryJoins(joins);
  133. if (selectedProperties != null) {
  134. selectedProperties = new ArrayList<PropertyName>(selectedProperties);
  135. selectedProperties.add(filterFac.property(this.nestedTargetXPath.toString()));
  136. }
  137. final Hints hints = new Hints();
  138. hints.put(Query.INCLUDE_MANDATORY_PROPS, includeMandatory);
  139. query.setHints(hints);
  140. query.setProperties(selectedProperties);
  141. FeatureSource fSource = DataAccessRegistry.getFeatureSource((Name) featureTypeName);
  142. if (fSource == null) {
  143. throw new IOException("Internal error: Source could not be found");
  144. }
  145. FeatureCollection collection = fSource.getFeatures(query);
  146. if (!(collection instanceof MappingFeatureCollection)) {
  147. throw new IOException("Internal error: Mapping feature Collection expected but found "
  148. + collection);
  149. }
  150. // copy unrolled filter
  151. ((MappingFeatureCollection) collection).setUnrolledFilter(instance.baseTableQuery
  152. .getFilter());
  153. FeatureIterator featureIterator = collection.features();
  154. if (!(featureIterator instanceof DataAccessMappingFeatureIterator)) {
  155. throw new IOException(
  156. "Internal error: Data Access Mapping feature Iterator expected but found "
  157. + featureIterator);
  158. }
  159. DataAccessMappingFeatureIterator daFeatureIterator = (DataAccessMappingFeatureIterator) featureIterator;
  160. List<Expression> foreignIds = new ArrayList<Expression>();
  161. for (int i = 0; i < query.getQueryJoins().size(); i++) {
  162. for (int j = 0; j < query.getQueryJoins().get(i).getSortBy().length; j++) {
  163. foreignIds.add(filterFac.property(JoiningJDBCFeatureSource.FOREIGN_ID + "_" + i
  164. + "_" + j));
  165. }
  166. }
  167. daFeatureIterator.setForeignIds(foreignIds);
  168. instance.featureIterators.put(featureTypeName, daFeatureIterator);
  169. instance.nestedSourceExpressions.put(featureTypeName, nestedSourceExpression);
  170. for (Instance.Skip toSkip : instance.skipped) {
  171. while (daFeatureIterator.hasNext()
  172. && daFeatureIterator.checkForeignIdValues(toSkip.idValues)) {
  173. daFeatureIterator.skip();
  174. }
  175. }
  176. return daFeatureIterator;
  177. }
  178. /**
  179. * Open an instance (cursor) for a specific caller. An instance holds a cursor and any
  180. * additional information to move through the features.
  181. *
  182. * @param caller
  183. * @param baseTableQuery
  184. * @throws IOException
  185. */
  186. public void open(Object caller, Query baseTableQuery) throws IOException {
  187. if (instances.get(caller) != null) {
  188. throw new IllegalArgumentException(
  189. "Trying to open Joining Nested Attribute Mapping that is already open!");
  190. } else {
  191. Instance instance = new Instance();
  192. instance.baseTableQuery = baseTableQuery;
  193. instances.put(caller, instance);
  194. }
  195. }
  196. /**
  197. * Close the instance of this caller.
  198. *
  199. * @param caller
  200. */
  201. public void close(Object caller) {
  202. Instance instance = instances.get(caller);
  203. if (instance != null) {
  204. for (FeatureIterator featureIterator : instance.featureIterators.values()) {
  205. featureIterator.close();
  206. }
  207. instance.featureIterators.clear();
  208. instances.remove(caller);
  209. } else {
  210. throw new IllegalArgumentException(
  211. "Trying to close Joining Nested Attribute Mapping hasn't been opened!");
  212. }
  213. }
  214. /**
  215. * Get matching input features that are stored in this mapping using a supplied link value.
  216. *
  217. * @param foreignKeyValue
  218. * @return The matching input feature
  219. * @throws IOException
  220. * @throws IOException
  221. */
  222. @Override
  223. public List<Feature> getInputFeatures(Object caller, Object foreignKeyValue,
  224. List<Object> idValues, Object feature, CoordinateReferenceSystem reprojection,
  225. List<PropertyName> selectedProperties, boolean includeMandatory) throws IOException {
  226. if (isSameSource()) {
  227. // if linkField is null, this method shouldn't be called because the mapping
  228. // should use the same table, and handles it differently
  229. throw new UnsupportedOperationException(
  230. "Link field is missing from feature chaining mapping!");
  231. }
  232. Instance instance = instances.get(caller);
  233. if (instance == null) {
  234. throw new IllegalArgumentException(
  235. "Trying to read Joining Nested Attribute Mapping that is not open.");
  236. }
  237. Object featureTypeName = getNestedFeatureType(feature);
  238. if (featureTypeName == null || !(featureTypeName instanceof Name)) {
  239. throw new IllegalArgumentException(
  240. "Internal error: Feature type name expected but found " + featureTypeName);
  241. }
  242. DataAccessMappingFeatureIterator featureIterator = instance.featureIterators
  243. .get((Name) featureTypeName);
  244. if (featureIterator == null) {
  245. featureIterator = initSourceFeatures(instance, (Name) featureTypeName, reprojection,
  246. selectedProperties, includeMandatory);
  247. }
  248. Expression nestedSourceExpression = instance.nestedSourceExpressions
  249. .get((Name) featureTypeName);
  250. if (nestedSourceExpression == null) {
  251. throw new IllegalArgumentException(
  252. "Internal error: nested source expression expected but found "
  253. + featureTypeName);
  254. }
  255. ArrayList<Feature> matchingFeatures = new ArrayList<Feature>();
  256. if (featureIterator != null) {
  257. while (featureIterator.hasNext()
  258. && featureIterator.peekNextValue(nestedSourceExpression).toString()
  259. .equals(foreignKeyValue.toString())
  260. && featureIterator.checkForeignIdValues(idValues)) {
  261. matchingFeatures.addAll(featureIterator.skip());
  262. }
  263. }
  264. // skip all others
  265. for (Name name : instance.featureIterators.keySet()) {
  266. DataAccessMappingFeatureIterator fIt = instance.featureIterators.get(name);
  267. if (fIt != featureIterator) {
  268. skipFeatures(fIt, instance.nestedSourceExpressions.get(name), foreignKeyValue,
  269. idValues);
  270. }
  271. }
  272. instance.skipped.add(new Instance.Skip(idValues));
  273. return matchingFeatures;
  274. }
  275. /**
  276. * Get the maching built features that are stored in this mapping using a supplied link value
  277. *
  278. * @param foreignKeyValue
  279. * @param reprojection
  280. * Reprojected CRS or null
  281. * @param selectedProperties
  282. * list of properties to get
  283. * @return The matching simple features
  284. * @throws IOException
  285. */
  286. @Override
  287. public List<Feature> getFeatures(Object caller, Object foreignKeyValue, List<Object> idValues,
  288. CoordinateReferenceSystem reprojection, Object feature,
  289. List<PropertyName> selectedProperties, boolean includeMandatory) throws IOException {
  290. if (isSameSource()) {
  291. // if linkField is null, this method shouldn't be called because the mapping
  292. // should use the same table, and handles it differently
  293. throw new UnsupportedOperationException(
  294. "Link field is missing from feature chaining mapping!");
  295. }
  296. Instance instance = instances.get(caller);
  297. if (instance == null) {
  298. throw new IllegalArgumentException(
  299. "Trying to read Joining Nested Attribute Mapping that is not open.");
  300. }
  301. Object featureTypeName = getNestedFeatureType(feature);
  302. if (featureTypeName == null || !(featureTypeName instanceof Name)) {
  303. throw new IllegalArgumentException("Something is wrong!!");
  304. }
  305. DataAccessMappingFeatureIterator featureIterator = instance.featureIterators
  306. .get((Name) featureTypeName);
  307. if (featureIterator == null) {
  308. featureIterator = initSourceFeatures(instance, (Name) featureTypeName, reprojection,
  309. selectedProperties, includeMandatory);
  310. }
  311. Expression nestedSourceExpression = instance.nestedSourceExpressions
  312. .get((Name) featureTypeName);
  313. if (nestedSourceExpression == null) {
  314. throw new IllegalArgumentException(
  315. "Internal error: nested source expression expected but found "
  316. + featureTypeName);
  317. }
  318. ArrayList<Feature> matchingFeatures = new ArrayList<Feature>();
  319. if (featureIterator != null) {
  320. while (featureIterator.hasNext() && featureIterator.checkForeignIdValues(idValues)) {
  321. matchingFeatures.add(featureIterator.next());
  322. }
  323. }
  324. // skip all others
  325. for (Name name : instance.featureIterators.keySet()) {
  326. DataAccessMappingFeatureIterator fIt = instance.featureIterators.get(name);
  327. if (fIt != featureIterator) {
  328. skipFeatures(fIt, instance.nestedSourceExpressions.get(name), foreignKeyValue,
  329. idValues);
  330. }
  331. }
  332. instance.skipped.add(new Instance.Skip(idValues));
  333. return matchingFeatures;
  334. }
  335. protected void skipFeatures(DataAccessMappingFeatureIterator featureIterator,
  336. Expression nestedSourceExpression, Object foreignKeyValue, List<Object> idValues)
  337. throws IOException {
  338. while (featureIterator.hasNext()
  339. && featureIterator.peekNextValue(nestedSourceExpression).toString().equals(foreignKeyValue.toString())
  340. && featureIterator.checkForeignIdValues(idValues)) {
  341. featureIterator.skip();
  342. }
  343. }
  344. /**
  345. * If we have decided not to build the parent feature, we need to skip all rows that were
  346. * returned to build it
  347. *
  348. * @param caller
  349. * @param foreignKeyValue
  350. * @throws IOException
  351. */
  352. public void skip(Object caller, Object foreignKeyValue, List<Object> idValues)
  353. throws IOException {
  354. Instance instance = instances.get(caller);
  355. if (instance == null) {
  356. throw new IllegalArgumentException(
  357. "Trying to read Joining Nested Attribute Mapping that is not open.");
  358. }
  359. // skip all
  360. for (Name name : instance.featureIterators.keySet()) {
  361. DataAccessMappingFeatureIterator fIt = instance.featureIterators.get(name);
  362. Expression nestedSourceExpression = instance.nestedSourceExpressions.get(name);
  363. skipFeatures(fIt, nestedSourceExpression, foreignKeyValue, idValues);
  364. }
  365. instance.skipped.add(new Instance.Skip(idValues));
  366. }
  367. }