KickJava   Java API By Example, From Geeks To Geeks.

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


1 /* ====================================================================
2  *
3  * The ObjectStyle Group Software License, version 1.1
4  * ObjectStyle Group - http://objectstyle.org/
5  *
6  * Copyright (c) 2002-2005, Andrei (Andrus) Adamchik and individual authors
7  * of the software. All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  * notice, this list of conditions and the following disclaimer in
18  * the documentation and/or other materials provided with the
19  * distribution.
20  *
21  * 3. The end-user documentation included with the redistribution, if any,
22  * must include the following acknowlegement:
23  * "This product includes software developed by independent contributors
24  * and hosted on ObjectStyle Group web site (http://objectstyle.org/)."
25  * Alternately, this acknowlegement may appear in the software itself,
26  * if and wherever such third-party acknowlegements normally appear.
27  *
28  * 4. The names "ObjectStyle Group" and "Cayenne" must not be used to endorse
29  * or promote products derived from this software without prior written
30  * permission. For written permission, email
31  * "andrus at objectstyle dot org".
32  *
33  * 5. Products derived from this software may not be called "ObjectStyle"
34  * or "Cayenne", nor may "ObjectStyle" or "Cayenne" appear in their
35  * names without prior written permission.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40  * DISCLAIMED. IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
41  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48  * SUCH DAMAGE.
49  * ====================================================================
50  *
51  * This software consists of voluntary contributions made by many
52  * individuals and hosted on ObjectStyle Group web site. For more
53  * information on the ObjectStyle Group, please see
54  * <http://objectstyle.org/>.
55  */

56 package org.objectstyle.cayenne.project;
57
58 import java.io.File JavaDoc;
59 import java.io.IOException JavaDoc;
60 import java.util.ArrayList JavaDoc;
61 import java.util.Collections JavaDoc;
62 import java.util.Iterator JavaDoc;
63 import java.util.List JavaDoc;
64
65 import org.apache.log4j.Logger;
66 import org.objectstyle.cayenne.conf.ConfigStatus;
67 import org.objectstyle.cayenne.conf.Configuration;
68 import org.objectstyle.cayenne.project.validator.Validator;
69
70 /**
71  * Describes a model of Cayenne project. Project is a set of
72  * files in the filesystem describing storing Cayenne DataMaps,
73  * DataNodes and other information.
74  *
75  * <p>Project has a project directory, which is a canonical directory.
76  * All project files are relative to the project directory.
77  * </p>
78  *
79  * @author Andrei Adamchik
80  */

81 public abstract class Project {
82     private static final Logger logObj = Logger.getLogger(Project.class);
83
84     public static final String JavaDoc CURRENT_PROJECT_VERSION = "1.1";
85
86     protected File JavaDoc projectDir;
87     protected List JavaDoc files = new ArrayList JavaDoc();
88     protected List JavaDoc upgradeMessages;
89     protected boolean modified;
90
91     /**
92      * Factory method to create the right project type given project file.
93      */

94     public static Project createProject(File JavaDoc projectFile) {
95         logObj.debug("createProject: " + projectFile);
96         String JavaDoc fileName = projectFile.getName();
97
98         if (fileName.endsWith(Configuration.DEFAULT_DOMAIN_FILE)) {
99             return new ApplicationProject(projectFile);
100         } else if (fileName.endsWith(DataMapFile.LOCATION_SUFFIX)) {
101             return new DataMapProject(projectFile);
102         } else {
103             throw new ProjectException(
104                 "Unsupported project file: " + projectFile);
105         }
106     }
107
108     /**
109      * Constructor for Project. <code>projectFile</code> must denote
110      * a file (existent or non-existent) in an existing directory.
111      * If projectFile has no parent directory, current directory is assumed.
112      */

113     public Project(File JavaDoc projectFile) {
114
115         if (projectFile != null) {
116             File JavaDoc parent = projectFile.getParentFile();
117             if (parent == null) {
118                 parent = new File JavaDoc(System.getProperty("user.dir"));
119             }
120
121             if (!parent.isDirectory()) {
122                 throw new ProjectException(
123                     "Project directory does not exist or is not a directory: "
124                         + parent);
125             }
126
127             try {
128                 projectDir = parent.getCanonicalFile();
129             } catch (IOException JavaDoc e) {
130                 throw new ProjectException("Error creating project.", e);
131             }
132         }
133
134         postInitialize(projectFile);
135     }
136
137     /**
138      * Finished project initialization. Called
139      * from constructor. Default implementation builds a file list
140      * and checks for upgrades.
141      */

142     protected void postInitialize(File JavaDoc projectFile) {
143         logObj.debug("postInitialize with: " + projectFile);
144         // take a snapshot of files used by the project
145
files = Collections.synchronizedList(buildFileList());
146         upgradeMessages = Collections.synchronizedList(new ArrayList JavaDoc());
147         checkForUpgrades();
148     }
149
150     /**
151      * Returns true if project location is not defined. For instance,
152      * when project was created in memory and is not tied to a file yet.
153      */

154     public boolean isLocationUndefined() {
155         return getMainFile() == null;
156     }
157
158     /**
159      * Returns true if the project needs to be upgraded.
160      */

161     public boolean isUpgradeNeeded() {
162         return upgradeMessages.size() > 0;
163     }
164
165     /**
166       * Returns a list of upgrade messages.
167       */

168     public List JavaDoc getUpgradeMessages() {
169         return upgradeMessages;
170     }
171
172     /**
173      * Returns true is project has renamed files.
174      * This is useful when converting from older versions
175      * of the modeler projects.
176      */

177     public boolean hasRenamedFiles() {
178         if (files == null) {
179             return false;
180         }
181
182         synchronized (files) {
183             Iterator JavaDoc it = files.iterator();
184             while (it.hasNext()) {
185                 if (((ProjectFile) it.next()).isRenamed()) {
186                     return true;
187                 }
188             }
189         }
190
191         return false;
192     }
193
194     /**
195      * Creates a list of project files.
196      */

197     public List JavaDoc buildFileList() {
198         List JavaDoc projectFiles = new ArrayList JavaDoc();
199
200         Iterator JavaDoc nodes = treeNodes();
201         while (nodes.hasNext()) {
202             ProjectPath nodePath = (ProjectPath) nodes.next();
203             Object JavaDoc obj = nodePath.getObject();
204             ProjectFile f = projectFileForObject(obj);
205
206             if (f != null) {
207                 projectFiles.add(f);
208             }
209         }
210
211         return projectFiles;
212     }
213
214     /**
215      * Creates an instance of Validator for validating this project.
216      */

217     public Validator getValidator() {
218         return new Validator(this);
219     }
220
221     /**
222      * Looks up and returns a file wrapper for a project
223      * object. Returns null if no file exists.
224      */

225     public ProjectFile findFile(Object JavaDoc obj) {
226         if (obj == null) {
227             return null;
228         }
229
230         // to avoid full scan, a map may be a better
231
// choice of collection here,
232
// though normally projects have very few files...
233
synchronized (files) {
234             Iterator JavaDoc it = files.iterator();
235             while (it.hasNext()) {
236                 ProjectFile file = (ProjectFile) it.next();
237                 if (file.getObject() == obj) {
238                     return file;
239                 }
240             }
241         }
242
243         return null;
244     }
245
246     /**
247      * Returns a canonical file built from symbolic name.
248      */

249     public File JavaDoc resolveFile(String JavaDoc symbolicName) {
250         try {
251             // substitute to Windows backslashes if needed
252
if (File.separatorChar != '/') {
253                 symbolicName = symbolicName.replace('/', File.separatorChar);
254             }
255             return new File JavaDoc(projectDir, symbolicName).getCanonicalFile();
256         } catch (IOException JavaDoc e) {
257             // error converting path
258
logObj.info("Can't convert to canonical form.", e);
259             return null;
260         }
261     }
262
263     /**
264       * Returns a "symbolic" name of a file. Returns null if file
265       * is invalid. Symbolic name is a string path of a file relative
266       * to the project directory. It is built in a platform independent
267       * fashion.
268       */

269     public String JavaDoc resolveSymbolicName(File JavaDoc file) {
270         String JavaDoc symbolicName = null;
271         try {
272             // accept absolute files only when
273
// they are in the project directory
274
String JavaDoc otherPath = file.getCanonicalFile().getPath();
275             String JavaDoc thisPath = projectDir.getPath();
276
277             // invalid absolute pathname, can't continue
278
if (otherPath.length() + 1 <= thisPath.length()
279                 || !otherPath.startsWith(thisPath)) {
280                 return null;
281             }
282
283             symbolicName = otherPath.substring(thisPath.length() + 1);
284
285             // substitute Windows backslashes if needed
286
if ((symbolicName != null) && (File.separatorChar != '/')) {
287                 symbolicName = symbolicName.replace(File.separatorChar, '/');
288             }
289
290             return symbolicName;
291
292         } catch (IOException JavaDoc e) {
293             // error converting path
294
logObj.info("Can't convert to canonical form.", e);
295             return null;
296         }
297     }
298
299     /**
300      * Returns project directory. This is a directory where
301      * project file is located.
302      */

303     public File JavaDoc getProjectDirectory() {
304         return projectDir;
305     }
306
307     public void setProjectDirectory(File JavaDoc dir) {
308         this.projectDir = dir;
309     }
310
311     /**
312      * Returns a canonical form of a main file associated with this project.
313      */

314     public File JavaDoc getMainFile() {
315         if (projectDir == null) {
316             return null;
317         }
318
319         ProjectFile f = projectFileForObject(this);
320         return (f != null) ? resolveFile(f.getLocation()) : null;
321     }
322
323     /**
324      * @return An object describing failures in the loaded project.
325      */

326     public abstract ConfigStatus getLoadStatus();
327
328     public abstract ProjectFile projectFileForObject(Object JavaDoc obj);
329
330     /**
331      * Returns a list of first-level children of the project.
332      */

333     public abstract List JavaDoc getChildren();
334
335     /**
336      * Determines whether the project needs to be upgraded.
337      * Populates internal list of upgrade messages with discovered
338      * information.
339      */

340     public abstract void checkForUpgrades();
341
342     /**
343      * Returns an Iterator over project tree of objects.
344      */

345     public Iterator JavaDoc treeNodes() {
346         return FlatProjectView
347             .getInstance()
348             .flattenProjectTree(this)
349             .iterator();
350     }
351     
352     /**
353      * @since 1.1
354      */

355     public abstract void upgrade() throws ProjectException;
356
357     /**
358      * Saves project. All currently existing files are updated,
359      * without checking for modifications. New files are created
360      * as needed, unused files are deleted.
361      */

362     public void save() throws ProjectException {
363
364         // sanity check
365
if (isLocationUndefined()) {
366             throw new ProjectException("Project location is undefined.");
367         }
368
369         // 1. Traverse project tree to find file wrappers that require update.
370
List JavaDoc filesToSave = new ArrayList JavaDoc();
371         List JavaDoc wrappedObjects = new ArrayList JavaDoc();
372         prepareSave(filesToSave, wrappedObjects);
373
374         // 2. Try saving individual file wrappers
375
processSave(filesToSave);
376
377         // 3. Commit changes
378
List JavaDoc savedFiles = new ArrayList JavaDoc();
379         Iterator JavaDoc saved = filesToSave.iterator();
380         while (saved.hasNext()) {
381             ProjectFile f = (ProjectFile) saved.next();
382             savedFiles.add(f.saveCommit());
383         }
384
385         // 4. Take care of deleted
386
processDelete(wrappedObjects, savedFiles);
387
388         // 5. Refresh file list
389
List JavaDoc freshList = buildFileList();
390         Iterator JavaDoc it = freshList.iterator();
391         while (it.hasNext()) {
392             ((ProjectFile) it.next()).synchronizeLocation();
393         }
394
395         files = freshList;
396
397         synchronized (upgradeMessages) {
398             upgradeMessages.clear();
399         }
400
401         // update state
402
setModified(false);
403     }
404
405     protected void prepareSave(List JavaDoc filesToSave, List JavaDoc wrappedObjects)
406         throws ProjectException {
407         Iterator JavaDoc nodes = treeNodes();
408         while (nodes.hasNext()) {
409             ProjectPath nodePath = (ProjectPath) nodes.next();
410             Object JavaDoc obj = nodePath.getObject();
411
412             ProjectFile existingFile = findFile(obj);
413
414             if (existingFile == null) {
415                 // check if project node can have a file
416
ProjectFile newFile = projectFileForObject(obj);
417                 if (newFile != null) {
418                     filesToSave.add(newFile);
419                 }
420             } else if (existingFile.canHandleObject()) {
421                 wrappedObjects.add(existingFile.getObject());
422                 filesToSave.add(existingFile);
423             }
424         }
425
426     }
427
428     /**
429      * Saves a list of modified files to temporary files.
430      */

431     protected void processSave(List JavaDoc modifiedFiles) throws ProjectException {
432         // notify that files will be saved
433
Iterator JavaDoc willSave = modifiedFiles.iterator();
434         while (willSave.hasNext()) {
435             ProjectFile f = (ProjectFile) willSave.next();
436             f.willSave();
437         }
438
439         try {
440             Iterator JavaDoc modified = modifiedFiles.iterator();
441             while (modified.hasNext()) {
442                 ProjectFile f = (ProjectFile) modified.next();
443
444                 if (logObj.isDebugEnabled()) {
445                     logObj.info("Saving file " + f.resolveFile());
446                 }
447
448                 f.saveTemp();
449             }
450         } catch (Exception JavaDoc ex) {
451             logObj.info("*** Project save failed, reverting.", ex);
452
453             // revert
454
Iterator JavaDoc modified = modifiedFiles.iterator();
455             while (modified.hasNext()) {
456                 ProjectFile f = (ProjectFile) modified.next();
457                 f.saveUndo();
458             }
459
460             throw new ProjectException(
461                 "Project save failed and was canceled.",
462                 ex);
463         }
464     }
465
466     protected void processDelete(List JavaDoc existingObjects, List JavaDoc savedFiles) {
467
468         // check for deleted
469
synchronized (files) {
470             Iterator JavaDoc oldFiles = files.iterator();
471             while (oldFiles.hasNext()) {
472                 ProjectFile f = (ProjectFile) oldFiles.next();
473                 File JavaDoc file = f.resolveOldFile();
474
475                 // this check is needed, since a file can reuse the name
476
// of a recently deleted file, and we don't want to delete
477
// new file by mistake
478
if (file == null || savedFiles.contains(file)) {
479                     continue;
480                 }
481
482                 boolean delete = false;
483                 if (f.isRenamed()) {
484                     delete = true;
485                     logObj.info("File renamed, deleting old version: " + file);
486                 } else if (f.getObject() == null) {
487                     delete = true;
488                     logObj.info("Null internal object, deleting file: " + file);
489                 } else if (!existingObjects.contains(f.getObject())) {
490                     delete = true;
491                     logObj.info(
492                         "Object deleted from the project, deleting file: "
493                             + file);
494                 } else if (!f.canHandleObject()) {
495                     // this happens too - node can start using JNDI for instance
496
delete = true;
497                     logObj.info(
498                         "Can no longer handle the object, deleting file: "
499                             + file);
500                 }
501
502                 if (delete) {
503                     if (!deleteFile(file)) {
504                         logObj.info("*** Failed to delete file, ignoring.");
505                     }
506                 }
507             }
508         }
509     }
510
511     protected boolean deleteFile(File JavaDoc f) {
512         return (f.exists()) ? f.delete() : true;
513     }
514
515     /**
516      * Returns <code>true</code> if the project is modified.
517      */

518     public boolean isModified() {
519         return modified;
520     }
521
522     /**
523      * Updates "modified" state of the project.
524      */

525     public void setModified(boolean modified) {
526         this.modified = modified;
527     }
528 }
529
Popular Tags