Source Code Cross Referenced for ProjectManager.java in  » IDE-Netbeans » project.ant » org » netbeans » api » project » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » IDE Netbeans » project.ant » org.netbeans.api.project 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003:         *
004:         * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005:         *
006:         * The contents of this file are subject to the terms of either the GNU
007:         * General Public License Version 2 only ("GPL") or the Common
008:         * Development and Distribution License("CDDL") (collectively, the
009:         * "License"). You may not use this file except in compliance with the
010:         * License. You can obtain a copy of the License at
011:         * http://www.netbeans.org/cddl-gplv2.html
012:         * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013:         * specific language governing permissions and limitations under the
014:         * License.  When distributing the software, include this License Header
015:         * Notice in each file and include the License file at
016:         * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
017:         * particular file as subject to the "Classpath" exception as provided
018:         * by Sun in the GPL Version 2 section of the License file that
019:         * accompanied this code. If applicable, add the following below the
020:         * License Header, with the fields enclosed by brackets [] replaced by
021:         * your own identifying information:
022:         * "Portions Copyrighted [year] [name of copyright owner]"
023:         *
024:         * Contributor(s):
025:         *
026:         * The Original Software is NetBeans. The Initial Developer of the Original
027:         * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028:         * Microsystems, Inc. All Rights Reserved.
029:         *
030:         * If you wish your version of this file to be governed by only the CDDL
031:         * or only the GPL Version 2, indicate your decision by adding
032:         * "[Contributor] elects to include this software in this distribution
033:         * under the [CDDL or GPL Version 2] license." If you do not indicate a
034:         * single choice of license, a recipient has the option to distribute
035:         * your version of this file under either the CDDL, the GPL Version 2 or
036:         * to extend the choice of license to its licensees as provided above.
037:         * However, if you add GPL Version 2 code and therefore, elected the GPL
038:         * Version 2 license, then the option applies only if the new code is
039:         * made subject to such option by the copyright holder.
040:         */
041:
042:        package org.netbeans.api.project;
043:
044:        import java.io.IOException;
045:        import java.lang.ref.Reference;
046:        import java.util.Arrays;
047:        import java.util.Collection;
048:        import java.util.HashSet;
049:        import java.util.Iterator;
050:        import java.util.Map;
051:        import java.util.Set;
052:        import java.util.WeakHashMap;
053:        import java.util.concurrent.Executor;
054:        import java.util.logging.Level;
055:        import java.util.logging.LogRecord;
056:        import java.util.logging.Logger;
057:        import org.netbeans.modules.projectapi.SimpleFileOwnerQueryImplementation;
058:        import org.netbeans.modules.projectapi.TimedWeakReference;
059:        import org.netbeans.spi.project.FileOwnerQueryImplementation;
060:        import org.netbeans.spi.project.ProjectFactory;
061:        import org.netbeans.spi.project.ProjectState;
062:        import org.openide.filesystems.FileChangeAdapter;
063:        import org.openide.filesystems.FileChangeListener;
064:        import org.openide.filesystems.FileEvent;
065:        import org.openide.filesystems.FileObject;
066:        import org.openide.filesystems.FileSystem;
067:        import org.openide.filesystems.FileUtil;
068:        import org.openide.util.Lookup;
069:        import org.openide.util.LookupEvent;
070:        import org.openide.util.LookupListener;
071:        import org.openide.util.Mutex;
072:        import org.openide.util.MutexException;
073:        import org.openide.util.Union2;
074:        import org.openide.util.WeakSet;
075:
076:        /**
077:         * Manages loaded projects.
078:         * @author Jesse Glick
079:         */
080:        public final class ProjectManager {
081:
082:            // XXX need to figure out how to convince the system that a Project object is modified
083:            // so that Save All and the exit dialog work... could temporarily use a DataLoader
084:            // which recognizes project dirs and gives them a SaveCookie, perhaps
085:            // see also #36280
086:            // (but currently customizers always save the project on exit, so not so high priority)
087:
088:            // XXX change listeners?
089:
090:            private static final Logger LOG = Logger
091:                    .getLogger(ProjectManager.class.getName());
092:            /** logger for timers/counters */
093:            private static final Logger TIMERS = Logger
094:                    .getLogger("TIMER.projects"); // NOI18N
095:
096:            private static final Lookup.Result<ProjectFactory> factories = Lookup
097:                    .getDefault().lookupResult(ProjectFactory.class);
098:
099:            private ProjectManager() {
100:                factories.addLookupListener(new LookupListener() {
101:                    public void resultChanged(LookupEvent e) {
102:                        clearNonProjectCache();
103:                    }
104:                });
105:            }
106:
107:            private static final ProjectManager DEFAULT = new ProjectManager();
108:
109:            /**
110:             * Returns the singleton project manager instance.
111:             * @return the default instance
112:             */
113:            public static ProjectManager getDefault() {
114:                return DEFAULT;
115:            }
116:
117:            private static final Executor FS_EXEC = new Executor() {
118:                public void execute(final Runnable command) {
119:                    try {
120:                        FileUtil.runAtomicAction(new FileSystem.AtomicAction() {
121:                            public void run() throws IOException {
122:                                command.run();
123:                            }
124:                        });
125:                    } catch (IOException ex) {
126:                        throw (IllegalStateException) new IllegalStateException()
127:                                .initCause(ex);
128:                    }
129:                }
130:            };
131:
132:            private static final Mutex MUTEX = new Mutex(
133:                    new Mutex.Privileged(), FS_EXEC);
134:
135:            /**
136:             * Get a read/write lock to be used for all project metadata accesses.
137:             * All methods relating to recognizing and loading projects, saving them,
138:             * getting or setting their metadata, etc. should be controlled by this
139:             * mutex and be marked as read operations or write operations. Unless
140:             * otherwise stated, project-related methods automatically acquire the
141:             * mutex for you, so you do not necessarily need to pay attention to it;
142:             * but you may directly acquire the mutex in order to ensure that a block
143:             * of reads does not have any interspersed writes, or in order to ensure
144:             * that a write is not clobbering an unrelated write, etc.
145:             * @return a general read/write lock for project metadata operations of all sorts
146:             */
147:            public static Mutex mutex() {
148:                return MUTEX;
149:            }
150:
151:            private static enum LoadStatus {
152:                /**
153:                 * Marker for a directory which is known to not be a project.
154:                 */
155:                NO_SUCH_PROJECT,
156:                /**
157:                 * Marker for a directory which is known to (probably) be a project but is not loaded.
158:                 */
159:                SOME_SUCH_PROJECT,
160:                /**
161:                 * Marker for a directory which may currently be being loaded as a project.
162:                 * When this is the value, other reader threads should wait for the result.
163:                 */
164:                LOADING_PROJECT;
165:
166:                public boolean is(Union2<Reference<Project>, LoadStatus> o) {
167:                    return o != null && o.hasSecond() && o.second() == this ;
168:                }
169:
170:                public Union2<Reference<Project>, LoadStatus> wrap() {
171:                    return Union2.createSecond(this );
172:                }
173:            }
174:
175:            /**
176:             * Cache of loaded projects (modified or not).
177:             * Also caches a dir which is <em>not</em> a project.
178:             */
179:            private final Map<FileObject, Union2<Reference<Project>, LoadStatus>> dir2Proj = new WeakHashMap<FileObject, Union2<Reference<Project>, LoadStatus>>();
180:
181:            /**
182:             * Set of modified projects (subset of loaded projects).
183:             */
184:            private final Set<Project> modifiedProjects = new HashSet<Project>();
185:
186:            private final Set<Project> removedProjects = new WeakSet<Project>();
187:
188:            /**
189:             * Mapping from projects to the factories that created them.
190:             */
191:            private final Map<Project, ProjectFactory> proj2Factory = new WeakHashMap<Project, ProjectFactory>();
192:
193:            /**
194:             * Checks for deleted projects.
195:             */
196:            private final FileChangeListener projectDeletionListener = new ProjectDeletionListener();
197:
198:            /**
199:             * Whether this thread is currently loading a project.
200:             */
201:            private ThreadLocal<Set<FileObject>> loadingThread = new ThreadLocal<Set<FileObject>>();
202:
203:            /**
204:             * Clear internal state.
205:             * Useful from unit tests.
206:             */
207:            void reset() {
208:                dir2Proj.clear();
209:                modifiedProjects.clear();
210:                proj2Factory.clear();
211:            }
212:
213:            /**
214:             * Find an open project corresponding to a given project directory.
215:             * Will be created in memory if necessary.
216:             * <p>
217:             * Acquires read access.
218:             * </p>
219:             * <p>
220:             * It is <em>not</em> guaranteed that the returned instance will be identical
221:             * to that which is created by the appropriate {@link ProjectFactory}. In
222:             * particular, the project manager is free to return only wrapper <code>Project</code>
223:             * instances which delegate to the factory's implementation. If you know your
224:             * factory created a particular project, you cannot safely cast the return value
225:             * of this method to your project type implementation class; you should instead
226:             * place an implementation of some suitable private interface into your project's
227:             * lookup, which would be safely proxied.
228:             * </p>
229:             * @param projectDirectory the project top directory
230:             * @return the project (object identity may or may not vary between calls)
231:             *         or null if the directory is not recognized as a project by any
232:             *         registered {@link ProjectFactory}
233:             *         (might be null even if {@link #isProject} returns true)
234:             * @throws IOException if the project was recognized but could not be loaded
235:             * @throws IllegalArgumentException if the supplied file object is null or not a folder
236:             */
237:            public Project findProject(final FileObject projectDirectory)
238:                    throws IOException, IllegalArgumentException {
239:                if (projectDirectory == null) {
240:                    throw new IllegalArgumentException(
241:                            "Attempted to pass a null directory to findProject"); // NOI18N
242:                }
243:                if (!projectDirectory.isFolder()) {
244:                    throw new IllegalArgumentException(
245:                            "Attempted to pass a non-directory to findProject: "
246:                                    + projectDirectory); // NOI18N
247:                }
248:                try {
249:                    return mutex().readAccess(
250:                            new Mutex.ExceptionAction<Project>() {
251:                                public Project run() throws IOException {
252:                                    // Read access, but still needs to synch on the cache since there
253:                                    // may be >1 reader.
254:                                    try {
255:                                        boolean wasSomeSuchProject;
256:                                        synchronized (dir2Proj) {
257:                                            Union2<Reference<Project>, LoadStatus> o;
258:                                            do {
259:                                                o = dir2Proj
260:                                                        .get(projectDirectory);
261:                                                if (LoadStatus.LOADING_PROJECT
262:                                                        .is(o)) {
263:                                                    try {
264:                                                        Set<FileObject> ldng = loadingThread
265:                                                                .get();
266:                                                        if (ldng != null
267:                                                                && ldng
268:                                                                        .contains(projectDirectory)) {
269:                                                            throw new IllegalStateException(
270:                                                                    "Attempt to call ProjectManager.findProject within the body of ProjectFactory.loadProject (hint: try using ProjectManager.mutex().postWriteRequest(...) within the body of your Project's constructor to prevent this)"); // NOI18N
271:                                                        }
272:                                                        LOG
273:                                                                .log(
274:                                                                        Level.FINE,
275:                                                                        "findProject({0}) in {1}: waiting for LOADING_PROJECT...",
276:                                                                        new Object[] {
277:                                                                                projectDirectory,
278:                                                                                Thread
279:                                                                                        .currentThread()
280:                                                                                        .getName() });
281:                                                        dir2Proj.wait();
282:                                                        LOG
283:                                                                .log(
284:                                                                        Level.FINE,
285:                                                                        "findProject({0}) in {1}: ...done waiting for LOADING_PROJECT",
286:                                                                        new Object[] {
287:                                                                                projectDirectory,
288:                                                                                Thread
289:                                                                                        .currentThread()
290:                                                                                        .getName() });
291:                                                    } catch (InterruptedException e) {
292:                                                        LOG.log(Level.WARNING,
293:                                                                null, e);
294:                                                    }
295:                                                }
296:                                            } while (LoadStatus.LOADING_PROJECT
297:                                                    .is(o));
298:                                            assert !LoadStatus.LOADING_PROJECT
299:                                                    .is(o);
300:                                            wasSomeSuchProject = LoadStatus.SOME_SUCH_PROJECT
301:                                                    .is(o);
302:                                            if (LoadStatus.NO_SUCH_PROJECT
303:                                                    .is(o)) {
304:                                                LOG
305:                                                        .log(
306:                                                                Level.FINE,
307:                                                                "findProject({0}) in {1}: NO_SUCH_PROJECT",
308:                                                                new Object[] {
309:                                                                        projectDirectory,
310:                                                                        Thread
311:                                                                                .currentThread()
312:                                                                                .getName() });
313:                                                return null;
314:                                            } else if (o != null
315:                                                    && !LoadStatus.SOME_SUCH_PROJECT
316:                                                            .is(o)) {
317:                                                Project p = o.first().get();
318:                                                if (p != null) {
319:                                                    LOG
320:                                                            .log(
321:                                                                    Level.FINE,
322:                                                                    "findProject({0}) in {1}: cached project",
323:                                                                    new Object[] {
324:                                                                            projectDirectory,
325:                                                                            Thread
326:                                                                                    .currentThread()
327:                                                                                    .getName() });
328:                                                    return p;
329:                                                }
330:                                            }
331:                                            // not in cache
332:                                            dir2Proj.put(projectDirectory,
333:                                                    LoadStatus.LOADING_PROJECT
334:                                                            .wrap());
335:                                            Set<FileObject> ldng = loadingThread
336:                                                    .get();
337:                                            if (ldng == null) {
338:                                                ldng = new HashSet<FileObject>();
339:                                                loadingThread.set(ldng);
340:                                            }
341:                                            ldng.add(projectDirectory);
342:                                            LOG
343:                                                    .log(
344:                                                            Level.FINE,
345:                                                            "findProject({0}) in {1}: will load new project...",
346:                                                            new Object[] {
347:                                                                    projectDirectory,
348:                                                                    Thread
349:                                                                            .currentThread()
350:                                                                            .getName() });
351:                                        }
352:                                        boolean resetLP = false;
353:                                        try {
354:                                            Project p = createProject(projectDirectory);
355:                                            LOG
356:                                                    .log(
357:                                                            Level.FINE,
358:                                                            "findProject({0}) in {1}: created new project",
359:                                                            new Object[] {
360:                                                                    projectDirectory,
361:                                                                    Thread
362:                                                                            .currentThread()
363:                                                                            .getName() });
364:                                            //Thread.dumpStack();
365:                                            synchronized (dir2Proj) {
366:                                                dir2Proj.notifyAll();
367:                                                projectDirectory
368:                                                        .addFileChangeListener(projectDeletionListener);
369:                                                if (p != null) {
370:                                                    dir2Proj
371:                                                            .put(
372:                                                                    projectDirectory,
373:                                                                    Union2
374:                                                                            .<Reference<Project>, LoadStatus> createFirst(new TimedWeakReference<Project>(
375:                                                                                    p)));
376:                                                    resetLP = true;
377:                                                    return p;
378:                                                } else {
379:                                                    dir2Proj
380:                                                            .put(
381:                                                                    projectDirectory,
382:                                                                    LoadStatus.NO_SUCH_PROJECT
383:                                                                            .wrap());
384:                                                    resetLP = true;
385:                                                    if (wasSomeSuchProject) {
386:                                                        LOG
387:                                                                .log(
388:                                                                        Level.FINE,
389:                                                                        "Directory {0} was initially claimed to be a project folder but really was not",
390:                                                                        FileUtil
391:                                                                                .getFileDisplayName(projectDirectory));
392:                                                    }
393:                                                    return null;
394:                                                }
395:                                            }
396:                                        } catch (IOException e) {
397:                                            LOG
398:                                                    .log(
399:                                                            Level.FINE,
400:                                                            "findProject({0}) in {1}: error loading project: {2}",
401:                                                            new Object[] {
402:                                                                    projectDirectory,
403:                                                                    Thread
404:                                                                            .currentThread()
405:                                                                            .getName(),
406:                                                                    e });
407:                                            // Do not cache the exception. Might be useful in some cases
408:                                            // but would also cause problems if there were a project that was
409:                                            // temporarily corrupted, fP is called, then it is fixed, then fP is
410:                                            // called again (without anything being GC'd)
411:                                            throw e;
412:                                        } finally {
413:                                            loadingThread.get().remove(
414:                                                    projectDirectory);
415:                                            if (!resetLP) {
416:                                                // IOException or a runtime exception interrupted.
417:                                                LOG
418:                                                        .log(
419:                                                                Level.FINE,
420:                                                                "findProject({0}) in {1}: cleaning up after error",
421:                                                                new Object[] {
422:                                                                        projectDirectory,
423:                                                                        Thread
424:                                                                                .currentThread()
425:                                                                                .getName() });
426:                                                synchronized (dir2Proj) {
427:                                                    assert LoadStatus.LOADING_PROJECT
428:                                                            .is(dir2Proj
429:                                                                    .get(projectDirectory));
430:                                                    dir2Proj
431:                                                            .remove(projectDirectory);
432:                                                    dir2Proj.notifyAll(); // make sure other threads can continue
433:                                                }
434:                                            }
435:                                        }
436:                                        // Workaround for issue #51911:
437:                                        // Log project creation exception here otherwise it can get lost
438:                                        // in following scenario:
439:                                        // If project creation calls ProjectManager.postWriteRequest() (what for 
440:                                        // example FreeformSources.initSources does) and then it throws an 
441:                                        // exception then this exception can get lost because leaving read mutex
442:                                        // will immediately execute the runnable posted by 
443:                                        // ProjectManager.postWriteRequest() and if this runnable fails (what
444:                                        // for FreeformSources.initSources will happen because
445:                                        // AntBasedProjectFactorySingleton.getProjectFor() will not find project in
446:                                        // its helperRef cache) then only this second fail is logged, but the cause - 
447:                                        // the failure to create project - is never logged. So, better log it here:
448:                                    } catch (Error e) {
449:                                        LOG.log(Level.FINE, null, e);
450:                                        throw e;
451:                                    } catch (RuntimeException e) {
452:                                        LOG.log(Level.FINE, null, e);
453:                                        throw e;
454:                                    } catch (IOException e) {
455:                                        LOG.log(Level.FINE, null, e);
456:                                        throw e;
457:                                    }
458:                                }
459:                            });
460:                } catch (MutexException e) {
461:                    throw (IOException) e.getException();
462:                }
463:            }
464:
465:            /**
466:             * Create a project from a given directory.
467:             * @param dir the project dir
468:             * @return a project made from it, or null if it is not recognized
469:             * @throws IOException if there was a problem loading the project
470:             */
471:            private Project createProject(FileObject dir) throws IOException {
472:                assert dir != null;
473:                assert dir.isFolder();
474:                assert mutex().isReadAccess();
475:                ProjectStateImpl state = new ProjectStateImpl();
476:                for (ProjectFactory factory : factories.allInstances()) {
477:                    Project p = factory.loadProject(dir, state);
478:                    if (p != null) {
479:                        if (TIMERS.isLoggable(Level.FINE)) {
480:                            LogRecord rec = new LogRecord(Level.FINE, "Project"); // NOI18N
481:                            rec.setParameters(new Object[] { p });
482:                            TIMERS.log(rec);
483:                        }
484:                        proj2Factory.put(p, factory);
485:                        state.attach(p);
486:                        return p;
487:                    }
488:                }
489:                return null;
490:            }
491:
492:            /**
493:             * Check whether a given directory is likely to contain a project without
494:             * actually loading it.
495:             * Should be faster and use less memory than {@link #findProject} when called
496:             * on a large number of directories.
497:             * <p>The result is not guaranteed to be accurate; there may be false positives
498:             * (directories for which <code>isProject</code> is true but {@link #findProject}
499:             * will return false), for example if there is trouble loading the project.
500:             * False negatives are possible only if there are bugs in the project factory.</p>
501:             * <p>Acquires read access.</p>
502:             * <p class="nonnormative">
503:             * You do <em>not</em> need to call this method if you just plan to call {@link #findProject}
504:             * afterwards. It is intended for only those clients which would discard the
505:             * result of {@link #findProject} other than to check for null, and which
506:             * can also tolerate false positives.
507:             * </p>
508:             * @param projectDirectory a directory which may be some project's top directory
509:             * @return true if the directory is likely to contain a project according to
510:             *              some registered {@link ProjectFactory}
511:             * @throws IllegalArgumentException if the supplied file object is null or not a folder
512:             */
513:            public boolean isProject(final FileObject projectDirectory)
514:                    throws IllegalArgumentException {
515:                if (projectDirectory == null) {
516:                    throw new IllegalArgumentException(
517:                            "Attempted to pass a null directory to isProject"); // NOI18N
518:                }
519:                if (!projectDirectory.isFolder()) {
520:                    //#78215 it can happen that a no longer existing folder is queried. throw 
521:                    // exception only for real wrong usage..
522:                    if (projectDirectory.isValid()) {
523:                        throw new IllegalArgumentException(
524:                                "Attempted to pass a non-directory to isProject: "
525:                                        + projectDirectory); // NOI18N
526:                    } else {
527:                        return false;
528:                    }
529:                }
530:                return mutex().readAccess(new Mutex.Action<Boolean>() {
531:                    public Boolean run() {
532:                        synchronized (dir2Proj) {
533:                            Union2<Reference<Project>, LoadStatus> o;
534:                            do {
535:                                o = dir2Proj.get(projectDirectory);
536:                                if (LoadStatus.LOADING_PROJECT.is(o)) {
537:                                    try {
538:                                        dir2Proj.wait();
539:                                    } catch (InterruptedException e) {
540:                                        e.printStackTrace();
541:                                    }
542:                                }
543:                            } while (LoadStatus.LOADING_PROJECT.is(o));
544:                            assert !LoadStatus.LOADING_PROJECT.is(o);
545:                            if (LoadStatus.NO_SUCH_PROJECT.is(o)) {
546:                                return false;
547:                            } else if (o != null) {
548:                                // Reference<Project> or SOME_SUCH_PROJECT
549:                                return true;
550:                            }
551:                            // Not in cache.
552:                            dir2Proj.put(projectDirectory,
553:                                    LoadStatus.LOADING_PROJECT.wrap());
554:                        }
555:                        boolean resetLP = false;
556:                        try {
557:                            boolean p = checkForProject(projectDirectory);
558:                            synchronized (dir2Proj) {
559:                                resetLP = true;
560:                                dir2Proj.notifyAll();
561:                                if (p) {
562:                                    dir2Proj
563:                                            .put(
564:                                                    projectDirectory,
565:                                                    LoadStatus.SOME_SUCH_PROJECT
566:                                                            .wrap());
567:                                    return true;
568:                                } else {
569:                                    dir2Proj.put(projectDirectory,
570:                                            LoadStatus.NO_SUCH_PROJECT.wrap());
571:                                    return false;
572:                                }
573:                            }
574:                        } finally {
575:                            if (!resetLP) {
576:                                // some runtime exception interrupted.
577:                                assert LoadStatus.LOADING_PROJECT.is(dir2Proj
578:                                        .get(projectDirectory));
579:                                dir2Proj.remove(projectDirectory);
580:                            }
581:                        }
582:                    }
583:                });
584:            }
585:
586:            private boolean checkForProject(FileObject dir) {
587:                assert dir != null;
588:                assert dir.isFolder() : dir;
589:                assert mutex().isReadAccess();
590:                Iterator it = factories.allInstances().iterator();
591:                while (it.hasNext()) {
592:                    ProjectFactory factory = (ProjectFactory) it.next();
593:                    if (factory.isProject(dir)) {
594:                        return true;
595:                    }
596:                }
597:                return false;
598:            }
599:
600:            /**
601:             * Clear the cached list of folders thought <em>not</em> to be projects.
602:             * This may be useful after creating project metadata in a folder, etc.
603:             * Cached project objects, i.e. folders that <em>are</em> known to be
604:             * projects, are not affected.
605:             */
606:            public void clearNonProjectCache() {
607:                synchronized (dir2Proj) {
608:                    dir2Proj.values().removeAll(
609:                            Arrays.asList(new Object[] {
610:                                    LoadStatus.NO_SUCH_PROJECT.wrap(),
611:                                    LoadStatus.SOME_SUCH_PROJECT.wrap(), }));
612:                    // XXX remove everything too? but then e.g. AntProjectFactorySingleton
613:                    // will stay while its delegates are changed, which does no good
614:                    // XXX should there be any way to signal that a particular
615:                    // folder should be "reloaded" by a new factory?
616:                }
617:            }
618:
619:            private final class ProjectStateImpl implements  ProjectState {
620:
621:                private Project p;
622:
623:                void attach(Project p) {
624:                    assert p != null;
625:                    assert this .p == null;
626:                    this .p = p;
627:                }
628:
629:                public void markModified() {
630:                    assert p != null;
631:                    LOG.log(Level.FINE, "markModified({0})", p
632:                            .getProjectDirectory());
633:                    mutex().writeAccess(new Mutex.Action<Void>() {
634:                        public Void run() {
635:                            if (!proj2Factory.containsKey(p)) {
636:                                throw new IllegalStateException(
637:                                        "An attempt to call ProjectState.markModified on a deleted project: "
638:                                                + p.getProjectDirectory()); // NOI18N
639:                            }
640:                            modifiedProjects.add(p);
641:                            return null;
642:                        }
643:                    });
644:                }
645:
646:                public void notifyDeleted() throws IllegalStateException {
647:                    assert p != null;
648:                    mutex().writeAccess(new Mutex.Action<Void>() {
649:                        public Void run() {
650:                            if (proj2Factory.get(p) == null) {
651:                                throw new IllegalStateException(
652:                                        "An attempt to call notifyDeleted more than once. Project: "
653:                                                + p.getProjectDirectory()); // NOI18N
654:                            }
655:
656:                            dir2Proj.remove(p.getProjectDirectory());
657:                            proj2Factory.remove(p);
658:                            modifiedProjects.remove(p);
659:                            removedProjects.add(p);
660:                            //#111892
661:                            Collection<? extends FileOwnerQueryImplementation> col = Lookup
662:                                    .getDefault().lookupAll(
663:                                            FileOwnerQueryImplementation.class);
664:                            for (FileOwnerQueryImplementation impl : col) {
665:                                if (impl instanceof  SimpleFileOwnerQueryImplementation) {
666:                                    ((SimpleFileOwnerQueryImplementation) impl)
667:                                            .resetLastFoundReferences();
668:                                }
669:                            }
670:                            return null;
671:                        }
672:                    });
673:                }
674:
675:            }
676:
677:            /**
678:             * Get a list of all projects which are modified and need to be saved.
679:             * <p>Acquires read access.
680:             * @return an immutable set of projects
681:             */
682:            public Set<Project> getModifiedProjects() {
683:                return mutex().readAccess(new Mutex.Action<Set<Project>>() {
684:                    public Set<Project> run() {
685:                        return new HashSet<Project>(modifiedProjects);
686:                    }
687:                });
688:            }
689:
690:            /**
691:             * Check whether a given project is current modified.
692:             * <p>Acquires read access.
693:             * @param p a project loaded by this manager
694:             * @return true if it is modified, false if has been saved since the last modification
695:             * @throws IllegalArgumentException if the project was not created through this manager
696:             */
697:            public boolean isModified(final Project p)
698:                    throws IllegalArgumentException {
699:                return mutex().readAccess(new Mutex.Action<Boolean>() {
700:                    public Boolean run() {
701:                        synchronized (dir2Proj) {
702:                            if (!proj2Factory.containsKey(p)) {
703:                                throw new IllegalArgumentException("Project "
704:                                        + p + " not created by "
705:                                        + ProjectManager.this 
706:                                        + " or was already deleted"); // NOI18N
707:                            }
708:                        }
709:                        return modifiedProjects.contains(p);
710:                    }
711:                });
712:            }
713:
714:            /**
715:             * Save one project (if it was in fact modified).
716:             * <p>Acquires write access.</p>
717:             * <p class="nonnormative">
718:             * Although the project infrastructure permits a modified project to be saved
719:             * at any time, current UI principles dictate that the "save project" concept
720:             * should be internal only - i.e. a project customizer should automatically
721:             * save the project when it is closed e.g. with an "OK" button. Currently there
722:             * is no UI display of modified projects; this module does not ensure that modified projects
723:             * are saved at system exit time the way modified files are, though the Project UI
724:             * implementation module currently does this check.
725:             * </p>
726:             * @param p the project to save
727:             * @throws IOException if it cannot be saved
728:             * @throws IllegalArgumentException if the project was not created through this manager
729:             */
730:            public void saveProject(final Project p) throws IOException,
731:                    IllegalArgumentException {
732:                try {
733:                    mutex().writeAccess(new Mutex.ExceptionAction<Void>() {
734:                        public Void run() throws IOException {
735:                            //removed projects are the ones that cannot be mapped to an existing project type anymore.
736:                            if (removedProjects.contains(p)) {
737:                                return null;
738:                            }
739:                            if (!proj2Factory.containsKey(p)) {
740:                                throw new IllegalArgumentException("Project "
741:                                        + p + " not created by "
742:                                        + ProjectManager.this 
743:                                        + " or was already deleted"); // NOI18N
744:                            }
745:                            if (modifiedProjects.contains(p)) {
746:                                ProjectFactory f = proj2Factory.get(p);
747:                                f.saveProject(p);
748:                                LOG.log(Level.FINE, "saveProject({0})", p
749:                                        .getProjectDirectory());
750:                                modifiedProjects.remove(p);
751:                            }
752:                            return null;
753:                        }
754:                    });
755:                } catch (MutexException e) {
756:                    //##91398 have a more descriptive error message, in case of RO folders.
757:                    // the correct reporting still up to the specific project type.
758:                    if (!p.getProjectDirectory().canWrite()) {
759:                        throw new IOException(
760:                                "Project folder is not writeable.");
761:                    }
762:                    throw (IOException) e.getException();
763:                }
764:            }
765:
766:            /**
767:             * Save all modified projects.
768:             * <p>Acquires write access.
769:             * @throws IOException if any of them cannot be saved
770:             */
771:            public void saveAllProjects() throws IOException {
772:                try {
773:                    mutex().writeAccess(new Mutex.ExceptionAction<Void>() {
774:                        public Void run() throws IOException {
775:                            Iterator<Project> it = modifiedProjects.iterator();
776:                            while (it.hasNext()) {
777:                                Project p = it.next();
778:                                ProjectFactory f = proj2Factory.get(p);
779:                                assert f != null : p;
780:                                f.saveProject(p);
781:                                LOG.log(Level.FINE, "saveProject({0})", p
782:                                        .getProjectDirectory());
783:                                it.remove();
784:                            }
785:                            return null;
786:                        }
787:                    });
788:                } catch (MutexException e) {
789:                    throw (IOException) e.getException();
790:                }
791:            }
792:
793:            /**
794:             * Checks whether a project is still valid.
795:             * <p>Acquires read access.</p>
796:             *
797:             * @since 1.6
798:             *
799:             * @param p a project loaded by this manager
800:             * @return true if the project is still valid, false if it has been deleted
801:             */
802:            public boolean isValid(final Project p) {
803:                return mutex().readAccess(new Mutex.Action<Boolean>() {
804:                    public Boolean run() {
805:                        synchronized (dir2Proj) {
806:                            return proj2Factory.containsKey(p);
807:                        }
808:                    }
809:                });
810:            }
811:
812:            /**
813:             * Removes cache entries for deleted projects.
814:             */
815:            private final class ProjectDeletionListener extends
816:                    FileChangeAdapter {
817:
818:                public ProjectDeletionListener() {
819:                }
820:
821:                @Override
822:                public void fileDeleted(FileEvent fe) {
823:                    synchronized (dir2Proj) {
824:                        dir2Proj.remove(fe.getFile());
825:                    }
826:                }
827:
828:            }
829:
830:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.