source: osm/applications/editors/josm/plugins/opendata/includes/org/geotools/feature/simple/SimpleFeatureBuilder.java@ 28000

Last change on this file since 28000 was 28000, checked in by donvip, 12 years ago

Import new "opendata" JOSM plugin

File size: 13.8 KB
Line 
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 */
17package org.geotools.feature.simple;
18
19import java.rmi.server.UID;
20import java.util.Arrays;
21import java.util.List;
22import java.util.Map;
23
24import org.geotools.data.DataUtilities;
25import org.geotools.factory.CommonFactoryFinder;
26import org.geotools.feature.type.Types;
27import org.geotools.util.Converters;
28import org.opengis.feature.FeatureFactory;
29import org.opengis.feature.simple.SimpleFeature;
30import org.opengis.feature.simple.SimpleFeatureType;
31import org.opengis.feature.type.AttributeDescriptor;
32import org.opengis.feature.type.Name;
33
34/**
35 * A builder for features.
36 * <p>
37 * Simple Usage:
38 * <code>
39 * <pre>
40 * //type of features we would like to build ( assume schema = (geom:Point,name:String) )
41 * SimpleFeatureType featureType = ...
42 *
43 * //create the builder
44 * SimpleFeatureBuilder builder = new SimpleFeatureBuilder();
45 *
46 * //set the type of created features
47 * builder.setType( featureType );
48 *
49 * //add the attributes
50 * builder.add( new Point( 0 , 0 ) );
51 * builder.add( "theName" );
52 *
53 * //build the feature
54 * SimpleFeature feature = builder.buildFeature( "fid" );
55 * </pre>
56 * </code>
57 * </p>
58 * <p>
59 * This builder builds a feature by maintaining state. Each call to {@link #add(Object)}
60 * creates a new attribute for the feature and stores it locally. When using the
61 * add method to add attributes to the feature, values added must be added in the
62 * same order as the attributes as defined by the feature type. The methods
63 * {@link #set(String, Object)} and {@link #set(int, Object)} are used to add
64 * attributes out of order.
65 * </p>
66 * <p>
67 * Each time the builder builds a feature with a call to {@link #buildFeature(String)}
68 * the internal state is reset.
69 * </p>
70 * <p>
71 * This builder can be used to copy features as well. The following code sample
72 * demonstrates:
73 * <code>
74 * <pre>
75 * //original feature
76 * SimpleFeature original = ...;
77 *
78 * //create and initialize the builder
79 * SimpleFeatureBuilder builder = new SimpleFeatureBuilder();
80 * builder.init(original);
81 *
82 * //create the new feature
83 * SimpleFeature copy = builder.buildFeature( original.getID() );
84 *
85 * </pre>
86 * </code>
87 * </p>
88 * <p>
89 * The builder also provides a number of static "short-hand" methods which can
90 * be used when its not ideal to instantiate a new builder, thought this will
91 * trigger some extra object allocations. In time critical code sections it's
92 * better to instantiate the builder once and use it to build all the required
93 * features.
94 * <code>
95 * <pre>
96 * SimpleFeatureType type = ..;
97 * Object[] values = ...;
98 *
99 * //build a new feature
100 * SimpleFeature feature = SimpleFeatureBuilder.build( type, values, "fid" );
101 *
102 * ...
103 *
104 * SimpleFeature original = ...;
105 *
106 * //copy the feature
107 * SimpleFeature feature = SimpleFeatureBuilder.copy( original );
108 * </pre>
109 * </code>
110 * </p>
111 * <p>
112 * This class is not thread safe nor should instances be shared across multiple
113 * threads.
114 * </p>
115 *
116 * @author Justin Deoliveira
117 * @author Jody Garnett
118 *
119 *
120 * @source $URL: http://svn.osgeo.org/geotools/branches/2.7.x/modules/library/main/src/main/java/org/geotools/feature/simple/SimpleFeatureBuilder.java $
121 */
122public class SimpleFeatureBuilder {
123
124 /** the feature type */
125 SimpleFeatureType featureType;
126
127 /** the feature factory */
128 FeatureFactory factory;
129
130 /** the attribute name to index index */
131 Map<String, Integer> index;
132
133 /** the values */
134 //List<Object> values;
135 Object[] values;
136
137 /** pointer for next attribute */
138 int next;
139
140 Map<Object, Object>[] userData;
141
142 Map<Object, Object> featureUserData;
143
144 boolean validating;
145
146 public SimpleFeatureBuilder(SimpleFeatureType featureType) {
147 this(featureType, CommonFactoryFinder.getFeatureFactory(null));
148 }
149
150 public SimpleFeatureBuilder(SimpleFeatureType featureType, FeatureFactory factory) {
151 this.featureType = featureType;
152 this.factory = factory;
153
154 if(featureType instanceof SimpleFeatureTypeImpl) {
155 index = ((SimpleFeatureTypeImpl) featureType).index;
156 } else {
157 this.index = SimpleFeatureTypeImpl.buildIndex(featureType);
158 }
159 reset();
160 }
161
162 public void reset() {
163 values = new Object[featureType.getAttributeCount()];
164 next = 0;
165 userData = null;
166 featureUserData = null;
167 }
168
169 /**
170 * Returns the simple feature type used by this builder as a feature template
171 * @return
172 */
173 public SimpleFeatureType getFeatureType() {
174 return featureType;
175 }
176
177 /**
178 * Initialize the builder with the provided feature.
179 * <p>
180 * This method adds all the attributes from the provided feature. It is
181 * useful when copying a feature.
182 * </p>
183 */
184 public void init( SimpleFeature feature ) {
185 reset();
186
187 // optimize the case in which we just build
188 if(feature instanceof SimpleFeatureImpl) {
189 SimpleFeatureImpl impl = (SimpleFeatureImpl) feature;
190 System.arraycopy(impl.values, 0, values, 0, impl.values.length);
191 } else {
192 for (Object value : feature.getAttributes()) {
193 add(value);
194 }
195 }
196 }
197
198
199
200 /**
201 * Adds an attribute.
202 * <p>
203 * This method should be called repeatedly for the number of attributes as
204 * specified by the type of the feature.
205 * </p>
206 */
207 public void add(Object value) {
208 set(next, value);
209 next++;
210 }
211
212 /**
213 * Adds a list of attributes.
214 */
215 public void addAll(List<Object> values) {
216 for (int i = 0; i < values.size(); i++) {
217 add(values.get(i));
218 }
219 }
220
221 /**
222 * Adds an array of attributes.
223 */
224 public void addAll(Object[] values) {
225 addAll(Arrays.asList(values));
226 }
227
228 /**
229 * Adds an attribute value by name.
230 * <p>
231 * This method can be used to add attribute values out of order.
232 * </p>
233 *
234 * @param name
235 * The name of the attribute.
236 * @param value
237 * The value of the attribute.
238 *
239 * @throws IllegalArgumentException
240 * If no such attribute with teh specified name exists.
241 */
242 public void set(Name name, Object value) {
243 set(name.getLocalPart(), value);
244 }
245
246 /**
247 * Adds an attribute value by name.
248 * <p>
249 * This method can be used to add attribute values out of order.
250 * </p>
251 *
252 * @param name
253 * The name of the attribute.
254 * @param value
255 * The value of the attribute.
256 *
257 * @throws IllegalArgumentException
258 * If no such attribute with teh specified name exists.
259 */
260 public void set(String name, Object value) {
261 int index = featureType.indexOf(name);
262 if (index == -1) {
263 throw new IllegalArgumentException("No such attribute:" + name);
264 }
265 set(index, value);
266 }
267
268 /**
269 * Adds an attribute value by index. *
270 * <p>
271 * This method can be used to add attribute values out of order.
272 * </p>
273 *
274 * @param index
275 * The index of the attribute.
276 * @param value
277 * The value of the attribute.
278 */
279 public void set(int index, Object value) {
280 if(index >= values.length)
281 throw new ArrayIndexOutOfBoundsException("Can handle "
282 + values.length + " attributes only, index is " + index);
283
284 AttributeDescriptor descriptor = featureType.getDescriptor(index);
285 values[index] = convert(value, descriptor);
286 if(validating)
287 Types.validate(descriptor, values[index]);
288 }
289
290 private Object convert(Object value, AttributeDescriptor descriptor) {
291 //make sure the type of the value and the binding of the type match up
292 if ( value != null ) {
293 Class<?> target = descriptor.getType().getBinding();
294 Object converted = Converters.convert(value, target);
295 if(converted != null)
296 value = converted;
297 } else {
298 //if the content is null and the descriptor says isNillable is false,
299 // then set the default value
300 if (!descriptor.isNillable()) {
301 value = descriptor.getDefaultValue();
302 if ( value == null ) {
303 //no default value, try to generate one
304 value = DataUtilities.defaultValue(descriptor.getType().getBinding());
305 }
306 }
307 }
308 return value;
309 }
310
311 /**
312 * Builds the feature.
313 * <p>
314 * The specified <tt>id</tt> may be <code>null</code>. In this case an
315 * id will be generated internally by the builder.
316 * </p>
317 * <p>
318 * After this method returns, all internal builder state is reset.
319 * </p>
320 *
321 * @param id
322 * The id of the feature, or <code>null</code>.
323 *
324 * @return The new feature.
325 */
326 public SimpleFeature buildFeature(String id) {
327 // ensure id
328 if (id == null) {
329 id = SimpleFeatureBuilder.createDefaultFeatureId();
330 }
331
332 Object[] values = this.values;
333 Map<Object,Object>[] userData = this.userData;
334 Map<Object,Object> featureUserData = this.featureUserData;
335 reset();
336 SimpleFeature sf = factory.createSimpleFeature(values, featureType, id);
337
338 // handle the per attribute user data
339 if(userData != null) {
340 for (int i = 0; i < userData.length; i++) {
341 if(userData[i] != null) {
342 sf.getProperty(featureType.getDescriptor(i).getName()).getUserData().putAll(userData[i]);
343 }
344 }
345 }
346
347 // handle the feature wide user data
348 if(featureUserData != null) {
349 sf.getUserData().putAll(featureUserData);
350 }
351
352 return sf;
353 }
354
355 /**
356 * Internal method for creating feature id's when none is specified.
357 */
358 public static String createDefaultFeatureId() {
359 // According to GML and XML schema standards, FID is a XML ID
360 // (http://www.w3.org/TR/xmlschema-2/#ID), whose acceptable values are those that match an
361 // NCNAME production (http://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-NCName):
362 // NCName ::= (Letter | '_') (NCNameChar)* /* An XML Name, minus the ":" */
363 // NCNameChar ::= Letter | Digit | '.' | '-' | '_' | CombiningChar | Extender
364 // We have to fix the generated UID replacing all non word chars with an _ (it seems
365 // they area all ":")
366 //return "fid-" + NON_WORD_PATTERN.matcher(new UID().toString()).replaceAll("_");
367 // optimization, since the UID toString uses only ":" and converts long and integers
368 // to strings for the rest, so the only non word character is really ":"
369 return "fid-" + new UID().toString().replace(':', '_');
370 }
371
372 /**
373 * Static method to build a new feature.
374 * <p>
375 * If multiple features need to be created, this method should not be used
376 * and instead an instance should be instantiated directly.
377 * </p>
378 * <p>
379 * This method is a short-hand convenience which creates a builder instance
380 * internally and adds all the specified attributes.
381 * </p>
382 * @param type SimpleFeatureType defining the structure for the created feature
383 * @param values Attribute values, must be in the order defined by SimpleFeatureType
384 * @param id FeatureID for the generated feature, use null to allow one to be supplied for you
385 */
386 public static SimpleFeature build( SimpleFeatureType type, Object[] values, String id ) {
387 SimpleFeatureBuilder builder = new SimpleFeatureBuilder(type);
388 builder.addAll(values);
389 return builder.buildFeature(id);
390 }
391
392 /**
393 * * Static method to build a new feature.
394 * <p>
395 * If multiple features need to be created, this method should not be used
396 * and instead an instance should be instantiated directly.
397 * </p>
398 * @param type SimpleFeatureType defining the structure for the created feature
399 * @param values Attribute values, must be in the order defined by SimpleFeatureType
400 * @param id FeatureID for the generated feature, use null to allow one to be supplied for you
401 */
402 public static SimpleFeature build( SimpleFeatureType type, List<Object> values, String id ) {
403 return build( type, values.toArray(), id );
404 }
405
406 /**
407 * Copy an existing feature (the values are reused so be careful with mutable values).
408 * <p>
409 * If multiple features need to be copied, this method should not be used
410 * and instead an instance should be instantiated directly.
411 * </p>
412 * <p>
413 * This method is a short-hand convenience which creates a builder instance
414 * and initializes it with the attributes from the specified feature.
415 * </p>
416 */
417 public static SimpleFeature copy(SimpleFeature original) {
418 if( original == null ) return null;
419
420 SimpleFeatureBuilder builder = new SimpleFeatureBuilder(original.getFeatureType());
421 builder.init(original); // this is a shallow copy
422 return builder.buildFeature(original.getID());
423 }
424}
Note: See TracBrowser for help on using the repository browser.