Source Code Cross Referenced for AntProjectHelper.java in  » IDE-Netbeans » project.ant » org » netbeans » spi » project » support » ant » 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.spi.project.support.ant 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003:         *
0004:         * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005:         *
0006:         * The contents of this file are subject to the terms of either the GNU
0007:         * General Public License Version 2 only ("GPL") or the Common
0008:         * Development and Distribution License("CDDL") (collectively, the
0009:         * "License"). You may not use this file except in compliance with the
0010:         * License. You can obtain a copy of the License at
0011:         * http://www.netbeans.org/cddl-gplv2.html
0012:         * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013:         * specific language governing permissions and limitations under the
0014:         * License.  When distributing the software, include this License Header
0015:         * Notice in each file and include the License file at
0016:         * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
0017:         * particular file as subject to the "Classpath" exception as provided
0018:         * by Sun in the GPL Version 2 section of the License file that
0019:         * accompanied this code. If applicable, add the following below the
0020:         * License Header, with the fields enclosed by brackets [] replaced by
0021:         * your own identifying information:
0022:         * "Portions Copyrighted [year] [name of copyright owner]"
0023:         *
0024:         * Contributor(s):
0025:         *
0026:         * The Original Software is NetBeans. The Initial Developer of the Original
0027:         * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028:         * Microsystems, Inc. All Rights Reserved.
0029:         *
0030:         * If you wish your version of this file to be governed by only the CDDL
0031:         * or only the GPL Version 2, indicate your decision by adding
0032:         * "[Contributor] elects to include this software in this distribution
0033:         * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034:         * single choice of license, a recipient has the option to distribute
0035:         * your version of this file under either the CDDL, the GPL Version 2 or
0036:         * to extend the choice of license to its licensees as provided above.
0037:         * However, if you add GPL Version 2 code and therefore, elected the GPL
0038:         * Version 2 license, then the option applies only if the new code is
0039:         * made subject to such option by the copyright holder.
0040:         */
0041:
0042:        package org.netbeans.spi.project.support.ant;
0043:
0044:        import java.io.ByteArrayOutputStream;
0045:        import java.io.File;
0046:        import java.io.IOException;
0047:        import java.io.OutputStream;
0048:        import java.util.ArrayList;
0049:        import java.util.HashSet;
0050:        import java.util.Iterator;
0051:        import java.util.List;
0052:        import java.util.Set;
0053:        import javax.xml.parsers.DocumentBuilder;
0054:        import javax.xml.parsers.DocumentBuilderFactory;
0055:        import javax.xml.parsers.ParserConfigurationException;
0056:        import org.netbeans.api.project.Project;
0057:        import org.netbeans.api.project.ProjectManager;
0058:        import org.netbeans.api.project.ant.AntArtifact;
0059:        import org.netbeans.modules.project.ant.AntBasedProjectFactorySingleton;
0060:        import org.netbeans.modules.project.ant.FileChangeSupport;
0061:        import org.netbeans.modules.project.ant.FileChangeSupportEvent;
0062:        import org.netbeans.modules.project.ant.FileChangeSupportListener;
0063:        import org.netbeans.modules.project.ant.ProjectLibraryProvider;
0064:        import org.netbeans.modules.project.ant.UserQuestionHandler;
0065:        import org.netbeans.modules.project.ant.Util;
0066:        import org.netbeans.spi.project.AuxiliaryConfiguration;
0067:        import org.netbeans.spi.project.CacheDirectoryProvider;
0068:        import org.netbeans.spi.project.ProjectState;
0069:        import org.netbeans.spi.queries.FileBuiltQueryImplementation;
0070:        import org.netbeans.spi.queries.SharabilityQueryImplementation;
0071:        import org.openide.ErrorManager;
0072:        import org.openide.filesystems.FileLock;
0073:        import org.openide.filesystems.FileObject;
0074:        import org.openide.filesystems.FileSystem;
0075:        import org.openide.filesystems.FileUtil;
0076:        import org.openide.util.Mutex;
0077:        import org.openide.util.MutexException;
0078:        import org.openide.util.RequestProcessor;
0079:        import org.openide.util.UserQuestionException;
0080:        import org.openide.xml.XMLUtil;
0081:        import org.w3c.dom.Document;
0082:        import org.w3c.dom.Element;
0083:        import org.w3c.dom.Node;
0084:        import org.w3c.dom.NodeList;
0085:        import org.xml.sax.InputSource;
0086:        import org.xml.sax.SAXException;
0087:
0088:        /**
0089:         * Support class for implementing Ant-based projects.
0090:         * @author Jesse Glick
0091:         */
0092:        public final class AntProjectHelper {
0093:
0094:            /**
0095:             * Relative path from project directory to the customary shared properties file.
0096:             */
0097:            public static final String PROJECT_PROPERTIES_PATH = "nbproject/project.properties"; // NOI18N
0098:
0099:            /**
0100:             * Relative path from project directory to the customary private properties file.
0101:             */
0102:            public static final String PRIVATE_PROPERTIES_PATH = "nbproject/private/private.properties"; // NOI18N
0103:
0104:            /**
0105:             * Relative path from project directory to the required shared project metadata file.
0106:             */
0107:            public static final String PROJECT_XML_PATH = AntBasedProjectFactorySingleton.PROJECT_XML_PATH;
0108:
0109:            /**
0110:             * Relative path from project directory to the required private project metadata file.
0111:             */
0112:            public static final String PRIVATE_XML_PATH = "nbproject/private/private.xml"; // NOI18N
0113:
0114:            /**
0115:             * XML namespace of Ant projects.
0116:             */
0117:            static final String PROJECT_NS = AntBasedProjectFactorySingleton.PROJECT_NS;
0118:
0119:            /**
0120:             * XML namespace of private component of Ant projects.
0121:             */
0122:            static final String PRIVATE_NS = "http://www.netbeans.org/ns/project-private/1"; // NOI18N
0123:
0124:            static {
0125:                AntBasedProjectFactorySingleton.HELPER_CALLBACK = new AntBasedProjectFactorySingleton.AntProjectHelperCallback() {
0126:                    public AntProjectHelper createHelper(FileObject dir,
0127:                            Document projectXml, ProjectState state,
0128:                            AntBasedProjectType type) {
0129:                        return new AntProjectHelper(dir, projectXml, state,
0130:                                type);
0131:                    }
0132:
0133:                    public void save(AntProjectHelper helper)
0134:                            throws IOException {
0135:                        helper.save();
0136:                    }
0137:                };
0138:            }
0139:
0140:            private static final RequestProcessor RP = new RequestProcessor(
0141:                    "AntProjectHelper.RP"); // NOI18N
0142:
0143:            /**
0144:             * Project base directory.
0145:             */
0146:            private final FileObject dir;
0147:
0148:            /**
0149:             * State object permitting modifications.
0150:             */
0151:            private final ProjectState state;
0152:
0153:            /**
0154:             * Ant-based project type factory.
0155:             */
0156:            private final AntBasedProjectType type;
0157:
0158:            /**
0159:             * Cached project.xml parse (null if not loaded).
0160:             * Access within {@link #modifiedMetadataPaths} monitor.
0161:             */
0162:            private Document projectXml;
0163:
0164:            /**
0165:             * Cached private.xml parse (null if not loaded).
0166:             * Access within {@link #modifiedMetadataPaths} monitor.
0167:             */
0168:            private Document privateXml;
0169:
0170:            /**
0171:             * Set of relative paths to metadata files which have been modified
0172:             * and which need to be saved.
0173:             * Also server as a monitor for {@link #projectXml} and {@link #privateXml} accesses;
0174:             * Xerces' DOM is not thread-safe <em>even for reading<em> (#50198).
0175:             */
0176:            private final Set<String> modifiedMetadataPaths = new HashSet<String>();
0177:
0178:            /**
0179:             * Registered listeners.
0180:             * Access must be directly synchronized.
0181:             */
0182:            private final List<AntProjectListener> listeners = new ArrayList<AntProjectListener>();
0183:
0184:            /**
0185:             * List of loaded properties.
0186:             */
0187:            private final ProjectProperties properties;
0188:
0189:            /** Listener to XML files; needs to be held as an instance field so it is not GC'd */
0190:            private final FileChangeSupportListener fileListener;
0191:
0192:            /** True if currently saving XML files. */
0193:            private boolean writingXML = false;
0194:
0195:            /**
0196:             * Hook waiting to be called. See issue #57794.
0197:             */
0198:            private ProjectXmlSavedHook pendingHook;
0199:            /**
0200:             * Number of metadata files remaining to be written before {@link #pendingHook} can be called.
0201:             * Javadoc for {@link ProjectXmlSavedHook} only guarantees that project.xml will be written,
0202:             * but best to be safe and make sure also private.xml and *.properties are too.
0203:             */
0204:            private int pendingHookCount;
0205:
0206:            // XXX lock any loaded XML files while the project is modified, to prevent manual editing,
0207:            // and reload any modified files if the project is unmodified
0208:
0209:            private AntProjectHelper(FileObject dir, Document projectXml,
0210:                    ProjectState state, AntBasedProjectType type) {
0211:                this .dir = dir;
0212:                assert dir != null && FileUtil.toFile(dir) != null;
0213:                this .state = state;
0214:                assert state != null;
0215:                this .type = type;
0216:                assert type != null;
0217:                this .projectXml = projectXml;
0218:                assert projectXml != null;
0219:                properties = new ProjectProperties(this );
0220:                fileListener = new FileListener();
0221:                FileChangeSupport.DEFAULT.addListener(fileListener,
0222:                        resolveFile(PROJECT_XML_PATH));
0223:                FileChangeSupport.DEFAULT.addListener(fileListener,
0224:                        resolveFile(PRIVATE_XML_PATH));
0225:            }
0226:
0227:            /**
0228:             * Get the corresponding Ant-based project type factory.
0229:             */
0230:            AntBasedProjectType getType() {
0231:                return type;
0232:            }
0233:
0234:            /**
0235:             * Retrieve project.xml or private.xml, loading from disk as needed.
0236:             * private.xml is created as a skeleton on demand.
0237:             */
0238:            private Document getConfigurationXml(boolean shared) {
0239:                assert ProjectManager.mutex().isReadAccess()
0240:                        || ProjectManager.mutex().isWriteAccess();
0241:                assert Thread.holdsLock(modifiedMetadataPaths);
0242:                Document xml = shared ? projectXml : privateXml;
0243:                if (xml == null) {
0244:                    String path = shared ? PROJECT_XML_PATH : PRIVATE_XML_PATH;
0245:                    xml = loadXml(path);
0246:                    if (xml == null) {
0247:                        // Missing or broken; create a skeleton.
0248:                        String element = shared ? "project" : "project-private"; // NOI18N
0249:                        String ns = shared ? PROJECT_NS : PRIVATE_NS;
0250:                        xml = XMLUtil.createDocument(element, ns, null, null);
0251:                        if (shared) {
0252:                            // #46048: need to generate minimal compliant XML skeleton.
0253:                            Element typeEl = xml.createElementNS(PROJECT_NS,
0254:                                    "type"); // NOI18N
0255:                            typeEl.appendChild(xml.createTextNode(getType()
0256:                                    .getType()));
0257:                            xml.getDocumentElement().appendChild(typeEl);
0258:                            xml.getDocumentElement().appendChild(
0259:                                    xml.createElementNS(PROJECT_NS,
0260:                                            "configuration")); // NOI18N
0261:                        }
0262:                    }
0263:                    if (shared) {
0264:                        projectXml = xml;
0265:                    } else {
0266:                        privateXml = xml;
0267:                    }
0268:                }
0269:                assert xml != null;
0270:                return xml;
0271:            }
0272:
0273:            /**
0274:             * If true, do not report XML load errors.
0275:             * For use only by unit tests.
0276:             */
0277:            static boolean QUIETLY_SWALLOW_XML_LOAD_ERRORS = false;
0278:
0279:            /**
0280:             * Try to load a config XML file from a named path.
0281:             * If the file does not exist, or there is any load error, return null.
0282:             */
0283:            private Document loadXml(String path) {
0284:                assert ProjectManager.mutex().isReadAccess()
0285:                        || ProjectManager.mutex().isWriteAccess();
0286:                assert Thread.holdsLock(modifiedMetadataPaths);
0287:                FileObject xml = dir.getFileObject(path);
0288:                if (xml == null || !xml.isData()) {
0289:                    return null;
0290:                }
0291:                File f = FileUtil.toFile(xml);
0292:                assert f != null;
0293:                try {
0294:                    return XMLUtil.parse(new InputSource(f.toURI().toString()),
0295:                            false, true, Util.defaultErrorHandler(), null);
0296:                } catch (IOException e) {
0297:                    if (!QUIETLY_SWALLOW_XML_LOAD_ERRORS) {
0298:                        ErrorManager.getDefault().notify(
0299:                                ErrorManager.INFORMATIONAL, e);
0300:                    }
0301:                } catch (SAXException e) {
0302:                    if (!QUIETLY_SWALLOW_XML_LOAD_ERRORS) {
0303:                        ErrorManager.getDefault().notify(
0304:                                ErrorManager.INFORMATIONAL, e);
0305:                    }
0306:                }
0307:                return null;
0308:            }
0309:
0310:            /**
0311:             * Save an XML config file to a named path.
0312:             * If the file does not yet exist, it is created.
0313:             */
0314:            private FileLock saveXml(final Document doc, final String path)
0315:                    throws IOException {
0316:                assert ProjectManager.mutex().isWriteAccess();
0317:                assert !writingXML;
0318:                assert Thread.holdsLock(modifiedMetadataPaths);
0319:                final FileLock[] _lock = new FileLock[1];
0320:                writingXML = true;
0321:                try {
0322:                    dir.getFileSystem().runAtomicAction(
0323:                            new FileSystem.AtomicAction() {
0324:                                public void run() throws IOException {
0325:                                    // Keep a copy of xml *while holding modifiedMetadataPaths monitor*.
0326:                                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
0327:                                    XMLUtil.write(doc, baos, "UTF-8"); // NOI18N
0328:                                    final byte[] data = baos.toByteArray();
0329:                                    final FileObject xml = FileUtil.createData(
0330:                                            dir, path);
0331:                                    try {
0332:                                        _lock[0] = xml.lock(); // unlocked by {@link #save}
0333:                                        OutputStream os = xml
0334:                                                .getOutputStream(_lock[0]);
0335:                                        try {
0336:                                            os.write(data);
0337:                                        } finally {
0338:                                            os.close();
0339:                                        }
0340:                                    } catch (UserQuestionException uqe) { // #46089
0341:                                        needPendingHook();
0342:                                        UserQuestionHandler
0343:                                                .handle(
0344:                                                        uqe,
0345:                                                        new UserQuestionHandler.Callback() {
0346:                                                            public void accepted() {
0347:                                                                // Try again.
0348:                                                                assert !writingXML;
0349:                                                                writingXML = true;
0350:                                                                try {
0351:                                                                    FileLock lock = xml
0352:                                                                            .lock();
0353:                                                                    try {
0354:                                                                        OutputStream os = xml
0355:                                                                                .getOutputStream(lock);
0356:                                                                        try {
0357:                                                                            os
0358:                                                                                    .write(data);
0359:                                                                        } finally {
0360:                                                                            os
0361:                                                                                    .close();
0362:                                                                        }
0363:                                                                    } finally {
0364:                                                                        lock
0365:                                                                                .releaseLock();
0366:                                                                    }
0367:                                                                    maybeCallPendingHook();
0368:                                                                } catch (IOException e) {
0369:                                                                    // Oh well.
0370:                                                                    ErrorManager
0371:                                                                            .getDefault()
0372:                                                                            .notify(
0373:                                                                                    e);
0374:                                                                    reload();
0375:                                                                } finally {
0376:                                                                    writingXML = false;
0377:                                                                }
0378:                                                            }
0379:
0380:                                                            public void denied() {
0381:                                                                reload();
0382:                                                            }
0383:
0384:                                                            public void error(
0385:                                                                    IOException e) {
0386:                                                                ErrorManager
0387:                                                                        .getDefault()
0388:                                                                        .notify(
0389:                                                                                e);
0390:                                                                reload();
0391:                                                            }
0392:
0393:                                                            private void reload() {
0394:                                                                // Revert the save.
0395:                                                                if (path
0396:                                                                        .equals(PROJECT_XML_PATH)) {
0397:                                                                    synchronized (modifiedMetadataPaths) {
0398:                                                                        projectXml = null;
0399:                                                                    }
0400:                                                                } else {
0401:                                                                    assert path
0402:                                                                            .equals(PRIVATE_XML_PATH) : path;
0403:                                                                    synchronized (modifiedMetadataPaths) {
0404:                                                                        privateXml = null;
0405:                                                                    }
0406:                                                                }
0407:                                                                fireExternalChange(path);
0408:                                                                cancelPendingHook();
0409:                                                            }
0410:                                                        });
0411:                                    }
0412:                                }
0413:                            });
0414:                } finally {
0415:                    writingXML = false;
0416:                }
0417:                return _lock[0];
0418:            }
0419:
0420:            /**
0421:             * Get the <code>&lt;configuration&gt;</code> element of project.xml
0422:             * or the document element of private.xml.
0423:             * Beneath this point you can load and store configuration fragments.
0424:             * @param shared if true, use project.xml, else private.xml
0425:             * @return the data root
0426:             */
0427:            private Element getConfigurationDataRoot(boolean shared) {
0428:                assert ProjectManager.mutex().isReadAccess()
0429:                        || ProjectManager.mutex().isWriteAccess();
0430:                assert Thread.holdsLock(modifiedMetadataPaths);
0431:                Document doc = getConfigurationXml(shared);
0432:                if (shared) {
0433:                    Element project = doc.getDocumentElement();
0434:                    Element config = Util.findElement(project, "configuration",
0435:                            PROJECT_NS); // NOI18N
0436:                    assert config != null;
0437:                    return config;
0438:                } else {
0439:                    return doc.getDocumentElement();
0440:                }
0441:            }
0442:
0443:            /**
0444:             * Add a listener to changes in the project configuration.
0445:             * <p>Thread-safe.
0446:             * @param listener a listener to add
0447:             */
0448:            public void addAntProjectListener(AntProjectListener listener) {
0449:                synchronized (listeners) {
0450:                    listeners.add(listener);
0451:                }
0452:            }
0453:
0454:            /**
0455:             * Remove a listener to changes in the project configuration.
0456:             * <p>Thread-safe.
0457:             * @param listener a listener to remove
0458:             */
0459:            public void removeAntProjectListener(AntProjectListener listener) {
0460:                synchronized (listeners) {
0461:                    listeners.remove(listener);
0462:                }
0463:            }
0464:
0465:            /**
0466:             * Fire a change of external provenance to all listeners.
0467:             * Acquires write access.
0468:             * @param path path to the changed file (XML or properties)
0469:             */
0470:            void fireExternalChange(final String path) {
0471:                final Mutex.Action<Void> action = new Mutex.Action<Void>() {
0472:                    public Void run() {
0473:                        fireChange(path, false);
0474:                        return null;
0475:                    }
0476:                };
0477:                if (ProjectManager.mutex().isWriteAccess()
0478:                        || ProjectLibraryProvider.FIRE_CHANGES_SYNCH) {
0479:                    // Run it right now. postReadRequest would be too late.
0480:                    ProjectManager.mutex().readAccess(action);
0481:                } else if (ProjectManager.mutex().isReadAccess()) {
0482:                    // Run immediately also. No need to switch to read access.
0483:                    action.run();
0484:                } else {
0485:                    // Not safe to acquire a new lock, so run later in read access.
0486:                    RP.post(new Runnable() {
0487:                        public void run() {
0488:                            ProjectManager.mutex().readAccess(action);
0489:                        }
0490:                    });
0491:                }
0492:            }
0493:
0494:            /**
0495:             * Fire a change to all listeners.
0496:             * Must be called from write access; enters read access while firing.
0497:             * @param path path to the changed file (XML or properties)
0498:             * @param expected true if the result of an API-initiated change, false if from external causes
0499:             */
0500:            private void fireChange(String path, boolean expected) {
0501:                assert ProjectManager.mutex().isReadAccess()
0502:                        || ProjectManager.mutex().isWriteAccess();
0503:                final AntProjectListener[] _listeners;
0504:                synchronized (listeners) {
0505:                    if (listeners.isEmpty()) {
0506:                        return;
0507:                    }
0508:                    _listeners = listeners
0509:                            .toArray(new AntProjectListener[listeners.size()]);
0510:                }
0511:                final AntProjectEvent ev = new AntProjectEvent(this , path,
0512:                        expected);
0513:                final boolean xml = path.equals(PROJECT_XML_PATH)
0514:                        || path.equals(PRIVATE_XML_PATH);
0515:                ProjectManager.mutex().readAccess(new Mutex.Action<Void>() {
0516:                    public Void run() {
0517:                        for (AntProjectListener l : _listeners) {
0518:                            try {
0519:                                if (xml) {
0520:                                    l.configurationXmlChanged(ev);
0521:                                } else {
0522:                                    l.propertiesChanged(ev);
0523:                                }
0524:                            } catch (RuntimeException e) {
0525:                                // Don't prevent other listeners from being notified.
0526:                                ErrorManager.getDefault().notify(e);
0527:                            }
0528:                        }
0529:                        return null;
0530:                    }
0531:                });
0532:            }
0533:
0534:            /**
0535:             * Call when explicitly modifying some piece of metadata.
0536:             */
0537:            private void modifying(String path) {
0538:                assert ProjectManager.mutex().isWriteAccess();
0539:                state.markModified();
0540:                synchronized (modifiedMetadataPaths) {
0541:                    modifiedMetadataPaths.add(path);
0542:                }
0543:                fireChange(path, true);
0544:            }
0545:
0546:            /**
0547:             * Get the top-level project directory.
0548:             * @return the project directory beneath which everything in the project lies
0549:             */
0550:            public FileObject getProjectDirectory() {
0551:                return dir;
0552:            }
0553:
0554:            /**Notification that this project has been deleted.
0555:             * @see org.netbeans.spi.project.ProjectState#notifyDeleted
0556:             *
0557:             * @since 1.8
0558:             */
0559:            public void notifyDeleted() {
0560:                state.notifyDeleted();
0561:            }
0562:
0563:            /**
0564:             * Mark this project as being modified without actually changing anything in it.
0565:             * Should only be called from {@link ProjectGenerator#createProject}.
0566:             */
0567:            void markModified() {
0568:                assert ProjectManager.mutex().isWriteAccess();
0569:                state.markModified();
0570:                // To make sure projectXmlSaved is called:
0571:                synchronized (modifiedMetadataPaths) {
0572:                    modifiedMetadataPaths.add(PROJECT_XML_PATH);
0573:                }
0574:            }
0575:
0576:            /**
0577:             * Check whether this project is currently modified including modifications
0578:             * to <code>project.xml</code>.
0579:             * Access from GeneratedFilesHelper.
0580:             */
0581:            boolean isProjectXmlModified() {
0582:                assert ProjectManager.mutex().isReadAccess()
0583:                        || ProjectManager.mutex().isWriteAccess();
0584:                return modifiedMetadataPaths.contains(PROJECT_XML_PATH);
0585:            }
0586:
0587:            /**
0588:             * Save all cached project metadata.
0589:             * If <code>project.xml</code> was one of the modified files, then
0590:             * {@link AntBasedProjectType#projectXmlSaved} is called, presumably
0591:             * creating <code>build-impl.xml</code> and/or <code>build.xml</code>.
0592:             */
0593:            private void save() throws IOException {
0594:                assert ProjectManager.mutex().isWriteAccess();
0595:                if (!getProjectDirectory().isValid()) {
0596:                    //ProjectManager.saveProject() is called when project is deleted externally..
0597:                    return;
0598:                }
0599:                Set<FileLock> locks = new HashSet<FileLock>();
0600:                try {
0601:                    synchronized (modifiedMetadataPaths) {
0602:                        assert !modifiedMetadataPaths.isEmpty();
0603:                        assert pendingHook == null;
0604:                        if (modifiedMetadataPaths.contains(PROJECT_XML_PATH)) {
0605:                            // Saving project.xml so look for that hook.
0606:                            Project p = AntBasedProjectFactorySingleton
0607:                                    .getProjectFor(this );
0608:                            pendingHook = p.getLookup().lookup(
0609:                                    ProjectXmlSavedHook.class);
0610:                            // might still be null
0611:                        }
0612:                        Iterator<String> it = modifiedMetadataPaths.iterator();
0613:                        while (it.hasNext()) {
0614:                            String path = it.next();
0615:                            if (path.equals(PROJECT_XML_PATH)) {
0616:                                assert projectXml != null;
0617:                                locks.add(saveXml(projectXml, path));
0618:                            } else if (path.equals(PRIVATE_XML_PATH)) {
0619:                                assert privateXml != null;
0620:                                locks.add(saveXml(privateXml, path));
0621:                            } else {
0622:                                // All else is assumed to be a properties file.
0623:                                locks.add(properties.write(path));
0624:                            }
0625:                            // As metadata files are saved, take them off the modified list.
0626:                            it.remove();
0627:                        }
0628:                        if (pendingHook != null && pendingHookCount == 0) {
0629:                            try {
0630:                                pendingHook.projectXmlSaved();
0631:                            } catch (IOException e) {
0632:                                // Treat it as still modified.
0633:                                modifiedMetadataPaths.add(PROJECT_XML_PATH);
0634:                                throw e;
0635:                            }
0636:                        }
0637:                    }
0638:                } finally {
0639:                    // #57791: release locks outside synchronized block.
0640:                    locks.remove(null);
0641:                    for (FileLock lock : locks) {
0642:                        lock.releaseLock();
0643:                    }
0644:                    // More #57794.
0645:                    if (pendingHookCount == 0) {
0646:                        pendingHook = null;
0647:                    }
0648:                }
0649:            }
0650:
0651:            /** See issue #57794. */
0652:            void maybeCallPendingHook() {
0653:                // XXX synchronization of this method?
0654:                assert pendingHookCount > 0;
0655:                pendingHookCount--;
0656:                //#67465: the pendingHook may be null if project.xml is not being written
0657:                //eg. only project.properties is being saved:
0658:                if (pendingHookCount == 0 && pendingHook != null) {
0659:                    try {
0660:                        ProjectManager.mutex().writeAccess(
0661:                                new Mutex.ExceptionAction<Void>() {
0662:                                    public Void run() throws IOException {
0663:                                        pendingHook.projectXmlSaved();
0664:                                        return null;
0665:                                    }
0666:                                });
0667:                    } catch (MutexException e) {
0668:                        // XXX mark project modified again??
0669:                        ErrorManager.getDefault().notify(e);
0670:                    } finally {
0671:                        pendingHook = null;
0672:                    }
0673:                }
0674:            }
0675:
0676:            void cancelPendingHook() {
0677:                assert pendingHookCount > 0;
0678:                pendingHookCount--;
0679:                if (pendingHookCount == 0) {
0680:                    pendingHook = null;
0681:                }
0682:            }
0683:
0684:            void needPendingHook() {
0685:                pendingHookCount++;
0686:            }
0687:
0688:            /**
0689:             * Load a property file from some location in the project.
0690:             * The returned object may be edited but you must call {@link #putProperties}
0691:             * to save any changes you make.
0692:             * If the file does not (yet) exist or could not be loaded for whatever reason,
0693:             * an empty properties list is returned instead.
0694:             * @param path a relative URI in the project directory, e.g.
0695:             *             {@link #PROJECT_PROPERTIES_PATH} or {@link #PRIVATE_PROPERTIES_PATH}
0696:             * @return a set of properties
0697:             */
0698:            public EditableProperties getProperties(final String path) {
0699:                if (path.equals(AntProjectHelper.PROJECT_XML_PATH)
0700:                        || path.equals(AntProjectHelper.PRIVATE_XML_PATH)) {
0701:                    throw new IllegalArgumentException(
0702:                            "Attempt to load properties from a project XML file"); // NOI18N
0703:                }
0704:                return ProjectManager.mutex().readAccess(
0705:                        new Mutex.Action<EditableProperties>() {
0706:                            public EditableProperties run() {
0707:                                return properties.getProperties(path);
0708:                            }
0709:                        });
0710:            }
0711:
0712:            /**
0713:             * Store a property file to some location in the project.
0714:             * A clone will be made of the supplied properties file so as to snapshot it.
0715:             * The new properties are not actually stored to disk immediately, but the project
0716:             * is marked modified so that they will be later.
0717:             * You can store to a path that does not yet exist and the file will be created
0718:             * if and when the project is saved.
0719:             * If the old value is the same as the new, nothing is done.
0720:             * Otherwise an expected properties change event is fired.
0721:             * <p>Acquires write access from {@link ProjectManager#mutex}. However, you are well
0722:             * advised to explicitly enclose a <em>complete</em> operation within write access,
0723:             * starting with {@link #getProperties}, to prevent race conditions.
0724:             * @param path a relative URI in the project directory, e.g.
0725:             *             {@link #PROJECT_PROPERTIES_PATH} or {@link #PRIVATE_PROPERTIES_PATH}
0726:             * @param props a set of properties to store, or null to delete any existing properties file there
0727:             */
0728:            public void putProperties(final String path,
0729:                    final EditableProperties props) {
0730:                if (path.equals(AntProjectHelper.PROJECT_XML_PATH)
0731:                        || path.equals(AntProjectHelper.PRIVATE_XML_PATH)) {
0732:                    throw new IllegalArgumentException(
0733:                            "Attempt to store properties from a project XML file"); // NOI18N
0734:                }
0735:                ProjectManager.mutex().writeAccess(new Mutex.Action<Void>() {
0736:                    public Void run() {
0737:                        if (properties.putProperties(path, props)) {
0738:                            modifying(path);
0739:                        }
0740:                        return null;
0741:                    }
0742:                });
0743:            }
0744:
0745:            /**
0746:             * Get a property provider that works with loadable project properties.
0747:             * Its current values should match {@link #getProperties}, and calls to
0748:             * {@link #putProperties} should cause it to fire changes.
0749:             * @param path a relative URI in the project directory, e.g.
0750:             *             {@link #PROJECT_PROPERTIES_PATH} or {@link #PRIVATE_PROPERTIES_PATH}
0751:             * @return a property provider implementation
0752:             */
0753:            public PropertyProvider getPropertyProvider(final String path) {
0754:                if (path.equals(AntProjectHelper.PROJECT_XML_PATH)
0755:                        || path.equals(AntProjectHelper.PRIVATE_XML_PATH)) {
0756:                    throw new IllegalArgumentException(
0757:                            "Attempt to store properties from a project XML file"); // NOI18N
0758:                }
0759:                return ProjectManager.mutex().readAccess(
0760:                        new Mutex.Action<PropertyProvider>() {
0761:                            public PropertyProvider run() {
0762:                                return properties.getPropertyProvider(path);
0763:                            }
0764:                        });
0765:            }
0766:
0767:            /**
0768:             * Get the primary configuration data for this project.
0769:             * The returned element will be named according to
0770:             * {@link AntBasedProjectType#getPrimaryConfigurationDataElementName} and
0771:             * {@link AntBasedProjectType#getPrimaryConfigurationDataElementNamespace}.
0772:             * The project may read this document fragment to get custom information
0773:             * from <code>nbproject/project.xml</code> and <code>nbproject/private/private.xml</code>.
0774:             * The fragment will have no parent node and while it may be modified, you must
0775:             * use {@link #putPrimaryConfigurationData} to store any changes.
0776:             * @param shared if true, refers to <code>project.xml</code>, else refers to
0777:             *               <code>private.xml</code>
0778:             * @return the configuration data that is available
0779:             */
0780:            public Element getPrimaryConfigurationData(final boolean shared) {
0781:                final String name = type
0782:                        .getPrimaryConfigurationDataElementName(shared);
0783:                assert name.indexOf(':') == -1;
0784:                final String namespace = type
0785:                        .getPrimaryConfigurationDataElementNamespace(shared);
0786:                assert namespace != null && namespace.length() > 0;
0787:                return ProjectManager.mutex().readAccess(
0788:                        new Mutex.Action<Element>() {
0789:                            public Element run() {
0790:                                synchronized (modifiedMetadataPaths) {
0791:                                    Element el = getConfigurationFragment(name,
0792:                                            namespace, shared);
0793:                                    if (el != null) {
0794:                                        return el;
0795:                                    } else {
0796:                                        // No such data, corrupt file.
0797:                                        return cloneSafely(getConfigurationXml(
0798:                                                shared).createElementNS(
0799:                                                namespace, name));
0800:                                    }
0801:                                }
0802:                            }
0803:                        });
0804:            }
0805:
0806:            /**
0807:             * Store the primary configuration data for this project.
0808:             * The supplied element must be named according to
0809:             * {@link AntBasedProjectType#getPrimaryConfigurationDataElementName} and
0810:             * {@link AntBasedProjectType#getPrimaryConfigurationDataElementNamespace}.
0811:             * The project may save this document fragment to set custom information
0812:             * in <code>nbproject/project.xml</code> and <code>nbproject/private/private.xml</code>.
0813:             * The fragment will be cloned and so further modifications will have no effect.
0814:             * <p>Acquires write access from {@link ProjectManager#mutex}. However, you are well
0815:             * advised to explicitly enclose a <em>complete</em> operation within write access,
0816:             * starting with {@link #getPrimaryConfigurationData}, to prevent race conditions.
0817:             * @param data the desired new configuration data
0818:             * @param shared if true, refers to <code>project.xml</code>, else refers to
0819:             *               <code>private.xml</code>
0820:             * @throws IllegalArgumentException if the element is not correctly named
0821:             */
0822:            public void putPrimaryConfigurationData(Element data, boolean shared)
0823:                    throws IllegalArgumentException {
0824:                String name = type
0825:                        .getPrimaryConfigurationDataElementName(shared);
0826:                assert name.indexOf(':') == -1;
0827:                String namespace = type
0828:                        .getPrimaryConfigurationDataElementNamespace(shared);
0829:                assert namespace != null && namespace.length() > 0;
0830:                if (!name.equals(data.getLocalName())
0831:                        || !namespace.equals(data.getNamespaceURI())) {
0832:                    throw new IllegalArgumentException(
0833:                            "Wrong name/namespace: expected {" + namespace
0834:                                    + "}" + name + " but was {"
0835:                                    + data.getNamespaceURI() + "}"
0836:                                    + data.getLocalName()); // NOI18N
0837:                }
0838:                putConfigurationFragment(data, shared);
0839:            }
0840:
0841:            private final class FileListener implements 
0842:                    FileChangeSupportListener {
0843:
0844:                public FileListener() {
0845:                }
0846:
0847:                private void change(File f) {
0848:                    if (writingXML) {
0849:                        return;
0850:                    }
0851:                    String path;
0852:                    synchronized (modifiedMetadataPaths) {
0853:                        if (f.equals(resolveFile(PROJECT_XML_PATH))) {
0854:                            if (modifiedMetadataPaths
0855:                                    .contains(PROJECT_XML_PATH)) {
0856:                                //#68872: don't do anything if the given file has non-saved changes:
0857:                                return;
0858:                            }
0859:                            path = PROJECT_XML_PATH;
0860:                            projectXml = null;
0861:                        } else if (f.equals(resolveFile(PRIVATE_XML_PATH))) {
0862:                            if (modifiedMetadataPaths
0863:                                    .contains(PRIVATE_XML_PATH)) {
0864:                                //#68872: don't do anything if the given file has non-saved changes:
0865:                                return;
0866:                            }
0867:                            path = PRIVATE_XML_PATH;
0868:                            privateXml = null;
0869:                        } else {
0870:                            throw new AssertionError(
0871:                                    "Unexpected file change in " + f); // NOI18N
0872:                        }
0873:                    }
0874:                    fireExternalChange(path);
0875:                }
0876:
0877:                public void fileCreated(FileChangeSupportEvent event) {
0878:                    change(event.getPath());
0879:                }
0880:
0881:                public void fileDeleted(FileChangeSupportEvent event) {
0882:                    change(event.getPath());
0883:                }
0884:
0885:                public void fileModified(FileChangeSupportEvent event) {
0886:                    change(event.getPath());
0887:                }
0888:
0889:            }
0890:
0891:            /**
0892:             * Get a piece of the configuration subtree by name.
0893:             * @param elementName the simple XML element name expected
0894:             * @param namespace the XML namespace expected
0895:             * @param shared to use project.xml vs. private.xml
0896:             * @return (a clone of) the named configuration fragment, or null if it does not exist
0897:             */
0898:            Element getConfigurationFragment(final String elementName,
0899:                    final String namespace, final boolean shared) {
0900:                return ProjectManager.mutex().readAccess(
0901:                        new Mutex.Action<Element>() {
0902:                            public Element run() {
0903:                                synchronized (modifiedMetadataPaths) {
0904:                                    Element root = getConfigurationDataRoot(shared);
0905:                                    Element data = Util.findElement(root,
0906:                                            elementName, namespace);
0907:                                    if (data != null) {
0908:                                        return cloneSafely(data);
0909:                                    } else {
0910:                                        return null;
0911:                                    }
0912:                                }
0913:                            }
0914:                        });
0915:            }
0916:
0917:            private static final DocumentBuilder db;
0918:            static {
0919:                try {
0920:                    db = DocumentBuilderFactory.newInstance()
0921:                            .newDocumentBuilder();
0922:                } catch (ParserConfigurationException e) {
0923:                    throw new AssertionError(e);
0924:                }
0925:            }
0926:
0927:            private static Element cloneSafely(Element el) {
0928:                // #50198: for thread safety, use a separate document.
0929:                // Using XMLUtil.createDocument is much too slow.
0930:                synchronized (db) {
0931:                    Document dummy = db.newDocument();
0932:                    return (Element) dummy.importNode(el, true);
0933:                }
0934:            }
0935:
0936:            /**
0937:             * Store a piece of the configuration subtree by name.
0938:             * @param fragment a piece of the subtree to store (overwrite or add)
0939:             * @param shared to use project.xml vs. private.xml
0940:             */
0941:            void putConfigurationFragment(final Element fragment,
0942:                    final boolean shared) {
0943:                ProjectManager.mutex().writeAccess(new Mutex.Action<Void>() {
0944:                    public Void run() {
0945:                        synchronized (modifiedMetadataPaths) {
0946:                            Element root = getConfigurationDataRoot(shared);
0947:                            Element existing = Util
0948:                                    .findElement(root, fragment.getLocalName(),
0949:                                            fragment.getNamespaceURI());
0950:                            // XXX first compare to existing and return if the same
0951:                            if (existing != null) {
0952:                                root.removeChild(existing);
0953:                            }
0954:                            // the children are alphabetize: find correct place to insert new node
0955:                            Node ref = null;
0956:                            NodeList list = root.getChildNodes();
0957:                            for (int i = 0; i < list.getLength(); i++) {
0958:                                Node node = list.item(i);
0959:                                if (node.getNodeType() != Node.ELEMENT_NODE) {
0960:                                    continue;
0961:                                }
0962:                                int comparison = node.getNodeName().compareTo(
0963:                                        fragment.getNodeName());
0964:                                if (comparison == 0) {
0965:                                    comparison = node.getNamespaceURI()
0966:                                            .compareTo(
0967:                                                    fragment.getNamespaceURI());
0968:                                }
0969:                                if (comparison > 0) {
0970:                                    ref = node;
0971:                                    break;
0972:                                }
0973:                            }
0974:                            root.insertBefore(root.getOwnerDocument()
0975:                                    .importNode(fragment, true), ref);
0976:                            modifying(shared ? PROJECT_XML_PATH
0977:                                    : PRIVATE_XML_PATH);
0978:                        }
0979:                        return null;
0980:                    }
0981:                });
0982:            }
0983:
0984:            /**
0985:             * Remove a piece of the configuration subtree by name.
0986:             * @param elementName the simple XML element name expected
0987:             * @param namespace the XML namespace expected
0988:             * @param shared to use project.xml vs. private.xml
0989:             * @return true if anything was actually removed
0990:             */
0991:            boolean removeConfigurationFragment(final String elementName,
0992:                    final String namespace, final boolean shared) {
0993:                return ProjectManager.mutex().writeAccess(
0994:                        new Mutex.Action<Boolean>() {
0995:                            public Boolean run() {
0996:                                synchronized (modifiedMetadataPaths) {
0997:                                    Element root = getConfigurationDataRoot(shared);
0998:                                    Element data = Util.findElement(root,
0999:                                            elementName, namespace);
1000:                                    if (data != null) {
1001:                                        root.removeChild(data);
1002:                                        modifying(shared ? PROJECT_XML_PATH
1003:                                                : PRIVATE_XML_PATH);
1004:                                        return true;
1005:                                    } else {
1006:                                        return false;
1007:                                    }
1008:                                }
1009:                            }
1010:                        });
1011:            }
1012:
1013:            /**
1014:             * Create an object permitting this project to store auxiliary configuration.
1015:             * Would be placed into the project's lookup.
1016:             * @return an auxiliary configuration provider object suitable for the project lookup
1017:             */
1018:            public AuxiliaryConfiguration createAuxiliaryConfiguration() {
1019:                return new ExtensibleMetadataProviderImpl(this );
1020:            }
1021:
1022:            /**
1023:             * Create an object permitting this project to expose a cache directory.
1024:             * Would be placed into the project's lookup.
1025:             * @return a cache directory provider object suitable for the project lookup
1026:             */
1027:            public CacheDirectoryProvider createCacheDirectoryProvider() {
1028:                return new ExtensibleMetadataProviderImpl(this );
1029:            }
1030:
1031:            /**
1032:             * Create an implementation of {@link org.netbeans.api.queries.FileBuiltQuery} that works with files
1033:             * within the project based on simple glob pattern mappings.
1034:             * <p>
1035:             * It is intended to be
1036:             * placed in {@link org.netbeans.api.project.Project#getLookup}.
1037:             * <p>
1038:             * It will return status objects for any files in the project matching a source
1039:             * glob pattern - this must include exactly one asterisk (<code>*</code>)
1040:             * representing a variable portion of a source file path (always slash-separated
1041:             * and relative to the project directory) and may include some Ant property
1042:             * references which will be resolved as per the property evaluator.
1043:             * A file is considered out of date if there is no file represented by the
1044:             * matching target pattern (which has the same format), or the target file is older
1045:             * than the source file, or the source file is modified as per
1046:             * {@link org.openide.loaders.DataObject#isModified}.
1047:             * An attempt is made to fire changes from the status object whenever the result
1048:             * should change from one call to the next.
1049:             * <p>
1050:             * The (evaluated) source and target patterns may be relative, resolved against
1051:             * the project directory (perhaps going outside it), or absolute.
1052:             * </p>
1053:             * <div class="nonnormative">
1054:             * <p>
1055:             * A typical set of source and target patterns would be:
1056:             * </p>
1057:             * <ol>
1058:             * <li><samp>${src.dir}/*.java</samp>
1059:             * <li><samp>${test.src.dir}/*.java</samp>
1060:             * </ol>
1061:             * <ol>
1062:             * <li><samp>${build.classes.dir}/*.class</samp>
1063:             * <li><samp>${test.build.classes.dir}/*.class</samp>
1064:             * </ol>
1065:             * </div>
1066:             * @param eval a property evaluator to interpret the patterns with
1067:             * @param from a list of glob patterns for source files
1068:             * @param to a matching list of glob patterns for built files
1069:             * @return a query implementation
1070:             * @throws IllegalArgumentException if either from or to patterns
1071:             *                                  have zero or multiple asterisks,
1072:             *                                  or the arrays are not of equal lengths
1073:             */
1074:            public FileBuiltQueryImplementation createGlobFileBuiltQuery(
1075:                    PropertyEvaluator eval, String[] from, String[] to)
1076:                    throws IllegalArgumentException {
1077:                return new GlobFileBuiltQuery(this , eval, from, to);
1078:            }
1079:
1080:            /**
1081:             * Create a basic implementation of {@link AntArtifact} which assumes everything of interest
1082:             * is in a fixed location under a standard Ant-based project.
1083:             * @param type the type of artifact, e.g. <a href="@JAVA/PROJECT@/org/netbeans/api/java/project/JavaProjectConstants.html#ARTIFACT_TYPE_JAR"><code>JavaProjectConstants.ARTIFACT_TYPE_JAR</code></a>
1084:             * @param locationProperty an Ant property name giving the project-relative
1085:             *                         location of the artifact, e.g. <samp>dist.jar</samp>
1086:             * @param eval a way to evaluate the location property (e.g. {@link #getStandardPropertyEvaluator})
1087:             * @param targetName the name of an Ant target which will build the artifact,
1088:             *                   e.g. <samp>jar</samp>
1089:             * @param cleanTargetName the name of an Ant target which will delete the artifact
1090:             *                        (and maybe other build products), e.g. <samp>clean</samp>
1091:             * @return an artifact
1092:             */
1093:            public AntArtifact createSimpleAntArtifact(String type,
1094:                    String locationProperty, PropertyEvaluator eval,
1095:                    String targetName, String cleanTargetName) {
1096:                return new SimpleAntArtifact(this , type, locationProperty,
1097:                        eval, targetName, cleanTargetName);
1098:            }
1099:
1100:            /**
1101:             * Create an implementation of the file sharability query.
1102:             * You may specify a list of source roots to include that should be considered sharable,
1103:             * as well as a list of build directories that should not be considered sharable.
1104:             * <p>
1105:             * The project directory itself is automatically included in the list of sharable directories
1106:             * so you need not explicitly specify it.
1107:             * Similarly, the <code>nbproject/private</code> subdirectory is automatically excluded
1108:             * from VCS, so you do not need to explicitly specify it.
1109:             * </p>
1110:             * <p>
1111:             * Any file (or directory) mentioned (explicitly or implicity) in the source
1112:             * directory list but not in any of the build directory lists, and not containing
1113:             * any build directories inside it, will be given as sharable. If a directory itself
1114:             * is sharable but some directory inside it is not, it will be given as mixed.
1115:             * A file or directory inside some build directory will be listed as not sharable.
1116:             * A file or directory matching neither the source list nor the build directory list
1117:             * will be treated as of unknown status, but in practice such a file should never
1118:             * have been passed to this implementation anyway - {@link org.netbeans.api.queries.SharabilityQuery} will
1119:             * normally only call an implementation in project lookup if the file is owned by
1120:             * that project.
1121:             * </p>
1122:             * <p>
1123:             * Each entry in either list should be a string evaluated first for Ant property
1124:             * escapes (if any), then treated as a file path relative to the project directory
1125:             * (or it may be absolute).
1126:             * </p>
1127:             * <p>
1128:             * It is permitted, and harmless, to include items that overlap others. For example,
1129:             * you can have both a directory and one of its children in the include list.
1130:             * </p>
1131:             * <p>
1132:             * Whether or not you use this method, all files named <code>*-private.properties</code>
1133:             * outside the project are marked unsharable, as are such files inside the project if currently referenced
1134:             * as project libraries. (See {@link #getProjectLibrariesPropertyProvider}.)
1135:             * </p>
1136:             * <div class="nonnormative">
1137:             * <p>
1138:             * Typical usage would be:
1139:             * </p>
1140:             * <pre>
1141:             * helper.createSharabilityQuery(helper.getStandardPropertyEvaluator(),
1142:             *                               new String[] {"${src.dir}", "${test.src.dir}"},
1143:             *                               new String[] {"${build.dir}", "${dist.dir}"})
1144:             * </pre>
1145:             * <p>
1146:             * A quick rule of thumb is that the include list should contain any
1147:             * source directories which <em>might</em> reside outside the project directory;
1148:             * and the exclude list should contain any directories which you would want
1149:             * to add to a <samp>.cvsignore</samp> file if using CVS (for example).
1150:             * </p>
1151:             * <p>
1152:             * Note that in this case <samp>${src.dir}</samp> and <samp>${test.src.dir}</samp>
1153:             * may be relative paths inside the project directory; relative paths pointing
1154:             * outside of the project directory; or absolute paths (generally outside of the
1155:             * project directory). If they refer to locations inside the project directory,
1156:             * including them does nothing but is harmless - since the project directory itself
1157:             * is always treated as sharable. If they refer to external locations, you will
1158:             * need to also make sure that {@link org.netbeans.api.project.FileOwnerQuery} actually maps files in those
1159:             * directories to this project, or else {@link org.netbeans.api.queries.SharabilityQuery} will never find
1160:             * this implementation in your project lookup and may return <code>UNKNOWN</code>.
1161:             * </p>
1162:             * </div>
1163:             * @param eval a property evaluator to interpret paths with
1164:             * @param sourceRoots a list of additional paths to treat as sharable
1165:             * @param buildDirectories a list of paths to treat as not sharable
1166:             * @return a sharability query implementation suitable for the project lookup
1167:             * @see Project#getLookup
1168:             */
1169:            public SharabilityQueryImplementation createSharabilityQuery(
1170:                    PropertyEvaluator eval, String[] sourceRoots,
1171:                    String[] buildDirectories) {
1172:                String[] includes = new String[sourceRoots.length + 1];
1173:                System.arraycopy(sourceRoots, 0, includes, 0,
1174:                        sourceRoots.length);
1175:                includes[sourceRoots.length] = ""; // NOI18N
1176:                String[] excludes = new String[buildDirectories.length + 1];
1177:                System.arraycopy(buildDirectories, 0, excludes, 0,
1178:                        buildDirectories.length);
1179:                excludes[buildDirectories.length] = "nbproject/private"; // NOI18N
1180:                return new SharabilityQueryImpl(this , eval, includes, excludes);
1181:            }
1182:
1183:            /**
1184:             * Get a property provider which defines <code>basedir</code> according to
1185:             * the project directory and also copies all system properties in the current VM.
1186:             * It may also define <code>ant.home</code> and <code>ant.core.lib</code> if it is able.
1187:             * @return a stock property provider for initial Ant-related definitions
1188:             * @see PropertyUtils#sequentialPropertyEvaluator
1189:             */
1190:            public PropertyProvider getStockPropertyPreprovider() {
1191:                return properties.getStockPropertyPreprovider();
1192:            }
1193:
1194:            /**
1195:             * Creates a property provider which can load definitions of project libraries.
1196:             * If this project refers to any project library definition files, they will
1197:             * be included, with <code>${base}</code> replaced by the appropriate value.
1198:             * @return a property provider
1199:             * @since org.netbeans.modules.project.ant/1 1.19
1200:             * @see <a href="http://www.netbeans.org/ns/ant-project-libraries/1.xsd">Schema for project library references</a>
1201:             */
1202:            public PropertyProvider getProjectLibrariesPropertyProvider() {
1203:                return ProjectLibraryProvider.createPropertyProvider(this );
1204:            }
1205:
1206:            /**
1207:             * Is this project shared with other or not, that is is it using shrared 
1208:             * libraries or not.
1209:             * @return <code>true</code> for shared project
1210:             * @since org.netbeans.modules.project.ant/1 1.19
1211:             */
1212:            public boolean isSharableProject() {
1213:                return getLibrariesLocation() != null;
1214:            }
1215:
1216:            /**
1217:             * Returns location of shared libraries associated with this project or null.
1218:             * @return relative or absolute OS path or null
1219:             * @since org.netbeans.modules.project.ant/1 1.19
1220:             */
1221:            public String getLibrariesLocation() {
1222:                return ProjectLibraryProvider.getLibrariesLocationText(this 
1223:                        .createAuxiliaryConfiguration());
1224:            }
1225:
1226:            /**
1227:             * Change project's associated shared libraries location. If location is 
1228:             * <code>null</code> then project will not have shared libraries and will
1229:             * be considered as not being shared.
1230:             * 
1231:             * @param location project relative or absolute OS path or null
1232:             * @since org.netbeans.modules.project.ant/1 1.18
1233:             */
1234:            public void setLibrariesLocation(String location) {
1235:                ProjectLibraryProvider.setLibrariesLocation(this , location);
1236:            }
1237:
1238:            /**
1239:             * Get a property evaluator that can evaluate properties according to the default
1240:             * file layout for Ant-based projects.
1241:             * First, {@link #getStockPropertyPreprovider stock properties} are predefined.
1242:             * Then {@link #PRIVATE_PROPERTIES_PATH} is loaded via {@link #getPropertyProvider},
1243:             * then {@link #getProjectLibrariesPropertyProvider},
1244:             * then global definitions from {@link PropertyUtils#globalPropertyProvider}
1245:             * (though these may be overridden using the property <code>user.properties.file</code>
1246:             * in <code>private.properties</code>), then {@link #PROJECT_PROPERTIES_PATH}.
1247:             * @return a standard property evaluator
1248:             */
1249:            public PropertyEvaluator getStandardPropertyEvaluator() {
1250:                return properties.getStandardPropertyEvaluator();
1251:            }
1252:
1253:            /**
1254:             * Find an absolute file path from a possibly project-relative path.
1255:             * @param filename a pathname which may be project-relative or absolute and may
1256:             *                 use / or \ as the path separator
1257:             * @return an absolute file corresponding to it
1258:             */
1259:            public File resolveFile(String filename) {
1260:                if (filename == null) {
1261:                    throw new NullPointerException(
1262:                            "Attempted to pass a null filename to resolveFile"); // NOI18N
1263:                }
1264:                return PropertyUtils
1265:                        .resolveFile(FileUtil.toFile(dir), filename);
1266:            }
1267:
1268:            /**
1269:             * Same as {@link #resolveFile}, but produce a <code>FileObject</code> if possible.
1270:             * @param filename a pathname according to Ant conventions
1271:             * @return a file object it represents, or null if there is no such file object in known filesystems
1272:             */
1273:            public FileObject resolveFileObject(String filename) {
1274:                if (filename == null) {
1275:                    throw new NullPointerException(
1276:                            "Must pass a non-null filename"); // NOI18N
1277:                }
1278:                return PropertyUtils.resolveFileObject(dir, filename);
1279:            }
1280:
1281:            /**
1282:             * Take an Ant-style path specification and convert it to a platform-specific absolute path.
1283:             * The path separator characters are converted to the local convention, and individual
1284:             * path components are resolved and cleaned up as for {@link #resolveFile}.
1285:             * @param path an Ant-style abstract path
1286:             * @return an absolute, locally usable path
1287:             */
1288:            public String resolvePath(String path) {
1289:                if (path == null) {
1290:                    throw new NullPointerException("Must pass a non-null path"); // NOI18N
1291:                }
1292:                // XXX consider memoizing results since this is probably called a lot
1293:                return PropertyUtils.resolvePath(FileUtil.toFile(dir), path);
1294:            }
1295:
1296:            @Override
1297:            public String toString() {
1298:                return "AntProjectHelper[" + getProjectDirectory() + "]"; // NOI18N
1299:            }
1300:
1301:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.