whitespace fixups
[jSite2.git] / src / net / pterodactylus / jsite / core / Project.java
1 /*
2  * jSite2 - Project.java -
3  * Copyright © 2008 David Roden
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 package net.pterodactylus.jsite.core;
21
22 import java.beans.PropertyChangeListener;
23 import java.io.File;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Map;
29
30 import net.pterodactylus.jsite.util.IdGenerator;
31 import net.pterodactylus.util.beans.AbstractBean;
32 import net.pterodactylus.util.number.Hex;
33
34 /**
35  * Container for project information. A Project is capable of notifying
36  * {@link PropertyChangeListener}s if any of the contained properties change.
37  *
38  * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
39  */
40 public class Project extends AbstractBean {
41
42         /** Name of the “name” property. */
43         public static final String PROPERTY_NAME = "name";
44
45         /** Name of the “description” property. */
46         public static final String PROPERTY_DESCRIPTION = "description";
47
48         /** Name of the “public key” property. */
49         public static final String PROPERTY_PUBLIC_KEY = "publicKey";
50
51         /** Name of the “private key” property. */
52         public static final String PROPERTY_PRIVATE_KEY = "privateKey";
53
54         /** Name of the “base path” property. */
55         public static final String PROPERTY_BASE_PATH = "basePath";
56
57         /** Name of the “default file” property. */
58         public static final String PROPERTY_DEFAULT_FILE = "defaultFile";
59
60         /** Name of the “node” property. */
61         public static final String PROPERTY_NODE = "node";
62
63         /** Internal ID. */
64         private String id;
65
66         /** The name of the project. */
67         private String name;
68
69         /** The description of the project. */
70         private String description;
71
72         /** The public key. */
73         private String publicKey;
74
75         /** The private key. */
76         private String privateKey;
77
78         /** The base path of the project. */
79         private String basePath;
80
81         /** The default file. */
82         private String defaultFile;
83
84         /** The overrides. */
85         private final Map<String, FileOverride> fileOverrides = new HashMap<String, FileOverride>();
86
87         /** The default node to insert to. */
88         private Node node;
89
90         /** The current root project file. */
91         private ProjectFileImpl rootProjectFile;
92
93         /**
94          * Creates a new project.
95          */
96         public Project() {
97                 id = Hex.toHex(IdGenerator.generateId());
98         }
99
100         /**
101          * Clones the given project.
102          *
103          * @param project
104          */
105         Project(Project project) {
106                 this();
107                 this.name = project.name;
108                 this.description = project.description;
109                 this.publicKey = project.publicKey;
110                 this.privateKey = project.privateKey;
111                 this.basePath = project.basePath;
112         }
113
114         /**
115          * Returns the internal ID.
116          *
117          * @return The internal ID
118          */
119         String getId() {
120                 return id;
121         }
122
123         /**
124          * Sets the internal ID.
125          *
126          * @param id
127          *            The internal ID
128          */
129         void setId(String id) {
130                 if (id == null) {
131                         this.id = Hex.toHex(IdGenerator.generateId());
132                 } else {
133                         this.id = id;
134                 }
135         }
136
137         /**
138          * Returns the name of the project.
139          *
140          * @return The name of the project
141          */
142         public String getName() {
143                 return name;
144         }
145
146         /**
147          * Sets the name of the project.
148          *
149          * @param name
150          *            The name of the project
151          */
152         public void setName(String name) {
153                 String oldName = this.name;
154                 this.name = name;
155                 fireIfPropertyChanged(PROPERTY_NAME, oldName, name);
156         }
157
158         /**
159          * Returns the description of the project.
160          *
161          * @return The description of the project
162          */
163         public String getDescription() {
164                 return description;
165         }
166
167         /**
168          * Sets the description of the project
169          *
170          * @param description
171          *            The description of the project
172          */
173         public void setDescription(String description) {
174                 String oldDescription = this.description;
175                 this.description = description;
176                 fireIfPropertyChanged(PROPERTY_DESCRIPTION, oldDescription, description);
177         }
178
179         /**
180          * Returns the public key of the project.
181          *
182          * @return The public key of the project
183          */
184         public String getPublicKey() {
185                 return publicKey;
186         }
187
188         /**
189          * Sets the public key of the project.
190          *
191          * @param publicKey
192          *            The public key of the project
193          */
194         void setPublicKey(String publicKey) {
195                 String oldPublicKey = this.publicKey;
196                 this.publicKey = publicKey;
197                 fireIfPropertyChanged(PROPERTY_PUBLIC_KEY, oldPublicKey, publicKey);
198         }
199
200         /**
201          * Returns the private key of the project.
202          *
203          * @return The private key of the project
204          */
205         public String getPrivateKey() {
206                 return privateKey;
207         }
208
209         /**
210          * Sets the private key of the project.
211          *
212          * @param privateKey
213          *            The private key of the project
214          */
215         void setPrivateKey(String privateKey) {
216                 String oldPrivateKey = this.privateKey;
217                 this.privateKey = privateKey;
218                 fireIfPropertyChanged(PROPERTY_PRIVATE_KEY, oldPrivateKey, privateKey);
219         }
220
221         /**
222          * Returns the base path of the project.
223          *
224          * @return The base path of the project
225          */
226         public String getBasePath() {
227                 return basePath;
228         }
229
230         /**
231          * Sets the base path of the project.
232          *
233          * @param basePath
234          *            The base path of the project
235          */
236         public void setBasePath(String basePath) {
237                 String oldBasePath = this.basePath;
238                 this.basePath = basePath;
239                 fireIfPropertyChanged(PROPERTY_BASE_PATH, oldBasePath, basePath);
240         }
241
242         /**
243          * Returns the default file.
244          *
245          * @return The default file
246          */
247         public String getDefaultFile() {
248                 return defaultFile;
249         }
250
251         /**
252          * Sets the default file.
253          *
254          * @param defaultFile
255          *            The default file
256          */
257         public void setDefaultFile(String defaultFile) {
258                 String oldDefaultFile = this.defaultFile;
259                 this.defaultFile = defaultFile;
260                 fireIfPropertyChanged(PROPERTY_DEFAULT_FILE, oldDefaultFile, defaultFile);
261         }
262
263         /**
264          * Adds a file override for the given file.
265          *
266          * @param projectFile
267          *            The file
268          * @param override
269          *            The override for the file
270          */
271         public void addFileOverride(ProjectFile projectFile, FileOverride override) {
272                 addFileOverride(projectFile.getCompletePath(), override);
273         }
274
275         /**
276          * Adds a file override for the given file.
277          *
278          * @param filePath
279          *            The file path
280          * @param override
281          *            The override for the file
282          */
283         public void addFileOverride(String filePath, FileOverride override) {
284                 fileOverrides.put(filePath, override);
285         }
286
287         /**
288          * Removes the file override for the given file.
289          *
290          * @param projectFile
291          *            The file for which to remove the override
292          */
293         public void removeFileOverride(ProjectFile projectFile) {
294                 removeFileOverride(projectFile.getCompletePath());
295         }
296
297         /**
298          * Removes the file override for the given file.
299          *
300          * @param filePath
301          *            The file path for which to remove the override
302          */
303         public void removeFileOverride(String filePath) {
304                 fileOverrides.remove(filePath);
305         }
306
307         /**
308          * Returns the file override for the given file.
309          *
310          * @param projectFile
311          *            The file for which to get the override
312          * @return The file override, or <code>null</code> if the given file does
313          *         not have an override
314          */
315         public FileOverride getFileOverride(ProjectFile projectFile) {
316                 return getFileOverride(projectFile.getCompletePath());
317         }
318
319         /**
320          * Returns the file override for the given file.
321          *
322          * @param filePath
323          *            The file path for which to get the override
324          * @return The file override, or <code>null</code> if the given file does
325          *         not have an override
326          */
327         public FileOverride getFileOverride(String filePath) {
328                 return fileOverrides.get(filePath);
329         }
330
331         /**
332          * Returns the list of {@link FileOverride}s.
333          *
334          * @return All file overrides
335          */
336         public Map<String, FileOverride> getFileOverrides() {
337                 return fileOverrides;
338         }
339
340         /**
341          * Scans the base path for files and returns the {@link ProjectFile} for the
342          * base path. From this file it is possible to reach all files in the base
343          * path. This method is disk-intensive and may take some time on larger
344          * directories!
345          *
346          * @return The file for the base path, or <code>null</code> if the base
347          *         path does not denote an existing directory
348          */
349         public ProjectFile getBaseFile() {
350                 File basePathFile = new File(basePath);
351                 if (!basePathFile.exists() || !basePathFile.isDirectory()) {
352                         return null;
353                 }
354                 rootProjectFile = new ProjectFileImpl(null, "", 0, true, false);
355                 scanDirectory(basePathFile, rootProjectFile);
356                 return rootProjectFile;
357         }
358
359         /**
360          * Returns the file that is specified by its complete path.
361          *
362          * @param completePath
363          *            The complete path of the file
364          * @return The project file at the given path, or <code>null</code> if
365          *         there is no such file
366          */
367         public ProjectFile getFile(String completePath) {
368                 if (rootProjectFile == null) {
369                         getBaseFile();
370                 }
371                 if ((rootProjectFile == null) || (completePath.length() == 0)) {
372                         return rootProjectFile;
373                 }
374                 String[] pathParts = completePath.split("\\" + File.separator);
375                 ProjectFileImpl currentProjectFile = rootProjectFile;
376                 for (String pathPart : pathParts) {
377                         currentProjectFile = currentProjectFile.getFile(pathPart);
378                         if (currentProjectFile == null) {
379                                 return null;
380                         }
381                 }
382                 return currentProjectFile;
383         }
384
385         /**
386          * Returns the default node to insert this project to.
387          *
388          * @return The node to insert this project to
389          */
390         public Node getNode() {
391                 return node;
392         }
393
394         /**
395          * Sets the default node to insert this project to.
396          *
397          * @param node
398          *            The node to insert this project to
399          */
400         public void setNode(Node node) {
401                 Node oldNode = this.node;
402                 this.node = node;
403                 fireIfPropertyChanged(PROPERTY_NODE, oldNode, node);
404         }
405
406         /**
407          * @see java.lang.Object#toString()
408          */
409         @Override
410         public String toString() {
411                 return getClass().getName() + "[id=" + id + ",name=" + name + ",description=" + description + ",publicKey=" + publicKey + ",privateKey=" + privateKey + ",basePath=" + basePath + ",defaultFile=" + defaultFile + ",node=" + node + "]";
412         }
413
414         //
415         // PRIVATE METHODS
416         //
417
418         /**
419          * Scans the given directory and recreates the file and directory structure
420          * in the given project file.
421          *
422          * @param directory
423          *            The directory to scan
424          * @param projectFile
425          *            The project file in which to recreate the directory and file
426          *            structure
427          */
428         private void scanDirectory(File directory, ProjectFileImpl projectFile) {
429                 if (!directory.isDirectory()) {
430                         return;
431                 }
432                 for (File file : directory.listFiles()) {
433                         ProjectFileImpl projectFileChild = projectFile.addFile(file.getName(), file.length(), file.isDirectory(), file.isHidden());
434                         if (file.isDirectory()) {
435                                 scanDirectory(file, projectFileChild);
436                         }
437                 }
438                 projectFile.sort();
439         }
440
441         /**
442          * Implementation of a {@link ProjectFile}.
443          *
444          * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
445          */
446         private static class ProjectFileImpl implements ProjectFile, Comparable<ProjectFileImpl> {
447
448                 /** The parent of this project file. */
449                 private final ProjectFileImpl parentProjectFile;
450
451                 /** The name of this project file. */
452                 private final String name;
453
454                 /** The size of the file. */
455                 private final long size;
456
457                 /** Whether this project file is a directory. */
458                 private final boolean directory;
459
460                 /** Whether this file is hidden. */
461                 private final boolean hidden;
462
463                 /** This project file’s children. */
464                 private List<ProjectFileImpl> childProjectFiles = new ArrayList<ProjectFileImpl>();
465
466                 /**
467                  * Creates a new project fie.
468                  *
469                  * @param parentProjectFile
470                  *            The parent of the project file, or <code>null</code> if
471                  *            the new project file does not have a parent
472                  * @param name
473                  *            The name of the project file
474                  * @param size
475                  *            The size of the file
476                  * @param isDirectory
477                  *            <code>true</code> if this project file is a directory,
478                  *            <code>false</code> otherwise
479                  * @param isHidden
480                  *            <code>true</code> if this project file is hidden,
481                  *            <code>false</code> otherwise
482                  */
483                 ProjectFileImpl(ProjectFileImpl parentProjectFile, String name, long size, boolean isDirectory, boolean isHidden) {
484                         this.parentProjectFile = parentProjectFile;
485                         this.name = name;
486                         this.size = size;
487                         this.directory = isDirectory;
488                         this.hidden = isHidden;
489                 }
490
491                 //
492                 // INTERFACE ProjectFile
493                 //
494
495                 /**
496                  * @see net.pterodactylus.jsite.core.ProjectFile#getName()
497                  */
498                 public String getName() {
499                         return name;
500                 }
501
502                 /**
503                  * @see net.pterodactylus.jsite.core.ProjectFile#getParent()
504                  */
505                 public ProjectFile getParent() {
506                         return parentProjectFile;
507                 }
508
509                 /**
510                  * {@inheritDoc}
511                  */
512                 public long getSize() {
513                         return size;
514                 }
515
516                 /**
517                  * @see net.pterodactylus.jsite.core.ProjectFile#getParents()
518                  */
519                 public List<ProjectFile> getParents() {
520                         List<ProjectFile> parentProjectFiles = new ArrayList<ProjectFile>();
521                         ProjectFileImpl currentProjectFile = this;
522                         do {
523                                 parentProjectFiles.add(0, currentProjectFile);
524                         } while ((currentProjectFile = currentProjectFile.parentProjectFile) != null);
525                         return parentProjectFiles;
526                 }
527
528                 /**
529                  * {@inheritDoc}
530                  */
531                 /* TODO - caching? */
532                 public String getCompletePath() {
533                         StringBuilder completePath = new StringBuilder();
534                         ProjectFileImpl currentProjectFile = this;
535                         while ((currentProjectFile != null) && (currentProjectFile.parentProjectFile != null)) {
536                                 completePath.insert(0, currentProjectFile.getName()).insert(0, File.separatorChar);
537                                 currentProjectFile = currentProjectFile.parentProjectFile;
538                         }
539                         return (completePath.length() > 0) ? completePath.substring(1) : "";
540                 }
541
542                 /**
543                  * @see net.pterodactylus.jsite.core.ProjectFile#isFile()
544                  */
545                 public boolean isFile() {
546                         return !directory;
547                 }
548
549                 /**
550                  * @see net.pterodactylus.jsite.core.ProjectFile#isDirectory()
551                  */
552                 public boolean isDirectory() {
553                         return directory;
554                 }
555
556                 /**
557                  * @see net.pterodactylus.jsite.core.ProjectFile#isHidden()
558                  */
559                 public boolean isHidden() {
560                         return hidden;
561                 }
562
563                 /**
564                  * Returns the project file with the given name. The project file has to
565                  * be a direct child of this project file.
566                  *
567                  * @param name
568                  *            The name of the file to get
569                  * @return The project file, or <code>null</code> if there is no
570                  *         project file by that name
571                  */
572                 public ProjectFileImpl getFile(String name) {
573                         if (!isDirectory()) {
574                                 return null;
575                         }
576                         for (ProjectFileImpl projectFile : childProjectFiles) {
577                                 if (projectFile.getName().equals(name)) {
578                                         return projectFile;
579                                 }
580                         }
581                         return null;
582                 }
583
584                 /**
585                  * @see net.pterodactylus.jsite.core.ProjectFile#getFiles()
586                  */
587                 public List<ProjectFile> getFiles() {
588                         List<ProjectFile> projectFiles = new ArrayList<ProjectFile>(childProjectFiles);
589                         return projectFiles;
590                 }
591
592                 //
593                 // ACTIONS
594                 //
595
596                 /**
597                  * Adds a new project file as child to this project file.
598                  *
599                  * @param name
600                  *            The name of the file
601                  * @param size
602                  *            The size of the file
603                  * @param isDirectory
604                  *            <code>true</code> if the new file is a directory,
605                  *            <code>false</code> otherwise
606                  * @param isHidden
607                  *            <code>true</code> if the new file is hidden,
608                  *            <code>false</code> otherwise
609                  * @return The created project file
610                  */
611                 public ProjectFileImpl addFile(String name, long size, boolean isDirectory, boolean isHidden) {
612                         ProjectFileImpl newProjectFile = new ProjectFileImpl(this, name, size, isDirectory, isHidden);
613                         childProjectFiles.add(newProjectFile);
614                         return newProjectFile;
615                 }
616
617                 /**
618                  * Sorts the children of this file.
619                  */
620                 public void sort() {
621                         Collections.sort(childProjectFiles);
622                 }
623
624                 //
625                 // INTERFACE Comparable
626                 //
627
628                 /**
629                  * {@inheritDoc}
630                  */
631                 public int compareTo(ProjectFileImpl otherProjectFileImpl) {
632                         return name.compareTo(otherProjectFileImpl.name);
633                 }
634
635         }
636
637 }