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 @ 40558

History | View | Annotate | Download (38.8 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
 * AUTHORS (In addition to CIT):
26
 * 2004-2009 IVER TI
27
 *   
28
 */
29

    
30
package org.gvsig.app.project;
31

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

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

    
94
/**
95
 * Clase que representa un proyecto de gvSIG
96
 * 
97
 * @author 2004-2005 Fernando Gonz?lez Cort?s
98
 * @author 2006-2009 Jose Manuel Vivo
99
 * @author 2005- Vicente Caballero
100
 * @author 2009- Joaquin del Cerro
101
 * 
102
 */
103

    
104
public class DefaultProject implements Serializable, PropertyChangeListener,
105
                Project {
106

    
107
        private Logger LOG = LoggerFactory.getLogger(DefaultProject.class);
108
        /**
109
         * @deprecated see ApplicationLocator.getManager().getVersion()
110
         */
111
        public static String VERSION = Version.format();
112

    
113
    private ObservableHelper observableHelper = new ObservableHelper();
114

    
115
        /**
116
         * 
117
         */
118
        private static final long serialVersionUID = -4449622027521773178L;
119

    
120
        private static final Logger logger = LoggerFactory.getLogger(Project.class);
121

    
122
        private static ProjectPreferences preferences = new ProjectPreferences();
123

    
124
        /**
125
         * Index used by the generator of unique names of documents.
126
         */
127
        private Map<String, Integer> nextDocumentIndexByType = new HashMap<String, Integer>();
128

    
129
        private PropertyChangeSupport change;
130

    
131
        private boolean modified = false;
132

    
133
        private String name = null;
134

    
135
        private String creationDate = null;
136

    
137
        private String modificationDate = null;
138

    
139
        private String owner = null;
140

    
141
        private String comments = null;
142

    
143
        private Color selectionColor = null;
144

    
145
        private List<Document> documents = null;
146

    
147
        private List<ProjectExtent> extents = null;
148

    
149
        private IProjection projection;
150

    
151
        /**
152
         * Creates a new Project object.
153
         */
154
        DefaultProject() {
155
                this.change = new PropertyChangeSupport(this);
156
                this.clean();
157
        }
158

    
159
        protected void clean() {
160
                this.owner = "";
161
                this.comments = "";
162
                this.name = PluginServices.getText(this, "untitled");
163
                this.creationDate = DateFormat.getDateInstance().format(new Date());
164
                this.modificationDate = this.creationDate;
165

    
166
                this.documents = new ArrayList<Document>();
167
                this.extents = new ArrayList<ProjectExtent>();
168

    
169
                this.setSelectionColor(getPreferences().getDefaultSelectionColor());
170

    
171
                this.projection = null; // se inicializa en el getProjection()
172
        }
173

    
174
        public static ProjectPreferences getPreferences() {
175
                return preferences;
176
        }
177

    
178
        public void propertyChange(PropertyChangeEvent evt) {
179
                change.firePropertyChange(evt);
180
        }
181

    
182
        public synchronized void addPropertyChangeListener(
183
                        PropertyChangeListener arg0) {
184
                change.addPropertyChangeListener(arg0);
185
        }
186

    
187
        /**
188
         * Return the creation date of the project
189
         * 
190
         * @return
191
         */
192
        public String getCreationDate() {
193
                return creationDate;
194
        }
195

    
196
        protected void setCreationDate(String creationDate) {
197
                this.creationDate = creationDate;
198
                change.firePropertyChange("setCreationDate", null, null);
199
        }
200

    
201
    public Document createDocument(String type) {
202
            logger.info("createDocument('{}')",type);
203
        return ProjectManager.getInstance().createDocument(type);
204
    }
205

    
206
    /**
207
     * Return the name of the project
208
     * 
209
     * @return
210
     */
211
    public String getName() {
212
        return name;
213
    }
214

    
215
        /**
216
         * Set the name of he project.
217
         * 
218
         * @param string
219
         */
220
        public void setName(String name) {
221
                this.name = name;
222
                change.firePropertyChange("setName", null, null);
223
        }
224

    
225
        /**
226
         * Return the comments associateds with the project
227
         * 
228
         * @return comments
229
         */
230
        public String getComments() {
231
                return comments;
232
        }
233

    
234
        /**
235
         * Set the comments associateds with the project
236
         * 
237
         * @param comments
238
         *            as string
239
         */
240
        public void setComments(String string) {
241
                comments = string;
242
                change.firePropertyChange("setComments", null, null);
243
        }
244

    
245
        /**
246
         * Retuen the modification date of the project.
247
         * 
248
         * @return modification date as string
249
         */
250
        public String getModificationDate() {
251
                return modificationDate;
252
        }
253

    
254
        protected void setModificationDate(String string) {
255
                modificationDate = string;
256
                change.firePropertyChange("setModificationDate", null, null);
257
        }
258

    
259
        /**
260
         * Return the author of the project,
261
         * 
262
         * @return author as string
263
         */
264
        public String getOwner() {
265
                return owner;
266
        }
267

    
268
        /**
269
         * Sets the author of the project
270
         * 
271
         * @param author
272
         *            name as string
273
         */
274
        public void setOwner(String owner) {
275
                this.owner = owner;
276
                change.firePropertyChange("setOwner", null, null);
277
        }
278

    
279
        /**
280
         * Obtiene el color de selecci?n que se usar? en el proyecto
281
         * 
282
         * @return
283
         */
284
        public Color getSelectionColor() {
285
                if (selectionColor == null) {
286
                        selectionColor = getPreferences().getDefaultSelectionColor();
287
                }
288
                return selectionColor;
289
        }
290

    
291
        /**
292
         * Sets the selecction color
293
         * 
294
         * @param selection
295
         *            color as string
296
         */
297
        public void setSelectionColor(String selectionColor) {
298
                this.setSelectionColor(StringUtilities.string2Color(selectionColor));
299
        }
300

    
301
        /**
302
         * Sets the selecction color
303
         * 
304
         * @param selection
305
         *            color as Color
306
         */
307
        public void setSelectionColor(Color selectionColor) {
308
                this.selectionColor = selectionColor;
309
                MapContext.setSelectionColor(selectionColor);
310
                change.firePropertyChange("selectionColor", null, selectionColor);
311
        }
312

    
313
        public IProjection getProjection() {
314
                if (projection == null) {
315
                        projection = getPreferences().getDefaultProjection();
316
                }
317
                return projection;
318
        }
319

    
320
        public void setProjection(IProjection projection) {
321
                this.projection = projection;
322
        }
323

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

    
342
        public boolean hasChanged() {
343
                // we return true if the project is not empty (until we have a better
344
                // method...)
345
                if ((this.getDocuments().size() != 0) || modified) {
346
                        return true;
347
                }
348
                return false;
349
        }
350

    
351
        /**
352
         * Return a list of documents in the project.
353
         * 
354
         * @return documents as List of IProjectDocument
355
         */
356
        public List<Document> getDocuments() {
357
                return Collections.unmodifiableList(documents);
358
        }
359

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

    
380
        /**
381
         * Adds a document to the project
382
         * 
383
         * @param document
384
         *            as IProjectDocument
385
         */
386
        public void add(Document document) {
387
        logger.info("add('{}')", document.toString());
388

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

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

    
421
        public Iterator<Document> iterator() {
422
                return documents.iterator();
423
        }
424

    
425
        public boolean isEmpty() {
426
                return documents.isEmpty();
427
        }
428

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

    
450
                throw new RuntimeException(MessageFormat.format(
451
                                "The layer '{1}' is not in a view", layer.getName()));
452
        }
453

    
454
        private boolean isView(FLayers layers, FLayer layer) {
455
                for (int i = 0; i < layers.getLayersCount(); i++) {
456
                        if (layers.getLayer(i) instanceof FLayers) {
457
                                if (isView((FLayers) layers.getLayer(i), layer)) {
458
                                        return true;
459
                                }
460
                        }
461
                        if (layers.getLayer(i) == layer) {
462
                                return true;
463
                        }
464
                }
465
                return false;
466
        }
467

    
468
        public void addExtent(ProjectExtent arg1) {
469
                extents.add(arg1);
470
                change.firePropertyChange("addExtent", null, null);
471
        }
472

    
473
        public ProjectExtent removeExtent(int arg0) {
474
                change.firePropertyChange("delExtent", null, null);
475
                return extents.remove(arg0);
476
        }
477

    
478
        public ProjectExtent[] getExtents() {
479
                return (ProjectExtent[]) extents.toArray(new ProjectExtent[0]);
480
        }
481

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

    
507
        public String getUniqueNameForDocument(String type, String name) {
508
                Document document = getDocument(name, type);
509
                if (document == null) {
510
                        return name;
511
                }
512

    
513
                String newName = null;
514
                int num = this.getNextDocumentIndex(type);
515
                while (document != null) {
516
                        newName = name + " - " + num++;
517
                        document = getDocument(newName, type);
518
                }
519
                this.setNextDocumentIndex(type, num);
520
                return newName;
521
        }
522

    
523
        private int getNextDocumentIndex(String type) {
524
                if (nextDocumentIndexByType.get(type) == null) {
525
                        nextDocumentIndexByType.put(type, new Integer(1));
526
                        return 1;
527
                }
528
                return nextDocumentIndexByType.get(type).intValue();
529
        }
530

    
531
        private void setNextDocumentIndex(String type, int newIndex) {
532
                if (nextDocumentIndexByType.get(type) == null) {
533
                        nextDocumentIndexByType.put(type, new Integer(newIndex));
534
                } else {
535
                        nextDocumentIndexByType.put(type, new Integer(newIndex));
536
                }
537
        }
538

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

    
553
        public void saveState(OutputStream out) throws PersistenceException {
554
                saveState(out, null);
555
        }
556

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

    
579
        private void relativizeFiles(PersistentState state, File rootFolder) {
580
                PersistentContext context = state.getContext();
581

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

    
659
    private boolean differentDrives(File file, File rootFolder) {
660

    
661
        if (rootFolder == null) {
662
            return false;
663
        }
664

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

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

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

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

    
756
                return new File(relative);
757
        }
758

    
759
        private void fixFiles(PersistentState state, File rootFolder) {
760
                PersistentContext context = state.getContext();
761
                URI cwd = new File(System.getProperty("user.dir")).toURI();
762

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

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

    
830
        private File fixFile(File file, File rootFolder) {
831
                if (file.isAbsolute()) {
832
                        return file;
833
                }
834
                File f = new File(rootFolder, file.getPath()).getAbsoluteFile();
835
                String targetPath = FilenameUtils.normalize(f.getPath());
836
                return new File(targetPath);
837
        }
838

    
839
        public void loadState(InputStream in) {
840
                loadState(in, null);
841
        }
842

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

    
863
        }
864

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

    
880
        @SuppressWarnings("unchecked")
881
        public void loadFromState(PersistentState state)
882
                        throws PersistenceException {
883
                this.clean();
884

    
885
                this.setComments(state.getString("comments"));
886
                this.setCreationDate(state.getString("creationDate"));
887
                this.setModificationDate(state.getString("modificationDate"));
888
                this.setName(state.getString("name"));
889
                this.setOwner(state.getString("owner"));
890
                this.setSelectionColor((Color) state.get("selectionColor"));
891
                this.setProjection((IProjection) state.get("projection"));
892

    
893
                List<ProjectExtent> extents = (List<ProjectExtent>) state
894
                                .get("extents");
895
                for (int i = 0; i < extents.size(); i++) {
896
                        this.addExtent(extents.get(i));
897
                }
898

    
899
                List<AbstractDocument> documents = (List<AbstractDocument>) state
900
                                .get("documents");
901
                for (int i = 0; i < documents.size(); i++) {
902
                        this.add(documents.get(i));
903
                }
904

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

    
924
        }
925

    
926
        public void saveToState(PersistentState state) throws PersistenceException {
927
                state.set("version", VERSION);
928
                state.set("comments", getComments());
929
                state.set("creationDate", this.getCreationDate());
930

    
931
                state.set("modificationDate", this.getModificationDate());
932
                state.set("name", this.getName());
933
                state.set("owner", this.getOwner());
934
                state.set("selectionColor", this.getSelectionColor());
935

    
936
                state.set("projection", this.getProjection());
937

    
938
                state.set("extents", this.extents);
939
                state.set("documents", this.getDocuments());
940

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

    
959
        }
960

    
961
        public static class DocumentWindowInfo implements Persistent {
962

    
963
                public static final String PERSISTENCE_DEFINITION_NAME = "DocumentWindowInfo";
964

    
965
                private WindowInfo windowInfo;
966
                private String documentType;
967
                private String documentName;
968

    
969
                public DocumentWindowInfo(){
970
                }
971
                
972
                DocumentWindowInfo(WindowInfo wi, String docType, String docName){
973
                        windowInfo = wi;
974
                        documentType = docType;
975
                        documentName = docName;
976
                }
977

    
978
                public WindowInfo getWindowInfo() {
979
                        return windowInfo;
980
                }
981
                
982
                public String getDocumentType() {
983
                        return documentType;
984
                }
985

    
986
                public String getDocumentName() {
987
                        return documentName;
988
                }
989

    
990
                public void saveToState(PersistentState state)
991
                                throws PersistenceException {
992
                        state.set("windowInfo", this.windowInfo);
993
                        state.set("documentType", this.documentType);
994
                        state.set("documentName", this.documentName);
995
                }
996

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

    
1020
                }
1021
        }
1022
        
1023
        public static void registerPersistent() {
1024
                AbstractDocument.registerPersistent();
1025
                DocumentWindowInfo.registerPersistent();
1026
                ProjectExtent.registerPersistent();
1027

    
1028
                PersistenceManager manager = ToolsLocator.getPersistenceManager();
1029
                DynStruct definition = manager.addDefinition(DefaultProject.class,
1030
                                "Project", "Project Persistence definition", null, null);
1031
                definition.addDynFieldString("version").setMandatory(true);
1032
                definition.addDynFieldString("comments").setMandatory(true);
1033
                definition.addDynFieldString("creationDate").setMandatory(true);
1034
                definition.addDynFieldString("modificationDate").setMandatory(true);
1035
                definition.addDynFieldString("name").setMandatory(true);
1036
                definition.addDynFieldString("owner").setMandatory(true);
1037

    
1038
                definition.addDynFieldObject("selectionColor")
1039
                                .setClassOfValue(Color.class).setMandatory(true);
1040
                definition.addDynFieldObject("projection")
1041
                                .setClassOfValue(IProjection.class).setMandatory(true);
1042

    
1043
                definition.addDynFieldList("extents")
1044
                                .setClassOfItems(ProjectExtent.class).setMandatory(true);
1045

    
1046
                definition.addDynFieldList("documents").setClassOfItems(Document.class)
1047
                                .setMandatory(true);
1048

    
1049
                definition.addDynFieldObject("projectWindowInfo").setClassOfValue(WindowInfo.class).setMandatory(false);
1050

    
1051
                definition.addDynFieldList("documentWindowsInformation").setClassOfItems(WindowInfo.class).setMandatory(false);
1052

    
1053
        }
1054

    
1055
        /**
1056
         * @deprecated use getPreferences().setDefaultSelectionColor()
1057
         */
1058
        public static void setDefaultSelectionColor(Color color) {
1059
                getPreferences().setDefaultSelectionColor(color);
1060
        }
1061

    
1062
        /**
1063
         * @deprecated use getPreferences().getDefaultSelectionColor()
1064
         */
1065

    
1066
        public static Color getDefaultSelectionColor() {
1067
                return getPreferences().getDefaultSelectionColor();
1068
        }
1069

    
1070
        /**
1071
         * @deprecated use getPreferences().getDefaultMapUnits()
1072
         */
1073
        public static int getDefaultMapUnits() {
1074
                return getPreferences().getDefaultMapUnits();
1075
        }
1076

    
1077
        /**
1078
         * @deprecated use getPreferences().getDefaultDistanceUnits()
1079
         */
1080
        public static int getDefaultDistanceUnits() {
1081
                return getPreferences().getDefaultDistanceUnits();
1082
        }
1083

    
1084
        /**
1085
         * @deprecated use getPreferences().getDefaultDistanceArea()
1086
         */
1087
        public static int getDefaultDistanceArea() {
1088
                return getPreferences().getDefaultDistanceArea();
1089
        }
1090

    
1091
        /**
1092
         * @deprecated use getPreferences().setDefaultMapUnits()
1093
         */
1094
        public static void setDefaultMapUnits(int mapUnits) {
1095
                getPreferences().setDefaultMapUnits(mapUnits);
1096
        }
1097

    
1098
        /**
1099
         * @deprecated use getPreferences().setDefaultDistanceUnits()
1100
         */
1101
        public static void setDefaultDistanceUnits(int distanceUnits) {
1102
                getPreferences().setDefaultDistanceUnits(distanceUnits);
1103
        }
1104

    
1105
        /**
1106
         * @deprecated use getPreferences().setDefaultDistanceArea()
1107
         */
1108
        public static void setDefaultDistanceArea(int distanceArea) {
1109
                getPreferences().setDefaultDistanceArea(distanceArea);
1110
        }
1111

    
1112
        /**
1113
         * @deprecated use getPreferences().setDefaultProjection()
1114
         */
1115
        public static void setDefaultProjection(IProjection defaultProjection) {
1116
                getPreferences().setDefaultProjection(defaultProjection);
1117
        }
1118

    
1119
        /**
1120
         * @deprecated use getPreferences().getDefaultProjection()
1121
         */
1122
        public static IProjection getDefaultProjection() {
1123
                return getPreferences().getDefaultProjection();
1124
        }
1125

    
1126
        /**
1127
         * @deprecated see {@link #setSelectionColor(String)}, to be remove in gvSIG
1128
         *             2.1.0
1129
         */
1130
        public void setColor(String color) {
1131
                this.setSelectionColor(StringUtilities.string2Color(color));
1132
        }
1133

    
1134
        /**
1135
         * Return the selection color
1136
         * 
1137
         * @return selection color as string
1138
         * @deprecated use {@link #getSelectionColor()}
1139
         */
1140
        public String getColor() {
1141
                return StringUtilities.color2String(selectionColor);
1142
        }
1143

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

    
1155
        /**
1156
         * Add a view to the project
1157
         * 
1158
         * @deprecated see {@link #add(AbstractDocument)}
1159
         */
1160
        public void addView(DefaultViewDocument v) {
1161
                add(v);
1162
        }
1163

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

    
1177
        /**
1178
         * @deprecated see {@link #getDocument(String, String)}
1179
         */
1180
        public Document getProjectDocumentByName(String name, String type) {
1181
                return this.getDocument(name, type);
1182
        }
1183

    
1184
        /**
1185
         * @deprecated see {@link #getDocuments(String)}
1186
         */
1187
        public List<Document> getDocumentsByType(String type) {
1188
                return this.getDocuments(type);
1189
        }
1190

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

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

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

    
1216
        public boolean canImportDocuments(String data, String doctype) {
1217
                // TODO Auto-generated method stub
1218
                return false;
1219
        }
1220

    
1221
        public String exportDocumentsAsText(List<Document> documents) {
1222
                // TODO Auto-generated method stub
1223
                return null;
1224
        }
1225

    
1226
        public void importDocuments(String data, String doctype) {
1227
                // TODO Auto-generated method stub
1228

    
1229
        }
1230
        public Document getActiveDocument() {
1231
                return this.getActiveDocument(null);
1232
        }
1233
        
1234
        public Document getActiveDocument(Class<? extends Document> documentClass) {
1235
                ApplicationManager application = ApplicationLocator.getManager();
1236

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

    
1268
        public void deleteObserver(Observer o) {
1269
                observableHelper.deleteObserver(o);
1270
        }
1271

    
1272
        public void deleteObservers() {
1273
                observableHelper.deleteObservers();
1274
        }
1275
        
1276
//        private void notifyObservers(ProjectNotifycation notifycation) {
1277
//                observableHelper.notifyObservers(this, notifycation);
1278
//        }
1279

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

    
1315
                private int type;
1316
                private Document document;
1317
                private File file;
1318
                private boolean processCanceled=false;
1319

    
1320
                DefaultProjectNotification(int type, Document document, File file) {
1321
                        this.type = type;
1322
                        this.document = document;
1323
                        this.file = file;
1324
                }
1325
                
1326
                public int getNotificationType() {
1327
                        return type;
1328
                }
1329

    
1330
                public Document getDocument() {
1331
                        return document;
1332
                }
1333

    
1334
                public void cancelProcess() {
1335
                        processCanceled = true;
1336
                }
1337
                
1338
                public boolean isProcessCanceled() {
1339
                        return processCanceled;
1340
                }
1341

    
1342
                public File getFile() {
1343
                        return file;
1344
                }
1345
                
1346
        }
1347

    
1348
}