PageRenderTime 27ms CodeModel.GetById 22ms RepoModel.GetById 6ms app.codeStats 0ms

/projects/geotools-9.2/modules/library/main/src/main/java/org/geotools/feature/simple/SimpleFeatureBuilder.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 610 lines | 235 code | 55 blank | 320 comment | 47 complexity | 9e6f1424cbad4d26ce33136a6f985ff0 MD5 | raw file
  1. /*
  2. * GeoTools - The Open Source Java GIS Toolkit
  3. * http://geotools.org
  4. *
  5. * (C) 2002-2008, 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.feature.simple;
  18. import java.rmi.server.UID;
  19. import java.util.Arrays;
  20. import java.util.HashMap;
  21. import java.util.List;
  22. import java.util.Map;
  23. import java.util.logging.Logger;
  24. import org.geotools.data.DataUtilities;
  25. import org.geotools.factory.CommonFactoryFinder;
  26. import org.geotools.feature.type.Types;
  27. import org.geotools.filter.identity.FeatureIdImpl;
  28. import org.geotools.util.Converters;
  29. import org.opengis.feature.FeatureFactory;
  30. import org.opengis.feature.IllegalAttributeException;
  31. import org.opengis.feature.Property;
  32. import org.opengis.feature.simple.SimpleFeature;
  33. import org.opengis.feature.simple.SimpleFeatureType;
  34. import org.opengis.feature.type.AttributeDescriptor;
  35. import org.opengis.feature.type.Name;
  36. import com.vividsolutions.jts.geom.Geometry;
  37. /**
  38. * A builder for features.
  39. * <p>
  40. * Simple Usage:
  41. * <code>
  42. * <pre>
  43. * //type of features we would like to build ( assume schema = (geom:Point,name:String) )
  44. * SimpleFeatureType featureType = ...
  45. *
  46. * //create the builder
  47. * SimpleFeatureBuilder builder = new SimpleFeatureBuilder();
  48. *
  49. * //set the type of created features
  50. * builder.setType( featureType );
  51. *
  52. * //add the attributes
  53. * builder.add( new Point( 0 , 0 ) );
  54. * builder.add( "theName" );
  55. *
  56. * //build the feature
  57. * SimpleFeature feature = builder.buildFeature( "fid" );
  58. * </pre>
  59. * </code>
  60. * </p>
  61. * <p>
  62. * This builder builds a feature by maintaining state. Each call to {@link #add(Object)}
  63. * creates a new attribute for the feature and stores it locally. When using the
  64. * add method to add attributes to the feature, values added must be added in the
  65. * same order as the attributes as defined by the feature type. The methods
  66. * {@link #set(String, Object)} and {@link #set(int, Object)} are used to add
  67. * attributes out of order.
  68. * </p>
  69. * <p>
  70. * Each time the builder builds a feature with a call to {@link #buildFeature(String)}
  71. * the internal state is reset.
  72. * </p>
  73. * <p>
  74. * This builder can be used to copy features as well. The following code sample
  75. * demonstrates:
  76. * <code>
  77. * <pre>
  78. * //original feature
  79. * SimpleFeature original = ...;
  80. *
  81. * //create and initialize the builder
  82. * SimpleFeatureBuilder builder = new SimpleFeatureBuilder();
  83. * builder.init(original);
  84. *
  85. * //create the new feature
  86. * SimpleFeature copy = builder.buildFeature( original.getID() );
  87. *
  88. * </pre>
  89. * </code>
  90. * </p>
  91. * <p>
  92. * The builder also provides a number of static "short-hand" methods which can
  93. * be used when its not ideal to instantiate a new builder, thought this will
  94. * trigger some extra object allocations. In time critical code sections it's
  95. * better to instantiate the builder once and use it to build all the required
  96. * features.
  97. * <code>
  98. * <pre>
  99. * SimpleFeatureType type = ..;
  100. * Object[] values = ...;
  101. *
  102. * //build a new feature
  103. * SimpleFeature feature = SimpleFeatureBuilder.build( type, values, "fid" );
  104. *
  105. * ...
  106. *
  107. * SimpleFeature original = ...;
  108. *
  109. * //copy the feature
  110. * SimpleFeature feature = SimpleFeatureBuilder.copy( original );
  111. * </pre>
  112. * </code>
  113. * </p>
  114. * <p>
  115. * This class is not thread safe nor should instances be shared across multiple
  116. * threads.
  117. * </p>
  118. *
  119. * @author Justin Deoliveira
  120. * @author Jody Garnett
  121. *
  122. *
  123. *
  124. * @source $URL$
  125. */
  126. public class SimpleFeatureBuilder {
  127. /**
  128. * logger
  129. */
  130. static Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.geotools.feature");
  131. /** the feature type */
  132. SimpleFeatureType featureType;
  133. /** the feature factory */
  134. FeatureFactory factory;
  135. /** the attribute name to index index */
  136. Map<String, Integer> index;
  137. /** the values */
  138. //List<Object> values;
  139. Object[] values;
  140. /** pointer for next attribute */
  141. int next;
  142. Map<Object, Object>[] userData;
  143. Map<Object, Object> featureUserData;
  144. boolean validating;
  145. public SimpleFeatureBuilder(SimpleFeatureType featureType) {
  146. this(featureType, CommonFactoryFinder.getFeatureFactory(null));
  147. }
  148. public SimpleFeatureBuilder(SimpleFeatureType featureType, FeatureFactory factory) {
  149. this.featureType = featureType;
  150. this.factory = factory;
  151. if(featureType instanceof SimpleFeatureTypeImpl) {
  152. index = ((SimpleFeatureTypeImpl) featureType).index;
  153. } else {
  154. this.index = SimpleFeatureTypeImpl.buildIndex(featureType);
  155. }
  156. reset();
  157. }
  158. public void reset() {
  159. values = new Object[featureType.getAttributeCount()];
  160. next = 0;
  161. userData = null;
  162. featureUserData = null;
  163. }
  164. /**
  165. * Returns the simple feature type used by this builder as a feature template
  166. * @return
  167. */
  168. public SimpleFeatureType getFeatureType() {
  169. return featureType;
  170. }
  171. /**
  172. * Initialize the builder with the provided feature.
  173. * <p>
  174. * This method adds all the attributes from the provided feature. It is
  175. * useful when copying a feature.
  176. * </p>
  177. */
  178. public void init( SimpleFeature feature ) {
  179. reset();
  180. // optimize the case in which we just build
  181. if(feature instanceof SimpleFeatureImpl) {
  182. SimpleFeatureImpl impl = (SimpleFeatureImpl) feature;
  183. System.arraycopy(impl.values, 0, values, 0, impl.values.length);
  184. if (impl.userData != null) {
  185. featureUserData = new HashMap(impl.userData);
  186. }
  187. } else {
  188. for (Object value : feature.getAttributes()) {
  189. add(value);
  190. }
  191. if (!feature.getUserData().isEmpty()) {
  192. featureUserData = new HashMap(feature.getUserData());
  193. }
  194. }
  195. }
  196. /**
  197. * Adds an attribute.
  198. * <p>
  199. * This method should be called repeatedly for the number of attributes as
  200. * specified by the type of the feature.
  201. * </p>
  202. */
  203. public void add(Object value) {
  204. set(next, value);
  205. next++;
  206. }
  207. /**
  208. * Adds a list of attributes.
  209. */
  210. public void addAll(List<Object> values) {
  211. for (int i = 0; i < values.size(); i++) {
  212. add(values.get(i));
  213. }
  214. }
  215. /**
  216. * Adds an array of attributes.
  217. */
  218. public void addAll(Object[] values) {
  219. addAll(Arrays.asList(values));
  220. }
  221. /**
  222. * Adds an attribute value by name.
  223. * <p>
  224. * This method can be used to add attribute values out of order.
  225. * </p>
  226. *
  227. * @param name
  228. * The name of the attribute.
  229. * @param value
  230. * The value of the attribute.
  231. *
  232. * @throws IllegalArgumentException
  233. * If no such attribute with teh specified name exists.
  234. */
  235. public void set(Name name, Object value) {
  236. set(name.getLocalPart(), value);
  237. }
  238. /**
  239. * Adds an attribute value by name.
  240. * <p>
  241. * This method can be used to add attribute values out of order.
  242. * </p>
  243. *
  244. * @param name
  245. * The name of the attribute.
  246. * @param value
  247. * The value of the attribute.
  248. *
  249. * @throws IllegalArgumentException
  250. * If no such attribute with teh specified name exists.
  251. */
  252. public void set(String name, Object value) {
  253. int index = featureType.indexOf(name);
  254. if (index == -1) {
  255. throw new IllegalArgumentException("No such attribute:" + name);
  256. }
  257. set(index, value);
  258. }
  259. /**
  260. * Adds an attribute value by index. *
  261. * <p>
  262. * This method can be used to add attribute values out of order.
  263. * </p>
  264. *
  265. * @param index
  266. * The index of the attribute.
  267. * @param value
  268. * The value of the attribute.
  269. */
  270. public void set(int index, Object value) {
  271. if(index >= values.length)
  272. throw new ArrayIndexOutOfBoundsException("Can handle "
  273. + values.length + " attributes only, index is " + index);
  274. AttributeDescriptor descriptor = featureType.getDescriptor(index);
  275. values[index] = convert(value, descriptor);
  276. if(validating)
  277. Types.validate(descriptor, values[index]);
  278. }
  279. private Object convert(Object value, AttributeDescriptor descriptor) {
  280. //make sure the type of the value and the binding of the type match up
  281. if ( value != null ) {
  282. Class<?> target = descriptor.getType().getBinding();
  283. Object converted = Converters.convert(value, target);
  284. if(converted != null)
  285. value = converted;
  286. } else {
  287. //if the content is null and the descriptor says isNillable is false,
  288. // then set the default value
  289. if (!descriptor.isNillable()) {
  290. value = descriptor.getDefaultValue();
  291. if ( value == null ) {
  292. //no default value, try to generate one
  293. value = DataUtilities.defaultValue(descriptor.getType().getBinding());
  294. }
  295. }
  296. }
  297. return value;
  298. }
  299. /**
  300. * Builds the feature.
  301. * <p>
  302. * The specified <tt>id</tt> may be <code>null</code>. In this case an
  303. * id will be generated internally by the builder.
  304. * </p>
  305. * <p>
  306. * After this method returns, all internal builder state is reset.
  307. * </p>
  308. *
  309. * @param id
  310. * The id of the feature, or <code>null</code>.
  311. *
  312. * @return The new feature.
  313. */
  314. public SimpleFeature buildFeature(String id) {
  315. // ensure id
  316. if (id == null) {
  317. id = SimpleFeatureBuilder.createDefaultFeatureId();
  318. }
  319. Object[] values = this.values;
  320. Map<Object,Object>[] userData = this.userData;
  321. Map<Object,Object> featureUserData = this.featureUserData;
  322. reset();
  323. SimpleFeature sf = factory.createSimpleFeature(values, featureType, id);
  324. // handle the per attribute user data
  325. if(userData != null) {
  326. for (int i = 0; i < userData.length; i++) {
  327. if(userData[i] != null) {
  328. sf.getProperty(featureType.getDescriptor(i).getName()).getUserData().putAll(userData[i]);
  329. }
  330. }
  331. }
  332. // handle the feature wide user data
  333. if(featureUserData != null) {
  334. sf.getUserData().putAll(featureUserData);
  335. }
  336. return sf;
  337. }
  338. /**
  339. * Quickly builds the feature using the specified values and id
  340. * @param id
  341. * @param values
  342. * @return
  343. */
  344. public SimpleFeature buildFeature(String id, Object[] values ) {
  345. addAll( values );
  346. return buildFeature( id );
  347. }
  348. /**
  349. * Internal method for creating feature id's when none is specified.
  350. */
  351. public static String createDefaultFeatureId() {
  352. // According to GML and XML schema standards, FID is a XML ID
  353. // (http://www.w3.org/TR/xmlschema-2/#ID), whose acceptable values are those that match an
  354. // NCNAME production (http://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-NCName):
  355. // NCName ::= (Letter | '_') (NCNameChar)* /* An XML Name, minus the ":" */
  356. // NCNameChar ::= Letter | Digit | '.' | '-' | '_' | CombiningChar | Extender
  357. // We have to fix the generated UID replacing all non word chars with an _ (it seems
  358. // they area all ":")
  359. //return "fid-" + NON_WORD_PATTERN.matcher(new UID().toString()).replaceAll("_");
  360. // optimization, since the UID toString uses only ":" and converts long and integers
  361. // to strings for the rest, so the only non word character is really ":"
  362. return "fid-" + new UID().toString().replace(':', '_');
  363. }
  364. /**
  365. * Internal method for a temporary FeatureId that can be assigned
  366. * a real value after a commit.
  367. * @param suggestedId suggsted id
  368. */
  369. public static FeatureIdImpl createDefaultFeatureIdentifier( String suggestedId ) {
  370. if( suggestedId != null ){
  371. return new FeatureIdImpl( suggestedId );
  372. }
  373. return new FeatureIdImpl( createDefaultFeatureId() );
  374. }
  375. /**
  376. * Static method to build a new feature.
  377. * <p>
  378. * If multiple features need to be created, this method should not be used
  379. * and instead an instance should be instantiated directly.
  380. * </p>
  381. * <p>
  382. * This method is a short-hand convenience which creates a builder instance
  383. * internally and adds all the specified attributes.
  384. * </p>
  385. * @param type SimpleFeatureType defining the structure for the created feature
  386. * @param values Attribute values, must be in the order defined by SimpleFeatureType
  387. * @param id FeatureID for the generated feature, use null to allow one to be supplied for you
  388. */
  389. public static SimpleFeature build( SimpleFeatureType type, Object[] values, String id ) {
  390. SimpleFeatureBuilder builder = new SimpleFeatureBuilder(type);
  391. builder.addAll(values);
  392. return builder.buildFeature(id);
  393. }
  394. /**
  395. * * Static method to build a new feature.
  396. * <p>
  397. * If multiple features need to be created, this method should not be used
  398. * and instead an instance should be instantiated directly.
  399. * </p>
  400. * @param type SimpleFeatureType defining the structure for the created feature
  401. * @param values Attribute values, must be in the order defined by SimpleFeatureType
  402. * @param id FeatureID for the generated feature, use null to allow one to be supplied for you
  403. */
  404. public static SimpleFeature build( SimpleFeatureType type, List<Object> values, String id ) {
  405. return build( type, values.toArray(), id );
  406. }
  407. /**
  408. * Copy an existing feature (the values are reused so be careful with mutable values).
  409. * <p>
  410. * If multiple features need to be copied, this method should not be used
  411. * and instead an instance should be instantiated directly.
  412. * </p>
  413. * <p>
  414. * This method is a short-hand convenience which creates a builder instance
  415. * and initializes it with the attributes from the specified feature.
  416. * </p>
  417. */
  418. public static SimpleFeature copy(SimpleFeature original) {
  419. if( original == null ) return null;
  420. SimpleFeatureBuilder builder = new SimpleFeatureBuilder(original.getFeatureType());
  421. builder.init(original); // this is a shallow copy
  422. return builder.buildFeature(original.getID());
  423. }
  424. /**
  425. * Perform a "deep copy" an existing feature resuling in a duplicate of any geometry
  426. * attributes.
  427. * <p>
  428. * This method is scary, expensive and will result in a deep copy of
  429. * Geometry which may take a significant amount of memory/time to perform.
  430. * </p>
  431. * @param original Content
  432. * @return copy
  433. */
  434. public static SimpleFeature deep(SimpleFeature original) {
  435. if (original == null)
  436. return null;
  437. SimpleFeatureBuilder builder = new SimpleFeatureBuilder(original.getFeatureType());
  438. for (Property property : original.getProperties()) {
  439. Object value = property.getValue();
  440. try {
  441. Object copy = value;
  442. if (value instanceof Geometry) {
  443. Geometry geometry = (Geometry) value;
  444. copy = geometry.clone();
  445. }
  446. builder.set(property.getName(), copy);
  447. } catch (Exception e) {
  448. throw new IllegalAttributeException(
  449. (AttributeDescriptor) property.getDescriptor(), value, e );
  450. }
  451. }
  452. return builder.buildFeature(original.getID());
  453. }
  454. /**
  455. * Builds a new feature whose attribute values are the default ones
  456. * @param featureType
  457. * @param featureId
  458. * @return
  459. */
  460. public static SimpleFeature template(SimpleFeatureType featureType, String featureId) {
  461. SimpleFeatureBuilder builder = new SimpleFeatureBuilder(featureType);
  462. for (AttributeDescriptor ad : featureType.getAttributeDescriptors()) {
  463. builder.add(ad.getDefaultValue());
  464. }
  465. return builder.buildFeature(featureId);
  466. }
  467. /**
  468. * Copies an existing feature, retyping it in the process.
  469. * <p> Be warned, this method will
  470. * create its own SimpleFeatureBuilder, which will trigger a scan of the SPI looking for
  471. * the current default feature factory, which is expensive and has scalability issues.<p>
  472. * If you need good performance consider using
  473. * {@link SimpleFeatureBuilder#retype(SimpleFeature, SimpleFeatureBuilder)} instead.
  474. * <p>
  475. * If the feature type contains attributes in which the original feature
  476. * does not have a value for, the value in the resulting feature is set to
  477. * <code>null</code>.
  478. * </p>
  479. * @param feature The original feature.
  480. * @param featureType The target feature type.
  481. *
  482. * @return The copied feature, with a new type.
  483. */
  484. public static SimpleFeature retype(SimpleFeature feature, SimpleFeatureType featureType) {
  485. SimpleFeatureBuilder builder = new SimpleFeatureBuilder(featureType);
  486. for (AttributeDescriptor att : featureType.getAttributeDescriptors()) {
  487. Object value = feature.getAttribute( att.getName() );
  488. builder.set(att.getName(), value);
  489. }
  490. return builder.buildFeature(feature.getID());
  491. }
  492. /**
  493. * Copies an existing feature, retyping it in the process.
  494. * <p>
  495. * If the feature type contains attributes in which the original feature
  496. * does not have a value for, the value in the resulting feature is set to
  497. * <code>null</code>.
  498. * </p>
  499. * @param feature The original feature.
  500. * @param SimpleFeatureBuilder A builder for the target feature type
  501. *
  502. * @return The copied feature, with a new type.
  503. * @since 2.5.3
  504. */
  505. public static SimpleFeature retype(SimpleFeature feature, SimpleFeatureBuilder builder) {
  506. builder.reset();
  507. for (AttributeDescriptor att : builder.getFeatureType().getAttributeDescriptors()) {
  508. Object value = feature.getAttribute( att.getName() );
  509. builder.set(att.getName(), value);
  510. }
  511. return builder.buildFeature(feature.getID());
  512. }
  513. /**
  514. * Adds some user data to the next attributed added to the feature.
  515. * <p>
  516. * This value is reset when the next attribute is added.
  517. * </p>
  518. * @param key The key of the user data
  519. * @param value The value of the user data.
  520. */
  521. public SimpleFeatureBuilder userData( Object key, Object value ) {
  522. return setUserData(next, key, value);
  523. }
  524. @SuppressWarnings("unchecked")
  525. public SimpleFeatureBuilder setUserData(int index, Object key, Object value) {
  526. if (userData == null) {
  527. userData = new Map[values.length];
  528. }
  529. if (userData[index] == null) {
  530. userData[index] = new HashMap<Object, Object>();
  531. }
  532. userData[index].put(key, value);
  533. return this;
  534. }
  535. /**
  536. * Sets a feature wide use data key/value pair. The user data map is reset
  537. * when the feature is built
  538. * @param key
  539. * @param value
  540. * @return
  541. */
  542. public SimpleFeatureBuilder featureUserData(Object key, Object value) {
  543. if(featureUserData == null) {
  544. featureUserData = new HashMap<Object, Object>();
  545. }
  546. featureUserData.put(key, value);
  547. return this;
  548. }
  549. public boolean isValidating() {
  550. return validating;
  551. }
  552. public void setValidating(boolean validating) {
  553. this.validating = validating;
  554. }
  555. }