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 |
|
---|
19 | import java.rmi.server.UID;
|
---|
20 | import java.util.Arrays;
|
---|
21 | import java.util.List;
|
---|
22 | import java.util.Map;
|
---|
23 |
|
---|
24 | import org.geotools.data.DataUtilities;
|
---|
25 | import org.geotools.factory.CommonFactoryFinder;
|
---|
26 | import org.geotools.feature.type.Types;
|
---|
27 | import org.geotools.util.Converters;
|
---|
28 | import org.opengis.feature.FeatureFactory;
|
---|
29 | import org.opengis.feature.simple.SimpleFeature;
|
---|
30 | import org.opengis.feature.simple.SimpleFeatureType;
|
---|
31 | import org.opengis.feature.type.AttributeDescriptor;
|
---|
32 | import 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 | */
|
---|
122 | public 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 | }
|
---|