KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cayenne > project > Project


1 /*****************************************************************
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  ****************************************************************/

19
20 package org.apache.cayenne.project;
21
22 import java.io.File JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.util.ArrayList JavaDoc;
25 import java.util.Collections JavaDoc;
26 import java.util.Iterator JavaDoc;
27 import java.util.List JavaDoc;
28
29 import org.apache.cayenne.conf.ConfigStatus;
30 import org.apache.cayenne.conf.Configuration;
31 import org.apache.cayenne.project.validator.Validator;
32
33 /**
34  * Describes a model of Cayenne project. Project is a set of files in the filesystem
35  * describing storing Cayenne DataMaps, DataNodes and other information.
36  * <p>
37  * Project has a project directory, which is a canonical directory. All project files are
38  * relative to the project directory.
39  * </p>
40  *
41  * @author Andrus Adamchik
42  */

43 public abstract class Project {
44
45     public static final String JavaDoc CURRENT_PROJECT_VERSION = "2.0";
46     static final int UPGRADE_STATUS_OLD = -1;
47     static final int UPGRADE_STATUS_CURRENT = 0;
48     static final int UPGRADE_STATUS_NEW = 1;
49
50     protected File JavaDoc projectDir;
51     protected List JavaDoc files = new ArrayList JavaDoc();
52     protected int upgradeStatus;
53     protected List JavaDoc upgradeMessages;
54     protected boolean modified;
55
56     /**
57      * Factory method to create the right project type given project file.
58      */

59     public static Project createProject(File JavaDoc projectFile) {
60         String JavaDoc fileName = projectFile.getName();
61
62         if (fileName.endsWith(Configuration.DEFAULT_DOMAIN_FILE)) {
63             return new ApplicationProject(projectFile);
64         }
65         else if (fileName.endsWith(DataMapFile.LOCATION_SUFFIX)) {
66             return new DataMapProject(projectFile);
67         }
68         else {
69             throw new ProjectException("Unsupported project file: " + projectFile);
70         }
71     }
72
73     /**
74      * @since 1.2
75      */

76     protected Project() {
77
78     }
79
80     /**
81      * Constructor for Project. <code>projectFile</code> must denote a file (existent or
82      * non-existent) in an existing directory. If projectFile has no parent directory,
83      * current directory is assumed.
84      */

85     public Project(File JavaDoc projectFile) {
86         initialize(projectFile);
87         postInitialize(projectFile);
88     }
89
90     /**
91      * @since 1.2
92      */

93     protected void initialize(File JavaDoc projectFile) {
94         if (projectFile != null) {
95             File JavaDoc parent = projectFile.getParentFile();
96             if (parent == null) {
97                 parent = new File JavaDoc(System.getProperty("user.dir"));
98             }
99
100             if (!parent.isDirectory()) {
101                 throw new ProjectException(
102                         "Project directory does not exist or is not a directory: "
103                                 + parent);
104             }
105
106             try {
107                 projectDir = parent.getCanonicalFile();
108             }
109             catch (IOException JavaDoc e) {
110                 throw new ProjectException("Error creating project.", e);
111             }
112         }
113     }
114
115     /**
116      * Finished project initialization. Called from constructor. Default implementation
117      * builds a file list and checks for upgrades.
118      */

119     protected void postInitialize(File JavaDoc projectFile) {
120         // take a snapshot of files used by the project
121
files = Collections.synchronizedList(buildFileList());
122         upgradeMessages = Collections.synchronizedList(new ArrayList JavaDoc());
123         checkForUpgrades();
124     }
125
126     /**
127      * Returns true if project location is not defined. For instance, when project was
128      * created in memory and is not tied to a file yet.
129      */

130     public boolean isLocationUndefined() {
131         return getMainFile() == null;
132     }
133
134     /**
135      * Returns project upgrade status. "0" means project version matches the framework
136      * version, "-1" means project is older than the framework, "+1" means the framework
137      * is older than the project.
138      *
139      * @since 2.0
140      */

141     public int getUpgradeStatus() {
142         return upgradeStatus;
143     }
144
145     /**
146      * Returns a list of upgrade messages.
147      */

148     public List JavaDoc getUpgradeMessages() {
149         return upgradeMessages;
150     }
151
152     /**
153      * Returns true is project has renamed files. This is useful when converting from
154      * older versions of the modeler projects.
155      */

156     public boolean hasRenamedFiles() {
157         if (files == null) {
158             return false;
159         }
160
161         synchronized (files) {
162             Iterator JavaDoc it = files.iterator();
163             while (it.hasNext()) {
164                 if (((ProjectFile) it.next()).isRenamed()) {
165                     return true;
166                 }
167             }
168         }
169
170         return false;
171     }
172
173     /**
174      * Creates a list of project files.
175      */

176     public List JavaDoc buildFileList() {
177         List JavaDoc projectFiles = new ArrayList JavaDoc();
178
179         Iterator JavaDoc nodes = treeNodes();
180         while (nodes.hasNext()) {
181             ProjectPath nodePath = (ProjectPath) nodes.next();
182             Object JavaDoc obj = nodePath.getObject();
183             ProjectFile f = projectFileForObject(obj);
184
185             if (f != null) {
186                 projectFiles.add(f);
187             }
188         }
189
190         return projectFiles;
191     }
192
193     /**
194      * Creates an instance of Validator for validating this project.
195      */

196     public Validator getValidator() {
197         return new Validator(this);
198     }
199
200     /**
201      * Looks up and returns a file wrapper for a project object. Returns null if no file
202      * exists.
203      */

204     public ProjectFile findFile(Object JavaDoc obj) {
205         if (obj == null) {
206             return null;
207         }
208
209         // to avoid full scan, a map may be a better
210
// choice of collection here,
211
// though normally projects have very few files...
212
synchronized (files) {
213             Iterator JavaDoc it = files.iterator();
214             while (it.hasNext()) {
215                 ProjectFile file = (ProjectFile) it.next();
216                 if (file.getObject() == obj) {
217                     return file;
218                 }
219             }
220         }
221
222         return null;
223     }
224
225     /**
226      * Returns a canonical file built from symbolic name.
227      */

228     public File JavaDoc resolveFile(String JavaDoc symbolicName) {
229         try {
230             // substitute to Windows backslashes if needed
231
if (File.separatorChar != '/') {
232                 symbolicName = symbolicName.replace('/', File.separatorChar);
233             }
234             return new File JavaDoc(projectDir, symbolicName).getCanonicalFile();
235         }
236         catch (IOException JavaDoc e) {
237             // error converting path
238
return null;
239         }
240     }
241
242     /**
243      * Returns a "symbolic" name of a file. Returns null if file is invalid. Symbolic name
244      * is a string path of a file relative to the project directory. It is built in a
245      * platform independent fashion.
246      */

247     public String JavaDoc resolveSymbolicName(File JavaDoc file) {
248         String JavaDoc symbolicName = null;
249         try {
250             // accept absolute files only when
251
// they are in the project directory
252
String JavaDoc otherPath = file.getCanonicalFile().getPath();
253             String JavaDoc thisPath = projectDir.getPath();
254
255             // invalid absolute pathname, can't continue
256
if (otherPath.length() + 1 <= thisPath.length()
257                     || !otherPath.startsWith(thisPath)) {
258                 return null;
259             }
260
261             symbolicName = otherPath.substring(thisPath.length() + 1);
262
263             // substitute Windows backslashes if needed
264
if ((symbolicName != null) && (File.separatorChar != '/')) {
265                 symbolicName = symbolicName.replace(File.separatorChar, '/');
266             }
267
268             return symbolicName;
269
270         }
271         catch (IOException JavaDoc e) {
272             // error converting path
273
return null;
274         }
275     }
276
277     /**
278      * Returns project directory. This is a directory where project file is located.
279      */

280     public File JavaDoc getProjectDirectory() {
281         return projectDir;
282     }
283
284     public void setProjectDirectory(File JavaDoc dir) {
285         this.projectDir = dir;
286     }
287
288     /**
289      * Returns a canonical form of a main file associated with this project.
290      */

291     public File JavaDoc getMainFile() {
292         if (projectDir == null) {
293             return null;
294         }
295
296         ProjectFile f = projectFileForObject(this);
297         return (f != null) ? resolveFile(f.getLocation()) : null;
298     }
299
300     /**
301      * @return An object describing failures in the loaded project.
302      */

303     public abstract ConfigStatus getLoadStatus();
304
305     public abstract ProjectFile projectFileForObject(Object JavaDoc obj);
306
307     /**
308      * Returns a list of first-level children of the project.
309      */

310     public abstract List JavaDoc getChildren();
311
312     /**
313      * Determines whether the project needs to be upgraded. Populates internal list of
314      * upgrade messages with discovered information.
315      */

316     public abstract void checkForUpgrades();
317
318     /**
319      * Returns an Iterator over project tree of objects.
320      */

321     public Iterator JavaDoc treeNodes() {
322         return FlatProjectView.getInstance().flattenProjectTree(this).iterator();
323     }
324
325     /**
326      * @since 1.1
327      */

328     public abstract void upgrade() throws ProjectException;
329
330     /**
331      * Saves project. All currently existing files are updated, without checking for
332      * modifications. New files are created as needed, unused files are deleted.
333      */

334     public void save() throws ProjectException {
335
336         // sanity check
337
if (isLocationUndefined()) {
338             throw new ProjectException("Project location is undefined.");
339         }
340
341         // 1. Traverse project tree to find file wrappers that require update.
342
List JavaDoc filesToSave = new ArrayList JavaDoc();
343         List JavaDoc wrappedObjects = new ArrayList JavaDoc();
344         prepareSave(filesToSave, wrappedObjects);
345
346         // 2. Try saving individual file wrappers
347
processSave(filesToSave);
348
349         // 3. Commit changes
350
List JavaDoc savedFiles = new ArrayList JavaDoc();
351         Iterator JavaDoc saved = filesToSave.iterator();
352         while (saved.hasNext()) {
353             ProjectFile f = (ProjectFile) saved.next();
354             savedFiles.add(f.saveCommit());
355         }
356
357         // 4. Take care of deleted
358
processDelete(wrappedObjects, savedFiles);
359
360         // 5. Refresh file list
361
List JavaDoc freshList = buildFileList();
362         Iterator JavaDoc it = freshList.iterator();
363         while (it.hasNext()) {
364             ((ProjectFile) it.next()).synchronizeLocation();
365         }
366
367         files = freshList;
368
369         synchronized (upgradeMessages) {
370             upgradeMessages.clear();
371         }
372
373         // update state
374
setModified(false);
375     }
376
377     protected void prepareSave(List JavaDoc filesToSave, List JavaDoc wrappedObjects)
378             throws ProjectException {
379         Iterator JavaDoc nodes = treeNodes();
380         while (nodes.hasNext()) {
381             ProjectPath nodePath = (ProjectPath) nodes.next();
382             Object JavaDoc obj = nodePath.getObject();
383
384             ProjectFile existingFile = findFile(obj);
385
386             if (existingFile == null) {
387                 // check if project node can have a file
388
ProjectFile newFile = projectFileForObject(obj);
389                 if (newFile != null) {
390                     filesToSave.add(newFile);
391                 }
392             }
393             else if (existingFile.canHandleObject()) {
394                 wrappedObjects.add(existingFile.getObject());
395                 filesToSave.add(existingFile);
396             }
397         }
398
399     }
400
401     /**
402      * Saves a list of modified files to temporary files.
403      */

404     protected void processSave(List JavaDoc modifiedFiles) throws ProjectException {
405         // notify that files will be saved
406
Iterator JavaDoc willSave = modifiedFiles.iterator();
407         while (willSave.hasNext()) {
408             ProjectFile f = (ProjectFile) willSave.next();
409             f.willSave();
410         }
411
412         try {
413             Iterator JavaDoc modified = modifiedFiles.iterator();
414             while (modified.hasNext()) {
415                 ProjectFile f = (ProjectFile) modified.next();
416                 f.saveTemp();
417             }
418         }
419         catch (Exception JavaDoc ex) {
420
421             // revert
422
Iterator JavaDoc modified = modifiedFiles.iterator();
423             while (modified.hasNext()) {
424                 ProjectFile f = (ProjectFile) modified.next();
425                 f.saveUndo();
426             }
427
428             throw new ProjectException("Project save failed and was canceled.", ex);
429         }
430     }
431
432     protected void processDelete(List JavaDoc existingObjects, List JavaDoc savedFiles) {
433
434         // check for deleted
435
synchronized (files) {
436             Iterator JavaDoc oldFiles = files.iterator();
437             while (oldFiles.hasNext()) {
438                 ProjectFile f = (ProjectFile) oldFiles.next();
439                 File JavaDoc file = f.resolveOldFile();
440
441                 // this check is needed, since a file can reuse the name
442
// of a recently deleted file, and we don't want to delete
443
// new file by mistake
444
if (file == null || savedFiles.contains(file)) {
445                     continue;
446                 }
447
448                 boolean delete = false;
449                 if (f.isRenamed()) {
450                     delete = true;
451                 }
452                 else if (f.getObject() == null) {
453                     delete = true;
454                 }
455                 else if (!existingObjects.contains(f.getObject())) {
456                     delete = true;
457                 }
458                 else if (!f.canHandleObject()) {
459                     // this happens too - node can start using JNDI for instance
460
delete = true;
461                 }
462
463                 if (delete) {
464                     deleteFile(file);
465                 }
466             }
467         }
468     }
469
470     protected boolean deleteFile(File JavaDoc f) {
471         return (f.exists()) ? f.delete() : true;
472     }
473
474     /**
475      * Returns <code>true</code> if the project is modified.
476      */

477     public boolean isModified() {
478         return modified;
479     }
480
481     /**
482      * Updates "modified" state of the project.
483      */

484     public void setModified(boolean modified) {
485         this.modified = modified;
486     }
487 }
488
Popular Tags