Source Code Cross Referenced for PropertyUtils.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.beans.PropertyChangeEvent;
0045:        import java.beans.PropertyChangeListener;
0046:        import java.io.File;
0047:        import java.io.FileInputStream;
0048:        import java.io.FileOutputStream;
0049:        import java.io.IOException;
0050:        import java.io.InputStream;
0051:        import java.io.OutputStream;
0052:        import java.lang.ref.Reference;
0053:        import java.lang.ref.SoftReference;
0054:        import java.net.URI;
0055:        import java.util.ArrayList;
0056:        import java.util.Collections;
0057:        import java.util.HashMap;
0058:        import java.util.HashSet;
0059:        import java.util.LinkedList;
0060:        import java.util.List;
0061:        import java.util.Map;
0062:        import java.util.Properties;
0063:        import java.util.Set;
0064:        import java.util.StringTokenizer;
0065:        import java.util.logging.Level;
0066:        import java.util.logging.Logger;
0067:        import java.util.regex.Pattern;
0068:        import javax.swing.event.ChangeEvent;
0069:        import javax.swing.event.ChangeListener;
0070:        import org.netbeans.api.project.ProjectManager;
0071:        import org.netbeans.modules.project.ant.FileChangeSupport;
0072:        import org.netbeans.modules.project.ant.FileChangeSupportEvent;
0073:        import org.netbeans.modules.project.ant.FileChangeSupportListener;
0074:        import org.openide.ErrorManager;
0075:        import org.openide.filesystems.FileLock;
0076:        import org.openide.filesystems.FileObject;
0077:        import org.openide.filesystems.FileUtil;
0078:        import org.openide.util.ChangeSupport;
0079:        import org.openide.util.Mutex;
0080:        import org.openide.util.MutexException;
0081:        import org.openide.util.NbCollections;
0082:        import org.openide.util.RequestProcessor;
0083:        import org.openide.util.TopologicalSortException;
0084:        import org.openide.util.Union2;
0085:        import org.openide.util.Utilities;
0086:        import org.openide.util.WeakListeners;
0087:
0088:        /**
0089:         * Support for working with Ant properties and property files.
0090:         * @author Jesse Glick
0091:         */
0092:        public class PropertyUtils {
0093:
0094:            private PropertyUtils() {
0095:            }
0096:
0097:            /**
0098:             * Location in user directory of per-user global properties.
0099:             * May be null if <code>netbeans.user</code> is not set.
0100:             */
0101:            static File userBuildProperties() {
0102:                String nbuser = System.getProperty("netbeans.user"); // NOI18N
0103:                if (nbuser != null) {
0104:                    return FileUtil.normalizeFile(new File(nbuser,
0105:                            "build.properties")); // NOI18N
0106:                } else {
0107:                    return null;
0108:                }
0109:            }
0110:
0111:            private static Map<File, Reference<PropertyProvider>> globalPropertyProviders = new HashMap<File, Reference<PropertyProvider>>();
0112:
0113:            /**
0114:             * Load global properties defined by the IDE in the user directory.
0115:             * Currently loads ${netbeans.user}/build.properties if it exists.
0116:             * <p>
0117:             * Acquires read access.
0118:             * <p>
0119:             * To listen to changes use {@link #globalPropertyProvider}.
0120:             * @return user properties (empty if missing or malformed)
0121:             */
0122:            public static EditableProperties getGlobalProperties() {
0123:                return ProjectManager.mutex().readAccess(
0124:                        new Mutex.Action<EditableProperties>() {
0125:                            public EditableProperties run() {
0126:                                File ubp = userBuildProperties();
0127:                                if (ubp != null && ubp.isFile()
0128:                                        && ubp.canRead()) {
0129:                                    try {
0130:                                        InputStream is = new FileInputStream(
0131:                                                ubp);
0132:                                        try {
0133:                                            EditableProperties properties = new EditableProperties(
0134:                                                    true);
0135:                                            properties.load(is);
0136:                                            return properties;
0137:                                        } finally {
0138:                                            is.close();
0139:                                        }
0140:                                    } catch (IOException e) {
0141:                                        Logger.getLogger(
0142:                                                PropertyUtils.class.getName())
0143:                                                .log(Level.INFO, null, e);
0144:                                    }
0145:                                }
0146:                                // Missing or erroneous.
0147:                                return new EditableProperties(true);
0148:                            }
0149:                        });
0150:            }
0151:
0152:            /**
0153:             * Edit global properties defined by the IDE in the user directory.
0154:             * <p>
0155:             * Acquires write access.
0156:             * @param properties user properties to set
0157:             * @throws IOException if they could not be stored
0158:             * @see #getGlobalProperties
0159:             */
0160:            public static void putGlobalProperties(
0161:                    final EditableProperties properties) throws IOException {
0162:                try {
0163:                    ProjectManager.mutex().writeAccess(
0164:                            new Mutex.ExceptionAction<Void>() {
0165:                                public Void run() throws IOException {
0166:                                    File ubp = userBuildProperties();
0167:                                    if (ubp != null) {
0168:                                        FileObject bp = FileUtil
0169:                                                .toFileObject(ubp);
0170:                                        if (bp == null) {
0171:                                            if (!ubp.exists()) {
0172:                                                ubp.getParentFile().mkdirs();
0173:                                                new FileOutputStream(ubp)
0174:                                                        .close();
0175:                                                assert ubp.isFile() : "Did not actually make "
0176:                                                        + ubp;
0177:                                            }
0178:                                            bp = FileUtil.toFileObject(ubp);
0179:                                            if (bp == null) {
0180:                                                // XXX ugly (and will not correctly notify changes) but better than nothing:
0181:                                                ErrorManager
0182:                                                        .getDefault()
0183:                                                        .log(
0184:                                                                ErrorManager.WARNING,
0185:                                                                "Warning - cannot properly write to "
0186:                                                                        + ubp
0187:                                                                        + "; might be because your user directory is on a Windows UNC path (issue #46813)? If so, try using mapped drive letters.");
0188:                                                OutputStream os = new FileOutputStream(
0189:                                                        ubp);
0190:                                                try {
0191:                                                    properties.store(os);
0192:                                                } finally {
0193:                                                    os.close();
0194:                                                }
0195:                                                return null;
0196:                                            }
0197:                                        }
0198:                                        FileLock lock = bp.lock();
0199:                                        try {
0200:                                            OutputStream os = bp
0201:                                                    .getOutputStream(lock);
0202:                                            try {
0203:                                                properties.store(os);
0204:                                            } finally {
0205:                                                os.close();
0206:                                            }
0207:                                        } finally {
0208:                                            lock.releaseLock();
0209:                                        }
0210:                                    } else {
0211:                                        throw new IOException(
0212:                                                "Do not know where to store build.properties; must set netbeans.user!"); // NOI18N
0213:                                    }
0214:                                    return null;
0215:                                }
0216:                            });
0217:                } catch (MutexException e) {
0218:                    throw (IOException) e.getException();
0219:                }
0220:            }
0221:
0222:            /**
0223:             * Create a property evaluator based on {@link #getGlobalProperties}
0224:             * and {@link #putGlobalProperties}.
0225:             * It will supply global properties and fire changes when this file
0226:             * is changed.
0227:             * @return a property producer
0228:             */
0229:            public static synchronized PropertyProvider globalPropertyProvider() {
0230:                File ubp = userBuildProperties();
0231:                if (ubp != null) {
0232:                    Reference<PropertyProvider> globalPropertyProvider = globalPropertyProviders
0233:                            .get(ubp);
0234:                    if (globalPropertyProvider != null) {
0235:                        PropertyProvider pp = globalPropertyProvider.get();
0236:                        if (pp != null) {
0237:                            return pp;
0238:                        }
0239:                    }
0240:                    PropertyProvider gpp = propertiesFilePropertyProvider(ubp);
0241:                    globalPropertyProviders.put(ubp,
0242:                            new SoftReference<PropertyProvider>(gpp));
0243:                    return gpp;
0244:                } else {
0245:                    return fixedPropertyProvider(Collections
0246:                            .<String, String> emptyMap());
0247:                }
0248:            }
0249:
0250:            /**
0251:             * Create a property provider based on a properties file.
0252:             * The file need not exist at the moment; if it is created or deleted an appropriate
0253:             * change will be fired. If its contents are changed on disk a change will also be fired.
0254:             * @param propertiesFile a path to a (possibly nonexistent) *.properties file
0255:             * @return a supplier of properties from such a file
0256:             * @see Properties#load
0257:             */
0258:            public static PropertyProvider propertiesFilePropertyProvider(
0259:                    File propertiesFile) {
0260:                assert propertiesFile != null;
0261:                return new FilePropertyProvider(propertiesFile);
0262:            }
0263:
0264:            /**
0265:             * Provider based on a named properties file.
0266:             */
0267:            private static final class FilePropertyProvider implements 
0268:                    PropertyProvider, FileChangeSupportListener {
0269:
0270:                private static final RequestProcessor RP = new RequestProcessor(
0271:                        "PropertyUtils.FilePropertyProvider.RP"); // NOI18N
0272:
0273:                private final File properties;
0274:                private final ChangeSupport cs = new ChangeSupport(this );
0275:                private Map<String, String> cached = null;
0276:                private long cachedTime = 0L;
0277:
0278:                public FilePropertyProvider(File properties) {
0279:                    this .properties = properties;
0280:                    FileChangeSupport.DEFAULT.addListener(this , properties);
0281:                }
0282:
0283:                public Map<String, String> getProperties() {
0284:                    long currTime = properties.lastModified();
0285:                    if (cached == null || cachedTime != currTime) {
0286:                        cachedTime = currTime;
0287:                        cached = loadProperties();
0288:                    }
0289:                    return cached;
0290:                }
0291:
0292:                private Map<String, String> loadProperties() {
0293:                    // XXX does this need to run in PM.mutex.readAccess?
0294:                    if (properties.isFile() && properties.canRead()) {
0295:                        try {
0296:                            InputStream is = new FileInputStream(properties);
0297:                            try {
0298:                                Properties props = new Properties();
0299:                                props.load(is);
0300:                                return NbCollections.checkedMapByFilter(props,
0301:                                        String.class, String.class, true);
0302:                            } finally {
0303:                                is.close();
0304:                            }
0305:                        } catch (IOException e) {
0306:                            Logger.getLogger(PropertyUtils.class.getName())
0307:                                    .log(Level.INFO, null, e);
0308:                        }
0309:                    }
0310:                    // Missing or erroneous.
0311:                    return Collections.emptyMap();
0312:                }
0313:
0314:                private void fireChange() {
0315:                    cachedTime = -1L; // force reload
0316:                    if (!cs.hasListeners()) {
0317:                        return;
0318:                    }
0319:                    final Mutex.Action<Void> action = new Mutex.Action<Void>() {
0320:                        public Void run() {
0321:                            cs.fireChange();
0322:                            return null;
0323:                        }
0324:                    };
0325:                    if (ProjectManager.mutex().isWriteAccess()) {
0326:                        // Run it right now. postReadRequest would be too late.
0327:                        ProjectManager.mutex().readAccess(action);
0328:                    } else if (ProjectManager.mutex().isReadAccess()) {
0329:                        // Run immediately also. No need to switch to read access.
0330:                        action.run();
0331:                    } else {
0332:                        // Not safe to acquire a new lock, so run later in read access.
0333:                        RP.post(new Runnable() {
0334:                            public void run() {
0335:                                ProjectManager.mutex().readAccess(action);
0336:                            }
0337:                        });
0338:                    }
0339:                }
0340:
0341:                public synchronized void addChangeListener(ChangeListener l) {
0342:                    cs.addChangeListener(l);
0343:                }
0344:
0345:                public synchronized void removeChangeListener(ChangeListener l) {
0346:                    cs.removeChangeListener(l);
0347:                }
0348:
0349:                public void fileCreated(FileChangeSupportEvent event) {
0350:                    //System.err.println("FPP: " + event);
0351:                    fireChange();
0352:                }
0353:
0354:                public void fileDeleted(FileChangeSupportEvent event) {
0355:                    //System.err.println("FPP: " + event);
0356:                    fireChange();
0357:                }
0358:
0359:                public void fileModified(FileChangeSupportEvent event) {
0360:                    //System.err.println("FPP: " + event);
0361:                    fireChange();
0362:                }
0363:
0364:                public String toString() {
0365:                    return "FilePropertyProvider[" + properties + ":"
0366:                            + getProperties() + "]"; // NOI18N
0367:                }
0368:
0369:            }
0370:
0371:            /**
0372:             * Evaluate all properties in a list of property mappings.
0373:             * <p>
0374:             * If there are any cyclic definitions within a single mapping,
0375:             * the evaluation will fail and return null.
0376:             * @param defs an ordered list of property mappings, e.g. {@link EditableProperties} instances
0377:             * @param predefs an unevaluated set of initial definitions
0378:             * @return values for all defined properties, or null if a circularity error was detected
0379:             */
0380:            private static Map<String, String> evaluateAll(
0381:                    Map<String, String> predefs, List<Map<String, String>> defs) {
0382:                Map<String, String> m = new HashMap<String, String>(predefs);
0383:                for (Map<String, String> curr : defs) {
0384:                    // Set of properties which we are deferring because they subst sibling properties:
0385:                    Map<String, Set<String>> dependOnSiblings = new HashMap<String, Set<String>>();
0386:                    for (Map.Entry<String, String> entry : curr.entrySet()) {
0387:                        String prop = entry.getKey();
0388:                        if (!m.containsKey(prop)) {
0389:                            String rawval = entry.getValue();
0390:                            //System.err.println("subst " + prop + "=" + rawval + " with " + m);
0391:                            Union2<String, Set<String>> o = substitute(rawval,
0392:                                    m, curr.keySet());
0393:                            if (o.hasFirst()) {
0394:                                m.put(prop, o.first());
0395:                            } else {
0396:                                dependOnSiblings.put(prop, o.second());
0397:                            }
0398:                        }
0399:                    }
0400:                    Set<String> toSort = new HashSet<String>(dependOnSiblings
0401:                            .keySet());
0402:                    for (Set<String> s : dependOnSiblings.values()) {
0403:                        toSort.addAll(s);
0404:                    }
0405:                    List<String> sorted;
0406:                    try {
0407:                        sorted = Utilities.topologicalSort(toSort,
0408:                                dependOnSiblings);
0409:                    } catch (TopologicalSortException e) {
0410:                        //System.err.println("Cyclic property refs: " + Arrays.asList(e.unsortableSets()));
0411:                        return null;
0412:                    }
0413:                    Collections.reverse(sorted);
0414:                    for (String prop : sorted) {
0415:                        if (!m.containsKey(prop)) {
0416:                            String rawval = curr.get(prop);
0417:                            m.put(prop, substitute(rawval, m, /*Collections.EMPTY_SET*/
0418:                                    curr.keySet()).first());
0419:                        }
0420:                    }
0421:                }
0422:                return m;
0423:            }
0424:
0425:            /**
0426:             * Try to substitute property references etc. in an Ant property value string.
0427:             * @param rawval the raw value to be substituted
0428:             * @param predefs a set of properties already defined
0429:             * @param siblingProperties a set of property names that are yet to be defined
0430:             * @return either a String, in case everything can be evaluated now;
0431:             *         or a Set<String> of elements from siblingProperties in case those properties
0432:             *         need to be defined in order to evaluate this one
0433:             */
0434:            private static Union2<String, Set<String>> substitute(
0435:                    String rawval, Map<String, String> predefs,
0436:                    Set<String> siblingProperties) {
0437:                assert rawval != null : "null rawval passed in";
0438:                if (rawval.indexOf('$') == -1) {
0439:                    // Shortcut:
0440:                    //System.err.println("shortcut");
0441:                    return Union2.createFirst(rawval);
0442:                }
0443:                // May need to subst something.
0444:                int idx = 0;
0445:                // Result in progress, if it is to be a String:
0446:                StringBuffer val = new StringBuffer();
0447:                // Or, result in progress, if it is to be a Set<String>:
0448:                Set<String> needed = new HashSet<String>();
0449:                while (true) {
0450:                    int shell = rawval.indexOf('$', idx);
0451:                    if (shell == -1 || shell == rawval.length() - 1) {
0452:                        // No more $, or only as last char -> copy all.
0453:                        //System.err.println("no more $");
0454:                        if (needed.isEmpty()) {
0455:                            val.append(rawval.substring(idx));
0456:                            return Union2.createFirst(val.toString());
0457:                        } else {
0458:                            return Union2.createSecond(needed);
0459:                        }
0460:                    }
0461:                    char c = rawval.charAt(shell + 1);
0462:                    if (c == '$') {
0463:                        // $$ -> $
0464:                        //System.err.println("$$");
0465:                        if (needed.isEmpty()) {
0466:                            val.append('$');
0467:                        }
0468:                        idx += 2;
0469:                    } else if (c == '{') {
0470:                        // Possibly a property ref.
0471:                        int end = rawval.indexOf('}', shell + 2);
0472:                        if (end != -1) {
0473:                            // Definitely a property ref.
0474:                            String otherprop = rawval.substring(shell + 2, end);
0475:                            //System.err.println("prop ref to " + otherprop);
0476:                            if (predefs.containsKey(otherprop)) {
0477:                                // Well-defined.
0478:                                if (needed.isEmpty()) {
0479:                                    val.append(rawval.substring(idx, shell));
0480:                                    val.append(predefs.get(otherprop));
0481:                                }
0482:                                idx = end + 1;
0483:                            } else if (siblingProperties.contains(otherprop)) {
0484:                                needed.add(otherprop);
0485:                                // don't bother updating val, it will not be used anyway
0486:                                idx = end + 1;
0487:                            } else {
0488:                                // No def, leave as is.
0489:                                if (needed.isEmpty()) {
0490:                                    val.append(rawval.substring(idx, end + 1));
0491:                                }
0492:                                idx = end + 1;
0493:                            }
0494:                        } else {
0495:                            // Unclosed ${ sequence, leave as is.
0496:                            if (needed.isEmpty()) {
0497:                                val.append(rawval.substring(idx));
0498:                                return Union2.createFirst(val.toString());
0499:                            } else {
0500:                                return Union2.createSecond(needed);
0501:                            }
0502:                        }
0503:                    } else {
0504:                        // $ followed by some other char, leave as is.
0505:                        // XXX is this actually right?
0506:                        if (needed.isEmpty()) {
0507:                            val.append(rawval.substring(idx, idx + 2));
0508:                        }
0509:                        idx += 2;
0510:                    }
0511:                }
0512:            }
0513:
0514:            private static final Pattern RELATIVE_SLASH_SEPARATED_PATH = Pattern
0515:                    .compile("[^:/\\\\.][^:/\\\\]*(/[^:/\\\\.][^:/\\\\]*)*"); // NOI18N
0516:
0517:            /**
0518:             * Find an absolute file path from a possibly relative path.
0519:             * @param basedir base file for relative filename resolving; must be an absolute path
0520:             * @param filename a pathname which may be relative or absolute and may
0521:             *                 use / or \ as the path separator
0522:             * @return an absolute file corresponding to it
0523:             * @throws IllegalArgumentException if basedir is not absolute
0524:             */
0525:            public static File resolveFile(File basedir, String filename)
0526:                    throws IllegalArgumentException {
0527:                if (basedir == null) {
0528:                    throw new NullPointerException(
0529:                            "null basedir passed to resolveFile"); // NOI18N
0530:                }
0531:                if (filename == null) {
0532:                    throw new NullPointerException(
0533:                            "null filename passed to resolveFile"); // NOI18N
0534:                }
0535:                if (!basedir.isAbsolute()) {
0536:                    throw new IllegalArgumentException(
0537:                            "nonabsolute basedir passed to resolveFile: "
0538:                                    + basedir); // NOI18N
0539:                }
0540:                File f;
0541:                if (RELATIVE_SLASH_SEPARATED_PATH.matcher(filename).matches()) {
0542:                    // Shortcut - simple relative path. Potentially faster.
0543:                    f = new File(basedir, filename.replace('/',
0544:                            File.separatorChar));
0545:                } else {
0546:                    // All other cases.
0547:                    String machinePath = filename.replace('/',
0548:                            File.separatorChar).replace('\\',
0549:                            File.separatorChar);
0550:                    f = new File(machinePath);
0551:                    if (!f.isAbsolute()) {
0552:                        f = new File(basedir, machinePath);
0553:                    }
0554:                    assert f.isAbsolute();
0555:                }
0556:                return FileUtil.normalizeFile(f);
0557:            }
0558:
0559:            /**
0560:             * Produce a machine-independent relativized version of a filename from a basedir.
0561:             * Unlike {@link URI#relativize} this will produce "../" sequences as needed.
0562:             * @param basedir a directory to resolve relative to (need not exist on disk)
0563:             * @param file a file or directory to find a relative path for
0564:             * @return a relativized path (slash-separated), or null if it is not possible (e.g. different DOS drives);
0565:             *         just <samp>.</samp> in case the paths are the same
0566:             * @throws IllegalArgumentException if the basedir is known to be a file and not a directory
0567:             */
0568:            public static String relativizeFile(File basedir, File file) {
0569:                if (basedir.isFile()) {
0570:                    throw new IllegalArgumentException(
0571:                            "Cannot relative w.r.t. a data file " + basedir); // NOI18N
0572:                }
0573:                if (basedir.equals(file)) {
0574:                    return "."; // NOI18N
0575:                }
0576:                StringBuffer b = new StringBuffer();
0577:                File base = basedir;
0578:                String filepath = file.getAbsolutePath();
0579:                while (!filepath.startsWith(slashify(base.getAbsolutePath()))) {
0580:                    base = base.getParentFile();
0581:                    if (base == null) {
0582:                        return null;
0583:                    }
0584:                    if (base.equals(file)) {
0585:                        // #61687: file is a parent of basedir
0586:                        b.append(".."); // NOI18N
0587:                        return b.toString();
0588:                    }
0589:                    b.append("../"); // NOI18N
0590:                }
0591:                URI u = base.toURI().relativize(file.toURI());
0592:                assert !u.isAbsolute() : u + " from " + basedir + " and "
0593:                        + file + " with common root " + base;
0594:                b.append(u.getPath());
0595:                if (b.charAt(b.length() - 1) == '/') {
0596:                    // file is an existing directory and file.toURI ends in /
0597:                    // we do not want the trailing slash
0598:                    b.setLength(b.length() - 1);
0599:                }
0600:                return b.toString();
0601:            }
0602:
0603:            private static String slashify(String path) {
0604:                if (path.endsWith(File.separator)) {
0605:                    return path;
0606:                } else {
0607:                    return path + File.separatorChar;
0608:                }
0609:            }
0610:
0611:            /*public? */static FileObject resolveFileObject(
0612:                    FileObject basedir, String filename) {
0613:                // an absolute path, or \-separated, or . or .. components, etc.; use the safer method.
0614:                return FileUtil.toFileObject(resolveFile(FileUtil
0615:                        .toFile(basedir), filename));
0616:            }
0617:
0618:            /*public? */static String resolvePath(File basedir, String path) {
0619:                StringBuffer b = new StringBuffer();
0620:                String[] toks = tokenizePath(path);
0621:                for (int i = 0; i < toks.length; i++) {
0622:                    if (i > 0) {
0623:                        b.append(File.pathSeparatorChar);
0624:                    }
0625:                    b.append(resolveFile(basedir, toks[i]).getAbsolutePath());
0626:                }
0627:                return b.toString();
0628:            }
0629:
0630:            /**
0631:             * Split an Ant-style path specification into components.
0632:             * Tokenizes on <code>:</code> and <code>;</code>, paying
0633:             * attention to DOS-style components such as <samp>C:\FOO</samp>.
0634:             * Also removes any empty components.
0635:             * @param path an Ant-style path (elements arbitrary) using DOS or Unix separators
0636:             * @return a tokenization of that path into components
0637:             */
0638:            public static String[] tokenizePath(String path) {
0639:                List<String> l = new ArrayList<String>();
0640:                StringTokenizer tok = new StringTokenizer(path, ":;", true); // NOI18N
0641:                char dosHack = '\0';
0642:                char lastDelim = '\0';
0643:                int delimCount = 0;
0644:                while (tok.hasMoreTokens()) {
0645:                    String s = tok.nextToken();
0646:                    if (s.length() == 0) {
0647:                        // Strip empty components.
0648:                        continue;
0649:                    }
0650:                    if (s.length() == 1) {
0651:                        char c = s.charAt(0);
0652:                        if (c == ':' || c == ';') {
0653:                            // Just a delimiter.
0654:                            lastDelim = c;
0655:                            delimCount++;
0656:                            continue;
0657:                        }
0658:                    }
0659:                    if (dosHack != '\0') {
0660:                        // #50679 - "C:/something" is also accepted as DOS path
0661:                        if (lastDelim == ':' && delimCount == 1
0662:                                && (s.charAt(0) == '\\' || s.charAt(0) == '/')) {
0663:                            // We had a single letter followed by ':' now followed by \something or /something
0664:                            s = "" + dosHack + ':' + s;
0665:                            // and use the new token with the drive prefix...
0666:                        } else {
0667:                            // Something else, leave alone.
0668:                            l.add(Character.toString(dosHack));
0669:                            // and continue with this token too...
0670:                        }
0671:                        dosHack = '\0';
0672:                    }
0673:                    // Reset count of # of delimiters in a row.
0674:                    delimCount = 0;
0675:                    if (s.length() == 1) {
0676:                        char c = s.charAt(0);
0677:                        if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
0678:                            // Probably a DOS drive letter. Leave it with the next component.
0679:                            dosHack = c;
0680:                            continue;
0681:                        }
0682:                    }
0683:                    l.add(s);
0684:                }
0685:                if (dosHack != '\0') {
0686:                    //the dosHack was the last letter in the input string (not followed by the ':')
0687:                    //so obviously not a drive letter.
0688:                    //Fix for issue #57304
0689:                    l.add(Character.toString(dosHack));
0690:                }
0691:                return l.toArray(new String[l.size()]);
0692:            }
0693:
0694:            private static final Pattern VALID_PROPERTY_NAME = Pattern
0695:                    .compile("[-._a-zA-Z0-9]+"); // NOI18N
0696:
0697:            /**
0698:             * Checks whether the name is usable as Ant property name.
0699:             * @param name name to check for usability as Ant property
0700:             * @return true if name is usable otherwise false
0701:             */
0702:            public static boolean isUsablePropertyName(String name) {
0703:                return VALID_PROPERTY_NAME.matcher(name).matches();
0704:            }
0705:
0706:            /**
0707:             * Returns name usable as Ant property which is based on the given
0708:             * name. All forbidden characters are either removed or replaced with
0709:             * suitable ones.
0710:             * @param name name to use as base for Ant property name
0711:             * @return name usable as Ant property name
0712:             */
0713:            public static String getUsablePropertyName(String name) {
0714:                if (isUsablePropertyName(name)) {
0715:                    return name;
0716:                }
0717:                StringBuffer sb = new StringBuffer(name);
0718:                for (int i = 0; i < sb.length(); i++) {
0719:                    if (!isUsablePropertyName(sb.substring(i, i + 1))) {
0720:                        sb.replace(i, i + 1, "_");
0721:                    }
0722:                }
0723:                return sb.toString();
0724:            }
0725:
0726:            /**
0727:             * Create a trivial property producer using only a fixed list of property definitions.
0728:             * Its values are constant, and it never fires changes.
0729:             * @param defs a map from property names to values (it is illegal to modify this map
0730:             *             after passing it to this method)
0731:             * @return a matching property producer
0732:             */
0733:            public static PropertyProvider fixedPropertyProvider(
0734:                    Map<String, String> defs) {
0735:                return new FixedPropertyProvider(defs);
0736:            }
0737:
0738:            private static final class FixedPropertyProvider implements 
0739:                    PropertyProvider {
0740:
0741:                private final Map<String, String> defs;
0742:
0743:                public FixedPropertyProvider(Map<String, String> defs) {
0744:                    this .defs = defs;
0745:                }
0746:
0747:                public Map<String, String> getProperties() {
0748:                    return defs;
0749:                }
0750:
0751:                public void addChangeListener(ChangeListener l) {
0752:                }
0753:
0754:                public void removeChangeListener(ChangeListener l) {
0755:                }
0756:
0757:            }
0758:
0759:            /**
0760:             * Create a property evaluator based on a series of definitions.
0761:             * <p>
0762:             * Each batch of definitions can refer to properties within itself
0763:             * (so long as there is no cycle) or any previous batch.
0764:             * However the special first provider cannot refer to properties within itself.
0765:             * </p>
0766:             * <p>
0767:             * This implementation acquires {@link ProjectManager#mutex} for all operations, in read mode,
0768:             * and fires changes synchronously. It also expects changes to be fired from property
0769:             * providers in read (or write) access.
0770:             * </p>
0771:             * @param preprovider an initial context (may be null)
0772:             * @param providers a sequential list of property groups
0773:             * @return an evaluator
0774:             */
0775:            public static PropertyEvaluator sequentialPropertyEvaluator(
0776:                    PropertyProvider preprovider, PropertyProvider... providers) {
0777:                return new SequentialPropertyEvaluator(preprovider, providers);
0778:            }
0779:
0780:            /**
0781:             * Creates a property provider similar to {@link #globalPropertyProvider}
0782:             * but which can use a different global properties file.
0783:             * If a specific file is pointed to, that is loaded; otherwise behaves like {@link #globalPropertyProvider}.
0784:             * Permits behavior similar to command-line Ant where not erroneous, but using the IDE's
0785:             * default global properties for projects which do not yet have this property registered.
0786:             * @param findUserPropertiesFile an evaluator in which to look up <code>propertyName</code>
0787:             * @param propertyName a property pointing to the global properties file (typically <code>"user.properties.file"</code>)
0788:             * @param basedir a base directory to use when resolving the path to the global properties file, if relative
0789:             * @return a provider of global properties
0790:             * @since org.netbeans.modules.project.ant/1 1.14
0791:             */
0792:            public static PropertyProvider userPropertiesProvider(
0793:                    PropertyEvaluator findUserPropertiesFile,
0794:                    String propertyName, File basedir) {
0795:                return new UserPropertiesProvider(findUserPropertiesFile,
0796:                        propertyName, basedir);
0797:            }
0798:
0799:            private static final class UserPropertiesProvider extends
0800:                    FilterPropertyProvider implements  PropertyChangeListener {
0801:                private final PropertyEvaluator findUserPropertiesFile;
0802:                private final String propertyName;
0803:                private final File basedir;
0804:
0805:                public UserPropertiesProvider(
0806:                        PropertyEvaluator findUserPropertiesFile,
0807:                        String propertyName, File basedir) {
0808:                    super (computeDelegate(findUserPropertiesFile, propertyName,
0809:                            basedir));
0810:                    this .findUserPropertiesFile = findUserPropertiesFile;
0811:                    this .propertyName = propertyName;
0812:                    this .basedir = basedir;
0813:                    findUserPropertiesFile.addPropertyChangeListener(this );
0814:                }
0815:
0816:                public void propertyChange(PropertyChangeEvent ev) {
0817:                    if (propertyName.equals(ev.getPropertyName())) {
0818:                        setDelegate(computeDelegate(findUserPropertiesFile,
0819:                                propertyName, basedir));
0820:                    }
0821:                }
0822:
0823:                private static PropertyProvider computeDelegate(
0824:                        PropertyEvaluator findUserPropertiesFile,
0825:                        String propertyName, File basedir) {
0826:                    String userPropertiesFile = findUserPropertiesFile
0827:                            .getProperty(propertyName);
0828:                    if (userPropertiesFile != null) {
0829:                        // Have some defined global properties file, so read it and listen to changes in it.
0830:                        File f = PropertyUtils.resolveFile(basedir,
0831:                                userPropertiesFile);
0832:                        if (f.equals(PropertyUtils.userBuildProperties())) {
0833:                            // Just to share the cache.
0834:                            return PropertyUtils.globalPropertyProvider();
0835:                        } else {
0836:                            return PropertyUtils
0837:                                    .propertiesFilePropertyProvider(f);
0838:                        }
0839:                    } else {
0840:                        // Use the in-IDE default.
0841:                        return PropertyUtils.globalPropertyProvider();
0842:                    }
0843:                }
0844:            }
0845:
0846:            private static final class SequentialPropertyEvaluator implements 
0847:                    PropertyEvaluator, ChangeListener {
0848:
0849:                private final PropertyProvider preprovider;
0850:                private final PropertyProvider[] providers;
0851:                private Map<String, String> defs;
0852:                private final List<PropertyChangeListener> listeners = new ArrayList<PropertyChangeListener>();
0853:
0854:                public SequentialPropertyEvaluator(
0855:                        final PropertyProvider preprovider,
0856:                        final PropertyProvider[] providers) {
0857:                    this .preprovider = preprovider;
0858:                    this .providers = providers;
0859:                    // XXX defer until someone asks for them
0860:                    defs = ProjectManager.mutex().readAccess(
0861:                            new Mutex.Action<Map<String, String>>() {
0862:                                public Map<String, String> run() {
0863:                                    return compose(preprovider, providers);
0864:                                }
0865:                            });
0866:                    // XXX defer until someone is listening?
0867:                    if (preprovider != null) {
0868:                        preprovider.addChangeListener(WeakListeners.change(
0869:                                this , preprovider));
0870:                    }
0871:                    for (PropertyProvider pp : providers) {
0872:                        pp.addChangeListener(WeakListeners.change(this , pp));
0873:                    }
0874:                }
0875:
0876:                public String getProperty(final String prop) {
0877:                    return ProjectManager.mutex().readAccess(
0878:                            new Mutex.Action<String>() {
0879:                                public String run() {
0880:                                    if (defs == null) {
0881:                                        return null;
0882:                                    }
0883:                                    return defs.get(prop);
0884:                                }
0885:                            });
0886:                }
0887:
0888:                public String evaluate(final String text) {
0889:                    if (text == null) {
0890:                        throw new NullPointerException(
0891:                                "Attempted to pass null to PropertyEvaluator.evaluate"); // NOI18N
0892:                    }
0893:                    return ProjectManager.mutex().readAccess(
0894:                            new Mutex.Action<String>() {
0895:                                public String run() {
0896:                                    if (defs == null) {
0897:                                        return null;
0898:                                    }
0899:                                    Union2<String, Set<String>> result = substitute(
0900:                                            text, defs, Collections
0901:                                                    .<String> emptySet());
0902:                                    assert result.hasFirst() : "Unexpected result "
0903:                                            + result
0904:                                            + " from "
0905:                                            + text
0906:                                            + " on "
0907:                                            + defs;
0908:                                    return result.first();
0909:                                }
0910:                            });
0911:                }
0912:
0913:                public Map<String, String> getProperties() {
0914:                    return ProjectManager.mutex().readAccess(
0915:                            new Mutex.Action<Map<String, String>>() {
0916:                                public Map<String, String> run() {
0917:                                    return defs;
0918:                                }
0919:                            });
0920:                }
0921:
0922:                public void addPropertyChangeListener(
0923:                        PropertyChangeListener listener) {
0924:                    synchronized (listeners) {
0925:                        listeners.add(listener);
0926:                    }
0927:                }
0928:
0929:                public void removePropertyChangeListener(
0930:                        PropertyChangeListener listener) {
0931:                    synchronized (listeners) {
0932:                        listeners.remove(listener);
0933:                    }
0934:                }
0935:
0936:                public void stateChanged(ChangeEvent e) {
0937:                    assert ProjectManager.mutex().isReadAccess()
0938:                            || ProjectManager.mutex().isWriteAccess();
0939:                    Map<String, String> newdefs = compose(preprovider,
0940:                            providers);
0941:                    // compose() may return null upon circularity errors
0942:                    Map<String, String> _defs = defs != null ? defs
0943:                            : Collections.<String, String> emptyMap();
0944:                    Map<String, String> _newdefs = newdefs != null ? newdefs
0945:                            : Collections.<String, String> emptyMap();
0946:                    if (!_defs.equals(_newdefs)) {
0947:                        Set<String> props = new HashSet<String>(_defs.keySet());
0948:                        props.addAll(_newdefs.keySet());
0949:                        List<PropertyChangeEvent> events = new LinkedList<PropertyChangeEvent>();
0950:                        for (String prop : props) {
0951:                            assert prop != null;
0952:                            String oldval = _defs.get(prop);
0953:                            String newval = _newdefs.get(prop);
0954:                            if (newval != null) {
0955:                                if (newval.equals(oldval)) {
0956:                                    continue;
0957:                                }
0958:                            } else {
0959:                                assert oldval != null : "should not have had "
0960:                                        + prop;
0961:                            }
0962:                            events.add(new PropertyChangeEvent(this , prop,
0963:                                    oldval, newval));
0964:                        }
0965:                        assert !events.isEmpty();
0966:                        defs = newdefs;
0967:                        PropertyChangeListener[] _listeners;
0968:                        synchronized (listeners) {
0969:                            _listeners = listeners
0970:                                    .toArray(new PropertyChangeListener[listeners
0971:                                            .size()]);
0972:                        }
0973:                        for (PropertyChangeListener l : _listeners) {
0974:                            for (PropertyChangeEvent ev : events) {
0975:                                l.propertyChange(ev);
0976:                            }
0977:                        }
0978:                    }
0979:                }
0980:
0981:                private static Map<String, String> compose(
0982:                        PropertyProvider preprovider,
0983:                        PropertyProvider[] providers) {
0984:                    assert ProjectManager.mutex().isReadAccess()
0985:                            || ProjectManager.mutex().isWriteAccess();
0986:                    Map<String, String> predefs;
0987:                    if (preprovider != null) {
0988:                        predefs = preprovider.getProperties();
0989:                    } else {
0990:                        predefs = Collections.emptyMap();
0991:                    }
0992:                    List<Map<String, String>> defs = new ArrayList<Map<String, String>>(
0993:                            providers.length);
0994:                    for (PropertyProvider pp : providers) {
0995:                        defs.add(pp.getProperties());
0996:                    }
0997:                    return evaluateAll(predefs, defs);
0998:                }
0999:
1000:            }
1001:
1002:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.