This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 228384
Collapse All | Expand All

(-)a/java.project/nbproject/project.xml (+8 lines)
Lines 128-133 Link Here
128
                    </run-dependency>
128
                    </run-dependency>
129
                </dependency>
129
                </dependency>
130
                <dependency>
130
                <dependency>
131
                    <code-name-base>org.netbeans.modules.java.source</code-name-base>
132
                    <build-prerequisite/>
133
                    <compile-dependency/>
134
                    <run-dependency>
135
                        <specification-version>0.118</specification-version>
136
                    </run-dependency>
137
                </dependency>
138
                <dependency>
131
                    <code-name-base>org.netbeans.modules.project.ant</code-name-base>
139
                    <code-name-base>org.netbeans.modules.project.ant</code-name-base>
132
                    <build-prerequisite/>
140
                    <build-prerequisite/>
133
                    <compile-dependency/>
141
                    <compile-dependency/>
(-)a/java.project/src/org/netbeans/modules/java/project/ProfileProblemsProviderImpl.java (-187 / +44 lines)
Lines 45-72 Link Here
45
import java.beans.PropertyChangeListener;
45
import java.beans.PropertyChangeListener;
46
import java.io.File;
46
import java.io.File;
47
import java.io.IOException;
47
import java.io.IOException;
48
import java.io.InputStream;
49
import java.net.URI;
50
import java.net.URL;
48
import java.net.URL;
51
import java.util.ArrayDeque;
49
import java.util.ArrayDeque;
52
import java.util.ArrayList;
50
import java.util.ArrayList;
53
import java.util.Arrays;
51
import java.util.Arrays;
54
import java.util.Collection;
52
import java.util.Collection;
55
import java.util.Collections;
53
import java.util.Collections;
54
import java.util.EnumSet;
56
import java.util.HashSet;
55
import java.util.HashSet;
57
import java.util.Iterator;
56
import java.util.Iterator;
58
import java.util.LinkedHashMap;
59
import java.util.List;
57
import java.util.List;
60
import java.util.Map;
61
import java.util.Queue;
58
import java.util.Queue;
62
import java.util.Set;
59
import java.util.Set;
63
import java.util.concurrent.Callable;
60
import java.util.concurrent.Callable;
64
import java.util.concurrent.Future;
61
import java.util.concurrent.Future;
65
import java.util.concurrent.FutureTask;
62
import java.util.concurrent.FutureTask;
66
import java.util.concurrent.RunnableFuture;
63
import java.util.concurrent.RunnableFuture;
67
import java.util.jar.Attributes;
68
import java.util.jar.Manifest;
69
import java.util.logging.Level;
70
import java.util.logging.Logger;
64
import java.util.logging.Logger;
71
import javax.swing.Icon;
65
import javax.swing.Icon;
72
import javax.swing.JButton;
66
import javax.swing.JButton;
Lines 78-83 Link Here
78
import org.netbeans.api.java.classpath.ClassPath;
72
import org.netbeans.api.java.classpath.ClassPath;
79
import org.netbeans.api.java.queries.SourceLevelQuery;
73
import org.netbeans.api.java.queries.SourceLevelQuery;
80
import org.netbeans.api.java.queries.SourceLevelQuery.Profile;
74
import org.netbeans.api.java.queries.SourceLevelQuery.Profile;
75
import org.netbeans.api.java.source.support.ProfileSupport;
81
import org.netbeans.api.project.FileOwnerQuery;
76
import org.netbeans.api.project.FileOwnerQuery;
82
import org.netbeans.api.project.Project;
77
import org.netbeans.api.project.Project;
83
import org.netbeans.api.project.ProjectManager;
78
import org.netbeans.api.project.ProjectManager;
Lines 89-95 Link Here
89
import org.netbeans.spi.project.support.ant.ReferenceHelper;
84
import org.netbeans.spi.project.support.ant.ReferenceHelper;
90
import org.netbeans.spi.project.ui.ProjectProblemsProvider;
85
import org.netbeans.spi.project.ui.ProjectProblemsProvider;
91
import org.netbeans.spi.project.ui.support.ProjectProblemsProviderSupport;
86
import org.netbeans.spi.project.ui.support.ProjectProblemsProviderSupport;
92
import org.openide.filesystems.FileObject;
93
import org.openide.filesystems.FileUtil;
87
import org.openide.filesystems.FileUtil;
94
import org.openide.util.Mutex;
88
import org.openide.util.Mutex;
95
import org.openide.util.NbBundle;
89
import org.openide.util.NbBundle;
Lines 105-114 Link Here
105
import org.netbeans.spi.java.classpath.ClassPathFactory;
99
import org.netbeans.spi.java.classpath.ClassPathFactory;
106
import org.netbeans.spi.java.project.classpath.support.ProjectClassPathSupport;
100
import org.netbeans.spi.java.project.classpath.support.ProjectClassPathSupport;
107
import org.netbeans.spi.project.support.ant.PropertyUtils;
101
import org.netbeans.spi.project.support.ant.PropertyUtils;
108
import org.openide.filesystems.URLMapper;
109
import org.openide.util.ImageUtilities;
102
import org.openide.util.ImageUtilities;
110
import org.openide.util.RequestProcessor;
103
import org.openide.util.RequestProcessor;
111
import org.openide.util.Union2;
112
/**
104
/**
113
 *
105
 *
114
 * @author Tomas Zezula
106
 * @author Tomas Zezula
Lines 326-336 Link Here
326
                    final String libName = rawEntry.substring(LIB_PREFIX.length(),rawEntry.lastIndexOf('.'));
318
                    final String libName = rawEntry.substring(LIB_PREFIX.length(),rawEntry.lastIndexOf('.'));
327
                    final Library lib = refHelper.findLibrary(libName);
319
                    final Library lib = refHelper.findLibrary(libName);
328
                    if (lib != null) {
320
                    if (lib != null) {
329
                        final Union2<Profile,String> minProfileOrName = findProfile(lib.getContent(VOL_CLASSPATH));
321
                        final Collection<? extends ProfileSupport.Violation> res =
330
                        final Profile minProfile = minProfileOrName.hasFirst() ? minProfileOrName.first() : null;
322
                            ProfileSupport.findProfileViolations(
331
                        if (isBroken(requiredProfile,  minProfile)) {
323
                                requiredProfile,
332
                            collector.add(new LibraryReference(classPathId, rawEntry, minProfile, lib));
324
                                Collections.<URL>emptySet(),
333
                        }
325
                                lib.getContent(VOL_CLASSPATH),
326
                                Collections.<URL>emptySet(),
327
                                EnumSet.of(ProfileSupport.Validation.BINARIES_BY_MANIFEST));
328
                            if (!res.isEmpty()) {
329
                                final Profile maxProfile = findMaxProfile(res);
330
                                collector.add(new LibraryReference(classPathId, rawEntry, maxProfile, lib));
331
                            }
334
                    }
332
                    }
335
                } else if (rawEntry.startsWith(PRJ_PREFIX)) {
333
                } else if (rawEntry.startsWith(PRJ_PREFIX)) {
336
                    final Object[] ref = refHelper.findArtifactAndLocation(rawEntry);
334
                    final Object[] ref = refHelper.findArtifactAndLocation(rawEntry);
Lines 350-359 Link Here
350
                        final File file = antProjectHelper.resolveFile(path);
348
                        final File file = antProjectHelper.resolveFile(path);
351
                        final URL root = FileUtil.urlForArchiveOrDir(file);
349
                        final URL root = FileUtil.urlForArchiveOrDir(file);
352
                        if (root != null) {
350
                        if (root != null) {
353
                            final Union2<Profile,String> minProfileOrName = findProfile(root);
351
                            final Collection<? extends ProfileSupport.Violation> res =
354
                            final Profile minProfile = minProfileOrName.hasFirst() ? minProfileOrName.first() : null;
352
                                ProfileSupport.findProfileViolations(
355
                            if (isBroken(requiredProfile, minProfile)) {
353
                                    requiredProfile,
356
                                collector.add(new FileReference(classPathId, rawEntry, minProfile, file));
354
                                    Collections.<URL>emptySet(),
355
                                    Collections.singleton(root),
356
                                    Collections.<URL>emptySet(),
357
                                    EnumSet.of(ProfileSupport.Validation.BINARIES_BY_MANIFEST));
358
                            if (!res.isEmpty()) {
359
                                final Profile maxProfile = findMaxProfile(res);
360
                                collector.add(new FileReference(classPathId, rawEntry, maxProfile, file));
357
                            }
361
                            }
358
                        }
362
                        }
359
                    }
363
                    }
Lines 373-382 Link Here
373
                    final File file = antProjectHelper.resolveFile(propName);
377
                    final File file = antProjectHelper.resolveFile(propName);
374
                    final URL root = FileUtil.urlForArchiveOrDir(file);
378
                    final URL root = FileUtil.urlForArchiveOrDir(file);
375
                    if (root != null) {
379
                    if (root != null) {
376
                        final Union2<Profile,String> minProfileOrName = findProfile(root);
380
                        final Collection<? extends ProfileSupport.Violation> res =
377
                        final Profile minProfile = minProfileOrName.hasFirst() ? minProfileOrName.first() : null;
381
                                ProfileSupport.findProfileViolations(
378
                        if (isBroken(requiredProfile, minProfile)) {
382
                                    requiredProfile,
379
                            collector.add(new FileReference(classPathId, rawEntry, minProfile, file));
383
                                    Collections.<URL>emptySet(),
384
                                    Collections.singleton(root),
385
                                    Collections.<URL>emptySet(),
386
                                    EnumSet.of(ProfileSupport.Validation.BINARIES_BY_MANIFEST));
387
                        if (!res.isEmpty()) {
388
                            final Profile maxProfile = findMaxProfile(res);
389
                            collector.add(new FileReference(classPathId, rawEntry, maxProfile, file));
380
                        }
390
                        }
381
                    }
391
                    }
382
                }
392
                }
Lines 405-464 Link Here
405
        return reference;
415
        return reference;
406
    }
416
    }
407
417
408
    @NonNull
418
    @CheckForNull
409
    private static Union2<Profile,String> findProfile(@NonNull final Iterable<? extends URL> roots) {
419
    private static Profile findMaxProfile(@NonNull final Iterable<? extends ProfileSupport.Violation> violations) {
410
        Profile current = Profile.DEFAULT;
420
        Profile current = null;
411
        for (URL root : roots) {
421
        for (ProfileSupport.Violation violation : violations) {
412
            final Union2<Profile,String> rootProfile = findProfile(root);
422
            Profile p = violation.getRequiredProfile();
413
            if (!rootProfile.hasFirst()) {
423
            if (p == null) {
414
                //Broken profile - no need to continue
424
                //Broken profile - no need to continue
415
                return rootProfile;
425
                return null;
416
            }
426
            }
417
            current = max(current, rootProfile.first());
427
            current = max(current, p);
418
        }
428
        }
419
        return Union2.<Profile,String>createFirst(current);
429
        return current;
420
    }
430
    }
421
431
    
422
    @NonNull
423
    private static Union2<Profile,String> findProfile(@NonNull URL root) {
424
        Union2<Profile,String> res;
425
        final ArchiveCache.Key key = ArchiveCache.createKey(root);
426
        if (key != null) {
427
            res = ArchiveCache.getProfile(key);
428
            if (res != null) {
429
                return res;
430
            }
431
        }
432
        String profileName = null;
433
        final FileObject rootFo = URLMapper.findFileObject(root);
434
        if (rootFo != null) {
435
            final FileObject manifestFile = rootFo.getFileObject(RES_MANIFEST);
436
            if (manifestFile != null) {
437
                try {
438
                    try (InputStream in = manifestFile.getInputStream()) {
439
                        final Manifest manifest = new Manifest(in);
440
                        final Attributes attrs = manifest.getMainAttributes();
441
                        profileName = attrs.getValue(ATTR_PROFILE);
442
                    }
443
                } catch (IOException ioe) {
444
                    LOG.log(
445
                        Level.INFO,
446
                        "Cannot read Profile attribute from: {0}", //NOI18N
447
                        FileUtil.getFileDisplayName(manifestFile));
448
                }
449
            }
450
        }
451
        final Profile profile = Profile.forName(profileName);
452
        res = profile != null ?
453
                Union2.<Profile,String>createFirst(profile) :
454
                Union2.<Profile,String>createSecond(profileName);
455
        if (key != null) {
456
            ArchiveCache.putProfile(key, res);
457
        }
458
        return res;
459
    }
460
461
462
    @CheckForNull
432
    @CheckForNull
463
    static Profile max (@NullAllowed Profile a, @NullAllowed Profile b) {
433
    static Profile max (@NullAllowed Profile a, @NullAllowed Profile b) {
464
        if (b == null) {
434
        if (b == null) {
Lines 745-862 Link Here
745
            res.run();
715
            res.run();
746
            return res;
716
            return res;
747
        }
717
        }
748
    }
718
    }    
749
750
751
    private static final class ArchiveCache {
752
753
        private static final int MAX_CACHE_SIZE = Integer.getInteger(
754
                "ProfileProblemsProviderImpl.ArchiveCache.size",    //NOI18N
755
                1<<10);        
756
757
        private static final Map<Key,Union2<Profile,String>> cache = new LinkedHashMap<Key,Union2<Profile,String>>(16, 0.75f, true) {
758
            @Override
759
            protected boolean removeEldestEntry(Map.Entry<Key, Union2<Profile,String>> entry) {
760
                return size() > MAX_CACHE_SIZE;
761
            }
762
        };
763
764
        private ArchiveCache() {}
765
766
        @CheckForNull
767
        static Union2<Profile,String> getProfile(@NonNull final Key key) {
768
            final Union2<Profile,String> res = cache.get(key);
769
            if (LOG.isLoggable(Level.FINER)) {
770
                LOG.log(
771
                    Level.FINER,
772
                    "cache[{0}]->{1}",  //NOI18N
773
                    new Object[]{
774
                        key,
775
                        res.hasFirst() ? res.first() : res.second()
776
                    });
777
            }
778
            return res;
779
        }
780
781
        static void putProfile(
782
            @NonNull final  Key key,
783
            @NonNull final Union2<Profile,String> profile) {
784
            if (LOG.isLoggable(Level.FINER)) {
785
                LOG.log(
786
                    Level.FINER,
787
                    "cache[{0}]<-{1}",   //NOI18N
788
                    new Object[]{
789
                        key,
790
                        profile.hasFirst() ? profile.first() : profile.second()
791
                    });
792
            }
793
            cache.put(key,profile);
794
        }
795
796
        @CheckForNull
797
        static Key createKey(@NonNull final URL rootURL) {
798
            final URL fileURL = FileUtil.getArchiveFile(rootURL);
799
            if (fileURL == null) {
800
                //Not an archive
801
                return null;
802
            }
803
            final FileObject fileFo = URLMapper.findFileObject(fileURL);
804
            if (fileFo == null) {
805
                return null;
806
            }
807
            return new Key(
808
                fileFo.toURI(),
809
                fileFo.lastModified().getTime(),
810
                fileFo.getSize());
811
        }
812
813
        private static final class Key {
814
815
            private final URI root;
816
            private final long mtime;
817
            private final long size;
818
819
            Key(
820
                    @NonNull final URI root,
821
                    final long mtime,
822
                    final long size) {
823
                this.root = root;
824
                this.mtime = mtime;
825
                this.size = size;
826
            }
827
828
            @Override
829
            public int hashCode() {
830
                int hash = 17;
831
                hash = 31 * hash + (this.root != null ? this.root.hashCode() : 0);
832
                hash = 31 * hash + (int) (this.mtime ^ (this.mtime >>> 32));
833
                hash = 31 * hash + (int) (this.size ^ (this.size >>> 32));
834
                return hash;
835
            }
836
837
            @Override
838
            public boolean equals(Object obj) {
839
                if (obj == this) {
840
                    return true;
841
                }
842
                if (!(obj instanceof Key)) {
843
                    return false;
844
                }
845
                final Key other = (Key) obj;
846
                return this.root.equals(other.root) &&
847
                    this.mtime == other.mtime &&
848
                    this.size == other.size;
849
850
            }
851
852
            @Override
853
            public String toString() {
854
                return String.format(
855
                    "Key{root: %s, mtime: %d, size: %d}",   //NOI18N
856
                    root,
857
                    mtime,
858
                    size);
859
            }
860
        }
861
    }
862
}
719
}
(-)a/java.source/apichanges.xml (+13 lines)
Lines 108-113 Link Here
108
    <!-- ACTUAL CHANGES BEGIN HERE: -->
108
    <!-- ACTUAL CHANGES BEGIN HERE: -->
109
109
110
    <changes>
110
    <changes>
111
        <change id="ProfileSupport">
112
             <api name="general"/>
113
             <summary>Added support for JDK 8 Profiles.</summary>
114
             <version major="0" minor="119"/>
115
             <date day="12" month="4" year="2013"/>
116
             <author login="tzezula"/>
117
            <compatibility addition="yes" binary="compatible" deletion="no" deprecation="no" modification="no" semantic="compatible" source="compatible"/>
118
             <description>
119
                 Added <code>ProfileSupport</code> class providing utility methods for JDK 8 profiles.
120
             </description>
121
             <class package="org.netbeans.api.java.source.support" name="ProfileSupport"/>
122
             <issue number="228384"/>
123
        </change>
111
         <change id="getSymbols">
124
         <change id="getSymbols">
112
             <api name="general"/>
125
             <api name="general"/>
113
             <summary>Added several methods to support Java 8 features.</summary>
126
             <summary>Added several methods to support Java 8 features.</summary>
(-)a/java.source/nbproject/project.properties (-1 / +1 lines)
Lines 46-52 Link Here
46
javadoc.title=Java Source
46
javadoc.title=Java Source
47
javadoc.arch=${basedir}/arch.xml
47
javadoc.arch=${basedir}/arch.xml
48
javadoc.apichanges=${basedir}/apichanges.xml
48
javadoc.apichanges=${basedir}/apichanges.xml
49
spec.version.base=0.118.0
49
spec.version.base=0.119.0
50
test.qa-functional.cp.extra=${refactoring.java.dir}/modules/ext/nb-javac-api.jar
50
test.qa-functional.cp.extra=${refactoring.java.dir}/modules/ext/nb-javac-api.jar
51
test.unit.run.cp.extra=${o.n.core.dir}/core/core.jar:\
51
test.unit.run.cp.extra=${o.n.core.dir}/core/core.jar:\
52
    ${o.n.core.dir}/lib/boot.jar:\
52
    ${o.n.core.dir}/lib/boot.jar:\
(-)a/java.source/src/org/netbeans/api/java/source/support/ProfileSupport.java (+848 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2013 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2013 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.api.java.source.support;
43
44
import java.io.File;
45
import java.io.IOException;
46
import java.io.InputStream;
47
import java.net.URI;
48
import java.net.URL;
49
import java.util.ArrayDeque;
50
import java.util.Collection;
51
import java.util.Collections;
52
import java.util.EnumSet;
53
import java.util.Enumeration;
54
import java.util.LinkedHashMap;
55
import java.util.Map;
56
import java.util.Queue;
57
import java.util.Set;
58
import java.util.concurrent.ConcurrentHashMap;
59
import java.util.concurrent.ConcurrentMap;
60
import java.util.concurrent.Executor;
61
import java.util.jar.Attributes;
62
import java.util.jar.Manifest;
63
import java.util.logging.Level;
64
import java.util.logging.Logger;
65
import javax.lang.model.element.ElementKind;
66
import javax.lang.model.element.TypeElement;
67
import javax.tools.JavaFileObject;
68
import org.netbeans.api.annotations.common.CheckForNull;
69
import org.netbeans.api.annotations.common.NonNull;
70
import org.netbeans.api.annotations.common.NullAllowed;
71
import org.netbeans.api.java.queries.SourceLevelQuery.Profile;
72
import org.netbeans.api.java.source.ElementHandle;
73
import org.netbeans.modules.classfile.Annotation;
74
import org.netbeans.modules.classfile.AnnotationComponent;
75
import org.netbeans.modules.classfile.CPEntry;
76
import org.netbeans.modules.classfile.ClassFile;
77
import org.netbeans.modules.classfile.ClassName;
78
import org.netbeans.modules.classfile.ElementValue;
79
import org.netbeans.modules.classfile.PrimitiveElementValue;
80
import org.netbeans.modules.java.source.ElementHandleAccessor;
81
import org.netbeans.modules.java.source.indexing.JavaIndex;
82
import org.netbeans.modules.java.source.parsing.Archive;
83
import org.netbeans.modules.java.source.parsing.CachingArchiveProvider;
84
import org.netbeans.modules.java.source.parsing.FileObjects;
85
import org.openide.filesystems.FileObject;
86
import org.openide.filesystems.FileUtil;
87
import org.openide.filesystems.URLMapper;
88
import org.openide.util.Exceptions;
89
import org.openide.util.Parameters;
90
import org.openide.util.RequestProcessor;
91
import org.openide.util.Union2;
92
import org.openide.util.Utilities;
93
94
/**
95
 * Utility methods for JDK 8 Profiles.
96
 * @author Tomas Zezula
97
 * @since 0.119
98
 */
99
public class ProfileSupport {
100
101
    private static final String RES_MANIFEST = "META-INF/MANIFEST.MF";              //NOI18N
102
    private static final String ATTR_PROFILE = "Profile";                           //NOI18N
103
    private static final String ANNOTATION_PROFILE = "jdk/Profile+Annotation";      //NOI18N
104
    private static final String ANNOTATION_VALUE = "value";                         //NOI18N
105
    private static final Logger LOG = Logger.getLogger(ProfileSupport.class.getName());
106
    private static final RequestProcessor RP = new RequestProcessor(ProfileSupport.class);
107
108
    private ProfileSupport() {}
109
110
    /**
111
     * Kind of profile validation.
112
     */
113
    public enum Validation {
114
        /**
115
         * Request to validate sources on source path.
116
         */
117
        SOURCES,
118
119
        /**
120
         * Request to validate binaries on compile class path by Profile attribute in Manifest.
121
         * The manifest based validation does not analyze class files and is significantly faster
122
         * compared to {@link Validation#BINARIES_BY_CLASS_FILES} but does not work for jar
123
         * files with no Profile attributes.
124
         */
125
        BINARIES_BY_MANIFEST,
126
127
        /**
128
         * Request to validate binaries on compile class path by references in class files.
129
         */
130
        BINARIES_BY_CLASS_FILES
131
    }
132
133
    /**
134
     * Violation of profile.
135
     * The violation can be caused either by jar file on classpath requiring
136
     * a higher profile in manifest or by a source or class file referring to
137
     * a type from higher profile.
138
     */
139
    public static class Violation {
140
141
        private final URL root;
142
        private final Profile profile;
143
        private final URL file;
144
        private final ElementHandle<TypeElement> type;
145
146
        private Violation(
147
                @NonNull final URL root,
148
                @NullAllowed final Profile profile,
149
                @NullAllowed final URL file,
150
                @NullAllowed final ElementHandle<TypeElement> type) {
151
            Parameters.notNull("root", root);   //NOI18N
152
            this.root = root;
153
            this.profile = profile;
154
            this.file = file;
155
            this.type = type;
156
        }
157
158
        /**
159
         * Returns the root which violates the tested profile.
160
         * @return the root {@link URL}
161
         */
162
        @NonNull
163
        public URL getRoot() {
164
            return root;
165
        }
166
167
        /**
168
         * Returns the {@link Profile} required by a root or file.
169
         * @return the {@link Profile} required by a root in the manifest
170
         * or by class (source) file under the root. May return a null if the
171
         * manifest contains invalid profile attribute.
172
         */
173
        @CheckForNull
174
        public Profile getRequiredProfile() {
175
            return profile;
176
        }
177
178
        /**
179
         * Returns the file which violates the tested profile.
180
         * @return the file referring to a type from higher profile. May return
181
         * null if the whole archive requires a higher profile.
182
         */
183
        @CheckForNull
184
        public URL getFile() {
185
            return file;
186
        }
187
188
        /**
189
         * Returns the type from higher {@link Profile} causing the violation.
190
         * @return the type causing the violation or null if the whole archive
191
         * requires a higher profile.
192
         */
193
        @CheckForNull
194
        public ElementHandle<TypeElement> getUsedType() {
195
            return type;
196
        }
197
    }
198
199
    /**
200
     * Asynchronous callback for collecting the profile {@link Violation}s.
201
     * For each classpath or source root a new {@link ViolationCollector} is
202
     * created by the {@link ViolationCollectorFactory#create}. When the validation
203
     * of the root is done the {@link ViolationCollector#finished} is called.
204
     * Threading: Validations of individual roots may run in parallel depending
205
     * on the {@link Executor} throughput.
206
     */
207
    public interface ViolationCollector {
208
        /**
209
         * Called to report a {@link Profile} violation.
210
         * @param violation the {@link Violation} to be reported.
211
         */
212
        void reportProfileViolation(@NonNull Violation violation);
213
        /**
214
         * Called when the validation of whole a root has finished.
215
         */
216
        void finished();
217
    }
218
219
    /**
220
     * Factory for {@link ViolationCollector}.
221
     * For each root a new {@link ViolationCollector} is created by the factory.
222
     * Threading: Validations of individual roots may run in parallel depending
223
     * on the {@link Executor} throughput.
224
     */
225
    public interface ViolationCollectorFactory {
226
        /**
227
         * Creates a new {@lni ViolationCollector} for given root.
228
         * @param root the root to be validated
229
         * @return a new {@link ViolationCollector}
230
         */
231
        @NonNull
232
        ViolationCollector create(@NonNull URL root);
233
234
        /**
235
         * Signals that the validation should be canceled.
236
         * @return if true the validation is canceled.
237
         */
238
        boolean isCancelled();
239
    }
240
241
    /**
242
     * Asynchronously finds the {@link Profile} violations in given source and classpath roots.
243
     * @param profileToCheck the {@link Profile} to be verified
244
     * @param bootClassPath  the boot classpath of JDK 8 platform to get the profile info from
245
     * @param compileClassPath the compile classpath to be validated
246
     * @param sourcePath the source path to be validated
247
     * @param check types of validation
248
     * @param collectorFactory the {@link Violation}s collector
249
     * @throws IllegalArgumentException if the bootClassPath is not a valid JDK 8 boot classpath
250
     */
251
    public static void findProfileViolations(
252
            @NonNull final Profile profileToCheck,
253
            @NonNull final Iterable<URL> bootClassPath,
254
            @NonNull final Iterable<URL> compileClassPath,
255
            @NonNull final Iterable<URL> sourcePath,
256
            @NonNull final Set<Validation> check,
257
            @NonNull final ViolationCollectorFactory collectorFactory) {
258
        findProfileViolations(
259
                profileToCheck,
260
                bootClassPath,
261
                compileClassPath,
262
                sourcePath,
263
                check,
264
                collectorFactory,
265
                RP);
266
    }
267
268
    /**
269
     * Synchronously finds the {@link Profile} violations in given source and classpath roots.
270
     * @param profileToCheck the {@link Profile} to be verified
271
     * @param bootClassPath  the boot classpath of JDK 8 platform to get the profile info from
272
     * @param compileClassPath the compile classpath to be validated
273
     * @param sourcePath the source path to be validated
274
     * @param check types of validation
275
     * @return the {@link Collection} of found {@link Violation}s
276
     * @throws IllegalArgumentException if the bootClassPath is not a valid JDK 8 boot classpath
277
     */
278
    @NonNull
279
    public static Collection<Violation> findProfileViolations(
280
            @NonNull final Profile profileToCheck,
281
            @NonNull final Iterable<URL> bootClassPath,
282
            @NonNull final Iterable<URL> compileClassPath,
283
            @NonNull final Iterable<URL> sourcePath,
284
            @NonNull final Set<Validation> check) {
285
        final DefaultProfileViolationCollector collector =
286
                new DefaultProfileViolationCollector();
287
        findProfileViolations(
288
                profileToCheck,
289
                bootClassPath,
290
                compileClassPath,
291
                sourcePath,
292
                check,
293
                collector,
294
                new CurrentThreadExecutor());
295
        return collector.getViolations();
296
    }
297
298
    /**
299
     * Asynchronously finds the {@link Profile} violations in given source and classpath roots.
300
     * @param profileToCheck the {@link Profile} to be verified
301
     * @param bootClassPath  the boot classpath of JDK 8 platform to get the profile info from
302
     * @param compileClassPath the compile classpath to be validated
303
     * @param sourcePath the source path to be validated
304
     * @param check types of validation
305
     * @param collectorFactory the {@link Violation}s collector
306
     * @param executor to use for the asynchronous operation, may have higher throughput
307
     * @throws IllegalArgumentException if the bootClassPath is not a valid JDK 8 boot classpath
308
     */
309
    public static void findProfileViolations(
310
            @NonNull final Profile profileToCheck,
311
            @NonNull final Iterable<URL> bootClassPath,
312
            @NonNull final Iterable<URL> compileClassPath,
313
            @NonNull final Iterable<URL> sourcePath,
314
            @NonNull final Set<Validation> check,
315
            @NonNull final ViolationCollectorFactory collectorFactory,
316
            @NonNull final Executor executor) {
317
        Parameters.notNull("profileToCheck", profileToCheck);   //NOI18N
318
        Parameters.notNull("compileClassPath", compileClassPath);   //NOI18N
319
        Parameters.notNull("sourcePath", sourcePath);   //NOI18N
320
        Parameters.notNull("check", check);     //NOI18N
321
        Parameters.notNull("collectorFactory", collectorFactory); //NOI18N
322
        Parameters.notNull("executor", executor);   //NOI18N
323
        final Context ctx = new Context(profileToCheck, bootClassPath, collectorFactory, check);
324
        if (check.contains(Validation.BINARIES_BY_MANIFEST) ||
325
            check.contains(Validation.BINARIES_BY_CLASS_FILES)) {
326
            for (final URL compileRoot : compileClassPath) {
327
                executor.execute(Validator.forBinary(compileRoot, ctx));
328
            }
329
        }
330
        if (check.contains(Validation.SOURCES)) {
331
            for (final URL sourceRoot : sourcePath) {
332
                executor.execute(Validator.forSource(sourceRoot, ctx));
333
            }
334
        }
335
    }
336
337
    private static final class Context {
338
339
        private final ArchiveCache archiveCache;
340
        private final TypeCache typeCache;
341
        private final Profile profileToCheck;
342
        private final ViolationCollectorFactory factory;
343
        private final Set<Validation> validations;
344
345
        Context(
346
                @NonNull final Profile profileToCheck,
347
                @NonNull final Iterable<? extends URL> bootClassPath,
348
                @NonNull final ViolationCollectorFactory factory,
349
                @NonNull final Set<Validation> validations) {
350
            assert profileToCheck != null;
351
            assert bootClassPath != null;
352
            assert factory != null;
353
            assert validations != null;
354
            this.archiveCache = ArchiveCache.getInstance();
355
            this.typeCache =  TypeCache.newInstance(bootClassPath);
356
            this.profileToCheck = profileToCheck;
357
            this.factory = factory;
358
            this.validations = EnumSet.copyOf(validations);
359
        }
360
361
        @NonNull
362
        ArchiveCache getArchiveCache() {
363
            return archiveCache;
364
        }
365
366
        @NonNull
367
        TypeCache getTypeCache() {
368
            return typeCache;
369
        }
370
371
        @NonNull
372
        Profile getRequredProfile() {
373
            return profileToCheck;
374
        }
375
376
        @NonNull
377
        ViolationCollector newCollector(@NonNull final URL root) {
378
            return factory.create(root);
379
        }
380
381
        boolean shouldValidate(@NonNull final Validation validation) {
382
            return validations.contains(validation);
383
        }
384
385
        boolean isCancelled() {
386
            return factory.isCancelled();
387
        }
388
389
    }
390
391
    private static abstract class Validator implements Runnable {
392
393
        protected final Context context;
394
        protected final URL root;
395
        
396
397
        Validator(            
398
            @NonNull final URL root,
399
            @NonNull final Context context) {
400
            assert root != null;
401
            assert context != null;
402
            this.root = root;
403
            this.context = context;
404
        }
405
406
        @Override
407
        public final void run() {
408
            final ViolationCollector collector = context.newCollector(root);
409
            assert collector != null;
410
            try {
411
                validate(collector);
412
            } finally {
413
                collector.finished();
414
            }
415
        }        
416
417
        protected final void validateBinaryRoot(
418
                @NonNull final URL root,
419
                @NonNull final ViolationCollector collector) {
420
            final FileObject rootFo = URLMapper.findFileObject(root);
421
            if (rootFo == null) {
422
                return;
423
            }
424
            final Enumeration<? extends FileObject> children = rootFo.getChildren(true);
425
            while (children.hasMoreElements()) {
426
                final FileObject fo = children.nextElement();
427
                if (isImportant(fo)) {
428
                    validateBinaryFile(fo, collector);
429
                }
430
            }
431
        }
432
433
        @NonNull
434
        protected URL map(@NonNull final FileObject fo) {
435
            return fo.toURL();
436
        }
437
438
        protected abstract void validate(@NonNull ViolationCollector collector);
439
440
        private boolean isImportant(@NonNull final FileObject file) {
441
            return file.isData() &&
442
                (FileObjects.CLASS.equals(file.getExt()) || FileObjects.SIG.equals(file.getExt()));
443
        }
444
445
        private void validateBinaryFile(
446
                @NonNull final FileObject fo,
447
                @NonNull final ViolationCollector collector) {
448
            final Profile profileToCheck = context.getRequredProfile();
449
            final TypeCache tc = context.getTypeCache();
450
            try {
451
                try (InputStream in = fo.getInputStream()) {
452
                    ClassFile cf = new ClassFile(in);
453
                    for (ClassName className : cf.getAllClassNames()) {
454
                        final Profile p = tc.profileForType(className);
455
                        if (p != null && profileToCheck.compareTo(p) < 0) {
456
                            collector.reportProfileViolation(
457
                                new Violation(
458
                                    root,
459
                                    p,
460
                                    map(fo),
461
                                    ElementHandleAccessor.getInstance().create(ElementKind.CLASS, className.getInternalName().replace('/', '.'))    //NOI18N
462
                            ));
463
                        }
464
                    }
465
                }
466
            } catch (IOException ioe) {
467
                LOG.log(
468
                    Level.INFO,
469
                    "Cannot validate file: {0}",    //NOI18N
470
                    FileUtil.getFileDisplayName(fo));
471
            }
472
        }
473
474
        static Validator forSource(
475
                @NonNull final URL root,
476
                @NonNull final Context context) {
477
            return new SourceValidator(root, context);
478
        }
479
480
        static Validator forBinary(
481
                @NonNull final URL root,
482
                @NonNull final Context context) {
483
            return new BinaryValidator(root, context);
484
        }
485
486
        private final static class BinaryValidator extends Validator {
487
488
            private BinaryValidator(
489
                @NonNull final URL root,
490
                @NonNull final Context context) {
491
                super(root, context);                
492
            }
493
494
            @Override
495
            protected void validate(@NonNull final ViolationCollector collector) {
496
                Profile current = null;
497
                if (context.shouldValidate(Validation.BINARIES_BY_MANIFEST)) {
498
                    final Union2<Profile,String> res = findProfileInManifest(root);
499
                    if (!res.hasFirst()) {
500
                        //Invalid value of profile in manifest of dependent jar
501
                        collector.reportProfileViolation(new Violation(root, null, null, null));
502
                        return;
503
                    }
504
                    if (res.first().compareTo(context.getRequredProfile()) > 0) {
505
                        //Hiher profile in manifest of dependent jar
506
                        collector.reportProfileViolation(new Violation(root, res.first(), null, null));
507
                        return;
508
                    }
509
                    current = res.first();
510
                }
511
                if (context.shouldValidate(Validation.BINARIES_BY_CLASS_FILES)) {
512
                    if (current == null || current == Profile.DEFAULT) {
513
                        validateBinaryRoot(root, collector);
514
                    }
515
                }
516
            }
517
518
            @NonNull
519
            private Union2<Profile,String> findProfileInManifest(@NonNull URL root) {
520
                final ArchiveCache ac = context.getArchiveCache();
521
                Union2<Profile,String> res;
522
                final ArchiveCache.Key key = ac.createKey(root);
523
                if (key != null) {
524
                    res = ac.getProfile(key);
525
                    if (res != null) {
526
                        return res;
527
                    }
528
                }
529
                String profileName = null;
530
                final FileObject rootFo = URLMapper.findFileObject(root);
531
                if (rootFo != null) {
532
                    final FileObject manifestFile = rootFo.getFileObject(RES_MANIFEST);
533
                    if (manifestFile != null) {
534
                        try {
535
                            try (InputStream in = manifestFile.getInputStream()) {
536
                                final Manifest manifest = new Manifest(in);
537
                                final Attributes attrs = manifest.getMainAttributes();
538
                                profileName = attrs.getValue(ATTR_PROFILE);
539
                            }
540
                        } catch (IOException ioe) {
541
                            LOG.log(
542
                                Level.INFO,
543
                                "Cannot read Profile attribute from: {0}", //NOI18N
544
                                FileUtil.getFileDisplayName(manifestFile));
545
                        }
546
                    }
547
                }
548
                final Profile profile = Profile.forName(profileName);
549
                res = profile != null ?
550
                        Union2.<Profile,String>createFirst(profile) :
551
                        Union2.<Profile,String>createSecond(profileName);
552
                if (key != null) {
553
                    ac.putProfile(key, res);
554
                }
555
                return res;
556
            }
557
558
        }
559
560
        private final static class SourceValidator extends Validator {
561
            private SourceValidator(
562
               @NonNull final URL root,
563
               @NonNull final Context context) {
564
                super(root, context);
565
            }
566
567
            @Override
568
            protected void validate(@NonNull final ViolationCollector collector) {
569
                try {
570
                    final File cacheRoot = JavaIndex.getClassFolder(root, true);
571
                    if (cacheRoot != null) {
572
                        validateBinaryRoot(Utilities.toURI(cacheRoot).toURL(), collector);
573
                    }
574
                } catch (IOException e) {
575
                    Exceptions.printStackTrace(e);
576
                }
577
            }
578
        }
579
    }
580
    
581
582
    private static class CurrentThreadExecutor implements Executor {
583
        @Override
584
        public void execute(Runnable command) {
585
            command.run();
586
        }
587
    }
588
589
    private static class DefaultProfileViolationCollector implements ViolationCollectorFactory, ViolationCollector {
590
591
        private final Queue<Violation> violations = new ArrayDeque<>();
592
593
        @Override
594
        public ViolationCollector create(@NonNull final URL root) {
595
            return this;
596
        }
597
598
        @Override
599
        public boolean isCancelled() {
600
            return false;
601
        }
602
603
        @Override
604
        public void reportProfileViolation(@NonNull final Violation violation) {
605
            violations.offer(violation);
606
        }
607
608
        @Override
609
        public void finished() {
610
        }
611
612
        Collection<Violation> getViolations() {
613
            return Collections.unmodifiableCollection(violations);
614
        }
615
    }
616
617
    //@ThreadSafe
618
    private static final class ArchiveCache {
619
620
        private static final int MAX_CACHE_SIZE = Integer.getInteger(
621
                "ProfileSupport.ArchiveCache.size",    //NOI18N
622
                1<<10);
623
624
        //@GuardedBy("ArchiveCache.class")
625
        private static volatile ArchiveCache instance;
626
627
        //@GuardedBy("cache")
628
        private final Map<Key,Union2<Profile,String>> cache;
629
630
        private ArchiveCache() {
631
            this.cache  = Collections.synchronizedMap(new LinkedHashMap<Key,Union2<Profile,String>>(16, 0.75f, true) {
632
                @Override
633
                protected boolean removeEldestEntry(Map.Entry<Key, Union2<Profile,String>> entry) {
634
                    return size() > MAX_CACHE_SIZE;
635
                }
636
            });
637
        }
638
639
        @NonNull
640
        static ArchiveCache getInstance() {
641
            ArchiveCache cache = instance;
642
            if (cache == null) {
643
                synchronized (ArchiveCache.class) {
644
                    cache = instance;
645
                    if (cache == null) {
646
                        instance = cache = new ArchiveCache();
647
                    }
648
                }
649
            }
650
            return cache;
651
        }
652
653
        @CheckForNull
654
        Union2<Profile,String> getProfile(@NonNull final Key key) {
655
            final Union2<Profile,String> res = cache.get(key);
656
            if (LOG.isLoggable(Level.FINER)) {
657
                LOG.log(
658
                    Level.FINER,
659
                    "cache[{0}]->{1}",  //NOI18N
660
                    new Object[]{
661
                        key,
662
                        res.hasFirst() ? res.first() : res.second()
663
                    });
664
            }
665
            return res;
666
        }
667
668
        void putProfile(
669
            @NonNull final  Key key,
670
            @NonNull final Union2<Profile,String> profile) {
671
            if (LOG.isLoggable(Level.FINER)) {
672
                LOG.log(
673
                    Level.FINER,
674
                    "cache[{0}]<-{1}",   //NOI18N
675
                    new Object[]{
676
                        key,
677
                        profile.hasFirst() ? profile.first() : profile.second()
678
                    });
679
            }
680
            cache.put(key,profile);
681
        }
682
683
        @CheckForNull
684
        Key createKey(@NonNull final URL rootURL) {
685
            final URL fileURL = FileUtil.getArchiveFile(rootURL);
686
            if (fileURL == null) {
687
                //Not an archive
688
                return null;
689
            }
690
            final FileObject fileFo = URLMapper.findFileObject(fileURL);
691
            if (fileFo == null) {
692
                return null;
693
            }
694
            return new Key(
695
                fileFo.toURI(),
696
                fileFo.lastModified().getTime(),
697
                fileFo.getSize());
698
        }
699
700
        private static final class Key {
701
702
            private final URI root;
703
            private final long mtime;
704
            private final long size;
705
706
            Key(
707
                    @NonNull final URI root,
708
                    final long mtime,
709
                    final long size) {
710
                this.root = root;
711
                this.mtime = mtime;
712
                this.size = size;
713
            }
714
715
            @Override
716
            public int hashCode() {
717
                int hash = 17;
718
                hash = 31 * hash + (this.root != null ? this.root.hashCode() : 0);
719
                hash = 31 * hash + (int) (this.mtime ^ (this.mtime >>> 32));
720
                hash = 31 * hash + (int) (this.size ^ (this.size >>> 32));
721
                return hash;
722
            }
723
724
            @Override
725
            public boolean equals(Object obj) {
726
                if (obj == this) {
727
                    return true;
728
                }
729
                if (!(obj instanceof Key)) {
730
                    return false;
731
                }
732
                final Key other = (Key) obj;
733
                return this.root.equals(other.root) &&
734
                    this.mtime == other.mtime &&
735
                    this.size == other.size;
736
737
            }
738
739
            @Override
740
            public String toString() {
741
                return String.format(
742
                    "Key{root: %s, mtime: %d, size: %d}",   //NOI18N
743
                    root,
744
                    mtime,
745
                    size);
746
            }
747
        }
748
    }
749
750
    //@ThreadSafe
751
    private static final class TypeCache {
752
753
        private final Object UNKNOWN = new Object();
754
        private final ConcurrentMap<String,Object> cache;
755
        private final Archive ctSym;
756
757
        private TypeCache(@NonNull final Archive ctSym) {
758
            assert ctSym != null;
759
            this.ctSym = ctSym;
760
            cache = new ConcurrentHashMap<>();
761
        }
762
763
        @NonNull
764
        static TypeCache newInstance(Iterable<? extends URL> bootClassPath) {
765
            Archive ctSym = null;
766
            final CachingArchiveProvider ap = CachingArchiveProvider.getDefault();
767
            for (URL root : bootClassPath) {
768
                if (ap.hasCtSym(root)) {
769
                    ctSym = ap.getArchive(root, true);
770
                    break;
771
                }
772
            }
773
            if (ctSym == null) {
774
                throw new IllegalArgumentException(
775
                    String.format(
776
                        "No profile info for boot classpath: %s",   //NOI18N
777
                        bootClassPath));
778
            }
779
            return new TypeCache(ctSym);
780
        }
781
782
        @CheckForNull
783
        Profile profileForType(@NonNull final ClassName className) {
784
            final String binName = className.getInternalName();
785
            Object res = cache.get(binName);
786
            if (res == null) {
787
                res = findProfile(binName);
788
                cache.put(binName, res);
789
            }
790
            return res == UNKNOWN ? null : (Profile) res;
791
        }
792
793
        @NonNull
794
        private Object findProfile(@NonNull final String binaryName) {
795
            Object res = UNKNOWN;
796
            final StringBuilder sb = new StringBuilder(binaryName);
797
            sb.append('.'); //NOI18N
798
            sb.append(FileObjects.CLASS);
799
            try {
800
                final JavaFileObject jfo = ctSym.getFile(sb.toString());
801
                if (jfo != null) {
802
                    try (InputStream in = jfo.openInputStream()) {
803
                        final ClassFile cf = new ClassFile(in);
804
                        final Annotation a = cf.getAnnotation(ClassName.getClassName(ANNOTATION_PROFILE));
805
                        if (a == null) {
806
                            res = Profile.COMPACT1;
807
                        } else {
808
                            final AnnotationComponent ac = a.getComponent(ANNOTATION_VALUE);
809
                            res = profileFromAnnotationComponent(ac);
810
                        }
811
                    }
812
                }
813
            } catch (IOException ioe) {
814
                Exceptions.printStackTrace(ioe);
815
            }
816
            return res;
817
        }
818
819
        @NonNull
820
        private static Profile profileFromAnnotationComponent(@NullAllowed final AnnotationComponent ac) {
821
            if (ac == null) {
822
                return Profile.COMPACT1;
823
            }
824
            try {
825
                final ElementValue ev = ac.getValue();
826
                if (!(ev instanceof PrimitiveElementValue)) {
827
                    return Profile.COMPACT1;
828
                }
829
                final CPEntry cpEntry = ((PrimitiveElementValue)ev).getValue();
830
                if (cpEntry.getTag() != 3) {
831
                    return Profile.COMPACT1;
832
                }
833
                final int ordinal = (Integer) cpEntry.getValue();
834
                if (ordinal <= 0) {
835
                    return Profile.COMPACT1;
836
                }
837
                final Profile[] values = Profile.values();
838
                if (ordinal >= values.length) {
839
                    return Profile.DEFAULT;
840
                }
841
                return values[ordinal-1];
842
            } catch (NumberFormatException nfe) {
843
                return Profile.COMPACT1;
844
            }
845
        }
846
    }
847
848
}
(-)a/java.source/src/org/netbeans/modules/java/source/parsing/CachingArchiveProvider.java (+28 lines)
Lines 161-166 Link Here
161
        }
161
        }
162
    }
162
    }
163
163
164
    /**
165
     * Maps ct.sym back to the base boot classpath root.
166
     * @param archiveOrCtSym the root or ct.sym folder to transform.
167
     * @return the boot classpath root corresponding to the ct.sym folder
168
     * or the given boot classpath root.
169
     */
164
    @NonNull
170
    @NonNull
165
    public URL mapCtSymToJar (@NonNull final URL archiveOrCtSym) {
171
    public URL mapCtSymToJar (@NonNull final URL archiveOrCtSym) {
166
        final URI result = ctSymToJar.get(toURI(archiveOrCtSym));
172
        final URI result = ctSymToJar.get(toURI(archiveOrCtSym));
Lines 175-180 Link Here
175
    }
181
    }
176
182
177
    /**
183
    /**
184
     * Checks if the given boot classpath root has the the ct.sym equivalent.
185
     * @param root the root to check
186
     * @return true if there is a ct.sym folder corresponding to given boot classpath
187
     * root
188
     */
189
    public boolean hasCtSym (@NonNull final URL root) {
190
        final URL fileURL = FileUtil.getArchiveFile(root);
191
        if (fileURL == null) {
192
            return false;
193
        }
194
        final File f = Utilities.toFile(fileURL);
195
        if (f == null || !f.exists()) {
196
            return false;
197
        }
198
        synchronized (this) {
199
            final Pair<File,String> res = mapJarToCtSym(f, root);
200
            return res.second != null;
201
        }
202
203
    }
204
205
    /**
178
     * Frees all the archives.
206
     * Frees all the archives.
179
     */
207
     */
180
    synchronized void clear() {
208
    synchronized void clear() {
(-)a/java.source/test/unit/src/org/netbeans/api/java/source/support/ProfileSupportTest.java (+411 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2013 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2013 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.api.java.source.support;
43
44
45
import java.beans.PropertyChangeListener;
46
import java.io.File;
47
import java.io.IOException;
48
import java.io.PrintWriter;
49
import java.io.StringWriter;
50
import java.net.MalformedURLException;
51
import java.net.URL;
52
import java.util.ArrayDeque;
53
import java.util.ArrayList;
54
import java.util.Arrays;
55
import java.util.Collection;
56
import java.util.Collections;
57
import java.util.Comparator;
58
import java.util.EnumSet;
59
import java.util.HashMap;
60
import java.util.List;
61
import java.util.Map;
62
import java.util.Queue;
63
import javax.lang.model.element.ElementKind;
64
import javax.lang.model.element.TypeElement;
65
import javax.tools.JavaCompiler;
66
import javax.tools.StandardJavaFileManager;
67
import javax.tools.ToolProvider;
68
import org.netbeans.api.annotations.common.CheckForNull;
69
import org.netbeans.api.annotations.common.NonNull;
70
import org.netbeans.api.java.classpath.ClassPath;
71
import org.netbeans.api.java.platform.JavaPlatform;
72
import org.netbeans.api.java.platform.JavaPlatformManager;
73
import org.netbeans.api.java.platform.Specification;
74
import org.netbeans.api.java.queries.SourceLevelQuery;
75
import org.netbeans.api.java.source.ElementHandle;
76
import org.netbeans.junit.MockServices;
77
import org.netbeans.junit.NbTestCase;
78
import org.netbeans.modules.java.platform.JavaPlatformProvider;
79
import org.netbeans.modules.java.source.ElementHandleAccessor;
80
import org.netbeans.modules.java.source.parsing.FileObjects;
81
import org.netbeans.modules.java.source.usages.Pair;
82
import org.netbeans.spi.java.classpath.support.ClassPathSupport;
83
import org.openide.filesystems.FileObject;
84
import org.openide.filesystems.FileUtil;
85
import org.openide.modules.SpecificationVersion;
86
import org.openide.util.Utilities;
87
88
/**
89
 *
90
 * @author Tomas Zezula
91
 */
92
public class ProfileSupportTest extends NbTestCase {
93
94
    private static final String JDK8Home = "/Users/tom/Projects/other/repos/jdk/jdk8/build/macosx-x86_64-normal-server-release/images/j2sdk-bundle/jdk1.8.0.jdk/Contents/Home";
95
96
    public ProfileSupportTest(@NonNull final String name) {
97
        super(name);
98
    }
99
100
    @Override
101
    protected void setUp() throws Exception {
102
        super.setUp();
103
        clearWorkDir();
104
        MockServices.setServices(PP.class);
105
    }
106
107
108
    public void testProfilesByClassFiles() throws IOException {
109
        final JavaPlatform jp = JavaPlatformManager.getDefault().getDefaultPlatform();
110
        if (jp == null || !jp.getSpecification().getVersion().equals(new SpecificationVersion("1.8"))) {  //NOI18N
111
            //Nothing to test
112
            return;
113
        }
114
        final Map<URL,Collection<URL>> testCp = createTestData();
115
        Collection<ProfileSupport.Violation> res = ProfileSupport.findProfileViolations(
116
            SourceLevelQuery.Profile.COMPACT1,
117
            toURLs(jp.getBootstrapLibraries().entries()),
118
            testCp.keySet(),
119
            Collections.<URL>emptySet(),
120
            EnumSet.of(ProfileSupport.Validation.BINARIES_BY_CLASS_FILES));
121
        assertEquals(            
122
            new HashMap<URL,Collection<Pair<ElementHandle<TypeElement>,SourceLevelQuery.Profile>>>(){
123
                {                    
124
                    final Collection<URL> files = testCp.entrySet().iterator().next().getValue();
125
                    put(
126
                        files.iterator().next(),
127
                        Arrays.<Pair<ElementHandle<TypeElement>,SourceLevelQuery.Profile>>asList(
128
                            Pair.<ElementHandle<TypeElement>,SourceLevelQuery.Profile>of(
129
                                ElementHandleAccessor.getInstance().create(ElementKind.CLASS, "java.sql.Date"),
130
                                SourceLevelQuery.Profile.COMPACT2),
131
                            Pair.<ElementHandle<TypeElement>,SourceLevelQuery.Profile>of(
132
                                ElementHandleAccessor.getInstance().create(ElementKind.CLASS, "javax.naming.Context"),
133
                                SourceLevelQuery.Profile.COMPACT3),
134
                            Pair.<ElementHandle<TypeElement>,SourceLevelQuery.Profile>of(
135
                                ElementHandleAccessor.getInstance().create(ElementKind.CLASS, "org.omg.CORBA.ORB"),
136
                                SourceLevelQuery.Profile.DEFAULT)
137
                        ));
138
                }
139
            },
140
            res);
141
142
        res = ProfileSupport.findProfileViolations(
143
            SourceLevelQuery.Profile.COMPACT2,
144
            toURLs(jp.getBootstrapLibraries().entries()),
145
            testCp.keySet(),
146
            Collections.<URL>emptySet(),
147
            EnumSet.of(ProfileSupport.Validation.BINARIES_BY_CLASS_FILES));
148
        assertEquals(
149
            new HashMap<URL,Collection<Pair<ElementHandle<TypeElement>,SourceLevelQuery.Profile>>>(){
150
                {
151
                    final Collection<URL> files = testCp.entrySet().iterator().next().getValue();
152
                    put(
153
                        files.iterator().next(),
154
                        Arrays.<Pair<ElementHandle<TypeElement>,SourceLevelQuery.Profile>>asList(
155
                            Pair.<ElementHandle<TypeElement>,SourceLevelQuery.Profile>of(
156
                                ElementHandleAccessor.getInstance().create(ElementKind.CLASS, "javax.naming.Context"),
157
                                SourceLevelQuery.Profile.COMPACT3),
158
                            Pair.<ElementHandle<TypeElement>,SourceLevelQuery.Profile>of(
159
                                ElementHandleAccessor.getInstance().create(ElementKind.CLASS, "org.omg.CORBA.ORB"),
160
                                SourceLevelQuery.Profile.DEFAULT)
161
                        ));
162
                }
163
            },
164
            res);
165
        res = ProfileSupport.findProfileViolations(
166
            SourceLevelQuery.Profile.COMPACT3,
167
            toURLs(jp.getBootstrapLibraries().entries()),
168
            testCp.keySet(),
169
            Collections.<URL>emptySet(),
170
            EnumSet.of(ProfileSupport.Validation.BINARIES_BY_CLASS_FILES));
171
        assertEquals(
172
            new HashMap<URL,Collection<Pair<ElementHandle<TypeElement>,SourceLevelQuery.Profile>>>(){
173
                {
174
                    final Collection<URL> files = testCp.entrySet().iterator().next().getValue();
175
                    put(
176
                        files.iterator().next(),
177
                        Arrays.<Pair<ElementHandle<TypeElement>,SourceLevelQuery.Profile>>asList(
178
                            Pair.<ElementHandle<TypeElement>,SourceLevelQuery.Profile>of(
179
                                ElementHandleAccessor.getInstance().create(ElementKind.CLASS, "org.omg.CORBA.ORB"),
180
                                SourceLevelQuery.Profile.DEFAULT)
181
                        ));
182
                }
183
            },
184
            res);
185
        res = ProfileSupport.findProfileViolations(
186
            SourceLevelQuery.Profile.DEFAULT,
187
            toURLs(jp.getBootstrapLibraries().entries()),
188
            testCp.keySet(),
189
            Collections.<URL>emptySet(),
190
            EnumSet.of(ProfileSupport.Validation.BINARIES_BY_CLASS_FILES));
191
        assertEquals(
192
            Collections.<URL,Collection<Pair<ElementHandle<TypeElement>,SourceLevelQuery.Profile>>>emptyMap(),
193
            res);
194
    }
195
196
    private void assertEquals(
197
            Map<URL,Collection<Pair<ElementHandle<TypeElement>,SourceLevelQuery.Profile>>> exp,
198
            Collection<ProfileSupport.Violation> res) {
199
        final Map<URL,List<Pair<ElementHandle<TypeElement>,SourceLevelQuery.Profile>>> resM =
200
                new HashMap<>();
201
        for (ProfileSupport.Violation v : res) {
202
            List<Pair<ElementHandle<TypeElement>,SourceLevelQuery.Profile>> e = resM.get(v.getFile());
203
            if (e == null) {
204
                e = new ArrayList<>();
205
                resM.put(v.getFile(), e);
206
            }
207
            e.add(Pair.<ElementHandle<TypeElement>,SourceLevelQuery.Profile>of(
208
                    v.getUsedType(),
209
                    v.getRequiredProfile()));
210
        }
211
        for (List<Pair<ElementHandle<TypeElement>,SourceLevelQuery.Profile>> l : resM.values()) {
212
            Collections.sort(l, new Comparator<Pair<ElementHandle<TypeElement>,SourceLevelQuery.Profile>>() {
213
                @Override
214
                public int compare(
215
                        Pair<ElementHandle<TypeElement>, SourceLevelQuery.Profile> o1,
216
                        Pair<ElementHandle<TypeElement>, SourceLevelQuery.Profile> o2) {
217
                    return o1.first.getBinaryName().compareTo(o2.first.getBinaryName());
218
                }
219
            });
220
        }
221
        assertEquals(exp, resM);
222
    };
223
224
225
    @NonNull
226
    private Collection<URL> toURLs(@NonNull final Collection<? extends ClassPath.Entry> entries) {
227
        final Queue<URL> q = new ArrayDeque<>();
228
        for (ClassPath.Entry e : entries) {
229
            q.offer(e.getURL());
230
        }
231
        return q;
232
    }
233
234
    private Map<URL,Collection<URL>> createTestData() throws IOException {
235
        final File workDir = getWorkDir();
236
        final File src = new File (workDir, "src");     //NOI18N
237
        final File bin = new File (workDir, "bin");     //NOI18N
238
        src.mkdirs();
239
        bin.mkdirs();
240
        final File testFile = new File (src,"Test.java");   //NOI18N
241
        try (PrintWriter out = new PrintWriter(testFile)) {
242
            out.println(
243
                "public class Test {\n"  +       //NOI18N
244
                "    String s;\n"        +       //NOI18N
245
                "    java.sql.Date d;\n" +       //NOI18N
246
                "    javax.naming.Context c;\n" +//NOI18N
247
                "    org.omg.CORBA.ORB orb;\n"  +//NOI18N
248
                "}\n");                    //NOI18N
249
        }
250
        final JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
251
        final StandardJavaFileManager standardFileManager = jc.getStandardFileManager(null, null, null);
252
        final StringWriter out = new StringWriter();
253
        final JavaCompiler.CompilationTask task = jc.getTask(
254
            out,
255
            standardFileManager,
256
            null,
257
            Arrays.<String>asList(
258
                "-s", src.getAbsolutePath(),    //NOI18N
259
                "-d", bin.getAbsolutePath(),    //NOI18N
260
                "-target", "1.8",               //NOI18N
261
                "-source", "1.8",               //NOI18N
262
                "-processorpath", ""),          //NOI18N
263
            Collections.<String>emptySet(),
264
            standardFileManager.getJavaFileObjects(testFile));
265
        final Boolean res = task.call();
266
        if (res != Boolean.TRUE) {
267
            throw new IOException(out.toString());
268
        }
269
        return Collections.<URL,Collection<URL>>singletonMap(
270
            Utilities.toURL(bin),
271
            collectClassFiles(bin));
272
    }
273
274
    @NonNull
275
    private Collection<URL> collectClassFiles(@NonNull final File folder) throws MalformedURLException {
276
        final Queue<URL> q = new ArrayDeque<>();
277
        collectClassFiles(folder, q);
278
        return q;
279
    }
280
281
    private void collectClassFiles(@NonNull final File folder, @NonNull final Queue<? super URL> q) throws MalformedURLException {
282
        final File[] chld = folder.listFiles();
283
        if (chld == null) {
284
            return;
285
        }
286
        for (File f : chld) {
287
            if (f.isDirectory()) {
288
                collectClassFiles(f, q);
289
            } else if (FileObjects.CLASS.equals(FileObjects.getExtension(f.getName()))) {
290
                q.offer(Utilities.toURL(f));
291
            }
292
        }
293
    }
294
295
296
    public static class PP implements JavaPlatformProvider {
297
298
        private final JavaPlatform jp;
299
300
        public PP () {
301
            final Pair<File,URL> jdk8 = findJDK8();
302
            jp = jdk8 == null ? null : new JP(jdk8);
303
        }
304
305
306
        @Override
307
        public JavaPlatform[] getInstalledPlatforms() {
308
            return jp == null ?
309
                new JavaPlatform[0] :
310
                new JavaPlatform[] {jp};
311
        }
312
313
        @Override
314
        public JavaPlatform getDefaultPlatform() {
315
            return jp;
316
        }
317
318
        @Override
319
        public void addPropertyChangeListener(PropertyChangeListener listener) {
320
        }
321
322
        @Override
323
        public void removePropertyChangeListener(PropertyChangeListener listener) {
324
        }
325
326
        @CheckForNull
327
        private static Pair<File,URL> findJDK8() {
328
            File jdkHome = null;
329
            if (JDK8Home != null) {
330
                jdkHome = new File(JDK8Home);
331
            } else {
332
                //TODO:
333
            }
334
            if (jdkHome == null || !jdkHome.isDirectory()) {
335
                return null;
336
            }
337
            final File rtJar = new File(
338
                    jdkHome,
339
                    "jre/lib/rt.jar".replace('/', File.separatorChar)); //NOI18N
340
            if (!rtJar.isFile()) {
341
                return null;
342
            }
343
            return Pair.of(
344
                    FileUtil.normalizeFile(jdkHome),
345
                    FileUtil.urlForArchiveOrDir(FileUtil.normalizeFile(rtJar)));
346
        }
347
348
        private static class JP extends JavaPlatform {
349
350
            private final FileObject home;
351
            private final ClassPath boot;
352
353
354
            JP(Pair<File,URL> p) {
355
                this.home = FileUtil.toFileObject(p.first);
356
                this.boot = ClassPathSupport.createClassPath(p.second);
357
            }
358
359
360
            @Override
361
            public String getDisplayName() {
362
                return "Mock Java Platform";    //NOI18N
363
            }
364
365
            @Override
366
            public Map<String, String> getProperties() {
367
                return Collections.<String,String>emptyMap();
368
            }
369
370
            @Override
371
            public ClassPath getBootstrapLibraries() {
372
                return boot;
373
            }
374
375
            @Override
376
            public ClassPath getStandardLibraries() {
377
                return ClassPath.EMPTY;
378
            }
379
380
            @Override
381
            public String getVendor() {
382
                return "me";    //NOI18N
383
            }
384
385
            @Override
386
            public Specification getSpecification() {
387
                return new Specification("j2se", new SpecificationVersion("1.8"));  //NOI18N
388
            }
389
390
            @Override
391
            public Collection<FileObject> getInstallFolders() {
392
                return Collections.singleton(home);
393
            }
394
395
            @Override
396
            public FileObject findTool(String toolName) {
397
                throw new UnsupportedOperationException("Not supported yet.");
398
            }
399
400
            @Override
401
            public ClassPath getSourceFolders() {
402
                return ClassPath.EMPTY;
403
            }
404
405
            @Override
406
            public List<URL> getJavadocFolders() {
407
                return Collections.<URL>emptyList();
408
            }
409
        }
410
    }
411
}

Return to bug 228384