Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.plugin / org.gvsig.app / org.gvsig.app.mainplugin / src / main / java / org / gvsig / app / project / DefaultProject.java @ 40596

History | View | Annotate | Download (38.7 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 3
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
21
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24

    
25
package org.gvsig.app.project;
26

    
27
import java.awt.Color;
28
import java.beans.PropertyChangeEvent;
29
import java.beans.PropertyChangeListener;
30
import java.beans.PropertyChangeSupport;
31
import java.io.File;
32
import java.io.FileInputStream;
33
import java.io.FileNotFoundException;
34
import java.io.FileOutputStream;
35
import java.io.InputStream;
36
import java.io.OutputStream;
37
import java.io.Serializable;
38
import java.net.MalformedURLException;
39
import java.net.URI;
40
import java.net.URISyntaxException;
41
import java.net.URL;
42
import java.text.DateFormat;
43
import java.text.MessageFormat;
44
import java.util.ArrayList;
45
import java.util.Collections;
46
import java.util.Date;
47
import java.util.HashMap;
48
import java.util.Iterator;
49
import java.util.List;
50
import java.util.Map;
51
import java.util.regex.Pattern;
52

    
53
import org.apache.commons.io.FilenameUtils;
54
import org.cresques.cts.IProjection;
55
import org.gvsig.andami.PluginServices;
56
import org.gvsig.andami.ui.mdiManager.IWindow;
57
import org.gvsig.andami.ui.mdiManager.MDIManager;
58
import org.gvsig.andami.ui.mdiManager.SingletonWindow;
59
import org.gvsig.andami.ui.mdiManager.WindowInfo;
60
import org.gvsig.app.ApplicationLocator;
61
import org.gvsig.app.ApplicationManager;
62
import org.gvsig.app.extension.ProjectExtension;
63
import org.gvsig.app.extension.Version;
64
import org.gvsig.app.project.documents.AbstractDocument;
65
import org.gvsig.app.project.documents.Document;
66
import org.gvsig.app.project.documents.exceptions.SaveException;
67
import org.gvsig.app.project.documents.gui.IDocumentWindow;
68
import org.gvsig.app.project.documents.gui.ProjectWindow;
69
import org.gvsig.app.project.documents.view.DefaultViewDocument;
70
import org.gvsig.app.project.documents.view.ViewManager;
71
import org.gvsig.fmap.dal.DataTypes;
72
import org.gvsig.fmap.mapcontext.MapContext;
73
import org.gvsig.fmap.mapcontext.layers.FLayer;
74
import org.gvsig.fmap.mapcontext.layers.FLayers;
75
import org.gvsig.tools.ToolsLocator;
76
import org.gvsig.tools.dynobject.DynField;
77
import org.gvsig.tools.dynobject.DynStruct;
78
import org.gvsig.tools.observer.ObservableHelper;
79
import org.gvsig.tools.observer.Observer;
80
import org.gvsig.tools.persistence.PersistenceManager;
81
import org.gvsig.tools.persistence.Persistent;
82
import org.gvsig.tools.persistence.PersistentContext;
83
import org.gvsig.tools.persistence.PersistentState;
84
import org.gvsig.tools.persistence.exception.PersistenceException;
85
import org.gvsig.utils.StringUtilities;
86
import org.slf4j.Logger;
87
import org.slf4j.LoggerFactory;
88

    
89
/**
90
 * Clase que representa un proyecto de gvSIG
91
 * 
92
 * @author 2004-2005 Fernando Gonz?lez Cort?s
93
 * @author 2006-2009 Jose Manuel Vivo
94
 * @author 2005- Vicente Caballero
95
 * @author 2009- Joaquin del Cerro
96
 * 
97
 */
98

    
99
public class DefaultProject implements Serializable, PropertyChangeListener,
100
                Project {
101

    
102
        private Logger LOG = LoggerFactory.getLogger(DefaultProject.class);
103
        /**
104
         * @deprecated see ApplicationLocator.getManager().getVersion()
105
         */
106
        public static String VERSION = Version.format();
107

    
108
    private ObservableHelper observableHelper = new ObservableHelper();
109

    
110
        /**
111
         * 
112
         */
113
        private static final long serialVersionUID = -4449622027521773178L;
114

    
115
        private static final Logger logger = LoggerFactory.getLogger(Project.class);
116

    
117
        private static ProjectPreferences preferences = new ProjectPreferences();
118

    
119
        /**
120
         * Index used by the generator of unique names of documents.
121
         */
122
        private Map<String, Integer> nextDocumentIndexByType = new HashMap<String, Integer>();
123

    
124
        private PropertyChangeSupport change;
125

    
126
        private boolean modified = false;
127

    
128
        private String name = null;
129

    
130
        private String creationDate = null;
131

    
132
        private String modificationDate = null;
133

    
134
        private String owner = null;
135

    
136
        private String comments = null;
137

    
138
        private Color selectionColor = null;
139

    
140
        private List<Document> documents = null;
141

    
142
        private List<ProjectExtent> extents = null;
143

    
144
        private IProjection projection;
145

    
146
        /**
147
         * Creates a new Project object.
148
         */
149
        DefaultProject() {
150
                this.change = new PropertyChangeSupport(this);
151
                this.clean();
152
        }
153

    
154
        protected void clean() {
155
                this.owner = "";
156
                this.comments = "";
157
                this.name = PluginServices.getText(this, "untitled");
158
                this.creationDate = DateFormat.getDateInstance().format(new Date());
159
                this.modificationDate = this.creationDate;
160

    
161
                this.documents = new ArrayList<Document>();
162
                this.extents = new ArrayList<ProjectExtent>();
163

    
164
                this.setSelectionColor(getPreferences().getDefaultSelectionColor());
165

    
166
                this.projection = null; // se inicializa en el getProjection()
167
        }
168

    
169
        public static ProjectPreferences getPreferences() {
170
                return preferences;
171
        }
172

    
173
        public void propertyChange(PropertyChangeEvent evt) {
174
                change.firePropertyChange(evt);
175
        }
176

    
177
        public synchronized void addPropertyChangeListener(
178
                        PropertyChangeListener arg0) {
179
                change.addPropertyChangeListener(arg0);
180
        }
181

    
182
        /**
183
         * Return the creation date of the project
184
         * 
185
         * @return
186
         */
187
        public String getCreationDate() {
188
                return creationDate;
189
        }
190

    
191
        protected void setCreationDate(String creationDate) {
192
                this.creationDate = creationDate;
193
                change.firePropertyChange("setCreationDate", null, null);
194
        }
195

    
196
    public Document createDocument(String type) {
197
            logger.info("createDocument('{}')",type);
198
        return ProjectManager.getInstance().createDocument(type);
199
    }
200

    
201
    /**
202
     * Return the name of the project
203
     * 
204
     * @return
205
     */
206
    public String getName() {
207
        return name;
208
    }
209

    
210
        /**
211
         * Set the name of he project.
212
         * 
213
         * @param string
214
         */
215
        public void setName(String name) {
216
                this.name = name;
217
                change.firePropertyChange("setName", null, null);
218
        }
219

    
220
        /**
221
         * Return the comments associateds with the project
222
         * 
223
         * @return comments
224
         */
225
        public String getComments() {
226
                return comments;
227
        }
228

    
229
        /**
230
         * Set the comments associateds with the project
231
         * 
232
         * @param comments
233
         *            as string
234
         */
235
        public void setComments(String string) {
236
                comments = string;
237
                change.firePropertyChange("setComments", null, null);
238
        }
239

    
240
        /**
241
         * Retuen the modification date of the project.
242
         * 
243
         * @return modification date as string
244
         */
245
        public String getModificationDate() {
246
                return modificationDate;
247
        }
248

    
249
        protected void setModificationDate(String string) {
250
                modificationDate = string;
251
                change.firePropertyChange("setModificationDate", null, null);
252
        }
253

    
254
        /**
255
         * Return the author of the project,
256
         * 
257
         * @return author as string
258
         */
259
        public String getOwner() {
260
                return owner;
261
        }
262

    
263
        /**
264
         * Sets the author of the project
265
         * 
266
         * @param author
267
         *            name as string
268
         */
269
        public void setOwner(String owner) {
270
                this.owner = owner;
271
                change.firePropertyChange("setOwner", null, null);
272
        }
273

    
274
        /**
275
         * Obtiene el color de selecci?n que se usar? en el proyecto
276
         * 
277
         * @return
278
         */
279
        public Color getSelectionColor() {
280
                if (selectionColor == null) {
281
                        selectionColor = getPreferences().getDefaultSelectionColor();
282
                }
283
                return selectionColor;
284
        }
285

    
286
        /**
287
         * Sets the selecction color
288
         * 
289
         * @param selection
290
         *            color as string
291
         */
292
        public void setSelectionColor(String selectionColor) {
293
                this.setSelectionColor(StringUtilities.string2Color(selectionColor));
294
        }
295

    
296
        /**
297
         * Sets the selecction color
298
         * 
299
         * @param selection
300
         *            color as Color
301
         */
302
        public void setSelectionColor(Color selectionColor) {
303
                this.selectionColor = selectionColor;
304
                MapContext.setSelectionColor(selectionColor);
305
                change.firePropertyChange("selectionColor", null, selectionColor);
306
        }
307

    
308
        public IProjection getProjection() {
309
                if (projection == null) {
310
                        projection = getPreferences().getDefaultProjection();
311
                }
312
                return projection;
313
        }
314

    
315
        public void setProjection(IProjection projection) {
316
                this.projection = projection;
317
        }
318

    
319
        /**
320
         * Sets the modified state of project.
321
         * 
322
         * Can't set to not modified.
323
         * 
324
         * @param modified
325
         *            as boolean
326
         */
327
        public void setModified(boolean modified) {
328
                this.modified = modified;
329
                if (modified == false) {
330
                        List<Document> documents = this.getDocuments();
331
                        for (int i = 0; i < documents.size(); i++) {
332
                                documents.get(i).setModified(false);
333
                        }
334
                }
335
        }
336

    
337
        public boolean hasChanged() {
338
                // we return true if the project is not empty (until we have a better
339
                // method...)
340
                if ((this.getDocuments().size() != 0) || modified) {
341
                        return true;
342
                }
343
                return false;
344
        }
345

    
346
        /**
347
         * Return a list of documents in the project.
348
         * 
349
         * @return documents as List of IProjectDocument
350
         */
351
        public List<Document> getDocuments() {
352
                return Collections.unmodifiableList(documents);
353
        }
354

    
355
        /**
356
         * Return a list with all documents of especified type.
357
         * 
358
         * @param type
359
         *            of document
360
         * 
361
         * @return List of IProjectDocument
362
         */
363
        public List<Document> getDocuments(String type) {
364
                List<Document> docs = new ArrayList<Document>();
365
                if (type != null) {
366
                        for (Document document : this.documents) {
367
                                if (type.equalsIgnoreCase(document.getTypeName())) {
368
                                        docs.add(document);
369
                                }
370
                        }
371
                }
372
                return Collections.unmodifiableList(docs);
373
        }
374

    
375
        /**
376
         * Adds a document to the project
377
         * 
378
         * @param document
379
         *            as IProjectDocument
380
         */
381
        public void add(Document document) {
382
        logger.info("add('{}')", document.toString());
383

    
384
        if( notifyObservers(ProjectNotification.BEFORE_ADD_DOCUMENT, document).isProcessCanceled() ) {
385
                return;
386
        }
387
                document.addPropertyChangeListener(this);
388
                document.setProject(this);
389
                document.setName(this.getUniqueNameForDocument(document.getTypeName(),
390
                                document.getName()));
391
                documents.add(document);
392
                document.afterAdd();
393
                this.setModified(true);
394
                notifyObservers(ProjectNotification.AFTER_ADD_DOCUMENT, document);
395
        change.firePropertyChange("addDocument", null, document);
396
        }
397

    
398
        /**
399
         * Remove a document of the project
400
         * 
401
         * @param document
402
         *            as IProjectDocument
403
         */
404
        public void remove(Document doc) {
405
        logger.info("remove('{}')", doc.toString());
406
        if( notifyObservers(ProjectNotification.BEFORE_REMOVE_DOCUMENT, doc).isProcessCanceled() ) {
407
                return;
408
        }
409
                documents.remove(doc);
410
                this.setModified(true);
411
        change.firePropertyChange("delDocument", doc, null);
412
                doc.afterRemove();
413
                notifyObservers(ProjectNotification.AFTER_REMOVE_DOCUMENT, doc);
414
        }
415

    
416
        public Iterator<Document> iterator() {
417
                return documents.iterator();
418
        }
419

    
420
        public boolean isEmpty() {
421
                return documents.isEmpty();
422
        }
423

    
424
        /**
425
         * Return the view that contains the especified layer.
426
         * 
427
         * @param layer
428
         * 
429
         * @return name of the view that contains the layer
430
         * 
431
         * @throws RuntimeException
432
         *             Si la capa que se pasa como par?metro no se encuentra en
433
         *             ninguna vista
434
         */
435
        public String getViewName(FLayer layer) {
436
                List<Document> views = getDocuments(ViewManager.TYPENAME);
437
                for (int v = 0; v < views.size(); v++) {
438
                        DefaultViewDocument pView = (DefaultViewDocument) views.get(v);
439
                        FLayers layers = pView.getMapContext().getLayers();
440
                        if (isView(layers, layer)) {
441
                                return pView.getName();
442
                        }
443
                }
444

    
445
                throw new RuntimeException(MessageFormat.format(
446
                                "The layer '{1}' is not in a view", layer.getName()));
447
        }
448

    
449
        private boolean isView(FLayers layers, FLayer layer) {
450
                for (int i = 0; i < layers.getLayersCount(); i++) {
451
                        if (layers.getLayer(i) instanceof FLayers) {
452
                                if (isView((FLayers) layers.getLayer(i), layer)) {
453
                                        return true;
454
                                }
455
                        }
456
                        if (layers.getLayer(i) == layer) {
457
                                return true;
458
                        }
459
                }
460
                return false;
461
        }
462

    
463
        public void addExtent(ProjectExtent arg1) {
464
                extents.add(arg1);
465
                change.firePropertyChange("addExtent", null, null);
466
        }
467

    
468
        public ProjectExtent removeExtent(int arg0) {
469
                change.firePropertyChange("delExtent", null, null);
470
                return extents.remove(arg0);
471
        }
472

    
473
        public ProjectExtent[] getExtents() {
474
                return (ProjectExtent[]) extents.toArray(new ProjectExtent[0]);
475
        }
476

    
477
        /**
478
         * Obtiene un documento a partir de su nombre y el nombre de registro en el
479
         * pointExtension, este ?ltimo se puede obtener del
480
         * Project****Factory.registerName.
481
         * 
482
         * @param name
483
         *            Nombre del documento
484
         * @param type
485
         *            nombre de registro en el extensionPoint
486
         * 
487
         * @return Documento
488
         */
489
        public Document getDocument(String name, String type) {
490
                if (type != null) {
491
                        for (int i = 0; i < documents.size(); i++) {
492
                                Document document = documents.get(i);
493
                                if (type.equalsIgnoreCase(document.getTypeName())
494
                                                && name.equalsIgnoreCase(document.getName())) {
495
                                        return document;
496
                                }
497
                        }
498
                }
499
                return null;
500
        }
501

    
502
        public String getUniqueNameForDocument(String type, String name) {
503
                Document document = getDocument(name, type);
504
                if (document == null) {
505
                        return name;
506
                }
507

    
508
                String newName = null;
509
                int num = this.getNextDocumentIndex(type);
510
                while (document != null) {
511
                        newName = name + " - " + num++;
512
                        document = getDocument(newName, type);
513
                }
514
                this.setNextDocumentIndex(type, num);
515
                return newName;
516
        }
517

    
518
        private int getNextDocumentIndex(String type) {
519
                if (nextDocumentIndexByType.get(type) == null) {
520
                        nextDocumentIndexByType.put(type, new Integer(1));
521
                        return 1;
522
                }
523
                return nextDocumentIndexByType.get(type).intValue();
524
        }
525

    
526
        private void setNextDocumentIndex(String type, int newIndex) {
527
                if (nextDocumentIndexByType.get(type) == null) {
528
                        nextDocumentIndexByType.put(type, new Integer(newIndex));
529
                } else {
530
                        nextDocumentIndexByType.put(type, new Integer(newIndex));
531
                }
532
        }
533

    
534
        public void saveState(File out) throws PersistenceException {
535
                FileOutputStream fout;
536
                if( notifyObservers(ProjectNotification.BEFORE_SAVE_TO_FILE, out).isProcessCanceled() ) {
537
                return;
538
        }
539
                try {
540
                        fout = new FileOutputStream(out);
541
                        saveState(fout, new File(out.getParent()));
542
                } catch (FileNotFoundException e) {
543
                        throw new PersistenceException(e);
544
                }
545
                notifyObservers(ProjectNotification.AFTER_SAVE_TO_FILE, out);
546
        }
547

    
548
        public void saveState(OutputStream out) throws PersistenceException {
549
                saveState(out, null);
550
        }
551

    
552
        public void saveState(OutputStream out, File rootFolder)
553
                        throws PersistenceException {
554
                if( notifyObservers(ProjectNotification.BEFORE_SAVE_TO_STREAM).isProcessCanceled() ) {
555
                return;
556
        }
557
                PersistenceManager manager = ToolsLocator.getPersistenceManager();
558
                PersistentState state = null;
559
                state = manager.getState(this, true);
560
                try {
561
                        if (rootFolder != null) {
562
                                relativizeFiles(state, rootFolder);
563
                        }
564
                } catch (Exception ex) {
565
                        state.getContext().addError(ex);
566
                }
567
                manager.saveState(state, out);
568
                if (state.getContext().getErrors() != null) {
569
                        throw state.getContext().getErrors();
570
                }
571
                notifyObservers(ProjectNotification.AFTER_SAVE_TO_STREAM);
572
        }
573

    
574
        private void relativizeFiles(PersistentState state, File rootFolder) {
575
                PersistentContext context = state.getContext();
576

    
577
                URI cwd = new File(System.getProperty("user.dir")).toURI();
578
                @SuppressWarnings("unchecked")
579
                Iterator<PersistentState> statesIterator = context.iterator();
580
                while (statesIterator.hasNext()) {
581
                        PersistentState aState = statesIterator.next();
582
                        DynStruct definition = aState.getDefinition();
583
                        DynField[] fields = definition.getDynFields();
584
                        for (DynField field : fields) {
585
                                if (field.getType() == DataTypes.FILE
586
                                                || field.getType() == DataTypes.FOLDER) {
587
                                        try {
588
                                                File value = aState.getFile(field.getName());
589
                                                value = relativizeFile(value, rootFolder);
590
                                                aState.set(field.getName(), value);
591
                                        } catch (PersistenceException e) {
592
                                                LOG.warn(
593
                                                                "Can't relativice field '" + field.getName()
594
                                                                                + "' for class '"
595
                                                                                + definition.getName() + "'.", e);
596
                                        }
597
                                } else if (field.getType() == DataTypes.URL) {
598
                                        try {
599
                                                URL value = aState.getURL(field.getName());
600
                                                if ("FILE".equalsIgnoreCase(value.getProtocol())) {
601
                                                        File file = new File(value.toURI());
602
                                                        if (differentDrives(file, rootFolder)) {
603
                                /*
604
                                 * Different drives
605
                                 * (should happen only in Windows)
606
                                 */
607
                                value = file.toURI().toURL();
608
                                                        } else {
609
                                /*
610
                                * Both in the Same drive (C: D: etc) or Unix-style
611
                                */
612
                                file = relativizeFile(file, rootFolder);
613
                                value = new URL("file","",file.toURI().toString().substring(cwd.toString().length()));
614
                                                        }
615
                                                        aState.set(field.getName(), value);
616
                                                }
617
                                        } catch (PersistenceException e) {
618
                                                // do nothind
619
                                        } catch (MalformedURLException e) {
620
                                                // do nothind
621
                                        } catch (URISyntaxException e) {
622
                                                // do nothind
623
                                        }
624
                                } else if (field.getType() == DataTypes.URI) {
625
                                        try {
626
                                                URI value = aState.getURI(field.getName());
627
                                                if (value.getScheme() == null || "FILE".equalsIgnoreCase(value.getScheme())) {
628
                                                        File file = new File(value.getPath());
629
                                                        if (differentDrives(file, rootFolder)) {
630
                                /*
631
                                 * Different drives
632
                                 * (should happen only in Windows)
633
                                 */
634
                                value = file.toURI();
635
                                                        } else {
636
                                /*
637
                                * Both in the Same drive (C: D: etc) or Unix-style
638
                                */
639
                                file = relativizeFile(file, rootFolder);
640
                                value = new URI(file.getPath());
641
                                                        }
642
                                                        aState.set(field.getName(), value);
643
                                                }
644
                                        } catch (PersistenceException e) {
645
                                                // do nothind
646
                                        } catch (URISyntaxException e) {
647
                                                // do nothind
648
                                        }
649
                                }
650
                        }
651
                }
652
        }
653

    
654
    private boolean differentDrives(File file, File rootFolder) {
655

    
656
        if (rootFolder == null) {
657
            return false;
658
        }
659

    
660
        if (file == null) {
661
            return false;
662
        }
663
        String basePath = rootFolder.getAbsolutePath();
664
        String targetPath = file.getPath();
665
        String pathSeparator = File.separator;
666
        // We need the -1 argument to split to make sure we get a trailing
667
        // "" token if the base ends in the path separator and is therefore
668
        // a directory. We require directory paths to end in the path
669
        // separator -- otherwise they are indistinguishable from files.
670
        String[] base = basePath.split(Pattern.quote(pathSeparator), -1);
671
        String[] target = targetPath.split(Pattern.quote(pathSeparator), 0);
672
        /*
673
         * If first component is different then they are different
674
         * drives. In Unix-type file systems, the first for both
675
         * should be "" so this will not happen
676
         */
677
        return !target[0].equals(base[0]);
678
    }
679
        
680
        private File relativizeFile(File file, File rootFolder) {
681
                if (rootFolder == null) {
682
                        return file;
683
                }
684
                if (file == null) {
685
                        return null;
686
                }
687
                
688
                boolean isDir = false;
689
                // isDir = file.isDirectory();
690
                isDir = rootFolder.isDirectory();
691
                String basePath = rootFolder.getAbsolutePath();
692
                
693
                String targetPath = FilenameUtils.normalize(file.getPath());
694
                
695
                String pathSeparator = File.separator;
696
                // We need the -1 argument to split to make sure we get a trailing
697
                // "" token if the base ends in the path separator and is therefore
698
                // a directory. We require directory paths to end in the path
699
                // separator -- otherwise they are indistinguishable from files.
700
                String[] base = basePath.split(Pattern.quote(pathSeparator), -1);
701
                String[] target = targetPath.split(Pattern.quote(pathSeparator), 0);
702

    
703
                // First get all the common elements. Store them as a string,
704
                // and also count how many of them there are.
705
                String common = "";
706
                int commonIndex = 0;
707
                for (int i = 0; i < target.length && i < base.length; i++) {
708
                        if (target[i].equals(base[i])) {
709
                                common += target[i] + pathSeparator;
710
                                commonIndex++;
711
                        } else
712
                                break;
713
                }
714

    
715
                if (commonIndex == 0) {
716
                        // Whoops -- not even a single common path element. This most
717
                        // likely indicates differing drive letters, like C: and D:.
718
                        // These paths cannot be relativized. Return the target path.
719
                        return file;
720
                        // This should never happen when all absolute paths
721
                        // begin with / as in *nix.
722
                }
723

    
724
                String relative = "";
725
                if (base.length == commonIndex) {
726
                        // Comment this out if you prefer that a relative path not start
727
                        // with ./
728
                        relative = "." + pathSeparator;
729
                } else {
730
                        int numDirsUp = base.length - commonIndex - (isDir ? 0 : 1); /*
731
                                                                                                                                                 * only
732
                                                                                                                                                 * subtract
733
                                                                                                                                                 * 1 if
734
                                                                                                                                                 * it is
735
                                                                                                                                                 * a
736
                                                                                                                                                 * file.
737
                                                                                                                                                 */
738
                        // The number of directories we have to backtrack is the length of
739
                        // the base path MINUS the number of common path elements, minus
740
                        // one because the last element in the path isn't a directory.
741
                        for (int i = 1; i <= (numDirsUp); i++) {
742
                                relative += ".." + pathSeparator;
743
                        }
744
                }
745
                // if we are comparing directories then we
746
                if (targetPath.length() > common.length()) {
747
                        // it's OK, it isn't a directory
748
                        relative += targetPath.substring(common.length());
749
                }
750

    
751
                return new File(relative);
752
        }
753

    
754
        private void fixFiles(PersistentState state, File rootFolder) {
755
                PersistentContext context = state.getContext();
756
                URI cwd = new File(System.getProperty("user.dir")).toURI();
757

    
758
                @SuppressWarnings("unchecked")
759
                Iterator<PersistentState> statesIterator = context.iterator();
760
                String className = null;
761
                
762
                while (statesIterator.hasNext()) {
763
                        PersistentState aState = statesIterator.next();
764
                        try {
765
                                className = aState.getTheClassName();
766
                        } catch (Throwable e) {
767
                                className = "Unknown";
768
                        }
769
                        try {
770
                                DynStruct definition = aState.getDefinition();
771
                                DynField[] fields = definition.getDynFields();
772
                                for (DynField field : fields) {
773
                                        if (field.getType() == DataTypes.FILE
774
                                                        || field.getType() == DataTypes.FOLDER) {
775
                                                try {
776
                                                        File value = aState.getFile(field.getName());
777
                                                        value = fixFile(value, rootFolder);
778
                                                        aState.set(field.getName(), value);
779
                                                } catch (PersistenceException e) {
780
                                                        LOG.warn(
781
                                                                        "Can't fix field '" + field.getName()
782
                                                                        + "' for class '"
783
                                                                        + definition.getName() + "'.", e);
784
                                                }
785
                                        } else if (field.getType() == DataTypes.URL) {
786
                                                try {
787
                                                        URL value = aState.getURL(field.getName());
788
                                                        if ("FILE".equalsIgnoreCase(value.getProtocol())) {
789
                                                                if (!value.getFile().startsWith("/")){
790
                                                                        value = new URL("file","",cwd.getRawPath()+value.getFile());
791
                                                                        File file = new File(value.toURI().getPath().substring(cwd.getPath().length()));
792
                                                                        file = fixFile(file, rootFolder);
793
                                                                        aState.set(field.getName(), file.toURI().toURL());
794
                                                                }
795
                                                        }
796
                                                } catch (PersistenceException e) {
797
                                                        // do nothing
798
                                                } catch (MalformedURLException e) {
799
                                                        // do nothing
800
                                                } catch (URISyntaxException e) {
801
                                                        // do nothing
802
                                                }
803

    
804
                                        } else if (field.getType() == DataTypes.URI) {
805
                                                try {
806
                                                        URI value = aState.getURI(field.getName());
807
                                                        if (value.getScheme() == null || "FILE".equalsIgnoreCase(value.getScheme())) {
808
                                                                if (!value.getPath().startsWith("/")){
809
                                                                        File file = new File(value.getPath());
810
                                                                        file = fixFile(file, rootFolder);
811
                                                                        aState.set(field.getName(), file.toURI());
812
                                                                }
813
                                                        }
814
                                                } catch (PersistenceException e) {
815
                                                        // do nothing
816
                                                } 
817
                                        }
818
                                }
819
                        }catch (RuntimeException e) {
820
                                logger.info("Can't fix relative paths in " + className, e);
821
                        }
822
                }
823
        }
824

    
825
        private File fixFile(File file, File rootFolder) {
826
                if (file.isAbsolute()) {
827
                        return file;
828
                }
829
                File f = new File(rootFolder, file.getPath()).getAbsoluteFile();
830
                String targetPath = FilenameUtils.normalize(f.getPath());
831
                return new File(targetPath);
832
        }
833

    
834
        public void loadState(InputStream in) {
835
                loadState(in, null);
836
        }
837

    
838
        public void loadState(InputStream in, File rootFolder) {
839
                if( notifyObservers(ProjectNotification.BEFORE_LOAD_FROM_STREAM).isProcessCanceled() ) {
840
                        return;
841
                }
842
                PersistenceManager manager = ToolsLocator.getPersistenceManager();
843
                try {
844
                        PersistentState state = manager.loadState(in);
845
                        try {
846
                                if (rootFolder != null) {
847
                                        fixFiles(state, rootFolder);
848
                                }
849
                        } catch (Exception ex) {
850
                                state.getContext().addError(ex);
851
                        }
852
                        this.loadFromState(state);
853
                } catch (PersistenceException e) {
854
                        LOG.info("Can't load project to stream",e);
855
                }
856
                notifyObservers(ProjectNotification.AFTER_LOAD_FROM_STREAM);
857

    
858
        }
859

    
860
        public void loadState(File in) {
861
                if( notifyObservers(ProjectNotification.BEFORE_LOAD_FROM_FILE, in).isProcessCanceled() ) {
862
                        return;
863
                }
864
                FileInputStream fin;
865
                try {
866
                        fin = new FileInputStream(in);
867
                        loadState(fin, new File(in.getParent()));
868
                } catch (FileNotFoundException e) {
869
                        // TODO Auto-generated catch block
870
                        e.printStackTrace();
871
                }
872
                notifyObservers(ProjectNotification.AFTER_LOAD_FROM_FILE, in);
873
        }
874

    
875
        @SuppressWarnings("unchecked")
876
        public void loadFromState(PersistentState state)
877
                        throws PersistenceException {
878
                this.clean();
879

    
880
                this.setComments(state.getString("comments"));
881
                this.setCreationDate(state.getString("creationDate"));
882
                this.setModificationDate(state.getString("modificationDate"));
883
                this.setName(state.getString("name"));
884
                this.setOwner(state.getString("owner"));
885
                this.setSelectionColor((Color) state.get("selectionColor"));
886
                this.setProjection((IProjection) state.get("projection"));
887

    
888
                List<ProjectExtent> extents = (List<ProjectExtent>) state
889
                                .get("extents");
890
                for (int i = 0; i < extents.size(); i++) {
891
                        this.addExtent(extents.get(i));
892
                }
893

    
894
                List<AbstractDocument> documents = (List<AbstractDocument>) state
895
                                .get("documents");
896
                for (int i = 0; i < documents.size(); i++) {
897
                        this.add(documents.get(i));
898
                }
899

    
900
                List<DocumentWindowInfo> persistentWindows = (List<DocumentWindowInfo>) state.get("documentWindowsInformation");
901
                                
902
                for (int i = 0; i < persistentWindows.size(); i++) {
903
                        DocumentWindowInfo persistentWindow = persistentWindows.get(i);
904
                        String docName = persistentWindow.getDocumentName();
905
                        String docType = persistentWindow.getDocumentType();
906
                        Document doc = this.getDocument(docName, docType);
907
                        IWindow win = doc.getFactory().getMainWindow(doc);
908
                        win.getWindowInfo().setWindowInfo(persistentWindow.getWindowInfo());
909
                        PluginServices.getMDIManager().addWindow(win);
910
                }
911
                
912
                if (state.hasValue("projectWindowInfo")){
913
                        WindowInfo projectWindowInfo = (WindowInfo)state.get("projectWindowInfo"); 
914
                        ProjectExtension pe = (ProjectExtension) PluginServices.getExtension(org.gvsig.app.extension.ProjectExtension.class);
915
                        pe.setProject(this);
916
                        pe.showProjectWindow(projectWindowInfo);
917
                }
918

    
919
        }
920

    
921
        public void saveToState(PersistentState state) throws PersistenceException {
922
                state.set("version", VERSION);
923
                state.set("comments", getComments());
924
                state.set("creationDate", this.getCreationDate());
925

    
926
                state.set("modificationDate", this.getModificationDate());
927
                state.set("name", this.getName());
928
                state.set("owner", this.getOwner());
929
                state.set("selectionColor", this.getSelectionColor());
930

    
931
                state.set("projection", this.getProjection());
932

    
933
                state.set("extents", this.extents);
934
                state.set("documents", this.getDocuments());
935

    
936
                List<DocumentWindowInfo> persistentWindows = new ArrayList<DocumentWindowInfo>();
937
                MDIManager mdiMan = PluginServices.getMDIManager();
938
                IWindow[] windows = mdiMan.getOrderedWindows();
939
                for (int i = windows.length - 1; i >= 0; i--) {
940
                        IWindow window = windows[i];
941
                        if (window instanceof IDocumentWindow){
942
                                WindowInfo wi =  mdiMan.getWindowInfo(window);
943
                                DocumentWindowInfo dwi = new DocumentWindowInfo(
944
                                                wi,
945
                                                ((IDocumentWindow) window).getDocument().getTypeName(),
946
                                                ((IDocumentWindow) window).getDocument().getName());
947
                                persistentWindows.add(dwi);
948
                        } else if (window instanceof ProjectWindow){
949
                                state.set("projectWindowInfo", mdiMan.getWindowInfo(window));
950
                        }
951
                }
952
                state.set("documentWindowsInformation", persistentWindows);
953

    
954
        }
955

    
956
        public static class DocumentWindowInfo implements Persistent {
957

    
958
                public static final String PERSISTENCE_DEFINITION_NAME = "DocumentWindowInfo";
959

    
960
                private WindowInfo windowInfo;
961
                private String documentType;
962
                private String documentName;
963

    
964
                public DocumentWindowInfo(){
965
                }
966
                
967
                DocumentWindowInfo(WindowInfo wi, String docType, String docName){
968
                        windowInfo = wi;
969
                        documentType = docType;
970
                        documentName = docName;
971
                }
972

    
973
                public WindowInfo getWindowInfo() {
974
                        return windowInfo;
975
                }
976
                
977
                public String getDocumentType() {
978
                        return documentType;
979
                }
980

    
981
                public String getDocumentName() {
982
                        return documentName;
983
                }
984

    
985
                public void saveToState(PersistentState state)
986
                                throws PersistenceException {
987
                        state.set("windowInfo", this.windowInfo);
988
                        state.set("documentType", this.documentType);
989
                        state.set("documentName", this.documentName);
990
                }
991

    
992
                public void loadFromState(PersistentState state)
993
                                throws PersistenceException {
994
                        this.windowInfo = (WindowInfo) state.get("windowInfo");
995
                        this.documentType = state.getString("documentType");
996
                        this.documentName = state.getString("documentName");
997
                }
998
                
999
                public static void registerPersistent() {
1000
                        PersistenceManager manager = ToolsLocator.getPersistenceManager();
1001
                        DynStruct definition = manager.getDefinition(PERSISTENCE_DEFINITION_NAME);
1002
                        if ( definition == null ){
1003
                                definition = manager.addDefinition(
1004
                                                DocumentWindowInfo.class,
1005
                                                PERSISTENCE_DEFINITION_NAME,
1006
                                                "DocumentWindowInfo persistence definition",
1007
                                                null, 
1008
                                                null
1009
                                );
1010
                                definition.addDynFieldObject("windowInfo").setMandatory(true).setClassOfValue(WindowInfo.class);
1011
                                definition.addDynFieldString("documentType").setMandatory(true);
1012
                                definition.addDynFieldString("documentName").setMandatory(true);
1013
                        }
1014

    
1015
                }
1016
        }
1017
        
1018
        public static void registerPersistent() {
1019
                AbstractDocument.registerPersistent();
1020
                DocumentWindowInfo.registerPersistent();
1021
                ProjectExtent.registerPersistent();
1022

    
1023
                PersistenceManager manager = ToolsLocator.getPersistenceManager();
1024
                DynStruct definition = manager.addDefinition(DefaultProject.class,
1025
                                "Project", "Project Persistence definition", null, null);
1026
                definition.addDynFieldString("version").setMandatory(true);
1027
                definition.addDynFieldString("comments").setMandatory(true);
1028
                definition.addDynFieldString("creationDate").setMandatory(true);
1029
                definition.addDynFieldString("modificationDate").setMandatory(true);
1030
                definition.addDynFieldString("name").setMandatory(true);
1031
                definition.addDynFieldString("owner").setMandatory(true);
1032

    
1033
                definition.addDynFieldObject("selectionColor")
1034
                                .setClassOfValue(Color.class).setMandatory(true);
1035
                definition.addDynFieldObject("projection")
1036
                                .setClassOfValue(IProjection.class).setMandatory(true);
1037

    
1038
                definition.addDynFieldList("extents")
1039
                                .setClassOfItems(ProjectExtent.class).setMandatory(true);
1040

    
1041
                definition.addDynFieldList("documents").setClassOfItems(Document.class)
1042
                                .setMandatory(true);
1043

    
1044
                definition.addDynFieldObject("projectWindowInfo").setClassOfValue(WindowInfo.class).setMandatory(false);
1045

    
1046
                definition.addDynFieldList("documentWindowsInformation").setClassOfItems(WindowInfo.class).setMandatory(false);
1047

    
1048
        }
1049

    
1050
        /**
1051
         * @deprecated use getPreferences().setDefaultSelectionColor()
1052
         */
1053
        public static void setDefaultSelectionColor(Color color) {
1054
                getPreferences().setDefaultSelectionColor(color);
1055
        }
1056

    
1057
        /**
1058
         * @deprecated use getPreferences().getDefaultSelectionColor()
1059
         */
1060

    
1061
        public static Color getDefaultSelectionColor() {
1062
                return getPreferences().getDefaultSelectionColor();
1063
        }
1064

    
1065
        /**
1066
         * @deprecated use getPreferences().getDefaultMapUnits()
1067
         */
1068
        public static int getDefaultMapUnits() {
1069
                return getPreferences().getDefaultMapUnits();
1070
        }
1071

    
1072
        /**
1073
         * @deprecated use getPreferences().getDefaultDistanceUnits()
1074
         */
1075
        public static int getDefaultDistanceUnits() {
1076
                return getPreferences().getDefaultDistanceUnits();
1077
        }
1078

    
1079
        /**
1080
         * @deprecated use getPreferences().getDefaultDistanceArea()
1081
         */
1082
        public static int getDefaultDistanceArea() {
1083
                return getPreferences().getDefaultDistanceArea();
1084
        }
1085

    
1086
        /**
1087
         * @deprecated use getPreferences().setDefaultMapUnits()
1088
         */
1089
        public static void setDefaultMapUnits(int mapUnits) {
1090
                getPreferences().setDefaultMapUnits(mapUnits);
1091
        }
1092

    
1093
        /**
1094
         * @deprecated use getPreferences().setDefaultDistanceUnits()
1095
         */
1096
        public static void setDefaultDistanceUnits(int distanceUnits) {
1097
                getPreferences().setDefaultDistanceUnits(distanceUnits);
1098
        }
1099

    
1100
        /**
1101
         * @deprecated use getPreferences().setDefaultDistanceArea()
1102
         */
1103
        public static void setDefaultDistanceArea(int distanceArea) {
1104
                getPreferences().setDefaultDistanceArea(distanceArea);
1105
        }
1106

    
1107
        /**
1108
         * @deprecated use getPreferences().setDefaultProjection()
1109
         */
1110
        public static void setDefaultProjection(IProjection defaultProjection) {
1111
                getPreferences().setDefaultProjection(defaultProjection);
1112
        }
1113

    
1114
        /**
1115
         * @deprecated use getPreferences().getDefaultProjection()
1116
         */
1117
        public static IProjection getDefaultProjection() {
1118
                return getPreferences().getDefaultProjection();
1119
        }
1120

    
1121
        /**
1122
         * @deprecated see {@link #setSelectionColor(String)}, to be remove in gvSIG
1123
         *             2.1.0
1124
         */
1125
        public void setColor(String color) {
1126
                this.setSelectionColor(StringUtilities.string2Color(color));
1127
        }
1128

    
1129
        /**
1130
         * Return the selection color
1131
         * 
1132
         * @return selection color as string
1133
         * @deprecated use {@link #getSelectionColor()}
1134
         */
1135
        public String getColor() {
1136
                return StringUtilities.color2String(selectionColor);
1137
        }
1138

    
1139
        /**
1140
         * Return the list of views of the project
1141
         * 
1142
         * @return views as ArrayList of ProjectDocument
1143
         * 
1144
         * @deprecated see {@link #getDocumentsByType(String)}
1145
         */
1146
        public List<Document> getViews() {
1147
                return getDocuments(ViewManager.TYPENAME);
1148
        }
1149

    
1150
        /**
1151
         * Add a view to the project
1152
         * 
1153
         * @deprecated see {@link #add(AbstractDocument)}
1154
         */
1155
        public void addView(DefaultViewDocument v) {
1156
                add(v);
1157
        }
1158

    
1159
        /**
1160
         * Remove a view of the project
1161
         * 
1162
         * @param index
1163
         *            of the view as integer
1164
         * 
1165
         * @deprecated see {@link #remove(AbstractDocument)}
1166
         */
1167
        public void delView(int i) {
1168
                List<Document> list = getDocuments(ViewManager.TYPENAME);
1169
                remove(list.get(i));
1170
        }
1171

    
1172
        /**
1173
         * @deprecated see {@link #getDocument(String, String)}
1174
         */
1175
        public Document getProjectDocumentByName(String name, String type) {
1176
                return this.getDocument(name, type);
1177
        }
1178

    
1179
        /**
1180
         * @deprecated see {@link #getDocuments(String)}
1181
         */
1182
        public List<Document> getDocumentsByType(String type) {
1183
                return this.getDocuments(type);
1184
        }
1185

    
1186
        /**
1187
         * @deprecated aun por decidir que API darle al copy/paste
1188
         */
1189
        public String exportToXML(AbstractDocument[] selectedItems)
1190
                        throws SaveException {
1191
                // FIXME jjdc:hay que decirdir que API darle al copy/paste
1192
                throw new UnsupportedOperationException("This method is not supported");
1193
        }
1194

    
1195
        /**
1196
         * @deprecated aun por decidir que API darle al copy/paste
1197
         */
1198
        public void importFromXML(String sourceString, String docType) {
1199
                // FIXME jjdc:hay que decirdir que API darle al copy/paste
1200
                throw new UnsupportedOperationException("This method is not supported");
1201
        }
1202

    
1203
        /**
1204
         * @deprecated aun por decidir que API darle al copy/paste
1205
         */
1206
        public boolean isValidXMLForImport(String sourceString, String docType) {
1207
                // FIXME jjdc:hay que decirdir que API darle al copy/paste
1208
                throw new UnsupportedOperationException("This method is not supported");
1209
        }
1210

    
1211
        public boolean canImportDocuments(String data, String doctype) {
1212
                // TODO Auto-generated method stub
1213
                return false;
1214
        }
1215

    
1216
        public String exportDocumentsAsText(List<Document> documents) {
1217
                // TODO Auto-generated method stub
1218
                return null;
1219
        }
1220

    
1221
        public void importDocuments(String data, String doctype) {
1222
                // TODO Auto-generated method stub
1223

    
1224
        }
1225
        public Document getActiveDocument() {
1226
                return this.getActiveDocument(null);
1227
        }
1228
        
1229
        public Document getActiveDocument(Class<? extends Document> documentClass) {
1230
                ApplicationManager application = ApplicationLocator.getManager();
1231

    
1232
                Document document = null;
1233
                IWindow[] windows = application.getUIManager().getOrderedWindows();
1234
                IWindow window = null;
1235
                for (int i = 0; i < windows.length; i++) {
1236
                    window = windows[i];
1237
                        if (window instanceof SingletonWindow) {
1238
                                // Cogemos no la primera ventana, si no la primera
1239
                                // ventana de tipo documento (SingletonWindow).
1240
                                // Y por si las mosca no es un documento, atrapamos
1241
                                // los errores y continuamos si no puede hacer un cast
1242
                                // del Model a Document
1243
                                try {
1244
                                        document = (Document) ((SingletonWindow) window).getWindowModel();
1245
                                        if( documentClass == null ) {
1246
                                                return document;
1247
                                        }
1248
                                        if( documentClass.isAssignableFrom(document.getClass())) {
1249
                                                return document;
1250
                                        }
1251
                                } catch (ClassCastException e) {
1252
                                        // Do nothing, skip this window
1253
                                }
1254
                        }
1255
                }
1256
                return null;
1257
        } 
1258
        
1259
        public void addObserver(Observer o) {
1260
                observableHelper.addObserver(o);                
1261
        }
1262

    
1263
        public void deleteObserver(Observer o) {
1264
                observableHelper.deleteObserver(o);
1265
        }
1266

    
1267
        public void deleteObservers() {
1268
                observableHelper.deleteObservers();
1269
        }
1270
        
1271
//        private void notifyObservers(ProjectNotifycation notifycation) {
1272
//                observableHelper.notifyObservers(this, notifycation);
1273
//        }
1274

    
1275
        private ProjectNotification notifyObservers(int type) {
1276
                DefaultProjectNotification notifycation =
1277
                    new DefaultProjectNotification(type, null, null);
1278
                try {
1279
                        observableHelper.notifyObservers(this, notifycation);
1280
                } catch (Exception ex) {
1281
                        LOG.info("Can't notify observers", ex);
1282
                }
1283
                return notifycation;
1284
        }
1285
        
1286
        private ProjectNotification notifyObservers(int type, Document document) {
1287
                DefaultProjectNotification notifycation =
1288
                    new DefaultProjectNotification(type, document, null);
1289
                try {
1290
                        observableHelper.notifyObservers(this, notifycation);
1291
                } catch (Exception ex) {
1292
                        LOG.info("Can't notify observers", ex);
1293
                }
1294
                return notifycation;
1295
        }
1296
        
1297
        private ProjectNotification notifyObservers(int type, File file) {
1298
                DefaultProjectNotification notifycation =
1299
                    new DefaultProjectNotification(type, null, file);
1300
                try {
1301
                        observableHelper.notifyObservers(this, notifycation);
1302
                } catch (Exception ex) {
1303
                        LOG.info("Can't notify observers", ex);
1304
                }
1305
                return notifycation;
1306
        }
1307
        
1308
        class DefaultProjectNotification implements ProjectNotification {
1309

    
1310
                private int type;
1311
                private Document document;
1312
                private File file;
1313
                private boolean processCanceled=false;
1314

    
1315
                DefaultProjectNotification(int type, Document document, File file) {
1316
                        this.type = type;
1317
                        this.document = document;
1318
                        this.file = file;
1319
                }
1320
                
1321
                public int getNotificationType() {
1322
                        return type;
1323
                }
1324

    
1325
                public Document getDocument() {
1326
                        return document;
1327
                }
1328

    
1329
                public void cancelProcess() {
1330
                        processCanceled = true;
1331
                }
1332
                
1333
                public boolean isProcessCanceled() {
1334
                        return processCanceled;
1335
                }
1336

    
1337
                public File getFile() {
1338
                        return file;
1339
                }
1340
                
1341
        }
1342

    
1343
}