Statistics
| Revision:

svn-gvsig-desktop / branches / v2_0_0_prep / libraries / libFMap_geometries / src / org / gvsig / fmap / geom / GeometryManager.java @ 21732

History | View | Annotate | Download (19.9 KB)

1
/* gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
2
 *
3
 * Copyright (C) 2004 IVER T.I. and Generalitat Valenciana.
4
 *
5
 * This program is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU General Public License
7
 * as published by the Free Software Foundation; either version 2
8
 * of the License, or (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
18
 *
19
 * For more information, contact:
20
 *
21
 *  Generalitat Valenciana
22
 *   Conselleria d'Infraestructures i Transport
23
 *   Av. Blasco Ib??ez, 50
24
 *   46010 VALENCIA
25
 *   SPAIN
26
 *
27
 *      +34 963862235
28
 *   gvsig@gva.es
29
 *      www.gvsig.gva.es
30
 *
31
 *    or
32
 *
33
 *   IVER T.I. S.A
34
 *   Salamanca 50
35
 *   46005 Valencia
36
 *   Spain
37
 *
38
 *   +34 963163400
39
 *   dac@iver.es
40
 */
41
package org.gvsig.fmap.geom;
42

    
43
import java.util.ArrayList;
44
import java.util.HashMap;
45
import java.util.List;
46
import java.util.Map;
47

    
48
import org.apache.log4j.Logger;
49
import org.gvsig.fmap.geom.operation.GeometryOperation;
50
import org.gvsig.fmap.geom.operation.GeometryOperationContext;
51
import org.gvsig.fmap.geom.operation.GeometryOperationException;
52
import org.gvsig.fmap.geom.operation.GeometryOperationNotSupportedException;
53
import org.gvsig.fmap.geom.type.GeometryType;
54
import org.gvsig.fmap.geom.type.GeometryTypeNotSupportedException;
55

    
56
/**
57
 * This singleton provides a centralized access to gvSIG's Geometry Model.
58
 * Its responsibilities are:<br>
59
 *
60
 * <ul>
61
 * <li>Offering a set of convenient methods for registering and retrieving geometry types.
62
 * <li>Offering a set of convenient methods for registering and retrieving geometry operations associated
63
 * to one or more geometry types.
64
 * <li><code>TODO:</code> Offering a set of convenient methods for registering and retrieving custom
65
 * geometry factories.
66
 * <ul>
67
 *
68
 * @author jiyarza
69
 *
70
 */
71
public class GeometryManager {
72

    
73
        private static Logger logger = Logger.getLogger(GeometryManager.class);
74

    
75
        private static GeometryManager instance;
76

    
77
        /** This list holds the unique name of all registered geometry operations.
78
         * The index in which they are stored is also the operation code used to invoke each one of them */
79
        private List geometryOperations = new ArrayList();
80

    
81
        /** Common operations are registered here. Type specific operations are registered in the corresponding GeometryType instance */
82
        private List commonOperations = new ArrayList();
83

    
84
        /** Common operations are assigned an index greater or equal than this constant, so that it is easy and efficient for the manager
85
         * to decide whether to lookup in the common register or in the type specific register. */
86
        private static final int COMMON_OPS_OFFSET = 1000;
87

    
88
        /** This map holds the instances of all registered GeometryType. The key is the name of the specific Geometry subclass.
89
         * In other words, the string "org.gvsig.fmap.geom.primitive.Point2D" is the hash key to obtain an instance of GeometryType holding the
90
         * operations associated to the class org.gvsig.fmap.geom.primitive.Point2D.
91
         */
92
        private Map geometryTypes = new HashMap();
93

    
94
        /** GeometryType index counter. Each time a new geometry type is registered it is assigned this counter's value as index and after that
95
         * it is incremented by 1 */
96
        private int geomTypeIndex = 65536;//2^16
97

    
98
        /**
99
         * Geometry Factory
100
         */
101
        private GeometryFactory factory = new GeometryFactory();
102

    
103
        /**
104
         * Singleton's private constructor
105
         */
106
        private GeometryManager() {
107
        }
108

    
109
        /**
110
         * Returns this singleton's unique instance
111
         * @return unique instance of GeometryManager
112
         */
113
        public static GeometryManager getInstance() {
114
                if (instance == null) {
115
                        instance = new GeometryManager();
116
                        instance.registerGeometryFactory(new GeometryFactory());
117
                }
118
                return instance;
119
        }
120

    
121
        /**
122
         * Registers a GeometryFactory that will be used instead of the default one.
123
         * @param factory
124
         */
125
        public void registerGeometryFactory(GeometryFactory factory) {
126
                this.factory = factory;
127
        }
128

    
129
        /**
130
         * Returns the current geometry factory
131
         * @return
132
         */
133
        public GeometryFactory getGeometryFactory() {
134
                return factory;
135
        }
136

    
137
        /**
138
         * Registers the unique name of one operation. If it already exists then this method does nothing but returning
139
         * the name's corresponding index.
140
         * @param geomOpName Name used to register the geometry operation
141
         * @return index assigned to the operation name passed as parameter
142
         */
143
        private int registerGeometryOperationName(String geomOpName) {
144
                if (geomOpName == null)
145
                        throw new IllegalArgumentException("geomOpName cannot be null.");
146

    
147
                int index = geometryOperations.indexOf(geomOpName);
148
                if (index == -1) {
149
                        geometryOperations.add(geomOpName);
150
                        index = geometryOperations.indexOf(geomOpName);
151
                }
152
                return index;
153
        }
154

    
155

    
156
        /**
157
         * Sets a common operation into the common operations map.
158
         * @param index index in which to set the operation
159
         * @param geomOp operation to be set
160
         */
161
        private void setCommonOperation(int index, GeometryOperation geomOp) {
162

    
163
                while (index > commonOperations.size()) {
164
                        commonOperations.add(null);
165
                }
166

    
167
                if (index == commonOperations.size()) {
168
                        commonOperations.add(geomOp);
169
                } else {
170
                        commonOperations.set(index, geomOp);
171
                }
172
        }
173

    
174
        /**
175
         * Registers a GeometryOperation associated to a GeometryType.
176
         * Returns an unique index that is used later to identify and invoke the operation.
177
         *
178
         * This method is only used if you already got a reference to the GeometryType. If not,
179
         * it is more convenient to use the method that receives the geometry class as parameter.
180
         *
181
         * By convention, the return value should be stored in a public constant within the class implementing
182
         * the operation:<BR>
183
         * <pre>
184
         * public class MyOperation extends GeometryOperation {
185
         *   public static final int CODE =
186
         *     GeometryManager.getInstance()
187
         *        .registerGeometryOperation("MyOperation", new MyOperation(), geomType);
188
         * }
189
         * </pre>
190
         * @param geomOpName Operation's unique name
191
         * @param geomOp Specific GeometryOperation's instance implementing this operation
192
         * @param geomType GeometryType instance to which this operation should be associated
193
         * @return Index assigned to this operation. This index is used later to access the operation.
194
         *
195
         */
196
        public int registerGeometryOperation(String geomOpName,
197
                        GeometryOperation geomOp, GeometryType geomType) {
198
                if (geomOp == null)
199
                        throw new IllegalArgumentException("geomOp cannot be null.");
200
                if (geomType == null)
201
                        throw new IllegalArgumentException("geomType cannot be null.");
202

    
203
                int index = registerGeometryOperationName(geomOpName);
204

    
205
                geomType.setGeometryOperation(index, geomOp);
206

    
207
                return index;
208
        }
209

    
210
        /**
211
         * Registers a GeometryOperation that is common for all GeometryType (registered yet or not)
212
         * Returns an unique index that is used later to identify and invoke the operation.
213
         *
214
         * By convention, the return value should be stored in a public constant within the class implementing
215
         * the operation:<BR>
216
         * <pre>
217
         * public class MyOperation extends GeometryOperation {
218
         *   public static final int CODE =
219
         *     GeometryManager.getInstance()
220
         *        .registerGeometryOperation("MyOperation", new MyOperation());
221
         * }
222
         * </pre>
223
         *
224
         * @param geomOpName Operation's unique name
225
         * @param geomOp Specific GeometryOperation's instance implementing this operation
226
         * @return Index assigned to this operation. This index is used later to access the operation.
227
         */
228
        public int registerGeometryOperation(String geomOpName,
229
                        GeometryOperation geomOp) {
230
                if (geomOpName == null)
231
                        throw new IllegalArgumentException("geomOpName cannot be null.");
232
                if (geomOp == null)
233
                        throw new IllegalArgumentException("geomOp cannot be null.");
234

    
235
                int index = registerGeometryOperationName(geomOpName);
236

    
237
                setCommonOperation(index, geomOp);
238

    
239

    
240
                return index + COMMON_OPS_OFFSET;
241

    
242
        }
243

    
244
        /**
245
         * Registers a GeometryOperation associated to a GeometryType.
246
         * Returns an unique index that is used later to identify and invoke the operation.<br>
247
         *
248
         * By convention, the return value should be stored in a public constant within the class implementing
249
         * the operation:<BR>
250
         * <pre>
251
         * public class MyOperation extends GeometryOperation {
252
         *   public static final int CODE =
253
         *     GeometryManager.getInstance()
254
         *        .registerGeometryOperation("MyOperation", new MyOperation(), MyGeometry.class);
255
         * }
256
         * </pre>
257
         *
258
         * This method is only used if you have not a reference to the GeometryType associated to the
259
         * geometry class. If you have such reference then it is slightly faster to use the method that receives
260
         * the GeometryType.<br>
261
         *
262
         * @param geomOpName Operation's unique name
263
         * @param geomOp Specific GeometryOperation's instance implementing this operation
264
         * @param geomClass Geometry implementation class
265
         * @return Index assigned to this operation. This index is used later to access the operation.
266
         */
267
        public int registerGeometryOperation(String geomOpName,
268
                        GeometryOperation geomOp, Class geomClass) {
269

    
270
                GeometryType geomType = getGeometryType(geomClass);
271
                return registerGeometryOperation(geomOpName, geomOp, geomType);
272
        }
273

    
274

    
275
        /**
276
         * Registers a GeometryOperation associated to a GeometryType.
277
         * Returns an unique index that is used later to identify and invoke the operation.<br>
278
         *
279
         * By convention, the return value should be stored in a public constant within the class implementing
280
         * the operation:<BR>
281
         * <pre>
282
         * public class MyOperation extends GeometryOperation {
283
         *   public static final int CODE =
284
         *     GeometryManager.getInstance()
285
         *        .registerGeometryOperation("MyOperation", MyOperation.class, myGeomType);
286
         * }
287
         * </pre>
288
         *
289
         * @param geomOpName Operation's unique name
290
         * @param geomOpClass GeometryOperation class
291
         * @param geomType GeometryType instance to which this operation should be associated
292
         * @return Index assigned to this operation. This index is used later to access the operation.
293
         * @throws IllegalAccessException, {@link InstantiationException} Either exception maybe thrown when
294
         * trying to instance the geometry operation class.
295
         */
296
        public int registerGeometryOperation(String geomOpName, Class geomOpClass,
297
                        GeometryType geomType)
298
                        throws IllegalAccessException, InstantiationException {
299

    
300
                GeometryOperation geomOp = (GeometryOperation) geomOpClass.newInstance();
301
                return registerGeometryOperation(geomOpName, geomOp, geomType);
302
        }
303

    
304
        /**
305
         * Registers a Geometry implementation class as a new geometry type and returns the
306
         * associated GeometryType instance. If the class is already registered
307
         * then this method does nothing but returning the associated GeometryType.<br>
308
         *
309
         * How to register a new geometry type:
310
         * <pre>
311
         *
312
         * public class MyGeom3D implements Solid {
313
         *   private static final GeometryType geomType = GeometryManager.getInstance()
314
         *           .registerGeometryType(MyGeom3D.class, "MyGeom3D");
315
         *
316
         *   public static final int .CODE = geomType.getType();
317
         * ...
318
         *   public int getType() {
319
         *      return .CODE;
320
         *   }
321
         * }
322
         * </pre>
323
         *
324
         * @param geomClass
325
         *            Geometry subclass. It must not be null and must implement Geometry, otherwise an exception
326
         *            is raised.
327
         * @param name
328
         *                           Symbolic name for the geometry type, it can be null. If it is null then the symbolic name
329
         *                       will be the simple class name.
330
         * @return Instance of GeometryType associated to the Geometry implementation class
331
         *         geomClass
332
         * @throws IllegalArgumentException
333
         *             If geomClass is null or does not implement Geometry
334
         */
335
        public GeometryType registerGeometryType(Class geomClass, String name) {
336

    
337
                if (geomClass == null) {
338
                        throw new IllegalArgumentException("geomClass cannot be null.");
339
                }
340

    
341
                if (!Geometry.class.isAssignableFrom(geomClass)) {
342
                        throw new IllegalArgumentException(geomClass.getName()
343
                                        + " must implement the Geometry interface");
344
                }
345

    
346
                // Check if it is registered
347
                GeometryType geomType = (GeometryType) geometryTypes.get(geomClass
348
                                .getName());
349

    
350
                // If it is not, register it
351
                if (geomType == null) {
352
                        geomType = new GeometryType(geomClass, name, geomTypeIndex++);
353
                        geometryTypes.put(geomClass.getName(), geomType);
354
                } else {
355
                        // If it is already registered, throw exception
356
                        throw new IllegalArgumentException("Attempt to register a geometry type that is already registered: " + geomClass.getName());
357
                }
358
                logger.debug("Class " + geomType.getGeometryClass().getName() + " registered with name " + geomType.getName());
359

    
360
                return geomType;
361
        }
362
        /**
363
         * Registers a Geometry implementation class as a new geometry type and returns the
364
         * associated GeometryType instance. If the class is already registered
365
         * then this method does nothing but returning the associated GeometryType.<br>
366
         *
367
         * How to register a new geometry type:
368
         * <pre>
369
         *
370
         * public class MyGeom3D implements Solid {
371
         *   private static final GeometryType geomType = GeometryManager.getInstance()
372
         *           .registerBasicGeometryType(Point2D.class, "Point2D",Geometry.TYPES.POINT);
373
         *
374
         *   public static final int .CODE = geomType.getType();
375
         * ...
376
         *   public int getType() {
377
         *      return .CODE;
378
         *   }
379
         * }
380
         * </pre>
381
         *
382
         * @param geomClass
383
         *            Geometry subclass. It must not be null and must implement Geometry, otherwise an exception
384
         *            is raised.
385
         * @param name
386
         *                           Symbolic name for the geometry type, it can be null. If it is null then the symbolic name
387
         *                       will be the simple class name.
388
         * @param type
389
         *                           Type of basic geometry.
390
         * @return Instance of GeometryType associated to the Geometry implementation class
391
         *         geomClass
392
         * @throws IllegalArgumentException
393
         *             If geomClass is null or does not implement Geometry
394
         */
395
        public GeometryType registerBasicGeometryType(Class geomClass, String name,int type){
396
                if (geomClass == null) {
397
                        throw new IllegalArgumentException("geomClass cannot be null.");
398
                }
399

    
400
                if (!Geometry.class.isAssignableFrom(geomClass)) {
401
                        throw new IllegalArgumentException(geomClass.getName()
402
                                        + " must implement the Geometry interface");
403
                }
404

    
405
                // Check if it is registered
406
                GeometryType geomType = (GeometryType) geometryTypes.get(geomClass
407
                                .getName());
408

    
409
                // If it is not, register it
410
                if (geomType == null) {
411
                        geomType = new GeometryType(geomClass, name, type);
412
                        geometryTypes.put(geomClass.getName(), geomType);
413
                } else {
414
                        // If it is already registered, throw exception
415
                        throw new IllegalArgumentException("Attempt to register a geometry type that is already registered: " + geomClass.getName());
416
                }
417
                logger.debug("Class " + geomType.getGeometryClass().getName() + " registered with name " + geomType.getName());
418

    
419
                return geomType;
420
        }
421
        /**
422
         * Registers a Geometry implementation as a new geometry type and returns the
423
         * associated GeometryType instance. If the class is already registered
424
         * then this method does nothing but returning the associated GeometryType.<br>
425
         *
426
         * In this case the symbolic name will be the geometry's simple class name
427
         *
428
         * How to register a new geometry type:
429
         * <pre>
430
         *
431
         * public class MyGeom3D implements Solid {
432
         *   private static final GeometryType geomType = GeometryManager.getInstance()
433
         *           .registerGeometryType(MyGeom3D.class);
434
         *
435
         *   public static final int .CODE = geomType.getType();
436
         * ...
437
         *   public int getType() {
438
         *      return .CODE;
439
         *   }
440
         * }
441
         * </pre>
442
         *
443
         * @param geomClass
444
         *            Geometry implementation class. It must not be null and must implement Geometry,
445
         *            otherwise an exception is thrown.
446
         * @return Instance of GeometryType associated to the Geometry implementation class
447
         * @throws IllegalArgumentException
448
         *             If geomClass is null or does not implement Geometry
449
         */
450
        public GeometryType registerGeometryType(Class geomClass) {
451
                return registerGeometryType(geomClass, null);
452
        }
453
        /**
454
         * Returns an instance of GeometryType given the associated Geometry implementation
455
         * class.
456
         *
457
         * @param geomClass
458
         * @return Instance of GeometryType associated to the Geometry implementation class
459
         */
460
        public GeometryType getGeometryType(Class geomClass) {
461
                logger.debug("getting " + geomClass.getName());
462
                return (GeometryType) geometryTypes.get(geomClass.getName());
463
        }
464

    
465
        /**
466
         * Returns the associated GeometryType given the fully qualified name of
467
         * the Geometry implementation class.
468
         *
469
         * @param className
470
         *            Fully qualified name of the Geometry implementation class
471
         * @return GeometryType associated to the class
472
         */
473
        public GeometryType getGeometryType(String className) {
474
                return (GeometryType) geometryTypes.get(className);
475
        }
476

    
477
        /**
478
         * Returns an operation given the Geometry implementation class and the operation
479
         * code. If opCode corresponds to a common operation (a common operation is an operation
480
         * registered for all geometries), then this method returns the common operation.
481
         * <br>
482
         * For better performance, if you need to call an operation multiple times,
483
         * use this method only once and keep the returned object in a local variable
484
         * over which you can iterate. For instance:
485
         *
486
         * <pre>
487
         * ...
488
         *  // Get the operation you need
489
         * GeometryManager gm = GeometryManager.getInstance();
490
         * GeometryOperation geomOp = null;
491
         * try {
492
         *    geomOp = gm.getGeometryOperation(Point2D.class, Draw2D.CODE);
493
         * } catch (GeometryTypeNotSupportedException gtnse) {
494
         *    // treat exception
495
         * } catch (GeometryOperationNotSupportedException gonse) {
496
         *    // treat exception
497
         * }
498
         *
499
         *  // Fill the operation context with required params
500
         * GeometryOperationContext ctx = new GeometryOperationContext();
501
         *
502
         *  // Here is the main loop where you call the operation
503
         * for (int i=0; i<MyGeometries.length; i++) {
504
         *    Object result = geomOp.invoke(myGeometries[i], ctx);
505
         * }
506
         *
507
         * </pre>
508
         *
509
         * @param geomClass
510
         * @param opCode
511
         * @return Geometry operation
512
         */
513
        public GeometryOperation getGeometryOperation(Class geomClass, int opCode) throws GeometryTypeNotSupportedException, GeometryOperationNotSupportedException {
514

    
515
                GeometryOperation geomOp = null;
516

    
517
                // Check if it is a common operation, and if so, get it from the common registry
518
                if (opCode >= COMMON_OPS_OFFSET) {
519
                        geomOp = ((GeometryOperation)commonOperations.get(opCode - COMMON_OPS_OFFSET));
520

    
521
                        if (geomOp == null) {
522
                                throw new GeometryOperationNotSupportedException(opCode);
523
                        }
524
                } else {
525
                        // If it is type specific, get it from its type        registry
526
                        if (geomClass != null) {
527
                                GeometryType geomType = getGeometryType(geomClass);
528

    
529
                                // If the geometry type is not registered, throw an exception
530
                                if (geomType == null) {
531
                                        throw new GeometryTypeNotSupportedException(geomClass);
532
                                }
533

    
534
                                // Get the operation
535
                                geomOp = geomType.getGeometryOperation(opCode);
536

    
537
                                // If the operation is not registered throw an exception
538
                                if (geomOp == null) {
539
                                        throw new GeometryOperationNotSupportedException(opCode, geomType);
540
                                }
541
                        }
542
                }
543
                return geomOp;
544
        }
545

    
546
        /**
547
         * Invokes an operation given its code, the geometry and the operation context holding the
548
         * parameters required for the operation.
549
         *
550
         * @param opCode Operation code.
551
         * @param geom Geometry to which apply the operation
552
         * @param ctx Context holding the operation parameters
553
         * @return The object returned by an operation, depends on each operation.
554
         */
555
        public Object invokeOperation(int opCode, Geometry geom, GeometryOperationContext ctx) throws GeometryOperationNotSupportedException, GeometryOperationException {
556

    
557
                GeometryOperation geomOp = null;
558

    
559
                if (opCode < COMMON_OPS_OFFSET) {
560
                        geomOp =  geom.getGeometryType().getGeometryOperation(opCode);
561
                } else {
562
                        geomOp = ((GeometryOperation)commonOperations.get(opCode - COMMON_OPS_OFFSET));
563
                }
564

    
565
                if (geomOp != null) {
566
                        return geomOp.invoke(geom, ctx);
567
                }
568

    
569
                throw new GeometryOperationNotSupportedException(opCode, geom.getGeometryType());
570
        }
571
}