Source Code Cross Referenced for ClassPath.java in  » IDE-Netbeans » gsf » org » netbeans » modules » gsfpath » api » classpath » 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 » gsf » org.netbeans.modules.gsfpath.api.classpath 
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.modules.gsfpath.api.classpath;
0043:
0044:        import java.beans.PropertyChangeEvent;
0045:        import java.beans.PropertyChangeListener;
0046:        import java.beans.PropertyChangeSupport;
0047:        import java.io.File;
0048:        import java.io.IOException;
0049:        import java.lang.ref.Reference;
0050:        import java.lang.ref.SoftReference;
0051:        import java.lang.ref.WeakReference;
0052:        import java.net.URI;
0053:        import java.net.URISyntaxException;
0054:        import java.net.URL;
0055:        import java.text.MessageFormat;
0056:        import java.util.ArrayList;
0057:        import java.util.Arrays;
0058:        import java.util.Collection;
0059:        import java.util.Collections;
0060:        import java.util.HashSet;
0061:        import java.util.LinkedHashSet;
0062:        import java.util.List;
0063:        import java.util.Map;
0064:        import java.util.Set;
0065:        import java.util.WeakHashMap;
0066:        import java.util.logging.Level;
0067:        import java.util.logging.Logger;
0068:        import org.netbeans.modules.gsfpath.classpath.ClassPathAccessor;
0069:        import org.netbeans.modules.gsfpath.spi.classpath.ClassPathImplementation;
0070:        import org.netbeans.modules.gsfpath.spi.classpath.ClassPathProvider;
0071:        import org.netbeans.modules.gsfpath.spi.classpath.FilteringPathResourceImplementation;
0072:        import org.netbeans.modules.gsfpath.spi.classpath.PathResourceImplementation;
0073:        import org.openide.filesystems.FileAttributeEvent;
0074:        import org.openide.filesystems.FileChangeListener;
0075:        import org.openide.filesystems.FileEvent;
0076:        import org.openide.filesystems.FileObject;
0077:        import org.openide.filesystems.FileRenameEvent;
0078:        import org.openide.filesystems.FileStateInvalidException;
0079:        import org.openide.filesystems.FileSystem;
0080:        import org.openide.filesystems.FileUtil;
0081:        import org.openide.filesystems.URLMapper;
0082:        import org.openide.util.Exceptions;
0083:        import org.openide.util.Lookup;
0084:        import org.openide.util.Utilities;
0085:        import org.openide.util.WeakListeners;
0086:
0087:        /**
0088:         * ClassPath objects should be used to access contents of the ClassPath, searching
0089:         * for resources, objects reachable using the ClassPath at runtime. It is intended
0090:         * to replace some of the functionality of <link>org.openide.filesystems.Repository</link>.
0091:         * <BR>
0092:         * ClassPath instances should be used to map from Java-style resource names
0093:         * to FileObject (NetBeans-style resource) and vice versa. It should be also used
0094:         * whenever the operation requires inspection of development or runtime project
0095:         * environment instead. The service supports either searching in the classpath
0096:         * resource space, properly hiding resources as the ClassLoader would do at runtime.
0097:         * It can effectively say whether a FileObject is within the reach of a ClassPath
0098:         * or whether it is <I>reachable</I> (visible to a ClassLoader). One can translate
0099:         * filenames to resource names and vice versa.
0100:         * <P>
0101:         * A client may obtain a ClassPath instance using
0102:         * <code>ClassPath.getClassPath(id)</code> static method, where the ID is an
0103:         * abstract name for the classpath wanted. There are some predefined classpath
0104:         * names predefined as symbolic constants, following individual types of services
0105:         * (compiler, debugger, executor). Names are not limited to the listed ones; an extension
0106:         * module might add its own private classpath type.
0107:         */
0108:        public final class ClassPath {
0109:
0110:            static {
0111:                ClassPathAccessor.DEFAULT = new ClassPathAccessor() {
0112:                    public ClassPath createClassPath(
0113:                            ClassPathImplementation spiClasspath) {
0114:                        return new ClassPath(spiClasspath);
0115:                    }
0116:
0117:                    public ClassPathImplementation getClassPathImpl(ClassPath cp) {
0118:                        return cp == null ? null : cp.impl;
0119:                    }
0120:                };
0121:            }
0122:
0123:            /**
0124:             * Classpath setting for executing things. This type can be used to learn
0125:             * runtime time classpath for execution of the file in question.
0126:             * <p class="nonnormative">
0127:             * It corresponds to the <code>-classpath</code> option to <code>java</code>
0128:             * (the Java launcher): i.e. all compiled classes outside the JRE that
0129:             * will be needed to run the program, or at least to load a certain class.
0130:             * It may also be thought of as corresponding to the list of URLs in a
0131:             * <code>URLClassLoader</code> (plus URLs present in parent class loaders
0132:             * but excluding the bootstrap and extension class loaders).
0133:             * </p>
0134:             */
0135:            public static final String EXECUTE = "classpath/execute";
0136:
0137:            /**
0138:             * Classpath for debugging things
0139:             * @deprecated Probably useless.
0140:             */
0141:            @Deprecated
0142:            public static final String DEBUG = "classpath/debug";
0143:
0144:            /**
0145:             * ClassPath for compiling things. This type can be used to learn
0146:             * compilation time classpath for the file in question.
0147:             * <p class="nonnormative">
0148:             * It corresponds to the <code>-classpath</code> option to <code>javac</code>:
0149:             * i.e. already-compiled classes which some new sources need to compile against,
0150:             * besides what is already in the JRE.
0151:             * </p>
0152:             */
0153:            public static final String COMPILE = "classpath/compile";
0154:
0155:            /**
0156:             * ClassPath for project sources. This type can be used to learn
0157:             * package root of the file in question.
0158:             * <div class="nonnormative">
0159:             * <p>
0160:             * It is similar to the <code>-sourcepath</code> option of <code>javac</code>.
0161:             * </p>
0162:             * <p>
0163:             * For typical source files, the sourcepath will consist of one element:
0164:             * the package root of the source file. If more than one package root is
0165:             * to be compiled together, all the sources should share a sourcepath
0166:             * with multiple roots.
0167:             * </p>
0168:             * <p>
0169:             * Note that each source file for which editor code completion (and similar
0170:             * actions) should work should have a classpath of this type.
0171:             * </p>
0172:             * </div>
0173:             * @since org.netbeans.modules.gsfpath.api/1 1.4
0174:             */
0175:            public static final String SOURCE = "classpath/source";
0176:
0177:            /**
0178:             * Boot ClassPath of the JDK. This type can be used to learn boot classpath
0179:             * which should be used for the file in question.
0180:             * <p class="nonnormative">
0181:             * It corresponds to the <code>-Xbootclasspath</code> and <code>-Xext</code>
0182:             * options to <code>java</code> (the Java launcher): i.e. all compiled
0183:             * classes in the JRE that will be needed to run the program.
0184:             * It may also be thought of as corresponding to the classes loadable
0185:             * by the primordial bootstrap class loader <em>plus</em> the standard
0186:             * extension and endorsed-library class loaders; i.e. class loaders lying
0187:             * below the regular application startup loader and any custom loaders.
0188:             * Generally there ought to be a single boot classpath for the entire
0189:             * application.
0190:             * </p>
0191:             * @since org.netbeans.modules.gsfpath.api/1 1.4
0192:             */
0193:            public static final String BOOT = "classpath/boot";
0194:
0195:            /**
0196:             * Name of the "roots" property
0197:             */
0198:            public static final String PROP_ROOTS = "roots";
0199:
0200:            /**
0201:             * Name of the "entries" property
0202:             */
0203:            public static final String PROP_ENTRIES = "entries";
0204:
0205:            /**
0206:             * Property to be fired when include/exclude set changes.
0207:             * @see FilteringPathResourceImplementation
0208:             * @since org.netbeans.modules.gsfpath.api/1 1.13
0209:             */
0210:            public static final String PROP_INCLUDES = "includes";
0211:
0212:            private static final Logger LOG = Logger.getLogger(ClassPath.class
0213:                    .getName());
0214:
0215:            private static final Lookup.Result<? extends ClassPathProvider> implementations = Lookup
0216:                    .getDefault().lookupResult(ClassPathProvider.class);
0217:
0218:            private final ClassPathImplementation impl;
0219:            private FileObject[] rootsCache;
0220:            /**
0221:             * Associates entry roots with the matching filter, if there is one.
0222:             * XXX not quite right since we could have the same root added twice with two different
0223:             * filters. But why would you do that?
0224:             */
0225:            private Map<FileObject, FilteringPathResourceImplementation> root2Filter = new WeakHashMap<FileObject, FilteringPathResourceImplementation>();
0226:            private PropertyChangeListener pListener;
0227:            private PropertyChangeListener weakPListener;
0228:            private RootsListener rootsListener;
0229:            private List<ClassPath.Entry> entriesCache;
0230:            private long invalidEntries; //Lamport ordering of events
0231:            private long invalidRoots; //Lamport ordering of events
0232:
0233:            /**
0234:             * Retrieves valid roots of ClassPath, in the proper order.
0235:             * If there's an entry in the ClassPath, which cannot be accessed,
0236:             * its root is not returned by this method. FileObjects returned
0237:             * are all folders.
0238:             * Note that this method ignores {@link FilteringPathResourceImplementation includes and excludes}.
0239:             * @return array of roots (folders) of the classpath. Never returns
0240:             * null.
0241:             */
0242:            public FileObject[] getRoots() {
0243:                long current;
0244:                synchronized (this ) {
0245:                    if (rootsCache != null) {
0246:                        return this .rootsCache;
0247:                    }
0248:                    current = this .invalidRoots;
0249:                }
0250:                List<ClassPath.Entry> entries = this .entries();
0251:                FileObject[] ret;
0252:                synchronized (this ) {
0253:                    if (this .invalidRoots == current) {
0254:                        if (rootsCache == null || rootsListener == null) {
0255:                            attachRootsListener();
0256:                            this .rootsCache = createRoots(entries);
0257:                        }
0258:                        ret = this .rootsCache;
0259:                    } else {
0260:                        ret = createRoots(entries);
0261:                    }
0262:                }
0263:                assert ret != null;
0264:                return ret;
0265:            }
0266:
0267:            private FileObject[] createRoots(final List<ClassPath.Entry> entries) {
0268:                List<FileObject> l = new ArrayList<FileObject>();
0269:                for (Entry entry : entries) {
0270:                    RootsListener rootsListener = this .getRootsListener();
0271:                    if (rootsListener != null) {
0272:                        rootsListener.addRoot(entry.getURL());
0273:                    }
0274:                    FileObject fo = entry.getRoot();
0275:                    if (fo != null) {
0276:                        l.add(fo);
0277:                        root2Filter.put(fo, entry.filter);
0278:                    }
0279:                }
0280:                return l.toArray(new FileObject[l.size()]);
0281:            }
0282:
0283:            /**
0284:             * Returns list of classpath entries from the ClassPath definition.
0285:             * The implementation must ensure that modifications done to the List are
0286:             * banned or at least not reflected in other Lists returned by this ClassPath
0287:             * instance. Clients must assume that the returned value is immutable.
0288:             * @return list of definition entries (Entry instances)
0289:             */
0290:            public List<ClassPath.Entry> entries() {
0291:                long current;
0292:                synchronized (this ) {
0293:                    if (this .entriesCache != null) {
0294:                        return this .entriesCache;
0295:                    }
0296:                    current = this .invalidEntries;
0297:                }
0298:                List<? extends PathResourceImplementation> resources = impl
0299:                        .getResources();
0300:                List<ClassPath.Entry> result;
0301:                synchronized (this ) {
0302:                    if (this .invalidEntries == current) {
0303:                        if (this .entriesCache == null) {
0304:                            this .entriesCache = createEntries(resources);
0305:                        }
0306:                        result = this .entriesCache;
0307:                    } else {
0308:                        result = createEntries(resources);
0309:                    }
0310:                }
0311:                assert result != null;
0312:                return result;
0313:            }
0314:
0315:            private List<ClassPath.Entry> createEntries(
0316:                    final List<? extends PathResourceImplementation> resources) {
0317:                //The ClassPathImplementation.getResources () should never return
0318:                // null but it was not explicitly stated in the javadoc
0319:                if (resources == null) {
0320:                    return Collections.<ClassPath.Entry> emptyList();
0321:                } else {
0322:                    List<ClassPath.Entry> cache = new ArrayList<ClassPath.Entry>();
0323:                    for (PathResourceImplementation pr : resources) {
0324:                        pr.removePropertyChangeListener(weakPListener);
0325:                        pr
0326:                                .addPropertyChangeListener(weakPListener = WeakListeners
0327:                                        .propertyChange(pListener, pr));
0328:                        for (URL root : pr.getRoots()) {
0329:                            cache
0330:                                    .add(new Entry(
0331:                                            root,
0332:                                            pr instanceof  FilteringPathResourceImplementation ? (FilteringPathResourceImplementation) pr
0333:                                                    : null));
0334:                        }
0335:                    }
0336:                    return Collections.unmodifiableList(cache);
0337:                }
0338:            }
0339:
0340:            private ClassPath(ClassPathImplementation impl) {
0341:                if (impl == null)
0342:                    throw new IllegalArgumentException();
0343:                this .impl = impl;
0344:                this .pListener = new SPIListener();
0345:                this .impl
0346:                        .addPropertyChangeListener(weakPListener = WeakListeners
0347:                                .propertyChange(this .pListener, this .impl));
0348:            }
0349:
0350:            /**
0351:             * Returns a FileObject for the specified resource. May return null,
0352:             * if the resource does not exist, or is not reachable through
0353:             * this ClassPath.<BR>
0354:             * If the <i>resourceName</i> identifies a package, this method will
0355:             * return the <code>FileObject</code> for the first <I>package fragment</I>
0356:             * in the <code>ClassPath</code>.
0357:             * {@link FilteringPathResourceImplementation} may cause an actual file
0358:             * beneath a registered root to not be returned.
0359:             * Note: do not pass names starting with slash to this method.
0360:             * @param resourceName name of the resource as it would be passed
0361:             *                     to {@link ClassLoader#getResource}
0362:             * @return FileObject for the resource, or null if the resource cannot
0363:             * be found in this ClassPath.
0364:             */
0365:            public final FileObject findResource(String resourceName) {
0366:                return findResourceImpl(getRoots(), new int[] { 0 },
0367:                        parseResourceName(resourceName));
0368:            }
0369:
0370:            /**
0371:             * Gives out an ordered collection containing all FileObjects, which correspond
0372:             * to a given ResourceName; only the first one is seen by the ClassLoader
0373:             * at runtime or can be linked against.  The resource name uses slashes ('/')
0374:             * as folder separator and must not start with slash.
0375:             * {@link FilteringPathResourceImplementation} may cause an actual file
0376:             * beneath a registered root to not be returned.
0377:             * @param resourceName resource name
0378:             * @return list of resources identified by the given name.
0379:             */
0380:            public final List<FileObject> findAllResources(String resourceName) {
0381:                FileObject[] roots = getRoots();
0382:                List<FileObject> l = new ArrayList<FileObject>(roots.length);
0383:                int[] idx = new int[] { 0 };
0384:                String[] namec = parseResourceName(resourceName);
0385:                while (idx[0] < roots.length) {
0386:                    FileObject f = findResourceImpl(roots, idx, namec);
0387:                    if (f != null)
0388:                        l.add(f);
0389:                }
0390:                return l;
0391:            }
0392:
0393:            /**
0394:             * Creates a suitable resource name for the given FileObject within the
0395:             * classpath. The method will return <code>null</code> if the fileobject
0396:             * is not underneath any of classpath roots.<BR>
0397:             * The returned name uses uses slashes ('/') as folder separators and
0398:             * dot ('.') to separate file name and its extension.
0399:             * Note that if the file object is in the classpath subtree, but is not reachable
0400:             * (it is hidden by some other resource, or {@link FilteringPathResourceImplementation excluded}), the resource name is still returned.
0401:             * @return Java-style resource name for the given file object (the empty string for the package root itself), or null if not
0402:             * within the classpath
0403:             * @param f FileObject whose resource name is requested
0404:             */
0405:            public final String getResourceName(FileObject f) {
0406:                return getResourceName(f, '/', true);
0407:            }
0408:
0409:            /**
0410:             * Computes a resource name for the FileObject, which uses `pathSep' character
0411:             * as a directory separator. The resource name can be returned without the file
0412:             * extension, if desired. Note that parent folder names are always returned with
0413:             * extension, if they have some.
0414:             * @param f FileObject whose resource name is requested.
0415:             * @param dirSep directory separator character
0416:             * @param includeExt whether the FileObject's extension should be included in the result
0417:             * @return resource name for the given FileObject (the empty string for the package root itself) or null
0418:             */
0419:            public final String getResourceName(FileObject f, char dirSep,
0420:                    boolean includeExt) {
0421:                FileObject owner = findOwnerRoot(f);
0422:                if (owner == null)
0423:                    return null;
0424:                String partName = FileUtil.getRelativePath(owner, f);
0425:                assert partName != null;
0426:                if (!includeExt) {
0427:                    int index = partName.lastIndexOf('.');
0428:                    if (index >= 0 && index > partName.lastIndexOf('/')) {
0429:                        partName = partName.substring(0, index);
0430:                    }
0431:                }
0432:                if (dirSep != '/') {
0433:                    partName = partName.replace('/', dirSep);
0434:                }
0435:                return partName;
0436:            }
0437:
0438:            /**
0439:             * Finds a root in this ClassPath, that owns the given file. File resources, that
0440:             * are not reachable (they are hidden by other resources, or {@link FilteringPathResourceImplementation} excluded) are still considered
0441:             * to be part of the classpath and "owned" by one of its roots.
0442:             * <br>
0443:             * <b>Note:</b> This implementation assumes that the FileSystem hosting a classpath root
0444:             * contains the entire classpath subtree rooted at that root folder.
0445:             * @return classpath root, which hosts the specified resouce. It can return null,
0446:             * if the resource is not within the ClassPath contents.
0447:             * @param resource resource to find root for.
0448:             */
0449:            public final FileObject findOwnerRoot(FileObject resource) {
0450:                FileObject[] roots = getRoots();
0451:                Set<FileObject> rootsSet = new HashSet<FileObject>(Arrays
0452:                        .asList(roots));
0453:                for (FileObject f = resource; f != null; f = f.getParent()) {
0454:                    if (rootsSet.contains(f)) {
0455:                        return f;
0456:                    }
0457:                }
0458:                return null;
0459:            }
0460:
0461:            /**
0462:             * Checks whether a FileObject lies on this classpath.
0463:             * {@link FilteringPathResourceImplementation} is considered.
0464:             * @return true, if the parameter is inside one of the classpath subtrees,
0465:             * false otherwise.
0466:             * @param f the FileObject to check
0467:             */
0468:            public final boolean contains(FileObject f) {
0469:                FileObject root = findOwnerRoot(f);
0470:                if (root == null) {
0471:                    return false;
0472:                }
0473:                FilteringPathResourceImplementation filter = root2Filter
0474:                        .get(root);
0475:                if (filter == null) {
0476:                    return true;
0477:                }
0478:                String path = FileUtil.getRelativePath(root, f);
0479:                assert path != null : "could not find " + f + " in " + root;
0480:                if (f.isFolder()) {
0481:                    path += "/"; // NOI18N
0482:                }
0483:                try {
0484:                    return filter.includes(root.getURL(), path);
0485:                } catch (FileStateInvalidException x) {
0486:                    throw new AssertionError(x);
0487:                }
0488:            }
0489:
0490:            /**
0491:             * Determines if the resource is <i>visible</i> in the classpath,
0492:             * that is if the file will be reached when a process attempts to
0493:             * load a resource of that name. It will return false when the resource
0494:             * is not contained in the classpath, or the resource is {@link FilteringPathResourceImplementation excluded}.
0495:             * @param resource the resource whose visibility should be tested
0496:             * @return true, if the resource is contained in the classpath and visible;
0497:             * false otherwise.
0498:             */
0499:            public final boolean isResourceVisible(FileObject resource) {
0500:                String resourceName = getResourceName(resource);
0501:                if (resourceName == null)
0502:                    return false;
0503:                return findResource(resourceName) == resource;
0504:            }
0505:
0506:            /**
0507:             * Adds a property change listener to the bean.
0508:             * @param l a listener to add
0509:             */
0510:            public final synchronized void addPropertyChangeListener(
0511:                    PropertyChangeListener l) {
0512:                attachRootsListener();
0513:                propSupport.addPropertyChangeListener(l);
0514:            }
0515:
0516:            /**
0517:             * Removes the listener registered by {@link addPropertyChangeListener}.
0518:             * @param l a listener to remove
0519:             */
0520:            public final void removePropertyChangeListener(
0521:                    PropertyChangeListener l) {
0522:                propSupport.removePropertyChangeListener(l);
0523:            }
0524:
0525:            /**
0526:             * Find the classpath of a given type, if any, defined for a given file.
0527:             * <p>This method may return null, if:</p>
0528:             * <ul>
0529:             * <li>the path type (<code>id</code> parameter) is not recognized
0530:             * <li>the path type is not defined for the given file object
0531:             * </ul>
0532:             * <p>
0533:             * Generally you may pass either an individual Java file, or the root of
0534:             * a Java package tree, interchangeably, since in most cases all files
0535:             * in a given tree will share a single classpath.
0536:             * </p>
0537:             * <p class="nonnormative">
0538:             * Typically classpaths for files are defined by the owning project, but
0539:             * there may be other ways classpaths are defined. See {@link ClassPathProvider}
0540:             * for more details.
0541:             * </p>
0542:             * @param f the file, whose classpath settings should be returned (may <em>not</em> be null as of org.netbeans.modules.gsfpath.api/1 1.4)
0543:             * @param id the type of the classpath (e.g. {@link #COMPILE})
0544:             * @return classpath of the desired type for the given file object, or <code>null</code>, if
0545:             *         there is no classpath available
0546:             * @see ClassPathProvider
0547:             */
0548:            public static ClassPath getClassPath(FileObject f, String id) {
0549:                if (f == null) {
0550:                    // What else can we do?? Backwards compatibility only.
0551:                    Thread.dumpStack();
0552:                    return null;
0553:                }
0554:                for (ClassPathProvider impl : implementations.allInstances()) {
0555:                    ClassPath cp = impl.findClassPath(f, id);
0556:                    if (cp != null) {
0557:                        LOG.log(Level.FINE,
0558:                                "getClassPath({0}, {1}) -> {2} from {3}",
0559:                                new Object[] { f, id, cp, impl });
0560:                        return cp;
0561:                    }
0562:                }
0563:                LOG.log(Level.FINE, "getClassPath({0}, {1}) -> nil",
0564:                        new Object[] { f, id });
0565:                return null;
0566:            }
0567:
0568:            /**
0569:             * Fires a property change event on the specified property, notifying the
0570:             * old and new values.
0571:             * @param what name of the property
0572:             * @param oldV old value
0573:             * @param newV new value
0574:             */
0575:            final void firePropertyChange(final String what, final Object oldV,
0576:                    final Object newV, final Object propagationId) {
0577:                final PropertyChangeEvent event = new PropertyChangeEvent(this ,
0578:                        what, oldV, newV);
0579:                event.setPropagationId(propagationId);
0580:                propSupport.firePropertyChange(event);
0581:            }
0582:
0583:            public String toString() {
0584:                return "ClassPath" + entries(); // NOI18N
0585:            }
0586:
0587:            /**
0588:             * Represents an individual entry in the ClassPath. An entry is a description
0589:             * of a folder, which is one of the ClassPath roots. Since the Entry does not
0590:             * control the folder's lifetime, the folder may be removed and the entry
0591:             * becomes invalid. It's also expected that ClassPath implementations may
0592:             * use other ClassPath entries as default or base for themselves, so Entries
0593:             * may be propagated between ClassPaths.
0594:             */
0595:            public final class Entry {
0596:
0597:                private final URL url;
0598:                private FileObject root;
0599:                private IOException lastError;
0600:                private FilteringPathResourceImplementation filter;
0601:
0602:                /**
0603:                 * Returns the ClassPath instance, which defines/introduces the Entry.
0604:                 * Note that the return value may differ from the ClassPath instance,
0605:                 * that produced this Entry from its <code>entries()</code> method.
0606:                 * @return the ClassPath that defines the entry.
0607:                 */
0608:                public ClassPath getDefiningClassPath() {
0609:                    return ClassPath.this ;
0610:                }
0611:
0612:                /**
0613:                 * The method returns the root folder represented by the Entry.
0614:                 * If the folder does not exist, or the folder is not readable,
0615:                 * the method may return null.
0616:                 * @return classpath entry root folder
0617:                 */
0618:                public FileObject getRoot() {
0619:                    synchronized (this ) {
0620:                        if (root != null && root.isValid()) {
0621:                            return root;
0622:                        }
0623:                    }
0624:                    FileObject _root = URLMapper.findFileObject(this .url);
0625:                    synchronized (this ) {
0626:                        if (root == null || !root.isValid()) {
0627:                            if (_root == null) {
0628:                                this .lastError = new IOException(
0629:                                        MessageFormat
0630:                                                .format(
0631:                                                        "The package root {0} does not exist or can not be read.",
0632:                                                        new Object[] { this .url }));
0633:                                return null;
0634:                            } else if (_root.isData()) {
0635:                                throw new IllegalArgumentException(
0636:                                        "Invalid ClassPath root: "
0637:                                                + this .url
0638:                                                + ". The root must be a folder.");
0639:                            }
0640:                            root = _root;
0641:                        }
0642:                        return root;
0643:                    }
0644:                }
0645:
0646:                /**
0647:                 * @return true, iff the Entry refers to an existing and readable
0648:                 * folder.
0649:                 */
0650:                public boolean isValid() {
0651:                    FileObject root = getRoot();
0652:                    return root != null && root.isValid();
0653:                }
0654:
0655:                /**
0656:                 * Retrieves the error condition of the Entry. The method will return
0657:                 * null, if the <code>getRoot()</code> would return a FileObject.
0658:                 * @return error condition for this Entry or null if the Entry is OK.
0659:                 */
0660:                public IOException getError() {
0661:                    IOException error = this .lastError;
0662:                    this .lastError = null;
0663:                    return error;
0664:                }
0665:
0666:                /**
0667:                 * Returns URL of the class path root.
0668:                 * This method is generally safer than {@link #getRoot} as
0669:                 * it can be called even if the root does not currently exist.
0670:                 * @return URL
0671:                 * @since org.netbeans.modules.gsfpath.api/1 1.4
0672:                 */
0673:                public URL getURL() {
0674:                    return this .url;
0675:                }
0676:
0677:                /**
0678:                 * Check whether a file is included in this entry.
0679:                 * @param resource a path relative to @{link #getURL} (must be terminted with <samp>/</samp> if a non-root folder)
0680:                 * @return true if it is {@link FilteringPathResourceImplementation#includes included}
0681:                 * @since org.netbeans.modules.gsfpath.api/1 1.13
0682:                 */
0683:                public boolean includes(String resource) {
0684:                    return filter == null || filter.includes(url, resource);
0685:                }
0686:
0687:                /**
0688:                 * Check whether a file is included in this entry.
0689:                 * @param file a URL beneath @{link #getURL}
0690:                 * @return true if it is {@link FilteringPathResourceImplementation#includes included}
0691:                 * @throws IllegalArgumentException in case the argument is not beneath {@link #getURL}
0692:                 * @since org.netbeans.modules.gsfpath.api/1 1.13
0693:                 */
0694:                public boolean includes(URL file) {
0695:                    if (!file.toExternalForm().startsWith(url.toExternalForm())) {
0696:                        throw new IllegalArgumentException(file + " not in "
0697:                                + url);
0698:                    }
0699:                    URI relative;
0700:                    try {
0701:                        relative = url.toURI().relativize(file.toURI());
0702:                    } catch (URISyntaxException x) {
0703:                        throw new AssertionError(x);
0704:                    }
0705:                    assert !relative.isAbsolute() : "could not locate " + file
0706:                            + " in " + url;
0707:                    return filter == null
0708:                            || filter.includes(url, relative.toString());
0709:                }
0710:
0711:                /**
0712:                 * Check whether a file is included in this entry.
0713:                 * @param file a file inside @{link #getRoot}
0714:                 * @return true if it is {@link FilteringPathResourceImplementation#includes included}
0715:                 * @throws IllegalArgumentException in case the argument is not beneath {@link #getRoot}, or {@link #getRoot} is null
0716:                 * @since org.netbeans.modules.gsfpath.api/1 1.13
0717:                 */
0718:                public boolean includes(FileObject file) {
0719:                    FileObject root = getRoot();
0720:                    if (root == null) {
0721:                        throw new IllegalArgumentException("no root in " + url);
0722:                    }
0723:                    String path = FileUtil.getRelativePath(root, file);
0724:                    if (path == null) {
0725:                        throw new IllegalArgumentException(file + " not in "
0726:                                + root);
0727:                    }
0728:                    if (file.isFolder()) {
0729:                        path += "/"; // NOI18N
0730:                    }
0731:                    return filter == null || filter.includes(url, path);
0732:                }
0733:
0734:                Entry(URL url, FilteringPathResourceImplementation filter) {
0735:                    if (url == null)
0736:                        throw new IllegalArgumentException();
0737:                    this .url = url;
0738:                    this .filter = filter;
0739:                }
0740:
0741:                public String toString() {
0742:                    return "Entry[" + url + "]"; // NOI18N
0743:                }
0744:
0745:                @Override
0746:                public boolean equals(Object other) {
0747:                    if (other instanceof  ClassPath.Entry) {
0748:                        return Utilities.compareObjects(
0749:                                ((ClassPath.Entry) other).url, this .url);
0750:                    }
0751:                    return false;
0752:                }
0753:
0754:                @Override
0755:                public int hashCode() {
0756:                    return this .url == null ? 0 : this .url.hashCode();
0757:                }
0758:            }
0759:
0760:            //-------------------- Implementation details ------------------------//
0761:
0762:            private final PropertyChangeSupport propSupport = new PropertyChangeSupport(
0763:                    this );
0764:
0765:            /**
0766:             * Attaches the listener to the ClassPath.entries.
0767:             * Not synchronized, HAS TO be called from the synchronized block!
0768:             */
0769:            private void attachRootsListener() {
0770:                if (this .rootsListener == null) {
0771:                    assert this .rootsCache == null;
0772:                    this .rootsListener = new RootsListener(this );
0773:                }
0774:            }
0775:
0776:            /**
0777:             * Returns an array of pairs of strings, first string in the pair is the
0778:             * name, the next one is either the extension or null.
0779:             */
0780:            private static String[] parseResourceName(String name) {
0781:                Collection<String> parsed = new ArrayList<String>(
0782:                        name.length() / 4);
0783:                char[] chars = name.toCharArray();
0784:                char ch;
0785:                int pos = 0;
0786:                int dotPos = -1;
0787:                int startPos = 0;
0788:
0789:                while (pos < chars.length) {
0790:                    ch = chars[pos];
0791:                    switch (ch) {
0792:                    case '.':
0793:                        dotPos = pos;
0794:                        break;
0795:                    case '/':
0796:                        // end of name component
0797:                        if (dotPos != -1) {
0798:                            parsed.add(name.substring(startPos, dotPos));
0799:                            parsed.add(name.substring(dotPos + 1, pos));
0800:                        } else {
0801:                            parsed.add(name.substring(startPos, pos));
0802:                            parsed.add(null);
0803:                        }
0804:                        // reset variables:
0805:                        startPos = pos + 1;
0806:                        dotPos = -1;
0807:                        break;
0808:                    }
0809:                    pos++;
0810:                }
0811:                // if the resource name ends with '/', just ignore the empty component
0812:                if (pos > startPos) {
0813:                    if (dotPos != -1) {
0814:                        parsed.add(name.substring(startPos, dotPos));
0815:                        parsed.add(name.substring(dotPos + 1, pos));
0816:                    } else {
0817:                        parsed.add(name.substring(startPos, pos));
0818:                        parsed.add(null);
0819:                    }
0820:                }
0821:                if ((parsed.size() % 2) != 0) {
0822:                    System.err.println("parsed size is not even!!");
0823:                    System.err.println("input = " + name);
0824:                }
0825:                return parsed.toArray(new String[parsed.size()]);
0826:            }
0827:
0828:            /**
0829:             * Finds a path underneath the `parent'. Name parts is an array of string pairs,
0830:             * the first String in a pair is the basename, the second is the extension or null
0831:             * for no extension.
0832:             */
0833:            private static FileObject findPath(FileObject parent,
0834:                    String[] nameParts) {
0835:                FileObject child;
0836:
0837:                for (int i = 0; i < nameParts.length && parent != null; i += 2, parent = child) {
0838:                    child = parent
0839:                            .getFileObject(nameParts[i], nameParts[i + 1]);
0840:                }
0841:                return parent;
0842:            }
0843:
0844:            /**
0845:             * Searches for a resource in one or more roots, gives back the index of
0846:             * the first untouched root.
0847:             */
0848:            private FileObject findResourceImpl(FileObject[] roots,
0849:                    int[] rootIndex, String[] nameComponents) {
0850:                int ridx;
0851:                FileObject f = null;
0852:                for (ridx = rootIndex[0]; ridx < roots.length && f == null; ridx++) {
0853:                    f = findPath(roots[ridx], nameComponents);
0854:                    FilteringPathResourceImplementation filter = root2Filter
0855:                            .get(roots[ridx]);
0856:                    if (filter != null) {
0857:                        try {
0858:                            if (f != null) {
0859:                                String path = FileUtil.getRelativePath(
0860:                                        roots[ridx], f);
0861:                                assert path != null;
0862:                                if (f.isFolder()) {
0863:                                    path += "/"; // NOI18N
0864:                                }
0865:                                if (!filter
0866:                                        .includes(roots[ridx].getURL(), path)) {
0867:                                    f = null;
0868:                                }
0869:                            }
0870:                        } catch (FileStateInvalidException x) {
0871:                            throw new AssertionError(x);
0872:                        }
0873:                    }
0874:                }
0875:                rootIndex[0] = ridx;
0876:                return f;
0877:            }
0878:
0879:            private static final Reference<ClassLoader> EMPTY_REF = new SoftReference<ClassLoader>(
0880:                    null);
0881:
0882:            private Reference<ClassLoader> refClassLoader = EMPTY_REF;
0883:
0884:            /* package private */synchronized void resetClassLoader(
0885:                    ClassLoader cl) {
0886:                if (refClassLoader.get() == cl)
0887:                    refClassLoader = EMPTY_REF;
0888:            }
0889:
0890:            /**
0891:             * Returns a ClassLoader for loading classes from this ClassPath.
0892:             * <p>
0893:             * If <code>cache</code> is false, then
0894:             * the method will always return a new class loader. If that parameter is true,
0895:             * the method may return a loader which survived from a previous call to the same <code>ClassPath</code>.
0896:             *
0897:             * @param cache true if it is permissible to cache class loaders between calls
0898:             * @return class loader which uses the roots in this class path to search for classes and resources
0899:             * @since 1.2.1
0900:             */
0901:            public final synchronized ClassLoader getClassLoader(boolean cache) {
0902:                // XXX consider adding ClassLoader and/or InputOutput and/or PermissionCollection params
0903:                ClassLoader o = refClassLoader.get();
0904:                if (!cache || o == null) {
0905:                    o = ClassLoaderSupport.create(this );
0906:                    refClassLoader = new SoftReference<ClassLoader>(o);
0907:                }
0908:                return o;
0909:            }
0910:
0911:            private class SPIListener implements  PropertyChangeListener {
0912:                private Object propIncludesPropagationId;
0913:
0914:                public void propertyChange(PropertyChangeEvent evt) {
0915:                    String prop = evt.getPropertyName();
0916:                    if (ClassPathImplementation.PROP_RESOURCES.equals(prop)
0917:                            || PathResourceImplementation.PROP_ROOTS
0918:                                    .equals(prop)) {
0919:                        synchronized (ClassPath.this ) {
0920:                            if (rootsListener != null) {
0921:                                rootsListener.removeAllRoots();
0922:                            }
0923:                            entriesCache = null;
0924:                            rootsCache = null;
0925:                            invalidEntries++;
0926:                            invalidRoots++;
0927:                        }
0928:                        firePropertyChange(PROP_ENTRIES, null, null, null);
0929:                        firePropertyChange(PROP_ROOTS, null, null, null);
0930:                    } else if (FilteringPathResourceImplementation.PROP_INCLUDES
0931:                            .equals(prop)) {
0932:                        boolean fire;
0933:                        synchronized (this ) {
0934:                            Object id = evt.getPropagationId();
0935:                            fire = propIncludesPropagationId == null
0936:                                    || !propIncludesPropagationId.equals(id);
0937:                            propIncludesPropagationId = id;
0938:                        }
0939:                        if (fire) {
0940:                            firePropertyChange(PROP_INCLUDES, null, null, evt
0941:                                    .getPropagationId());
0942:                        }
0943:                    }
0944:                    if (ClassPathImplementation.PROP_RESOURCES.equals(prop)) {
0945:                        final List<? extends PathResourceImplementation> resources = impl
0946:                                .getResources();
0947:                        if (resources == null) {
0948:                            LOG
0949:                                    .warning("ClassPathImplementation.getResources cannot return null; impl class: "
0950:                                            + impl.getClass().getName());
0951:                            return;
0952:                        }
0953:                        for (PathResourceImplementation pri : resources) {
0954:                            pri.removePropertyChangeListener(weakPListener);
0955:                            pri
0956:                                    .addPropertyChangeListener(weakPListener = WeakListeners
0957:                                            .propertyChange(pListener, pri));
0958:                        }
0959:                    }
0960:                }
0961:            }
0962:
0963:            private synchronized RootsListener getRootsListener() {
0964:                return this .rootsListener;
0965:            }
0966:
0967:            private static class RootsListener extends WeakReference<ClassPath>
0968:                    implements  FileChangeListener, Runnable {
0969:
0970:                private boolean initialized;
0971:                private Set<String> roots;
0972:
0973:                private RootsListener(ClassPath owner) {
0974:                    super (owner, Utilities.activeReferenceQueue());
0975:                    roots = new HashSet<String>();
0976:                }
0977:
0978:                public void addRoot(URL url) {
0979:                    if (!isInitialized()) {
0980:                        FileUtil.addFileChangeListener(this );
0981:                        setInitialized(true);
0982:                    }
0983:                    if ("jar".equals(url.getProtocol())) { //NOI18N
0984:                        url = FileUtil.getArchiveFile(url);
0985:                    }
0986:                    String path = url.getPath();
0987:                    if (path.endsWith("/")) { //NOI18N
0988:                        path = path.substring(0, path.length() - 1);
0989:                    }
0990:                    roots.add(path);
0991:                }
0992:
0993:                public void removeRoot(URL url) {
0994:                    if ("jar".equals(url.getProtocol())) { //NOI18N
0995:                        url = FileUtil.getArchiveFile(url);
0996:                    }
0997:                    String path = url.getPath();
0998:                    if (path.endsWith("/")) { //NOI18N
0999:                        path = path.substring(0, path.length() - 1);
1000:                    }
1001:                    roots.remove(path);
1002:                }
1003:
1004:                public void removeAllRoots() {
1005:                    this .roots.clear();
1006:                    FileUtil.removeFileChangeListener(this );
1007:                    initialized = false; //Already synchronized
1008:                }
1009:
1010:                public void fileFolderCreated(FileEvent fe) {
1011:                    this .processEvent(fe);
1012:                }
1013:
1014:                public void fileDataCreated(FileEvent fe) {
1015:                    this .processEvent(fe);
1016:                }
1017:
1018:                public void fileChanged(FileEvent fe) {
1019:                    if (!isInitialized()) {
1020:                        return; //Cache already cleared
1021:                    }
1022:                    String path = getPath(fe.getFile());
1023:                    if (this .roots.contains(path)) {
1024:                        ClassPath cp = get();
1025:                        if (cp != null) {
1026:                            synchronized (cp) {
1027:                                cp.rootsCache = null;
1028:                                cp.invalidRoots++;
1029:                                this .removeAllRoots(); //No need to listen
1030:                            }
1031:                            cp.firePropertyChange(PROP_ROOTS, null, null, null);
1032:                        }
1033:                    }
1034:                }
1035:
1036:                public void fileDeleted(FileEvent fe) {
1037:                    this .processEvent(fe);
1038:                }
1039:
1040:                public void fileRenamed(FileRenameEvent fe) {
1041:                    this .processEvent(fe);
1042:                }
1043:
1044:                public void fileAttributeChanged(FileAttributeEvent fe) {
1045:                }
1046:
1047:                public void run() {
1048:                    if (isInitialized()) {
1049:                        FileUtil.removeFileChangeListener(this );
1050:                    }
1051:                }
1052:
1053:                private void processEvent(FileEvent fe) {
1054:                    if (!isInitialized()) {
1055:                        return; //Not interesting, cache already cleared
1056:                    }
1057:                    String path = getPath(fe.getFile());
1058:                    if (path == null)
1059:                        return;
1060:                    ClassPath cp = get();
1061:                    if (cp == null) {
1062:                        return;
1063:                    }
1064:                    boolean fire = false;
1065:                    synchronized (cp) {
1066:                        for (String rootPath : roots) {
1067:                            if (rootPath.startsWith(path)) {
1068:                                cp.rootsCache = null;
1069:                                cp.invalidRoots++;
1070:                                this .removeAllRoots(); //No need to listen
1071:                                fire = true;
1072:                                break;
1073:                            }
1074:                        }
1075:                    }
1076:                    if (fire) {
1077:                        cp.firePropertyChange(PROP_ROOTS, null, null, null);
1078:                    }
1079:                }
1080:
1081:                private static String getPath(FileObject fo) {
1082:                    if (fo == null)
1083:                        return null;
1084:                    try {
1085:                        URL url = fo.getURL();
1086:                        String path = url.getPath();
1087:                        if (path.endsWith("/")) { //NOI18N
1088:                            path = path.substring(0, path.length() - 1);
1089:                        }
1090:                        return path;
1091:                    } catch (FileStateInvalidException e) {
1092:                        Exceptions.printStackTrace(e);
1093:                        return null;
1094:                    }
1095:                }
1096:
1097:                private synchronized boolean isInitialized() {
1098:                    return this .initialized;
1099:                }
1100:
1101:                private synchronized void setInitialized(boolean newValue) {
1102:                    this.initialized = newValue;
1103:                }
1104:            }
1105:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.