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 215628
Collapse All | Expand All

(-)a/java.j2seproject/nbproject/project.xml (-2 / +2 lines)
Lines 159-165 Link Here
159
                    <compile-dependency/>
159
                    <compile-dependency/>
160
                    <run-dependency>
160
                    <run-dependency>
161
                        <release-version>1</release-version>
161
                        <release-version>1</release-version>
162
                        <specification-version>1.46</specification-version>
162
                        <specification-version>1.48</specification-version>
163
                    </run-dependency>
163
                    </run-dependency>
164
                </dependency>
164
                </dependency>
165
                <dependency>
165
                <dependency>
Lines 212-218 Link Here
212
                    <compile-dependency/>
212
                    <compile-dependency/>
213
                    <run-dependency>
213
                    <run-dependency>
214
                        <release-version>1</release-version>
214
                        <release-version>1</release-version>
215
                        <specification-version>1.59</specification-version>
215
                        <specification-version>1.60</specification-version>
216
                    </run-dependency>
216
                    </run-dependency>
217
                </dependency>
217
                </dependency>
218
                <dependency>
218
                <dependency>
(-)a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/J2SEProject.java (-34 / +68 lines)
Lines 58-71 Link Here
58
import java.util.Collection;
58
import java.util.Collection;
59
import java.util.Collections;
59
import java.util.Collections;
60
import java.util.HashMap;
60
import java.util.HashMap;
61
import java.util.HashSet;
62
import java.util.LinkedList;
61
import java.util.LinkedList;
63
import java.util.List;
62
import java.util.List;
64
import java.util.Map;
63
import java.util.Map;
65
import java.util.Set;
66
import java.util.WeakHashMap;
64
import java.util.WeakHashMap;
67
import java.util.concurrent.ExecutionException;
68
import java.util.concurrent.Future;
69
import java.util.concurrent.atomic.AtomicBoolean;
65
import java.util.concurrent.atomic.AtomicBoolean;
70
import java.util.logging.Level;
66
import java.util.logging.Level;
71
import java.util.logging.Logger;
67
import java.util.logging.Logger;
Lines 79-84 Link Here
79
import org.netbeans.api.annotations.common.NonNull;
75
import org.netbeans.api.annotations.common.NonNull;
80
import org.netbeans.api.java.classpath.ClassPath;
76
import org.netbeans.api.java.classpath.ClassPath;
81
import org.netbeans.api.java.classpath.GlobalPathRegistry;
77
import org.netbeans.api.java.classpath.GlobalPathRegistry;
78
import org.netbeans.api.java.platform.JavaPlatform;
79
import org.netbeans.api.java.platform.JavaPlatformManager;
82
import org.netbeans.api.java.project.JavaProjectConstants;
80
import org.netbeans.api.java.project.JavaProjectConstants;
83
import org.netbeans.api.java.project.classpath.ProjectClassPathModifier;
81
import org.netbeans.api.java.project.classpath.ProjectClassPathModifier;
84
import org.netbeans.api.project.Project;
82
import org.netbeans.api.project.Project;
Lines 88-94 Link Here
88
import org.netbeans.api.project.ant.AntBuildExtender;
86
import org.netbeans.api.project.ant.AntBuildExtender;
89
import org.netbeans.api.project.libraries.Library;
87
import org.netbeans.api.project.libraries.Library;
90
import org.netbeans.api.project.libraries.LibraryManager;
88
import org.netbeans.api.project.libraries.LibraryManager;
91
import org.netbeans.api.project.ui.OpenProjects;
92
import org.netbeans.api.queries.FileBuiltQuery.Status;
89
import org.netbeans.api.queries.FileBuiltQuery.Status;
93
import org.netbeans.modules.java.api.common.Roots;
90
import org.netbeans.modules.java.api.common.Roots;
94
import org.netbeans.modules.java.api.common.SourceRoots;
91
import org.netbeans.modules.java.api.common.SourceRoots;
Lines 139-145 Link Here
139
import org.openide.util.Mutex;
136
import org.openide.util.Mutex;
140
import org.openide.util.MutexException;
137
import org.openide.util.MutexException;
141
import org.openide.util.NbBundle;
138
import org.openide.util.NbBundle;
142
import org.openide.util.RequestProcessor;
143
import org.openide.util.lookup.Lookups;
139
import org.openide.util.lookup.Lookups;
144
import org.w3c.dom.Document;
140
import org.w3c.dom.Document;
145
import org.w3c.dom.Element;
141
import org.w3c.dom.Element;
Lines 151-159 Link Here
151
import static org.netbeans.spi.project.support.ant.GeneratedFilesHelper.FLAG_OLD_STYLESHEET;
147
import static org.netbeans.spi.project.support.ant.GeneratedFilesHelper.FLAG_OLD_STYLESHEET;
152
import static org.netbeans.spi.project.support.ant.GeneratedFilesHelper.FLAG_UNKNOWN;
148
import static org.netbeans.spi.project.support.ant.GeneratedFilesHelper.FLAG_UNKNOWN;
153
import org.netbeans.spi.whitelist.support.WhiteListQueryMergerSupport;
149
import org.netbeans.spi.whitelist.support.WhiteListQueryMergerSupport;
154
import org.openide.filesystems.FileLock;
155
import org.openide.filesystems.URLMapper;
150
import org.openide.filesystems.URLMapper;
156
import org.openide.modules.SpecificationVersion;
151
import org.openide.modules.SpecificationVersion;
152
import org.openide.xml.XMLUtil;
157
/**
153
/**
158
 * Represents one plain J2SE project.
154
 * Represents one plain J2SE project.
159
 * @author Jesse Glick, et al.
155
 * @author Jesse Glick, et al.
Lines 189-195 Link Here
189
    };
185
    };
190
    private static final Icon J2SE_PROJECT_ICON = ImageUtilities.loadImageIcon("org/netbeans/modules/java/j2seproject/ui/resources/j2seProject.png", false); // NOI18N
186
    private static final Icon J2SE_PROJECT_ICON = ImageUtilities.loadImageIcon("org/netbeans/modules/java/j2seproject/ui/resources/j2seProject.png", false); // NOI18N
191
    private static final Logger LOG = Logger.getLogger(J2SEProject.class.getName());
187
    private static final Logger LOG = Logger.getLogger(J2SEProject.class.getName());
192
    private static final RequestProcessor RP = new RequestProcessor(J2SEProject.class.getName(), 1);
193
188
194
    private final AuxiliaryConfiguration aux;
189
    private final AuxiliaryConfiguration aux;
195
    private final AntProjectHelper helper;
190
    private final AntProjectHelper helper;
Lines 362-368 Link Here
362
    }
357
    }
363
358
364
    private Lookup createLookup(final AuxiliaryConfiguration aux) {
359
    private Lookup createLookup(final AuxiliaryConfiguration aux) {
365
        FileEncodingQueryImplementation encodingQuery = QuerySupport.createFileEncodingQuery(evaluator(), J2SEProjectProperties.SOURCE_ENCODING);
360
        final FileEncodingQueryImplementation encodingQuery = QuerySupport.createFileEncodingQuery(evaluator(), J2SEProjectProperties.SOURCE_ENCODING);
361
        final J2SELogicalViewProvider lvp = new J2SELogicalViewProvider(this, this.updateHelper, evaluator(), refHelper);
362
        final Runnable hook = new PlatformChangedHook();
366
        final Lookup base = Lookups.fixed(
363
        final Lookup base = Lookups.fixed(
367
            J2SEProject.this,
364
            J2SEProject.this,
368
            QuerySupport.createProjectInformation(updateHelper, this, J2SE_PROJECT_ICON),
365
            QuerySupport.createProjectInformation(updateHelper, this, J2SE_PROJECT_ICON),
Lines 370-376 Link Here
370
            helper.createCacheDirectoryProvider(),
367
            helper.createCacheDirectoryProvider(),
371
            helper.createAuxiliaryProperties(),
368
            helper.createAuxiliaryProperties(),
372
            refHelper.createSubprojectProvider(),
369
            refHelper.createSubprojectProvider(),
373
            new J2SELogicalViewProvider(this, this.updateHelper, evaluator(), refHelper),
370
            lvp,
374
            // new J2SECustomizerProvider(this, this.updateHelper, evaluator(), refHelper),
371
            // new J2SECustomizerProvider(this, this.updateHelper, evaluator(), refHelper),
375
            new CustomizerProviderImpl(this, this.updateHelper, evaluator(), refHelper, this.genFilesHelper),        
372
            new CustomizerProviderImpl(this, this.updateHelper, evaluator(), refHelper, this.genFilesHelper),        
376
            LookupMergerSupport.createClassPathProviderMerger(cpProvider),
373
            LookupMergerSupport.createClassPathProviderMerger(cpProvider),
Lines 404-410 Link Here
404
            QuerySupport.createBinaryForSourceQueryImplementation(this.sourceRoots, this.testRoots, this.helper, this.evaluator()), //Does not use APH to get/put properties/cfgdata
401
            QuerySupport.createBinaryForSourceQueryImplementation(this.sourceRoots, this.testRoots, this.helper, this.evaluator()), //Does not use APH to get/put properties/cfgdata
405
            QuerySupport.createAnnotationProcessingQuery(this.helper, this.evaluator(), ProjectProperties.ANNOTATION_PROCESSING_ENABLED, ProjectProperties.ANNOTATION_PROCESSING_ENABLED_IN_EDITOR, ProjectProperties.ANNOTATION_PROCESSING_RUN_ALL_PROCESSORS, ProjectProperties.ANNOTATION_PROCESSING_PROCESSORS_LIST, ProjectProperties.ANNOTATION_PROCESSING_SOURCE_OUTPUT, ProjectProperties.ANNOTATION_PROCESSING_PROCESSOR_OPTIONS),
402
            QuerySupport.createAnnotationProcessingQuery(this.helper, this.evaluator(), ProjectProperties.ANNOTATION_PROCESSING_ENABLED, ProjectProperties.ANNOTATION_PROCESSING_ENABLED_IN_EDITOR, ProjectProperties.ANNOTATION_PROCESSING_RUN_ALL_PROCESSORS, ProjectProperties.ANNOTATION_PROCESSING_PROCESSORS_LIST, ProjectProperties.ANNOTATION_PROCESSING_SOURCE_OUTPUT, ProjectProperties.ANNOTATION_PROCESSING_PROCESSOR_OPTIONS),
406
            LookupProviderSupport.createActionProviderMerger(),
403
            LookupProviderSupport.createActionProviderMerger(),
407
            WhiteListQueryMergerSupport.createWhiteListQueryMerger()
404
            WhiteListQueryMergerSupport.createWhiteListQueryMerger(),
405
            BrokenReferencesSupport.createReferenceProblemsProvider(helper, refHelper, eval, lvp.getBreakableProperties(), lvp.getPlatformProperties()),
406
            BrokenReferencesSupport.createPlatformVersionProblemProvider(helper, eval, hook, JavaPlatform.getDefault().getSpecification().getName(), J2SEProjectProperties.JAVA_PLATFORM, J2SEProjectProperties.JAVAC_SOURCE, J2SEProjectProperties.JAVAC_TARGET),
407
            UILookupMergerSupport.createProjectProblemsProviderMerger()
408
        );
408
        );
409
        lookup = base; // in case LookupProvider's call Project.getLookup
409
        lookup = base; // in case LookupProvider's call Project.getLookup
410
        return LookupProviderSupport.createCompositeLookup(base, "Projects/org-netbeans-modules-java-j2seproject/Lookup"); //NOI18N
410
        return LookupProviderSupport.createCompositeLookup(base, "Projects/org-netbeans-modules-java-j2seproject/Lookup"); //NOI18N
Lines 676-704 Link Here
676
                    LOG.log(Level.WARNING, "Unsupported charset: {0} in project: {1}", new Object[]{prop, FileUtil.getFileDisplayName(getProjectDirectory())}); //NOI18N
676
                    LOG.log(Level.WARNING, "Unsupported charset: {0} in project: {1}", new Object[]{prop, FileUtil.getFileDisplayName(getProjectDirectory())}); //NOI18N
677
                }
677
                }
678
            }
678
            }
679
            RP.post(new Runnable() {
680
                @Override
681
                public void run() {
682
                    final Future<Project[]> projects = OpenProjects.getDefault().openProjects();
683
                    try {
684
                        projects.get();
685
                    } catch (ExecutionException ex) {
686
                        Exceptions.printStackTrace(ex);
687
                    } catch (InterruptedException ie) {
688
                        Exceptions.printStackTrace(ie);
689
                    }
690
                    J2SELogicalViewProvider physicalViewProvider = getLookup().lookup(J2SELogicalViewProvider.class);
691
                    if (physicalViewProvider != null &&  physicalViewProvider.hasBrokenLinks()) {
692
                        BrokenReferencesSupport.showAlert(
693
                                helper,
694
                                refHelper,
695
                                eval,
696
                                physicalViewProvider.getBreakableProperties(),
697
                                physicalViewProvider.getPlatformProperties());
698
                    }
699
                }
700
            });
701
702
            //Update per project CopyLibs if needed
679
            //Update per project CopyLibs if needed
703
            new UpdateCopyLibs(J2SEProject.this).run();
680
            new UpdateCopyLibs(J2SEProject.this).run();
704
            
681
            
Lines 1059-1062 Link Here
1059
1036
1060
    }
1037
    }
1061
1038
1039
    private final class PlatformChangedHook implements Runnable {
1040
        @Override
1041
        public void run() {
1042
            final String platformId = evaluator().getProperty(J2SEProjectProperties.JAVA_PLATFORM);
1043
            if (platformId == null) {
1044
                return;
1045
            }
1046
            JavaPlatform platform = null;
1047
            for (JavaPlatform jp : JavaPlatformManager.getDefault().getPlatforms(null, null)) {
1048
                if (platformId.equals(jp.getProperties().get("platform.ant.name"))) {   //NOI18N
1049
                    platform = jp;
1050
                    break;
1051
                }
1052
            }
1053
            if (platform == null) {
1054
                return;
1055
            }
1056
            final boolean remove = platform.equals(JavaPlatformManager.getDefault().getDefaultPlatform());
1057
            final Element root = helper.getPrimaryConfigurationData(true);
1058
            boolean changed = false;
1059
            if (remove) {
1060
                final Element platformElement = XMLUtil.findElement(
1061
                    root,
1062
                    "explicit-platform",    //NOI18N
1063
                    PROJECT_CONFIGURATION_NAMESPACE);
1064
                if (platformElement != null) {
1065
                    root.removeChild(platformElement);
1066
                    changed = true;
1067
                }
1068
            } else {
1069
                Element insertBefore = null;
1070
                for (Element e : XMLUtil.findSubElements(root)) {
1071
                    final String name = e.getNodeName();
1072
                    if (! "name".equals(name) &&                  //NOI18N
1073
                        ! "minimum-ant-version".equals(name)) {   //NOI18N
1074
                        insertBefore = e;
1075
                        break;
1076
                    }
1077
                }
1078
                final Element platformNode = insertBefore.getOwnerDocument().createElementNS(
1079
                        PROJECT_CONFIGURATION_NAMESPACE,
1080
                        "explicit-platform"); //NOI18N
1081
                platformNode.setAttribute(
1082
                        "explicit-source-supported",                                                             //NOI18N
1083
                        platform.getSpecification().getVersion().compareTo(new SpecificationVersion("1.3"))>0?   //NOI18N
1084
                            "true":                                                                              //NOI18N
1085
                            "false");                                                                            //NOI18N
1086
                root.insertBefore(platformNode, insertBefore);
1087
                changed = true;
1088
            }
1089
            if (changed) {
1090
                helper.putPrimaryConfigurationData(root, true);
1091
            }
1092
        }
1093
1094
    }
1095
1062
}
1096
}
(-)a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/ui/Bundle.properties (-2 lines)
Lines 54-61 Link Here
54
ACSD_Customizer_Ok_Option=N/A
54
ACSD_Customizer_Ok_Option=N/A
55
ACSD_Customizer_Cancel_Option=N/A
55
ACSD_Customizer_Cancel_Option=N/A
56
56
57
LBL_Fix_Broken_Links_Action=Resolve Reference Problems...
58
59
AD_J2SECustomizerProviderOk=N/A
57
AD_J2SECustomizerProviderOk=N/A
60
AD_J2SECustomizerProviderCancel=N/A
58
AD_J2SECustomizerProviderCancel=N/A
61
59
(-)a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/ui/J2SELogicalViewProvider.java (-263 / +73 lines)
Lines 46-69 Link Here
46
46
47
import java.awt.Color;
47
import java.awt.Color;
48
import java.awt.Image;
48
import java.awt.Image;
49
import java.awt.event.ActionEvent;
50
import java.beans.PropertyChangeEvent;
49
import java.beans.PropertyChangeEvent;
51
import java.beans.PropertyChangeListener;
50
import java.beans.PropertyChangeListener;
52
import java.io.CharConversionException;
51
import java.io.CharConversionException;
53
import java.io.IOException;
54
import java.lang.ref.WeakReference;
55
import java.net.URL;
52
import java.net.URL;
56
import java.util.Collection;
57
import java.util.HashMap;
58
import java.util.Map;
59
import javax.swing.AbstractAction;
60
import javax.swing.Action;
53
import javax.swing.Action;
61
import javax.swing.UIManager;
54
import javax.swing.UIManager;
62
import javax.swing.event.ChangeEvent;
55
import javax.swing.event.ChangeEvent;
63
import javax.swing.event.ChangeListener;
56
import javax.swing.event.ChangeListener;
64
import org.netbeans.api.annotations.common.NonNull;
57
import org.netbeans.api.annotations.common.NonNull;
65
import org.netbeans.api.java.platform.JavaPlatform;
66
import org.netbeans.api.java.platform.JavaPlatformManager;
67
import org.netbeans.api.java.project.JavaProjectConstants;
58
import org.netbeans.api.java.project.JavaProjectConstants;
68
import org.netbeans.api.project.FileOwnerQuery;
59
import org.netbeans.api.project.FileOwnerQuery;
69
import org.netbeans.api.project.Project;
60
import org.netbeans.api.project.Project;
Lines 71-110 Link Here
71
import org.netbeans.api.project.ProjectManager;
62
import org.netbeans.api.project.ProjectManager;
72
import org.netbeans.api.project.ProjectUtils;
63
import org.netbeans.api.project.ProjectUtils;
73
import org.netbeans.api.project.SourceGroup;
64
import org.netbeans.api.project.SourceGroup;
74
import org.netbeans.api.project.libraries.LibraryManager;
65
import org.netbeans.api.project.ui.ProjectProblems;
75
import org.netbeans.modules.java.api.common.SourceRoots;
66
import org.netbeans.modules.java.api.common.SourceRoots;
76
import org.netbeans.modules.java.api.common.ant.UpdateHelper;
67
import org.netbeans.modules.java.api.common.ant.UpdateHelper;
77
import org.netbeans.modules.java.api.common.project.ProjectProperties;
68
import org.netbeans.modules.java.api.common.project.ProjectProperties;
78
import org.netbeans.modules.java.api.common.project.ui.LogicalViewProvider2;
69
import org.netbeans.modules.java.api.common.project.ui.LogicalViewProvider2;
79
import org.netbeans.modules.java.api.common.util.CommonProjectUtils;
80
import org.netbeans.modules.java.j2seproject.J2SEProjectUtil;
70
import org.netbeans.modules.java.j2seproject.J2SEProjectUtil;
81
import org.netbeans.modules.java.j2seproject.ui.customizer.J2SEProjectProperties;
71
import org.netbeans.modules.java.j2seproject.ui.customizer.J2SEProjectProperties;
82
import org.netbeans.modules.java.j2seproject.J2SEProject;
72
import org.netbeans.modules.java.j2seproject.J2SEProject;
83
import org.netbeans.spi.java.project.support.ui.BrokenReferencesSupport;
84
import org.netbeans.spi.java.project.support.ui.PackageView;
73
import org.netbeans.spi.java.project.support.ui.PackageView;
85
import org.netbeans.spi.project.support.ant.PropertyEvaluator;
74
import org.netbeans.spi.project.support.ant.PropertyEvaluator;
86
import org.netbeans.spi.project.support.ant.ReferenceHelper;
75
import org.netbeans.spi.project.support.ant.ReferenceHelper;
76
import org.netbeans.spi.project.ui.ProjectProblemsProvider;
87
import org.netbeans.spi.project.ui.support.CommonProjectActions;
77
import org.netbeans.spi.project.ui.support.CommonProjectActions;
88
import org.netbeans.spi.project.ui.support.NodeFactorySupport;
78
import org.netbeans.spi.project.ui.support.NodeFactorySupport;
89
import org.netbeans.spi.project.ui.support.DefaultProjectOperations;
79
import org.netbeans.spi.project.ui.support.DefaultProjectOperations;
90
import org.openide.ErrorManager;
91
import org.openide.awt.ActionID;
80
import org.openide.awt.ActionID;
92
import org.openide.awt.ActionReference;
81
import org.openide.awt.ActionReference;
93
import org.openide.awt.ActionRegistration;
82
import org.openide.awt.ActionReferences;
94
import org.openide.awt.DynamicMenuContent;
95
import org.openide.filesystems.FileObject;
83
import org.openide.filesystems.FileObject;
96
import org.openide.filesystems.FileUtil;
84
import org.openide.filesystems.FileUtil;
97
import org.openide.modules.SpecificationVersion;
98
import org.openide.nodes.AbstractNode;
85
import org.openide.nodes.AbstractNode;
99
import org.openide.nodes.Node;
86
import org.openide.nodes.Node;
100
import org.openide.util.ChangeSupport;
87
import org.openide.util.ChangeSupport;
101
import org.openide.util.ContextAwareAction;
102
import org.openide.util.HelpCtx;
88
import org.openide.util.HelpCtx;
103
import org.openide.util.ImageUtilities;
89
import org.openide.util.ImageUtilities;
104
import org.openide.util.Lookup;
105
import org.openide.util.NbBundle;
90
import org.openide.util.NbBundle;
106
import org.openide.util.RequestProcessor;
91
import org.openide.util.RequestProcessor;
107
import org.openide.util.Utilities;
108
import org.openide.util.WeakListeners;
92
import org.openide.util.WeakListeners;
109
import org.openide.util.lookup.Lookups;
93
import org.openide.util.lookup.Lookups;
110
import org.openide.xml.XMLUtil;
94
import org.openide.xml.XMLUtil;
Lines 113-121 Link Here
113
 * Support for creating logical views.
97
 * Support for creating logical views.
114
 * @author Petr Hrebejk
98
 * @author Petr Hrebejk
115
 */
99
 */
100
@ActionReferences({
101
    @ActionReference(
102
        id=@ActionID(id="org.netbeans.modules.project.ui.problems.BrokenProjectActionFactory",category="Project"),
103
        position = 2600,
104
        path = "Projects/org-netbeans-modules-java-j2seproject/Actions")
105
})
116
public class J2SELogicalViewProvider implements LogicalViewProvider2 {
106
public class J2SELogicalViewProvider implements LogicalViewProvider2 {
117
    
107
    
118
    private static final RequestProcessor RP = new RequestProcessor(J2SELogicalViewProvider.class);
108
    private static final RequestProcessor RP = new RequestProcessor(J2SELogicalViewProvider.class);
109
    private static final String[] BREAKABLE_PROPERTIES = {
110
        ProjectProperties.JAVAC_CLASSPATH,
111
        ProjectProperties.RUN_CLASSPATH,
112
        J2SEProjectProperties.DEBUG_CLASSPATH,
113
        ProjectProperties.RUN_TEST_CLASSPATH,
114
        J2SEProjectProperties.DEBUG_TEST_CLASSPATH,
115
        ProjectProperties.ENDORSED_CLASSPATH,
116
        ProjectProperties.JAVAC_TEST_CLASSPATH,
117
    };
118
    private static final String COMPILE_ON_SAVE_DISABLED_BADGE_PATH = "org/netbeans/modules/java/j2seproject/ui/resources/compileOnSaveDisabledBadge.gif";
119
    private static final Image compileOnSaveDisabledBadge;
120
121
    static {
122
        URL errorBadgeIconURL = J2SELogicalViewProvider.class.getClassLoader().getResource(COMPILE_ON_SAVE_DISABLED_BADGE_PATH);
123
        String compileOnSaveDisabledTP = "<img src=\"" + errorBadgeIconURL + "\">&nbsp;" + NbBundle.getMessage(J2SELogicalViewProvider.class, "TP_CompileOnSaveDisabled");
124
        compileOnSaveDisabledBadge = ImageUtilities.assignToolTipToImage(ImageUtilities.loadImage(COMPILE_ON_SAVE_DISABLED_BADGE_PATH), compileOnSaveDisabledTP); // NOI18N
125
    }
119
    
126
    
120
    private final J2SEProject project;
127
    private final J2SEProject project;
121
    private final UpdateHelper helper;
128
    private final UpdateHelper helper;
Lines 123-130 Link Here
123
    private final ReferenceHelper resolver;
130
    private final ReferenceHelper resolver;
124
    private final ChangeSupport changeSupport = new ChangeSupport(this);
131
    private final ChangeSupport changeSupport = new ChangeSupport(this);
125
    private final PropertyChangeListener pcl;
132
    private final PropertyChangeListener pcl;
126
    private Map<URL,Object[]> activeLibManLocs;
133
    private final RequestProcessor.Task task = RP.create(new Runnable() {
134
        public @Override void run() {
135
            setBroken(ProjectProblems.isBroken(project));
136
            setCompileOnSaveDisabled(isCompileOnSaveDisabled());
137
        }
138
    });
139
127
    private volatile boolean listenersInited;
140
    private volatile boolean listenersInited;
141
    private volatile boolean broken;         //Represents a state where project has a broken reference repairable by broken reference support
142
    private volatile boolean compileOnSaveDisabled;  //true iff Compile-on-Save is disabled
128
    
143
    
129
    public J2SELogicalViewProvider(J2SEProject project, UpdateHelper helper, PropertyEvaluator evaluator, ReferenceHelper resolver) {
144
    public J2SELogicalViewProvider(J2SEProject project, UpdateHelper helper, PropertyEvaluator evaluator, ReferenceHelper resolver) {
130
        this.project = project;
145
        this.project = project;
Lines 137-146 Link Here
137
        assert resolver != null;
152
        assert resolver != null;
138
        pcl = new PropertyChangeListener() {
153
        pcl = new PropertyChangeListener() {
139
            public @Override void propertyChange(PropertyChangeEvent evt) {
154
            public @Override void propertyChange(PropertyChangeEvent evt) {
140
                if (LibraryManager.PROP_OPEN_LIBRARY_MANAGERS.equals(evt.getPropertyName())) {
155
                final String propName = evt.getPropertyName();
141
                    addLibraryManagerListener();
156
                if (propName == null ||
157
                    ProjectProblemsProvider.PROP_PROBLEMS.equals(evt.getPropertyName()) ||
158
                    ProjectProperties.COMPILE_ON_SAVE.equals(evt.getPropertyName()) ||
159
                    propName.startsWith(ProjectProperties.COMPILE_ON_SAVE_UNSUPPORTED_PREFIX)) {
160
                    testBroken();
142
                }
161
                }
143
                testBroken();
144
            }
162
            }
145
        };
163
        };
146
    }
164
    }
Lines 155-212 Link Here
155
                synchronized (J2SELogicalViewProvider.class) {
173
                synchronized (J2SELogicalViewProvider.class) {
156
                    if (!listenersInited) {
174
                    if (!listenersInited) {
157
                        evaluator.addPropertyChangeListener(pcl);
175
                        evaluator.addPropertyChangeListener(pcl);
158
                        JavaPlatformManager.getDefault().addPropertyChangeListener(WeakListeners.propertyChange(pcl, JavaPlatformManager.getDefault()));
176
                        final ProjectProblemsProvider ppp = project.getLookup().lookup(ProjectProblemsProvider.class);
159
                        LibraryManager.addOpenManagersPropertyChangeListener(new OpenManagersWeakListener(pcl));
177
                        if (ppp != null) {
160
                        addLibraryManagerListener();
178
                            ppp.addPropertyChangeListener(pcl);
179
                        }
161
                        listenersInited = true;
180
                        listenersInited = true;
162
                    }
181
                    }
163
                }
182
                }
164
            }
183
            }
165
        });
184
        });
166
    }
185
    }
167
168
    private void addLibraryManagerListener() {
169
        final Map<URL,Object[]> oldLMs;
170
        final boolean attachToDefault;
171
        synchronized (this) {
172
            attachToDefault = activeLibManLocs == null;
173
            if (attachToDefault) {
174
                activeLibManLocs = new HashMap<URL,Object[]>();
175
            }
176
            oldLMs = new HashMap<URL,Object[]>(activeLibManLocs);
177
        }
178
        if (attachToDefault) {
179
            final LibraryManager manager = LibraryManager.getDefault();
180
            manager.addPropertyChangeListener(WeakListeners.propertyChange(pcl, manager));
181
        }
182
        final Collection<? extends LibraryManager> managers = LibraryManager.getOpenManagers();
183
        final Map<URL,LibraryManager> managerByLocation = new HashMap<URL, LibraryManager>();
184
        for (LibraryManager manager : managers) {
185
            final URL url = manager.getLocation();
186
            if (url != null) {
187
                managerByLocation.put(url, manager);
188
            }
189
        }
190
        final HashMap<URL,Object[]> toRemove = new HashMap<URL,Object[]>(oldLMs);
191
        toRemove.keySet().removeAll(managerByLocation.keySet());
192
        for (Object[] pair : toRemove.values()) {
193
            ((LibraryManager)pair[0]).removePropertyChangeListener((PropertyChangeListener)pair[1]);
194
        }
195
        managerByLocation.keySet().removeAll(oldLMs.keySet());
196
        final HashMap<URL,Object[]> toAdd = new HashMap<URL,Object[]>();
197
        for (Map.Entry<URL,LibraryManager> e : managerByLocation.entrySet()) {
198
            final LibraryManager manager = e.getValue();
199
            final PropertyChangeListener listener = WeakListeners.propertyChange(pcl, manager);
200
            manager.addPropertyChangeListener(listener);
201
            toAdd.put(e.getKey(), new Object[] {manager, listener});
202
        }
203
        synchronized (this) {
204
            activeLibManLocs.keySet().removeAll(toRemove.keySet());
205
            activeLibManLocs.putAll(toAdd);
206
        }
207
    }
208
    
186
    
209
    @Override public Node createLogicalView() {
187
    @Override
188
    public Node createLogicalView() {
210
        initListeners();
189
        initListeners();
211
        return new J2SELogicalViewRootNode();
190
        return new J2SELogicalViewRootNode();
212
    }
191
    }
Lines 264-272 Link Here
264
        }
243
        }
265
        return false;
244
        return false;
266
    }
245
    }
267
    
246
            
268
    
269
    
270
    public void addChangeListener(ChangeListener l) {
247
    public void addChangeListener(ChangeListener l) {
271
        changeSupport.addChangeListener(l);
248
        changeSupport.addChangeListener(l);
272
    }
249
    }
Lines 274-353 Link Here
274
    public void removeChangeListener(ChangeListener l) {
251
    public void removeChangeListener(ChangeListener l) {
275
        changeSupport.removeChangeListener(l);
252
        changeSupport.removeChangeListener(l);
276
    }
253
    }
277
    
278
    private final RequestProcessor.Task task = RP.create(new Runnable() {
279
        public @Override void run() {
280
            boolean old = broken;
281
            boolean _broken = hasBrokenLinks();
282
            if (old != _broken) {
283
                setBroken(_broken);
284
            }
285
            old = illegalState;
286
            boolean _illegalState = hasInvalidJdkVersion();
287
            if (old != _illegalState) {
288
                setIllegalState(_illegalState);
289
            }
290
            old = compileOnSaveDisabled;
291
            boolean _compileOnSaveDisabled = isCompileOnSaveDisabled();
292
            if (old != _compileOnSaveDisabled) {
293
                setCompileOnSaveDisabled(_compileOnSaveDisabled);
294
            }
295
        }
296
    });
297
254
298
    /**
255
    @Override
299
     * Used by J2SEProjectCustomizer to mark the project as broken when it warns user
256
    public void testBroken() {
300
     * about project's broken references and advises him to use BrokenLinksAction to correct it.
301
     */
302
    public @Override void testBroken() {
303
        task.schedule(500);
257
        task.schedule(500);
304
    }
258
    }
305
    
306
    // Private innerclasses ----------------------------------------------------
307
    
308
    private static final String[] BREAKABLE_PROPERTIES = {
309
        ProjectProperties.JAVAC_CLASSPATH,
310
        ProjectProperties.RUN_CLASSPATH,
311
        J2SEProjectProperties.DEBUG_CLASSPATH,
312
        ProjectProperties.RUN_TEST_CLASSPATH,
313
        J2SEProjectProperties.DEBUG_TEST_CLASSPATH,
314
        ProjectProperties.ENDORSED_CLASSPATH,
315
        ProjectProperties.JAVAC_TEST_CLASSPATH,
316
    };
317
    
318
    public boolean hasBrokenLinks () {
319
        return BrokenReferencesSupport.isBroken(helper.getAntProjectHelper(), resolver, getBreakableProperties(),
320
                new String[] {J2SEProjectProperties.JAVA_PLATFORM});
321
    }
322
    
323
    private boolean hasInvalidJdkVersion () {
324
        String javaSource = this.evaluator.getProperty("javac.source");     //NOI18N
325
        String javaTarget = this.evaluator.getProperty("javac.target");    //NOI18N
326
        if (javaSource == null && javaTarget == null) {
327
            //No need to check anything
328
            return false;
329
        }
330
        
331
        final String platformId = this.evaluator.getProperty("platform.active");  //NOI18N
332
        final JavaPlatform activePlatform = CommonProjectUtils.getActivePlatform (platformId);
333
        if (activePlatform == null) {
334
            return true;
335
        }        
336
        SpecificationVersion platformVersion = activePlatform.getSpecification().getVersion();
337
        try {
338
            return (javaSource != null && new SpecificationVersion (javaSource).compareTo(platformVersion)>0)
339
                   || (javaTarget != null && new SpecificationVersion (javaTarget).compareTo(platformVersion)>0);
340
        } catch (NumberFormatException nfe) {
341
            ErrorManager.getDefault().log("Invalid javac.source: "+javaSource+" or javac.target: "+javaTarget+" of project:"
342
                +this.project.getProjectDirectory().getPath());
343
            return true;
344
        }
345
    }
346
259
347
    private boolean isCompileOnSaveDisabled() {
348
         return !J2SEProjectUtil.isCompileOnSaveEnabled(project) && J2SEProjectUtil.isCompileOnSaveSupported(project);
349
    }
350
    
351
    public String[] getBreakableProperties() {
260
    public String[] getBreakableProperties() {
352
        SourceRoots roots = this.project.getSourceRoots();
261
        SourceRoots roots = this.project.getSourceRoots();
353
        String[] srcRootProps = roots.getRootProperties();
262
        String[] srcRootProps = roots.getRootProperties();
Lines 364-394 Link Here
364
        return new String[] {J2SEProjectProperties.JAVA_PLATFORM};
273
        return new String[] {J2SEProjectProperties.JAVA_PLATFORM};
365
    }
274
    }
366
    
275
    
367
    private static Image brokenProjectBadge = ImageUtilities.loadImage("org/netbeans/modules/java/j2seproject/ui/resources/brokenProjectBadge.gif", true);
276
    private boolean isCompileOnSaveDisabled() {
368
    private static final String COMPILE_ON_SAVE_DISABLED_BADGE_PATH = "org/netbeans/modules/java/j2seproject/ui/resources/compileOnSaveDisabledBadge.gif";
277
         return !J2SEProjectUtil.isCompileOnSaveEnabled(project) && J2SEProjectUtil.isCompileOnSaveSupported(project);
369
    private static final Image compileOnSaveDisabledBadge;
278
    }                
370
371
    static {
372
        URL errorBadgeIconURL = J2SELogicalViewProvider.class.getClassLoader().getResource(COMPILE_ON_SAVE_DISABLED_BADGE_PATH);
373
        String compileOnSaveDisabledTP = "<img src=\"" + errorBadgeIconURL + "\">&nbsp;" + NbBundle.getMessage(J2SELogicalViewProvider.class, "TP_CompileOnSaveDisabled");
374
        compileOnSaveDisabledBadge = ImageUtilities.assignToolTipToImage(ImageUtilities.loadImage(COMPILE_ON_SAVE_DISABLED_BADGE_PATH), compileOnSaveDisabledTP); // NOI18N
375
    }
376
279
377
    private final class J2SELogicalViewRootNode extends AbstractNode implements ChangeListener, PropertyChangeListener {
280
    private final class J2SELogicalViewRootNode extends AbstractNode implements ChangeListener, PropertyChangeListener {
378
281
379
        private final ProjectInformation info = ProjectUtils.getInformation(project);
282
        private final ProjectInformation info = ProjectUtils.getInformation(project);        
380
        
283
        
381
        @SuppressWarnings("LeakingThisInConstructor")
284
        @SuppressWarnings("LeakingThisInConstructor")
382
        public J2SELogicalViewRootNode() {
285
        public J2SELogicalViewRootNode() {
383
            super(NodeFactorySupport.createCompositeChildren(project, "Projects/org-netbeans-modules-java-j2seproject/Nodes"), 
286
            super(NodeFactorySupport.createCompositeChildren(project, "Projects/org-netbeans-modules-java-j2seproject/Nodes"), 
384
                  Lookups.singleton(project));
287
                  Lookups.singleton(project));
385
            setIconBaseWithExtension("org/netbeans/modules/java/j2seproject/ui/resources/j2seProject.png");
288
            setIconBaseWithExtension("org/netbeans/modules/java/j2seproject/ui/resources/j2seProject.png");
386
            if (hasBrokenLinks()) {
289
            broken = ProjectProblems.isBroken(project);
387
                broken = true;
388
            }
389
            else if (hasInvalidJdkVersion ()) {
390
                illegalState = true;
391
            }
392
            compileOnSaveDisabled = isCompileOnSaveDisabled();
290
            compileOnSaveDisabled = isCompileOnSaveDisabled();
393
            addChangeListener(WeakListeners.change(this, J2SELogicalViewProvider.this));
291
            addChangeListener(WeakListeners.change(this, J2SELogicalViewProvider.this));
394
            info.addPropertyChangeListener(WeakListeners.propertyChange(this, info));
292
            info.addPropertyChangeListener(WeakListeners.propertyChange(this, info));
Lines 408-436 Link Here
408
            } catch (CharConversionException ex) {
306
            } catch (CharConversionException ex) {
409
                return dispName;
307
                return dispName;
410
            }            
308
            }            
411
            return broken || illegalState ? "<font color=\"#"+Integer.toHexString(getErrorForeground().getRGB() & 0xffffff) +"\">" + dispName + "</font>" : null; //NOI18N
309
            return broken ? "<font color=\"#"+Integer.toHexString(getErrorForeground().getRGB() & 0xffffff) +"\">" + dispName + "</font>" : null; //NOI18N
412
        }
310
        }
413
        
311
        
414
        @Override
312
        @Override
415
        public Image getIcon(int type) {
313
        public Image getIcon(int type) {
416
            Image original = super.getIcon(type);
314
            final Image original = super.getIcon(type);
417
315
            return !broken && compileOnSaveDisabled ? ImageUtilities.mergeImages(original, compileOnSaveDisabledBadge, 8, 0) : original;
418
            if (broken || illegalState) {
419
                return ImageUtilities.mergeImages(original, brokenProjectBadge, 8, 0);
420
            } else {
421
                return compileOnSaveDisabled ? ImageUtilities.mergeImages(original, compileOnSaveDisabledBadge, 8, 0) : original;
422
            }
423
        }
316
        }
424
        
317
        
425
        @Override
318
        @Override
426
        public Image getOpenedIcon(int type) {
319
        public Image getOpenedIcon(int type) {
427
            Image original = super.getOpenedIcon(type);
320
            final Image original = super.getOpenedIcon(type);
428
            
321
            return !broken && compileOnSaveDisabled ? ImageUtilities.mergeImages(original, compileOnSaveDisabledBadge, 8, 0) : original;
429
            if (broken || illegalState) {
430
                return ImageUtilities.mergeImages(original, brokenProjectBadge, 8, 0);
431
            } else {
432
                return compileOnSaveDisabled ? ImageUtilities.mergeImages(original, compileOnSaveDisabledBadge, 8, 0) : original;
433
            }
434
        }
322
        }
435
        
323
        
436
        public @Override void stateChanged(ChangeEvent e) {
324
        public @Override void stateChanged(ChangeEvent e) {
Lines 472-498 Link Here
472
            return new HelpCtx(J2SELogicalViewRootNode.class);
360
            return new HelpCtx(J2SELogicalViewRootNode.class);
473
        }
361
        }
474
362
475
    }
363
    }    
476
477
    private boolean broken;         //Represents a state where project has a broken reference repairable by broken reference support
478
    private boolean illegalState;   //Represents a state where project is not in legal state, eg invalid source/target level
479
    private boolean compileOnSaveDisabled;  //true iff Compile-on-Save is disabled
480
364
481
    // Private methods -------------------------------------------------
365
    // Private methods -------------------------------------------------
482
366
483
    private void setBroken(boolean broken) {
367
    private void setBroken(boolean broken) {
484
        this.broken = broken;
368
        //Weak consistent, only visibility required
485
        changeSupport.fireChange();
369
        if (this.broken != broken) {
486
    }
370
            this.broken = broken;
487
371
            changeSupport.fireChange();
488
    private void setIllegalState (boolean illegalState) {
372
        }
489
        this.illegalState = illegalState;
373
    }    
490
        changeSupport.fireChange();
491
    }
492
374
493
    private void setCompileOnSaveDisabled (boolean value) {
375
    private void setCompileOnSaveDisabled (boolean value) {
494
        this.compileOnSaveDisabled = value;
376
        //Weak consistent, only visibility required
495
        changeSupport.fireChange();
377
        if (this.compileOnSaveDisabled != value) {
378
            this.compileOnSaveDisabled = value;
379
            changeSupport.fireChange();
380
        }
496
    }
381
    }
497
382
498
    private static @NonNull Color getErrorForeground() {
383
    private static @NonNull Color getErrorForeground() {
Lines 501-580 Link Here
501
            result = Color.RED;
386
            result = Color.RED;
502
        }
387
        }
503
        return result;
388
        return result;
504
    }
389
    }    
505
506
    @ActionID(id = "org.netbeans.modules.java.j2seproject.ui.J2SELogicalViewProvider$BrokenLinksActionFactory", category = "Project")
507
    @ActionRegistration(displayName = "#LBL_Fix_Broken_Links_Action", lazy=false)
508
    @ActionReference(position = 2600, path = "Projects/org-netbeans-modules-java-j2seproject/Actions")
509
    public static final class BrokenLinksActionFactory extends AbstractAction implements ContextAwareAction {
510
511
        /** for layer registration */
512
        public BrokenLinksActionFactory() {
513
            putValue(Action.NAME, NbBundle.getMessage(J2SELogicalViewProvider.class, "LBL_Fix_Broken_Links_Action"));
514
            setEnabled(false);
515
            putValue(DynamicMenuContent.HIDE_WHEN_DISABLED, true);
516
        }
517
518
        public @Override void actionPerformed(ActionEvent e) {
519
            assert false;
520
        }
521
522
        public @Override Action createContextAwareInstance(Lookup actionContext) {
523
            Collection<? extends Project> p = actionContext.lookupAll(Project.class);
524
            if (p.size() != 1) {
525
                return this;
526
            }
527
            J2SELogicalViewProvider lvp = p.iterator().next().getLookup().lookup(J2SELogicalViewProvider.class);
528
            if (lvp == null) {
529
                return this;
530
            }
531
            return lvp.new BrokenLinksAction();
532
        }
533
534
    }
535
536
    /** This action is created only when project has broken references.
537
     * Once these are resolved the action is disabled.
538
     */
539
    private class BrokenLinksAction extends AbstractAction {
540
541
        public BrokenLinksAction() {
542
            putValue(Action.NAME, NbBundle.getMessage(J2SELogicalViewProvider.class, "LBL_Fix_Broken_Links_Action"));
543
            setEnabled(broken);
544
            putValue(DynamicMenuContent.HIDE_WHEN_DISABLED, true);
545
        }
546
547
        public void actionPerformed(ActionEvent e) {
548
            try {
549
                helper.requestUpdate();
550
                BrokenReferencesSupport.showCustomizer(helper.getAntProjectHelper(), resolver, getBreakableProperties(), getPlatformProperties());
551
                testBroken();
552
            } catch (IOException ioe) {
553
                ErrorManager.getDefault().notify(ioe);
554
            }
555
        }
556
557
    }
558
559
    private static class OpenManagersWeakListener extends WeakReference<PropertyChangeListener> implements Runnable, PropertyChangeListener {
560
561
        public OpenManagersWeakListener(final PropertyChangeListener listener) {
562
            super(listener, Utilities.activeReferenceQueue());
563
        }
564
565
        @Override
566
        public void run() {
567
            LibraryManager.removeOpenManagersPropertyChangeListener(this);
568
        }
569
570
        @Override
571
        public void propertyChange(PropertyChangeEvent evt) {
572
            final PropertyChangeListener listener = get();
573
            if (listener != null) {
574
                listener.propertyChange(evt);
575
            }
576
        }
577
578
    }
579
580
}
390
}
(-)a/java.project/apichanges.xml (+16 lines)
Lines 109-114 Link Here
109
    <!-- ACTUAL CHANGES BEGIN HERE: -->
109
    <!-- ACTUAL CHANGES BEGIN HERE: -->
110
110
111
    <changes>
111
    <changes>
112
        <change id="ProjectProblems">
113
            <api name="general"/>
114
            <summary>Changed <code>BrokenReferencesSupport</code> to delegate to <code>ProjectProblems</code></summary>
115
            <version major="1" minor="46"/>
116
            <date day="24" month="7" year="2012"/>
117
            <author login="tzezula"/>
118
            <compatibility addition="yes" deprecation="yes"/>
119
            <description>
120
                <p>
121
                    Added <code>PreferredProjectPlatform</code> to provide a
122
                    <code>JavaPlatform</code> which should be used for a new project.
123
                </p>
124
            </description>
125
            <class package="org.netbeans.spi.java.project.support.ui" name="BrokenReferencesSupport"/>
126
            <issue number="215628"/>
127
        </change>
112
        <change id="PreferredProjectPlatform">
128
        <change id="PreferredProjectPlatform">
113
            <api name="general"/>
129
            <api name="general"/>
114
            <summary>Added <code>PreferredProjectPlatform</code></summary>
130
            <summary>Added <code>PreferredProjectPlatform</code></summary>
(-)a/java.project/manifest.mf (-1 / +1 lines)
Lines 3-9 Link Here
3
OpenIDE-Module-Layer: org/netbeans/modules/java/project/layer.xml
3
OpenIDE-Module-Layer: org/netbeans/modules/java/project/layer.xml
4
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/java/project/Bundle.properties
4
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/java/project/Bundle.properties
5
OpenIDE-Module-Needs: javax.script.ScriptEngine.freemarker
5
OpenIDE-Module-Needs: javax.script.ScriptEngine.freemarker
6
OpenIDE-Module-Specification-Version: 1.47
6
OpenIDE-Module-Specification-Version: 1.48
7
OpenIDE-Module-Recommends: org.netbeans.spi.java.project.runner.JavaRunnerImplementation
7
OpenIDE-Module-Recommends: org.netbeans.spi.java.project.runner.JavaRunnerImplementation
8
AutoUpdate-Show-In-Client: false
8
AutoUpdate-Show-In-Client: false
9
9
(-)a/java.project/nbproject/project.xml (-1 / +10 lines)
Lines 59-64 Link Here
59
                    </run-dependency>
59
                    </run-dependency>
60
                </dependency>
60
                </dependency>
61
                <dependency>
61
                <dependency>
62
                    <code-name-base>org.jdesktop.layout</code-name-base>
63
                    <build-prerequisite/>
64
                    <compile-dependency/>
65
                    <run-dependency>
66
                        <release-version>1</release-version>
67
                        <specification-version>1.22</specification-version>
68
                    </run-dependency>
69
                </dependency>
70
                <dependency>
62
                    <code-name-base>org.netbeans.api.annotations.common</code-name-base>
71
                    <code-name-base>org.netbeans.api.annotations.common</code-name-base>
63
                    <build-prerequisite/>
72
                    <build-prerequisite/>
64
                    <compile-dependency/>
73
                    <compile-dependency/>
Lines 151-157 Link Here
151
                    <compile-dependency/>
160
                    <compile-dependency/>
152
                    <run-dependency>
161
                    <run-dependency>
153
                        <release-version>1</release-version>
162
                        <release-version>1</release-version>
154
                        <specification-version>1.59</specification-version>
163
                        <specification-version>1.60</specification-version>
155
                    </run-dependency>
164
                    </run-dependency>
156
                </dependency>
165
                </dependency>
157
                <dependency>
166
                <dependency>
(-)a/java.project/src/org/netbeans/modules/java/project/Bundle.properties (-28 / +5 lines)
Lines 50-59 Link Here
50
50
51
Templates/Classes=Java
51
Templates/Classes=Java
52
52
53
LBL_BrokenLinksCustomizer_Fix=&Resolve...
54
LBL_BrokenLinksCustomizer_Description=&Description\:
55
LBL_BrokenLinksCustomizer_List=Reference &Problems:
56
57
# JavaTargetChooserPanelGUI
53
# JavaTargetChooserPanelGUI
58
LBL_JavaTargetChooserPanelGUI_Name=Name and Location
54
LBL_JavaTargetChooserPanelGUI_Name=Name and Location
59
LBL_JavaTargetChooserPanelGUI_jLabel1=&Location\:
55
LBL_JavaTargetChooserPanelGUI_jLabel1=&Location\:
Lines 82-91 Link Here
82
# {0} - name of the existing file
78
# {0} - name of the existing file
83
MSG_file_already_exist=The file {0} already exists.
79
MSG_file_already_exist=The file {0} already exists.
84
80
85
#BrokenReferencesAlertPanel
86
MSG_Broken_References_Label=<html><b>Reference Problems</b></html>
87
MSG_Broken_References=<html>One or more project resources could not be found.<br>Right-click the project in the Projects window and choose<br><b>Resolve Reference Problems</b> to find the missing resources.</html>
88
MSG_Broken_References_Again=&Do not show this message again
89
81
90
AD_JavaTargetChooserPanelGUI=N/A
82
AD_JavaTargetChooserPanelGUI=N/A
91
83
Lines 99-124 Link Here
99
91
100
AD_fileTextField=N/A
92
AD_fileTextField=N/A
101
93
102
MSG_BrokenReferencesAlertPanel_notAgain=&Do not show this message again
103
104
ACSN_BrokenReferencesAlertPanel_notAgain=Do not show this message again
105
106
ACSD_BrokenReferencesAlertPanel_notAgain=N/A
107
108
ACSN_BrokenReferencesAlertPanel=Broken References Panel
109
110
ACSD_BrokenReferencesAlertPanel=N/A
111
112
ACSD_BrokenLinksCustomizer_Description=N/A
113
114
ACSD_BrokenLinksCustomizer_List=N/A
115
116
ACSD_BrokenLinksCustomizer_Fix=N/A
117
118
ACSN_BrokenReferencesCustomizer=Broken References Customizer
119
120
ACSD_BrokenReferencesCustomizer=N/A
121
122
# PackageDisplayUtils
94
# PackageDisplayUtils
123
LBL_DefaultPackage=<default package>
95
LBL_DefaultPackage=<default package>
124
# {0} - full package name
96
# {0} - full package name
Lines 130-132 Link Here
130
102
131
103
132
ERR_JavaTargetChooser_CantUseDefaultPackage=Provide a package name.
104
ERR_JavaTargetChooser_CantUseDefaultPackage=Provide a package name.
105
FixProjectSourceLevel.downgradeLevel.text=&Downgrade Project Source/Binary Format to {0}
106
FixProjectSourceLevel.jLabel1.text=&Java Platform:
107
FixProjectSourceLevel.useOtherPlatform.text=Use Other Java &Platform
108
FixProjectSourceLevel.managePlatforms.text=&Manage Platforms...
109
FixProjectSourceLevel.jLabel2.text=The project source/binary format requires a newer java platform ({0}).
(-)a/java.project/src/org/netbeans/modules/java/project/FixProjectSourceLevel.form (+128 lines)
Line 0 Link Here
1
<?xml version="1.0" encoding="UTF-8" ?>
2
3
<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
4
  <NonVisualComponents>
5
    <Component class="javax.swing.ButtonGroup" name="buttonGroup1">
6
    </Component>
7
  </NonVisualComponents>
8
  <AuxValues>
9
    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
10
    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
11
    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
12
    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
13
    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
14
    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="2"/>
15
    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
16
    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
17
    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
18
  </AuxValues>
19
20
  <Layout>
21
    <DimensionLayout dim="0">
22
      <Group type="103" groupAlignment="0" attributes="0">
23
          <Group type="102" attributes="0">
24
              <EmptySpace max="-2" attributes="0"/>
25
              <Group type="103" groupAlignment="0" attributes="0">
26
                  <Group type="102" attributes="0">
27
                      <EmptySpace min="29" pref="29" max="-2" attributes="0"/>
28
                      <Component id="jLabel1" min="-2" max="-2" attributes="0"/>
29
                      <EmptySpace max="-2" attributes="0"/>
30
                      <Component id="platforms" pref="148" max="32767" attributes="0"/>
31
                      <EmptySpace type="separate" max="-2" attributes="0"/>
32
                      <Component id="managePlatforms" min="-2" max="-2" attributes="0"/>
33
                  </Group>
34
                  <Group type="102" attributes="0">
35
                      <Group type="103" groupAlignment="0" attributes="0">
36
                          <Component id="downgradeLevel" min="-2" max="-2" attributes="0"/>
37
                          <Component id="useOtherPlatform" alignment="0" min="-2" max="-2" attributes="0"/>
38
                          <Component id="jLabel2" alignment="0" min="-2" max="-2" attributes="0"/>
39
                      </Group>
40
                      <EmptySpace min="0" pref="0" max="32767" attributes="0"/>
41
                  </Group>
42
              </Group>
43
              <EmptySpace max="-2" attributes="0"/>
44
          </Group>
45
      </Group>
46
    </DimensionLayout>
47
    <DimensionLayout dim="1">
48
      <Group type="103" groupAlignment="0" attributes="0">
49
          <Group type="102" alignment="1" attributes="0">
50
              <EmptySpace max="-2" attributes="0"/>
51
              <Component id="jLabel2" min="-2" max="-2" attributes="0"/>
52
              <EmptySpace max="-2" attributes="0"/>
53
              <Component id="useOtherPlatform" min="-2" max="-2" attributes="0"/>
54
              <EmptySpace max="-2" attributes="0"/>
55
              <Group type="103" groupAlignment="3" attributes="0">
56
                  <Component id="jLabel1" alignment="3" min="-2" max="-2" attributes="0"/>
57
                  <Component id="platforms" alignment="3" min="-2" max="-2" attributes="0"/>
58
                  <Component id="managePlatforms" alignment="3" min="-2" max="-2" attributes="0"/>
59
              </Group>
60
              <EmptySpace max="-2" attributes="0"/>
61
              <Component id="downgradeLevel" min="-2" max="-2" attributes="0"/>
62
              <EmptySpace pref="29" max="32767" attributes="0"/>
63
          </Group>
64
      </Group>
65
    </DimensionLayout>
66
  </Layout>
67
  <SubComponents>
68
    <Component class="javax.swing.JRadioButton" name="downgradeLevel">
69
      <Properties>
70
        <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
71
          <ComponentRef name="buttonGroup1"/>
72
        </Property>
73
        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
74
          <ResourceString bundle="org/netbeans/modules/java/project/Bundle.properties" key="FixProjectSourceLevel.downgradeLevel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;, platformVersion)"/>
75
        </Property>
76
      </Properties>
77
    </Component>
78
    <Component class="javax.swing.JRadioButton" name="useOtherPlatform">
79
      <Properties>
80
        <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
81
          <ComponentRef name="buttonGroup1"/>
82
        </Property>
83
        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
84
          <ResourceString bundle="org/netbeans/modules/java/project/Bundle.properties" key="FixProjectSourceLevel.useOtherPlatform.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
85
        </Property>
86
      </Properties>
87
    </Component>
88
    <Component class="javax.swing.JLabel" name="jLabel1">
89
      <Properties>
90
        <Property name="labelFor" type="java.awt.Component" editor="org.netbeans.modules.form.ComponentChooserEditor">
91
          <ComponentRef name="platforms"/>
92
        </Property>
93
        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
94
          <ResourceString bundle="org/netbeans/modules/java/project/Bundle.properties" key="FixProjectSourceLevel.jLabel1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
95
        </Property>
96
      </Properties>
97
    </Component>
98
    <Component class="javax.swing.JComboBox" name="platforms">
99
      <Properties>
100
        <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
101
          <StringArray count="4">
102
            <StringItem index="0" value="Item 1"/>
103
            <StringItem index="1" value="Item 2"/>
104
            <StringItem index="2" value="Item 3"/>
105
            <StringItem index="3" value="Item 4"/>
106
          </StringArray>
107
        </Property>
108
      </Properties>
109
    </Component>
110
    <Component class="javax.swing.JButton" name="managePlatforms">
111
      <Properties>
112
        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
113
          <ResourceString bundle="org/netbeans/modules/java/project/Bundle.properties" key="FixProjectSourceLevel.managePlatforms.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
114
        </Property>
115
      </Properties>
116
      <Events>
117
        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="managePlatforms"/>
118
      </Events>
119
    </Component>
120
    <Component class="javax.swing.JLabel" name="jLabel2">
121
      <Properties>
122
        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
123
          <Connection code="org.openide.util.NbBundle.getMessage(FixProjectSourceLevel.class, &quot;FixProjectSourceLevel.jLabel2.text&quot;, minVersion)" type="code"/>
124
        </Property>
125
      </Properties>
126
    </Component>
127
  </SubComponents>
128
</Form>
(-)a/java.project/src/org/netbeans/modules/java/project/FixProjectSourceLevel.java (+235 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2012 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 2012 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.modules.java.project;
43
44
import java.awt.Component;
45
import javax.swing.DefaultComboBoxModel;
46
import javax.swing.DefaultListCellRenderer;
47
import javax.swing.JList;
48
import org.netbeans.api.annotations.common.NonNull;
49
import org.netbeans.api.java.platform.JavaPlatform;
50
import org.netbeans.api.java.platform.JavaPlatformManager;
51
import org.netbeans.api.java.platform.PlatformsCustomizer;
52
import org.netbeans.api.java.platform.Specification;
53
import org.openide.modules.SpecificationVersion;
54
import org.openide.util.Parameters;
55
56
/**
57
 *
58
 * @author Tomas Zezula
59
 */
60
public final class FixProjectSourceLevel extends javax.swing.JPanel {
61
62
    private final SpecificationVersion platformVersion;
63
    private final SpecificationVersion minVersion;
64
65
    public FixProjectSourceLevel(
66
            @NonNull final String type,
67
            @NonNull final SpecificationVersion minVersion,
68
            @NonNull final SpecificationVersion platformVersion) {
69
        Parameters.notNull("type", type);
70
        Parameters.notNull("minVersion", minVersion);
71
        this.platformVersion = platformVersion;
72
        this.minVersion = minVersion;
73
        initComponents();
74
        postInit(type, minVersion);
75
    }
76
77
78
    boolean isDowngradeLevel() {
79
        return this.downgradeLevel.isSelected();
80
    }
81
82
    final JavaPlatform getSelectedPlatform() {
83
        if (!this.useOtherPlatform.isSelected()) {
84
            throw new IllegalStateException();
85
        }
86
        return (JavaPlatform) platforms.getSelectedItem();
87
    }
88
89
    /**
90
     * This method is called from within the constructor to initialize the form.
91
     * WARNING: Do NOT modify this code. The content of this method is always
92
     * regenerated by the Form Editor.
93
     */
94
    @SuppressWarnings("unchecked")
95
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
96
    private void initComponents() {
97
98
        buttonGroup1 = new javax.swing.ButtonGroup();
99
        downgradeLevel = new javax.swing.JRadioButton();
100
        useOtherPlatform = new javax.swing.JRadioButton();
101
        jLabel1 = new javax.swing.JLabel();
102
        platforms = new javax.swing.JComboBox();
103
        managePlatforms = new javax.swing.JButton();
104
        jLabel2 = new javax.swing.JLabel();
105
106
        buttonGroup1.add(downgradeLevel);
107
        org.openide.awt.Mnemonics.setLocalizedText(downgradeLevel, org.openide.util.NbBundle.getMessage(FixProjectSourceLevel.class, "FixProjectSourceLevel.downgradeLevel.text", platformVersion)); // NOI18N
108
109
        buttonGroup1.add(useOtherPlatform);
110
        org.openide.awt.Mnemonics.setLocalizedText(useOtherPlatform, org.openide.util.NbBundle.getMessage(FixProjectSourceLevel.class, "FixProjectSourceLevel.useOtherPlatform.text")); // NOI18N
111
112
        jLabel1.setLabelFor(platforms);
113
        org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(FixProjectSourceLevel.class, "FixProjectSourceLevel.jLabel1.text")); // NOI18N
114
115
        platforms.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
116
117
        org.openide.awt.Mnemonics.setLocalizedText(managePlatforms, org.openide.util.NbBundle.getMessage(FixProjectSourceLevel.class, "FixProjectSourceLevel.managePlatforms.text")); // NOI18N
118
        managePlatforms.addActionListener(new java.awt.event.ActionListener() {
119
            public void actionPerformed(java.awt.event.ActionEvent evt) {
120
                managePlatforms(evt);
121
            }
122
        });
123
124
        org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(FixProjectSourceLevel.class, "FixProjectSourceLevel.jLabel2.text", minVersion));
125
126
        org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this);
127
        this.setLayout(layout);
128
        layout.setHorizontalGroup(
129
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
130
            .add(layout.createSequentialGroup()
131
                .addContainerGap()
132
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
133
                    .add(layout.createSequentialGroup()
134
                        .add(29, 29, 29)
135
                        .add(jLabel1)
136
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
137
                        .add(platforms, 0, 148, Short.MAX_VALUE)
138
                        .add(18, 18, 18)
139
                        .add(managePlatforms))
140
                    .add(layout.createSequentialGroup()
141
                        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
142
                            .add(downgradeLevel)
143
                            .add(useOtherPlatform)
144
                            .add(jLabel2))
145
                        .add(0, 0, Short.MAX_VALUE)))
146
                .addContainerGap())
147
        );
148
        layout.setVerticalGroup(
149
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
150
            .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup()
151
                .addContainerGap()
152
                .add(jLabel2)
153
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
154
                .add(useOtherPlatform)
155
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
156
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
157
                    .add(jLabel1)
158
                    .add(platforms, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
159
                    .add(managePlatforms))
160
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
161
                .add(downgradeLevel)
162
                .addContainerGap(29, Short.MAX_VALUE))
163
        );
164
    }// </editor-fold>//GEN-END:initComponents
165
166
    private void managePlatforms(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_managePlatforms
167
        PlatformsCustomizer.showCustomizer(null);
168
        ((PlatformModel)platforms.getModel()).refresh();
169
    }//GEN-LAST:event_managePlatforms
170
171
    // Variables declaration - do not modify//GEN-BEGIN:variables
172
    private javax.swing.ButtonGroup buttonGroup1;
173
    private javax.swing.JRadioButton downgradeLevel;
174
    private javax.swing.JLabel jLabel1;
175
    private javax.swing.JLabel jLabel2;
176
    private javax.swing.JButton managePlatforms;
177
    private javax.swing.JComboBox platforms;
178
    private javax.swing.JRadioButton useOtherPlatform;
179
    // End of variables declaration//GEN-END:variables
180
181
    private void postInit(
182
            @NonNull String type,
183
            @NonNull SpecificationVersion minVersion) {
184
        useOtherPlatform.setSelected(true);
185
        platforms.setModel(new PlatformModel(type, minVersion));
186
        platforms.setRenderer(new PlatformRenderer());
187
    }
188
189
190
    private static final class PlatformModel extends DefaultComboBoxModel {
191
192
        private final String type;
193
        private final SpecificationVersion minVersion;
194
195
196
        PlatformModel(
197
                @NonNull final String type,
198
                @NonNull final SpecificationVersion minVersion) {
199
            this.type = type;
200
            this.minVersion = minVersion;
201
            refresh();
202
        }
203
204
205
        void refresh() {
206
            removeAllElements();
207
            final JavaPlatform[] platforms = JavaPlatformManager.getDefault().getPlatforms(
208
                    null,
209
                    new Specification(type, null));
210
            for (JavaPlatform jp : platforms) {
211
                if (minVersion.compareTo(jp.getSpecification().getVersion())<=0) {
212
                    addElement(jp);
213
                }
214
            }
215
        }
216
    }
217
218
    private static class PlatformRenderer extends DefaultListCellRenderer {
219
220
        @Override
221
        public Component getListCellRendererComponent(
222
                final JList list,
223
                Object value,
224
                final int index,
225
                final boolean isSelected,
226
                final boolean cellHasFocus) {
227
            if (value instanceof JavaPlatform) {
228
                value = ((JavaPlatform)value).getDisplayName();
229
            }
230
            return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
231
        }
232
233
    }
234
}
235
(-)a/java.project/src/org/netbeans/modules/java/project/ProjectProblemsProviders.java (+1361 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2012 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 2012 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.modules.java.project;
43
44
import java.beans.PropertyChangeEvent;
45
import java.beans.PropertyChangeListener;
46
import java.beans.PropertyChangeSupport;
47
import java.io.File;
48
import java.io.IOException;
49
import java.lang.ref.Reference;
50
import java.lang.ref.WeakReference;
51
import java.net.URI;
52
import java.net.URL;
53
import java.util.ArrayDeque;
54
import java.util.ArrayList;
55
import java.util.Arrays;
56
import java.util.Collection;
57
import java.util.Collections;
58
import java.util.HashMap;
59
import java.util.HashSet;
60
import java.util.LinkedHashSet;
61
import java.util.Map;
62
import java.util.Queue;
63
import java.util.Set;
64
import java.util.concurrent.Callable;
65
import java.util.concurrent.ExecutionException;
66
import java.util.concurrent.Future;
67
import java.util.concurrent.FutureTask;
68
import java.util.concurrent.RunnableFuture;
69
import java.util.concurrent.TimeUnit;
70
import java.util.concurrent.TimeoutException;
71
import java.util.concurrent.atomic.AtomicBoolean;
72
import java.util.logging.Level;
73
import java.util.logging.Logger;
74
import java.util.regex.Matcher;
75
import java.util.regex.Pattern;
76
import javax.swing.JFileChooser;
77
import org.netbeans.api.annotations.common.CheckForNull;
78
import org.netbeans.api.annotations.common.NonNull;
79
import org.netbeans.api.annotations.common.NullAllowed;
80
import org.netbeans.api.java.platform.JavaPlatform;
81
import org.netbeans.api.java.platform.JavaPlatformManager;
82
import org.netbeans.api.java.platform.PlatformsCustomizer;
83
import org.netbeans.api.java.platform.Specification;
84
import org.netbeans.api.project.FileOwnerQuery;
85
import org.netbeans.api.project.Project;
86
import org.netbeans.api.project.ProjectManager;
87
import org.netbeans.api.project.ProjectUtils;
88
import org.netbeans.api.project.libraries.LibrariesCustomizer;
89
import org.netbeans.api.project.libraries.Library;
90
import org.netbeans.api.project.libraries.LibraryManager;
91
import org.netbeans.spi.project.libraries.support.LibrariesSupport;
92
import org.netbeans.spi.project.support.ant.AntProjectHelper;
93
import org.netbeans.spi.project.support.ant.EditableProperties;
94
import org.netbeans.spi.project.support.ant.PropertyEvaluator;
95
import org.netbeans.spi.project.support.ant.PropertyUtils;
96
import org.netbeans.spi.project.support.ant.ReferenceHelper;
97
import org.netbeans.spi.project.ui.ProjectProblemsProvider;
98
import org.openide.filesystems.FileObject;
99
import org.openide.util.NbBundle;
100
import org.openide.util.Parameters;
101
102
import static org.netbeans.modules.java.project.Bundle.*;
103
import org.netbeans.spi.java.project.support.ui.BrokenReferencesSupport;
104
import org.netbeans.spi.project.support.ant.ui.VariablesSupport;
105
import org.netbeans.spi.project.ui.ProjectProblemResolver;
106
import org.netbeans.spi.project.ui.ProjectProblemsProvider.Result;
107
import org.netbeans.spi.project.ui.support.ProjectChooser;
108
import org.openide.DialogDescriptor;
109
import org.openide.DialogDisplayer;
110
import org.openide.modules.SpecificationVersion;
111
import org.openide.util.Exceptions;
112
import org.openide.util.Lookup;
113
import org.openide.util.Mutex;
114
import org.openide.util.RequestProcessor;
115
import org.openide.util.Utilities;
116
import org.openide.util.WeakListeners;
117
118
/**
119
 *
120
 * @author Tomas Zezula
121
 */
122
public class ProjectProblemsProviders {
123
124
    private static final Logger LOG = Logger.getLogger(ProjectProblemsProviders.class.getName());
125
    private static final RequestProcessor RP = new RequestProcessor(ProjectProblemsProviders.class);
126
127
    private ProjectProblemsProviders() {
128
        throw new IllegalStateException(String.format("The %s cannot be instantiated.",this.getClass().getName())); //NOI18N
129
    }
130
131
   
132
133
    public static ProjectProblemsProvider createReferenceProblemProvider(
134
            @NonNull final AntProjectHelper projectHelper,
135
            @NonNull final ReferenceHelper referenceHelper,
136
            @NonNull final PropertyEvaluator evaluator,
137
            @NonNull final String[] properties,
138
            @NonNull final String[] platformProperties) {
139
        final ReferenceProblemProviderImpl pp = new ReferenceProblemProviderImpl(projectHelper, evaluator, referenceHelper, properties, platformProperties);
140
        pp.attachListeners();
141
        return pp;
142
    }
143
    
144
    public static ProjectProblemsProvider createPlatformVersionProblemProvider(
145
            @NonNull final AntProjectHelper helper,
146
            @NonNull final PropertyEvaluator evaluator,
147
            @NullAllowed final Runnable hook,
148
            @NonNull final String platformType,
149
            @NonNull final String platformProperty,
150
            @NonNull final String... platformVersionProperties) {
151
        final PlatformVersionProblemProviderImpl pp = new PlatformVersionProblemProviderImpl(
152
                helper,
153
                evaluator,
154
                hook,
155
                platformType,
156
                platformProperty,
157
                platformVersionProperties);
158
        pp.attachListeners();
159
        return pp;
160
    }
161
162
    //<editor-fold defaultstate="collapsed" desc="Helper Methods & Types">
163
    @NonNull
164
    private static Set<? extends ProjectProblemsProvider.ProjectProblem> getReferenceProblems(
165
            @NullAllowed final AntProjectHelper helper,
166
            @NullAllowed final PropertyEvaluator evaluator,
167
            @NullAllowed final ReferenceHelper refHelper,
168
            @NonNull final String[] ps,
169
            final boolean abortAfterFirstProblem) {
170
        Set<ProjectProblemsProvider.ProjectProblem> set = new LinkedHashSet<ProjectProblemsProvider.ProjectProblem>();
171
        StringBuilder all = new StringBuilder();
172
        // this call waits for list of libraries to be refreshhed
173
        LibraryManager.getDefault().getLibraries();
174
        if (helper == null || evaluator == null || refHelper == null) {
175
            return set;
176
        }
177
        final Queue<FileResolver> fileReoslvers = new ArrayDeque<FileResolver>();
178
        EditableProperties ep = helper.getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
179
        for (String p : ps) {
180
            // evaluate given property and tokenize it
181
182
            String prop = evaluator.getProperty(p);
183
            if (prop == null) {
184
                continue;
185
            }
186
            LOG.log(Level.FINE, "Evaluated {0}={1}", new Object[] {p, prop});
187
            String[] vals = PropertyUtils.tokenizePath(prop);
188
189
            // no check whether after evaluating there are still some
190
            // references which could not be evaluated
191
            for (String v : vals) {
192
                // we are checking only: project reference, file reference, library reference
193
                if (!(v.startsWith("${file.reference.") || v.startsWith("${project.") || v.startsWith("${libs.") || v.startsWith("${var."))) { // NOI18N
194
                    all.append(v);
195
                    continue;
196
                }
197
                if (v.startsWith("${project.")) { // NOI18N
198
                    // something in the form: "${project.<projID>}/dist/foo.jar"
199
                    String val = v.substring(2, v.indexOf('}')); // NOI18N
200
                    set.add(
201
                        ProjectProblemsProvider.ProjectProblem.createError(
202
                            getDisplayName(RefType.PROJECT, val),
203
                            getDescription(RefType.PROJECT, val),
204
                            new ProjectResolver(val, helper)));
205
                } else {
206
                    final String val = v.substring(2, v.length() - 1);
207
                    final ProjectProblemsProvider.ProjectProblem problem;
208
                    if (v.startsWith("${file.reference")) { // NOI18N
209
                        final FileResolver fr = new FileResolver(val, helper, fileReoslvers);
210
                        fileReoslvers.offer(fr);
211
                        problem = ProjectProblemsProvider.ProjectProblem.createError(
212
                            getDisplayName(RefType.FILE, val),
213
                            getDescription(RefType.FILE, val),
214
                            fr);
215
216
                    } else if (v.startsWith("${var")) { // NOI18N
217
                        problem = ProjectProblemsProvider.ProjectProblem.createError(
218
                            getDisplayName(RefType.VARIABLE, val),
219
                            getDescription(RefType.VARIABLE, val),
220
                            new VariableResolver(RefType.VARIABLE, val));
221
                    } else {
222
                        problem = ProjectProblemsProvider.ProjectProblem.createError(
223
                            getDisplayName(RefType.LIBRARY, val),
224
                            getDescription(RefType.LIBRARY, val),
225
                            new LibraryResolver(RefType.LIBRARY, val, refHelper));
226
                    }
227
                    set.add(problem);
228
                }
229
                if (abortAfterFirstProblem) {
230
                    break;
231
                }
232
            }
233
            if (set.size() > 0 && abortAfterFirstProblem) {
234
                break;
235
            }
236
237
            // test that resolved variable based property points to an existing file
238
            String path = ep.getProperty(p);
239
            if (path != null) {
240
                for (String v : PropertyUtils.tokenizePath(path)) {
241
                    if (v.startsWith("${file.reference.")) {    //NOI18N
242
                        v = ep.getProperty(v.substring(2, v.length() - 1));
243
                    }
244
                    if (v != null && v.startsWith("${var.")) {    //NOI18N
245
                        String value = evaluator.evaluate(v);
246
                        if (value.startsWith("${var.")) { // NOI18N
247
                            // this problem was already reported
248
                            continue;
249
                        }
250
                        File f = getFile(helper, evaluator, value);
251
                        if (f.exists()) {
252
                            continue;
253
                        }
254
                        set.add(
255
                            ProjectProblemsProvider.ProjectProblem.createError(
256
                                getDisplayName(RefType.VARIABLE_CONTENT, v),
257
                                getDescription(RefType.VARIABLE_CONTENT, v),
258
                                new VariableResolver(RefType.VARIABLE_CONTENT, v)));
259
                    }
260
                }
261
            }
262
263
        }
264
265
        // Check also that all referenced project really exist and are reachable.
266
        // If they are not report them as broken reference.
267
        // XXX: there will be API in PropertyUtils for listing of Ant
268
        // prop names in String. Consider using it here.
269
        final Map<String, String> entries = evaluator.getProperties();
270
        if (entries == null) {
271
            throw new IllegalArgumentException("Properies mapping could not be computed (e.g. due to a circular definition). Evaluator: "+evaluator.toString());  //NOI18N
272
        }
273
        for (Map.Entry<String, String> entry : entries.entrySet()) {
274
            String key = entry.getKey();
275
            String value = entry.getValue();
276
            if (key.startsWith("project.")) { // NOI18N
277
                if ("project.license".equals(key)) {    //NOI18N
278
                    continue;
279
                }
280
                File f = getFile(helper, evaluator, value);
281
                if (f.exists()) {
282
                    continue;
283
                }
284
                // Check that the value is really used by some property.
285
                // If it is not then ignore such a project.
286
                if (all.indexOf(value) == -1) {
287
                    continue;
288
                }
289
                set.add(
290
                    ProjectProblemsProvider.ProjectProblem.createError(
291
                        getDisplayName(RefType.PROJECT, key),
292
                        getDescription(RefType.PROJECT, key),
293
                        new ProjectResolver(key, helper)));
294
            }
295
            else if (key.startsWith("file.reference")) {    //NOI18N
296
                File f = getFile(helper, evaluator, value);
297
                String unevaluatedValue = ep.getProperty(key);
298
                boolean alreadyChecked = unevaluatedValue != null ? unevaluatedValue.startsWith("${var.") : false; // NOI18N
299
                if (f.exists() || all.indexOf(value) == -1 || alreadyChecked) { // NOI18N
300
                    continue;
301
                }
302
                final FileResolver fr = new FileResolver(key, helper, fileReoslvers);
303
                fileReoslvers.offer(fr);
304
                set.add(
305
                    ProjectProblemsProvider.ProjectProblem.createError(
306
                        getDisplayName(RefType.FILE, key),
307
                        getDescription(RefType.FILE, key),
308
                        fr));
309
            }
310
        }
311
312
        //Check for libbraries with broken classpath content
313
        Set<String> usedLibraries = new HashSet<String>();
314
        Pattern libPattern = Pattern.compile("\\$\\{(libs\\.[-._a-zA-Z0-9]+\\.classpath)\\}"); //NOI18N
315
        for (String p : ps) {
316
            String propertyValue = ep.getProperty(p);
317
            if (propertyValue != null) {
318
                for (String v : PropertyUtils.tokenizePath(propertyValue)) {
319
                    Matcher m = libPattern.matcher(v);
320
                    if (m.matches()) {
321
                        usedLibraries.add (m.group(1));
322
                    }
323
                }
324
            }
325
        }
326
        for (String libraryRef : usedLibraries) {
327
            String libraryName = libraryRef.substring(5,libraryRef.length()-10);
328
            Library lib = refHelper.findLibrary(libraryName);
329
            if (lib == null) {
330
                // Should already have been caught before?
331
                set.add(
332
                    ProjectProblemsProvider.ProjectProblem.createError(
333
                        getDisplayName(RefType.LIBRARY, libraryRef),
334
                        getDescription(RefType.LIBRARY, libraryRef),
335
                        new LibraryResolver(RefType.LIBRARY, libraryRef, refHelper)));
336
            }
337
            else {
338
                //XXX: Should check all the volumes (sources, javadoc, ...)?
339
                for (URI uri : lib.getURIContent("classpath")) { // NOI18N
340
                    URI uri2 = LibrariesSupport.getArchiveFile(uri);
341
                    if (uri2 == null) {
342
                        uri2 = uri;
343
                    }
344
                    FileObject fo = LibrariesSupport.resolveLibraryEntryFileObject(lib.getManager().getLocation(), uri2);
345
                    if (null == fo && !canResolveEvaluatedUri(helper.getStandardPropertyEvaluator(), lib.getManager().getLocation(), uri2)) {
346
                        set.add(
347
                            ProjectProblemsProvider.ProjectProblem.createError(
348
                                getDisplayName(RefType.LIBRARY_CONTENT, libraryRef),
349
                                getDescription(RefType.LIBRARY_CONTENT, libraryRef),
350
                                new LibraryResolver(RefType.LIBRARY_CONTENT, libraryRef, refHelper)));
351
                        break;
352
                    }
353
                }
354
            }
355
        }
356
357
        return set;
358
    }
359
360
    @NonNull
361
    private static Set<ProjectProblemsProvider.ProjectProblem> getPlatformProblems(
362
            @NullAllowed final PropertyEvaluator evaluator,
363
            @NonNull final String[] platformProperties,
364
            boolean abortAfterFirstProblem) {
365
        final Set<ProjectProblemsProvider.ProjectProblem> set = new LinkedHashSet<ProjectProblemsProvider.ProjectProblem>();
366
        if (evaluator == null) {
367
            return set;
368
        }
369
        for (String pprop : platformProperties) {
370
            String prop = evaluator.getProperty(pprop);
371
            if (prop == null) {
372
                continue;
373
            }
374
            if (!existPlatform(prop)) {
375
376
                // XXX: the J2ME stores in project.properties also platform
377
                // display name and so show this display name instead of just
378
                // prop ID if available.
379
                if (evaluator.getProperty(pprop + ".description") != null) { // NOI18N
380
                    prop = evaluator.getProperty(pprop + ".description"); // NOI18N
381
                }
382
383
                set.add(
384
                    ProjectProblemsProvider.ProjectProblem.createError(
385
                        getDisplayName(RefType.PLATFORM, prop),
386
                        getDescription(RefType.PLATFORM, prop),
387
                        new PlatformResolver(prop)));
388
            }
389
            if (set.size() > 0 && abortAfterFirstProblem) {
390
                break;
391
            }
392
        }
393
        return set;
394
    }
395
396
    private static File getFile (AntProjectHelper helper, PropertyEvaluator evaluator, String name) {
397
        if (helper != null) {
398
            return new File(helper.resolvePath(name));
399
        } else {
400
            File f = new File(name);
401
            if (!f.exists()) {
402
                // perhaps the file is relative?
403
                String basedir = evaluator.getProperty("basedir"); // NOI18N
404
                assert basedir != null;
405
                f = new File(new File(basedir), name);
406
            }
407
            return f;
408
        }
409
    }
410
411
    /** Tests whether evaluated URI can be resolved. To support library entry
412
     * like "${MAVEN_REPO}/struts/struts.jar".
413
     */
414
    private static boolean canResolveEvaluatedUri(PropertyEvaluator eval, URL libBase, URI libUri) {
415
        if (libUri.isAbsolute()) {
416
            return false;
417
        }
418
        String path = LibrariesSupport.convertURIToFilePath(libUri);
419
        String newPath = eval.evaluate(path);
420
        if (newPath.equals(path)) {
421
            return false;
422
        }
423
        URI newUri = LibrariesSupport.convertFilePathToURI(newPath);
424
        return null != LibrariesSupport.resolveLibraryEntryFileObject(libBase, newUri);
425
    }
426
427
    private static boolean existPlatform(String platform) {
428
        if (platform.equals("default_platform")) { // NOI18N
429
            return true;
430
        }
431
        for (JavaPlatform plat : JavaPlatformManager.getDefault().getInstalledPlatforms()) {
432
            // XXX: this should be defined as PROPERTY somewhere
433
            if (platform.equals(plat.getProperties().get("platform.ant.name")) && // NOI18N
434
                    plat.getInstallFolders().size() > 0) {
435
                return true;
436
            }
437
        }
438
        return false;
439
    }
440
441
    @NonNull
442
    @NbBundle.Messages({
443
        "LBL_BrokenLinksCustomizer_BrokenLibrary=\"{0}\" library could not be found",
444
        "LBL_BrokenLinksCustomizer_BrokenDefinableLibrary=\"{0}\" library must be defined",
445
        "LBL_BrokenLinksCustomizer_BrokenLibraryContent=\"{0}\" library has missing items",
446
        "LBL_BrokenLinksCustomizer_BrokenProjectReference=\"{0}\" project could not be found",
447
        "LBL_BrokenLinksCustomizer_BrokenFileReference=\"{0}\" file/folder could not be found",
448
        "LBL_BrokenLinksCustomizer_BrokenVariable=\"{0}\" variable could not be found",
449
        "LBL_BrokenLinksCustomizer_BrokenVariableContent=\"{0}\" variable based file/folder could not be found",
450
        "LBL_BrokenLinksCustomizer_BrokenPlatform=\"{0}\" platform could not be found",
451
    })
452
    private static String getDisplayName(
453
            @NonNull final RefType type,
454
            @NonNull final String id) {
455
        switch (type) {
456
            case LIBRARY:
457
                return LBL_BrokenLinksCustomizer_BrokenLibrary(getDisplayId(type, id));
458
            case DEFINABLE_LIBRARY:
459
                return LBL_BrokenLinksCustomizer_BrokenDefinableLibrary(getDisplayId(type, id));
460
            case LIBRARY_CONTENT:
461
                return LBL_BrokenLinksCustomizer_BrokenLibraryContent(getDisplayId(type, id));
462
            case PROJECT:
463
                return LBL_BrokenLinksCustomizer_BrokenProjectReference(getDisplayId(type, id));
464
            case FILE:
465
                return LBL_BrokenLinksCustomizer_BrokenFileReference(getDisplayId(type, id));
466
            case PLATFORM:
467
                return LBL_BrokenLinksCustomizer_BrokenPlatform(getDisplayId(type, id));
468
            case VARIABLE:
469
                return LBL_BrokenLinksCustomizer_BrokenVariable(getDisplayId(type, id));
470
            case VARIABLE_CONTENT:
471
                return LBL_BrokenLinksCustomizer_BrokenVariableContent(getDisplayId(type, id));
472
            default:
473
                assert false;
474
                return id;
475
        }
476
    }
477
478
    @NbBundle.Messages({
479
        "LBL_BrokenLinksCustomizer_BrokenLibraryDesc=Problem: The project uses a class library called \"{0}\", but this class library was not found.\nSolution: Click Resolve to open the Library Manager and create a new class library called \"{0}\".",
480
        "LBL_BrokenLinksCustomizer_BrokenDefinableLibraryDesc=Problem: The project uses a class library called \"{0}\", but this class library is not currently defined locally.\nSolution: Click Resolve to download or otherwise automatically define this library.",
481
        "LBL_BrokenLinksCustomizer_BrokenLibraryContentDesc=Problem: The project uses the class library called \"{0}\" but the classpath items of this library are missing.\nSolution: Click Resolve to open the Library Manager and locate the missing classpath items of \"{0}\" library.",
482
        "LBL_BrokenLinksCustomizer_BrokenProjectReferenceDesc=Problem: The project classpath includes a reference to the project called \"{0}\", but this project was not found.\nSolution: Click Resolve and locate the missing project.",
483
        "LBL_BrokenLinksCustomizer_BrokenFileReferenceDesc=Problem: The project uses the file/folder called \"{0}\", but this file/folder was not found.\nSolution: Click Resolve and locate the missing file/folder.",
484
        "LBL_BrokenLinksCustomizer_BrokenVariableReferenceDesc=Problem: The project uses the variable called \"{0}\", but this variable was not found.\nSolution: Click Resolve and setup this variable there.",
485
        "LBL_BrokenLinksCustomizer_BrokenVariableContentDesc=Problem: The project uses the variable based file/folder \"{0}\", but this file/folder was not found.\nSolution: Click Resolve and update your variable to point to correct location.",
486
        "LBL_BrokenLinksCustomizer_BrokenPlatformDesc=Problem: The project uses the Java Platform called \"{0}\", but this platform was not found.\nSolution: Click Resolve and create new platform called \"{0}\"."
487
    })
488
    private static String getDescription(
489
            @NonNull final RefType type,
490
            @NonNull final String id
491
            ) {
492
        switch (type) {
493
            case LIBRARY:
494
                return LBL_BrokenLinksCustomizer_BrokenLibraryDesc(getDisplayId(type, id));
495
            case DEFINABLE_LIBRARY:
496
                return LBL_BrokenLinksCustomizer_BrokenDefinableLibraryDesc(getDisplayId(type, id));
497
            case LIBRARY_CONTENT:
498
                return LBL_BrokenLinksCustomizer_BrokenLibraryContentDesc(getDisplayId(type, id));
499
            case PROJECT:
500
                return LBL_BrokenLinksCustomizer_BrokenProjectReferenceDesc(getDisplayId(type, id));
501
            case FILE:
502
                return LBL_BrokenLinksCustomizer_BrokenFileReferenceDesc(getDisplayId(type, id));
503
            case PLATFORM:
504
                return LBL_BrokenLinksCustomizer_BrokenPlatformDesc(getDisplayId(type, id));
505
            case VARIABLE:
506
                return LBL_BrokenLinksCustomizer_BrokenVariableReferenceDesc(getDisplayId(type, id));
507
            case VARIABLE_CONTENT:
508
                return LBL_BrokenLinksCustomizer_BrokenVariableContent(getDisplayId(type, id));
509
            default:
510
                assert false;
511
                return id;
512
        }
513
    }
514
515
    private static String getDisplayId(
516
            @NonNull final RefType type,
517
            @NonNull final String id) {
518
        switch (type) {
519
            case LIBRARY:
520
            case DEFINABLE_LIBRARY:
521
            case LIBRARY_CONTENT:
522
                // libs.<name>.classpath
523
                return id.substring(5, id.length()-10);
524
            case PROJECT:
525
                // project.<name>
526
                return id.substring(8);
527
            case FILE:
528
                // file.reference.<name>
529
                return id.substring(15);
530
            case PLATFORM:
531
                return id;
532
            case VARIABLE:
533
                return id.substring(4, id.indexOf('}')); // NOI18N
534
            case VARIABLE_CONTENT:
535
                return id.substring(6, id.indexOf('}')) + id.substring(id.indexOf('}')+1); // NOI18N
536
            default:
537
                assert false;
538
                return id;
539
        }
540
    }
541
542
    private enum RefType {
543
        PROJECT,
544
        FILE,
545
        PLATFORM,
546
        LIBRARY,
547
        DEFINABLE_LIBRARY,
548
        LIBRARY_CONTENT,
549
        VARIABLE,
550
        VARIABLE_CONTENT,
551
    }
552
    //</editor-fold>
553
554
    
555
    //<editor-fold defaultstate="collapsed" desc="Resolver implementations">
556
    private static abstract class BaseResolver implements ProjectProblemResolver {
557
558
        protected final RefType type;
559
        protected final String id;
560
561
        BaseResolver(
562
            @NonNull final RefType type,
563
            @NonNull final String id) {
564
            Parameters.notNull("type", type);   //NOI18N
565
            Parameters.notNull("id", id);   //NOI18N
566
            this.type = type;
567
            this.id = id;
568
        }
569
        
570
        @Override
571
        public final int hashCode() {
572
            int result = 17;
573
            result = 31 * result + type.hashCode();
574
            result = 31 * result + id.hashCode();
575
            return result;
576
        }
577
        
578
        @Override
579
        public final boolean equals(@NullAllowed final Object other) {
580
            if (!(other instanceof BaseResolver)) {
581
                return false;
582
            }
583
            final BaseResolver otherResolver = (BaseResolver) other;
584
            return type.equals(otherResolver.type) &&
585
                   id.equals(otherResolver.id);
586
        }
587
588
        @Override
589
        public String toString() {
590
            return String.format(
591
               "Resolver for %s %s",    //NOI18N
592
               type,
593
               id);
594
        }
595
596
597
598
    }
599
600
    private static class PlatformResolver extends BaseResolver {
601
602
        PlatformResolver(@NonNull final String id) {
603
            super(RefType.PLATFORM, id);
604
        }
605
606
        @Override
607
        @NonNull
608
        public Future<ProjectProblemsProvider.Result> resolve() {
609
            PlatformsCustomizer.showCustomizer(null);
610
            return new Done(ProjectProblemsProvider.Result.create(ProjectProblemsProvider.Status.RESOLVED));
611
        }
612
613
    }
614
    
615
    private static class LibraryResolver extends BaseResolver {
616
617
        private final Callable<Library> definer;
618
        private final Reference<ReferenceHelper> refHelper;
619
620
        LibraryResolver(
621
                @NonNull RefType type,
622
                @NonNull final String id,
623
                @NonNull final ReferenceHelper refHelper) {
624
            this(translate(type, id),id, refHelper);
625
        }
626
627
        private LibraryResolver(
628
                @NonNull final Object[] typeDefiner/*todo: replace by Pair*/,
629
                @NonNull final String id,
630
                @NonNull final ReferenceHelper refHelper) {
631
            super((RefType)typeDefiner[0],id);
632
            this.definer = (Callable<Library>)typeDefiner[1];
633
            this.refHelper = new WeakReference<ReferenceHelper>(refHelper);
634
        }
635
636
        @Override
637
        @NonNull
638
        public Future<ProjectProblemsProvider.Result> resolve() {
639
            if (type == RefType.DEFINABLE_LIBRARY) {
640
                return resolveByDefiner();
641
            } else {
642
                return new Done(ProjectProblemsProvider.Result.create(resolveByLibraryManager()));
643
            }
644
        }
645
646
        private ProjectProblemsProvider.Status resolveByLibraryManager() {
647
            final LibraryManager lm = getProjectLibraryManager();
648
            if (lm == null) {
649
                //Closed and freed project
650
                return ProjectProblemsProvider.Status.UNRESOLVED;
651
            }
652
            LibrariesCustomizer.showCustomizer(null,lm);
653
            return ProjectProblemsProvider.Status.RESOLVED;
654
        }
655
656
        private Future<ProjectProblemsProvider.Result> resolveByDefiner() {
657
            assert definer != null;
658
            final RunnableFuture<ProjectProblemsProvider.Result> future =
659
                    new FutureTask<ProjectProblemsProvider.Result>(
660
                    new Callable<ProjectProblemsProvider.Result>() {
661
                        @Override
662
                        public ProjectProblemsProvider.Result call() throws Exception {
663
                            ProjectProblemsProvider.Status result = ProjectProblemsProvider.Status.UNRESOLVED;
664
                            try {
665
                                Library lib = definer.call();
666
                                LOG.log(Level.FINE, "found {0}", lib);  //NOI18N
667
                                result = ProjectProblemsProvider.Status.RESOLVED;
668
                            } catch (Exception x) {
669
                                LOG.log(Level.INFO, null, x);
670
                                result = resolveByLibraryManager();
671
                            }
672
                            return ProjectProblemsProvider.Result.create(result);
673
                        }
674
                    });
675
            RP.post(future);
676
            return future;
677
        }
678
679
        @CheckForNull
680
        private LibraryManager getProjectLibraryManager() {
681
            final ReferenceHelper rh = refHelper.get();
682
            return rh == null ?
683
                null:
684
                rh.getProjectLibraryManager() != null ?
685
                    rh.getProjectLibraryManager():
686
                    LibraryManager.getDefault();
687
        }
688
689
        @NonNull
690
        private static Object[] translate(
691
           @NonNull RefType original,
692
           @NonNull String id) {
693
            Callable<Library> _definer = null;
694
            if (original == RefType.LIBRARY) {
695
                final String name = id.substring(5, id.length() - 10);
696
                for (BrokenReferencesSupport.LibraryDefiner ld : Lookup.getDefault().lookupAll(BrokenReferencesSupport.LibraryDefiner.class)) {
697
                    _definer = ld.missingLibrary(name);
698
                    if (_definer != null) {
699
                        return new Object[] {RefType.DEFINABLE_LIBRARY, _definer};
700
                    }
701
                }
702
            }
703
            return new Object[] {original, null};
704
        }
705
    }    
706
707
    private static class VariableResolver extends BaseResolver {
708
        VariableResolver(@NonNull final RefType type, @NonNull final String id) {
709
            super(type, id);
710
        }
711
712
        @Override
713
        @NonNull
714
        public Future<ProjectProblemsProvider.Result> resolve() {
715
            VariablesSupport.showVariablesCustomizer();
716
            return new Done(ProjectProblemsProvider.Result.create(ProjectProblemsProvider.Status.RESOLVED));
717
        }
718
    }
719
720
    private static abstract class ReferenceResolver extends BaseResolver {
721
722
        static File lastSelectedFile;
723
724
        private final Reference<AntProjectHelper> antProjectHelper;
725
726
        ReferenceResolver(
727
                @NonNull final RefType type,
728
                @NonNull final String id,
729
                @NonNull final AntProjectHelper antProjectHelper) {
730
            super (type, id);
731
            this.antProjectHelper = new WeakReference<AntProjectHelper>(antProjectHelper);
732
        }
733
734
        abstract void updateReference(@NonNull final File file);
735
736
        final void updateReferenceImpl(@NonNull final File file) {
737
            final String reference = id;
738
            final AntProjectHelper helper = antProjectHelper.get();
739
            if (helper == null) {
740
                //Closed and freed project, ignore
741
                return;
742
            }
743
            FileObject myProjDirFO = helper.getProjectDirectory();
744
            final String propertiesFile = AntProjectHelper.PRIVATE_PROPERTIES_PATH;
745
            final String path = file.getAbsolutePath();
746
            Project p;
747
            try {
748
                p = ProjectManager.getDefault().findProject(myProjDirFO);
749
            } catch (IOException ex) {
750
                Exceptions.printStackTrace(ex);
751
                p = null;
752
            }
753
            final Project proj = p;
754
            ProjectManager.mutex().postWriteRequest(new Runnable() {
755
                    public @Override void run() {
756
                        EditableProperties props = helper.getProperties(propertiesFile);
757
                        if (!path.equals(props.getProperty(reference))) {
758
                            props.setProperty(reference, path);
759
                            helper.putProperties(propertiesFile, props);
760
                        }
761
762
                        if (proj != null) {
763
                            try {
764
                                ProjectManager.getDefault().saveProject(proj);
765
                            } catch (IOException ex) {
766
                                Exceptions.printStackTrace(ex);
767
                            }
768
                        }
769
                    }
770
                });
771
        }
772
    }
773
774
    private static class ProjectResolver extends ReferenceResolver {
775
        ProjectResolver(@NonNull final String id, @NonNull AntProjectHelper antProjectHelper) {
776
            super (RefType.PROJECT, id, antProjectHelper);
777
        }
778
779
        @Override
780
        @NonNull
781
        @NbBundle.Messages({
782
            "LBL_BrokenLinksCustomizer_Resolve_Project=Browse Project \"{0}\"",
783
        })
784
        public Future<ProjectProblemsProvider.Result> resolve() {
785
            ProjectProblemsProvider.Status result = ProjectProblemsProvider.Status.UNRESOLVED;
786
            final JFileChooser chooser = ProjectChooser.projectChooser();
787
            chooser.setDialogTitle(LBL_BrokenLinksCustomizer_Resolve_Project(getDisplayId(type, id)));
788
            if (lastSelectedFile != null) {
789
                chooser.setSelectedFile(lastSelectedFile);
790
            }
791
            int option = chooser.showOpenDialog(null);
792
            if (option == JFileChooser.APPROVE_OPTION) {
793
                updateReference(chooser.getSelectedFile());
794
                lastSelectedFile = chooser.getSelectedFile();
795
                result = ProjectProblemsProvider.Status.RESOLVED;
796
            }
797
            return new Done(ProjectProblemsProvider.Result.create(result));
798
        }
799
800
        @Override
801
        void updateReference(@NonNull final File file) {
802
            updateReferenceImpl(file);
803
        }
804
805
    }
806
807
    private static class FileResolver extends ReferenceResolver {
808
809
        private final Queue<? extends FileResolver> peers;
810
        private ProjectProblemsProvider.Status resolved =
811
                ProjectProblemsProvider.Status.UNRESOLVED;
812
813
        FileResolver(
814
                @NonNull final String id,
815
                @NonNull final AntProjectHelper antProjectHelper,
816
                @NonNull final Queue<? extends FileResolver> peers) {
817
            super(RefType.FILE, id, antProjectHelper);
818
            this.peers = peers;
819
        }
820
821
        @Override
822
        @NonNull
823
        @NbBundle.Messages({
824
            "LBL_BrokenLinksCustomizer_Resolve_File=Browse \"{0}\""
825
        })
826
        public Future<ProjectProblemsProvider.Result> resolve() {
827
            final JFileChooser chooser = new JFileChooser();
828
            chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
829
            chooser.setDialogTitle(LBL_BrokenLinksCustomizer_Resolve_File(getDisplayId(type, id)));
830
            if (lastSelectedFile != null) {
831
                chooser.setSelectedFile(lastSelectedFile);
832
            }
833
            int option = chooser.showOpenDialog(null);
834
            if (option == JFileChooser.APPROVE_OPTION) {
835
                updateReference(chooser.getSelectedFile());
836
                lastSelectedFile = chooser.getSelectedFile();
837
                resolved = ProjectProblemsProvider.Status.RESOLVED;
838
            }
839
            return new Done(ProjectProblemsProvider.Result.create(resolved));
840
        }
841
842
        @Override
843
        void updateReference(@NonNull final File file) {
844
            updateReferenceImpl(file);
845
            final File parentFolder = file.getParentFile();
846
            for (FileResolver peer : peers) {
847
                if (this != peer && peer.resolved == ProjectProblemsProvider.Status.UNRESOLVED) {
848
                    final File f = new File(parentFolder, getDisplayId(type, id));
849
                    if (f.exists()) {
850
                        updateReferenceImpl(f);
851
                    }
852
                }
853
            }
854
        }
855
856
857
    }
858
859
    private static class SourceTargetResolver implements ProjectProblemResolver {
860
861
        private final String type;
862
        private final String platformProp;
863
        private final Collection<? extends String> invalidVersionProps;
864
        private final SpecificationVersion minVersion;
865
        private final SpecificationVersion platformVersion;
866
        private final Reference<AntProjectHelper> helperRef;
867
        private final Runnable hook;
868
869
        SourceTargetResolver(
870
            @NonNull final AntProjectHelper helper,
871
            @NullAllowed final Runnable hook,
872
            @NonNull final String type,
873
            @NonNull final String platformProp,
874
            @NonNull final Collection<? extends String> invalidVersionProps,
875
            @NonNull final SpecificationVersion minVersion,
876
            @NonNull final SpecificationVersion platformVersion) {
877
            Parameters.notNull("helper", helper);   //NOI18N
878
            Parameters.notNull("type", type);   //NOI18N
879
            Parameters.notNull("platformProp", platformProp);   //NOI18N
880
            Parameters.notNull("invalidVersionProps", invalidVersionProps); //NOI18N
881
            Parameters.notNull("minVersion", minVersion);   //NOI18N
882
            Parameters.notNull("platformVersion", platformVersion); //NOI18N
883
            this.helperRef = new WeakReference<AntProjectHelper>(helper);
884
            this.hook = hook;
885
            this.type = type;
886
            this.platformProp = platformProp;
887
            this.invalidVersionProps = invalidVersionProps;
888
            this.minVersion = minVersion;
889
            this.platformVersion = platformVersion;
890
        }
891
892
893
        @NbBundle.Messages({"LBL_ResolveJDKVersion=Resolve Invalid Java Platform Version - \"{0}\" Project"})
894
        @Override
895
        public Future<Result> resolve() {
896
            final AntProjectHelper helper = helperRef.get();
897
            if (helper != null) {
898
                final Project project = FileOwnerQuery.getOwner(helper.getProjectDirectory());
899
                final FixProjectSourceLevel changeVersion = new FixProjectSourceLevel(type, minVersion, platformVersion);
900
                final DialogDescriptor dd = new DialogDescriptor(changeVersion, LBL_ResolveJDKVersion(ProjectUtils.getInformation(project).getDisplayName()));
901
                if (DialogDisplayer.getDefault().notify(dd) == DialogDescriptor.OK_OPTION) {
902
                    final Callable<ProjectProblemsProvider.Result> resultFnc =
903
                            new Callable<Result>() {
904
                        @Override
905
                        public Result call() throws Exception {
906
                            if (changeVersion.isDowngradeLevel()) {
907
                                final EditableProperties props = helper.getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
908
                                for (String p : invalidVersionProps) {
909
                                    props.put(p, platformVersion.toString());
910
                                }
911
                                helper.putProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH, props);
912
                                ProjectManager.getDefault().saveProject(FileOwnerQuery.getOwner(helper.getProjectDirectory()));
913
                                return ProjectProblemsProvider.Result.create(ProjectProblemsProvider.Status.RESOLVED);
914
                            } else {
915
                                final JavaPlatform jp = changeVersion.getSelectedPlatform();
916
                                if (jp != null) {
917
                                    final String antName = jp.getProperties().get("platform.ant.name"); //NOI18N
918
                                    if (antName != null) {
919
                                        final EditableProperties props = helper.getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
920
                                        props.setProperty(platformProp, antName);
921
                                        helper.putProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH, props);
922
                                        if (hook != null) {
923
                                            hook.run();
924
                                        }
925
                                        ProjectManager.getDefault().saveProject(project);
926
                                        return ProjectProblemsProvider.Result.create(ProjectProblemsProvider.Status.RESOLVED);
927
                                    }
928
                                }
929
                            }
930
                            return ProjectProblemsProvider.Result.create(ProjectProblemsProvider.Status.UNRESOLVED);
931
                        }
932
                    };
933
                    final RunnableFuture<Result> result = new FutureTask<Result>(resultFnc);
934
                    RP.post(result);
935
                    return result;
936
                }
937
            }
938
            return new Done(
939
                    Result.create(ProjectProblemsProvider.Status.UNRESOLVED));
940
        }
941
942
        @Override
943
        public boolean equals(Object other) {
944
            if (!(other instanceof SourceTargetResolver)) {
945
                return false;
946
            }
947
            return true;
948
        }
949
950
        @Override
951
        public int hashCode() {
952
            return 17;
953
954
        }
955
956
957
    }
958
959
    private static final class Done implements Future<ProjectProblemsProvider.Result> {
960
961
        private final ProjectProblemsProvider.Result result;
962
963
        Done(@NonNull final ProjectProblemsProvider.Result result) {
964
            Parameters.notNull("result", result);   //NOI18N
965
            this.result = result;
966
        }
967
968
        @Override
969
        public boolean cancel(boolean mayInterruptIfRunning) {
970
            return false;
971
        }
972
973
        @Override
974
        public boolean isCancelled() {
975
            return false;
976
        }
977
978
        @Override
979
        public boolean isDone() {
980
            return true;
981
        }
982
983
        @Override
984
        public ProjectProblemsProvider.Result get() throws InterruptedException, ExecutionException {
985
            return result;
986
        }
987
988
        @Override
989
        public ProjectProblemsProvider.Result get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
990
            return get();
991
        }
992
993
    }
994
    //</editor-fold>
995
996
    //<editor-fold defaultstate="collapsed" desc="ProjectProblemsProvider implementations">
997
    private static final class ReferenceProblemProviderImpl implements ProjectProblemsProvider, PropertyChangeListener {
998
999
        private final PropertyChangeSupport support = new PropertyChangeSupport(this);
1000
1001
        private final Object problemsLock = new Object();
1002
        //@GuardedBy("problemsLock")
1003
        private Collection<? extends ProjectProblem> problems;
1004
        //@GuardedBy("problemsLock")
1005
        private long eventId;
1006
        private final AtomicBoolean listenersInitialized = new AtomicBoolean();
1007
1008
        private final AntProjectHelper helper;
1009
        private final PropertyEvaluator eval;
1010
        private final ReferenceHelper refHelper;
1011
        private final String[] refProps;
1012
        private final String[] platformProps;
1013
1014
        private Map<URL,Object[]> activeLibManLocs;
1015
1016
1017
        ReferenceProblemProviderImpl(
1018
                @NonNull final AntProjectHelper helper,
1019
                @NonNull final PropertyEvaluator eval,
1020
                @NonNull final ReferenceHelper refHelper,
1021
                @NonNull final String[] refProps,
1022
                @NonNull final String[] platformProps) {
1023
            assert helper != null;
1024
            assert eval != null;
1025
            assert refHelper != null;
1026
            assert refProps != null;
1027
            assert platformProps != null;
1028
            this.helper = helper;
1029
            this.eval = eval;
1030
            this.refHelper = refHelper;
1031
            this.refProps = Arrays.copyOf(refProps, refProps.length);
1032
            this.platformProps = Arrays.copyOf(platformProps, platformProps.length);
1033
        }
1034
1035
        @Override
1036
        public void addPropertyChangeListener(@NonNull final PropertyChangeListener listener) {
1037
            Parameters.notNull("listener", listener);   //NOI18N
1038
            support.addPropertyChangeListener(listener);
1039
        }
1040
1041
        @Override
1042
        public void removePropertyChangeListener(@NonNull final PropertyChangeListener listener) {
1043
            Parameters.notNull("listener", listener);   //NOI18N
1044
            support.removePropertyChangeListener(listener);
1045
        }
1046
1047
        @Override
1048
        public Collection<? extends ProjectProblem> getProblems() {
1049
            Collection<? extends ProjectProblem> curProblems;
1050
            long curEventId;
1051
            synchronized (problemsLock) {
1052
                curProblems = problems;
1053
                curEventId = eventId;
1054
            }
1055
            if (curProblems != null) {
1056
                return curProblems;
1057
            }
1058
            curProblems = ProjectManager.mutex().readAccess(
1059
                new Mutex.Action<Collection<? extends ProjectProblem>>(){
1060
                    @Override
1061
                    public Collection<? extends ProjectProblem> run() {
1062
                        final Set<ProjectProblem> newProblems = new LinkedHashSet<ProjectProblem>();
1063
                        newProblems.addAll(getReferenceProblems(helper,eval,refHelper,refProps,false));
1064
                        newProblems.addAll(getPlatformProblems(eval,platformProps,false));
1065
                        return Collections.unmodifiableSet(newProblems);
1066
                    }
1067
                });
1068
            synchronized (problemsLock){
1069
                if (curEventId == eventId) {
1070
                    //No canonical mapping needed
1071
                    problems = curProblems;
1072
                } else if (problems != null) {
1073
                    curProblems = problems;
1074
                }
1075
            }
1076
            assert curProblems != null;
1077
            return curProblems;
1078
        }
1079
1080
        @Override
1081
        public void propertyChange(PropertyChangeEvent evt) {
1082
            if (LibraryManager.PROP_OPEN_LIBRARY_MANAGERS.equals(evt.getPropertyName())) {
1083
                addLibraryManagerListener();
1084
            }
1085
            synchronized (problemsLock) {
1086
                problems = null;
1087
                eventId++;
1088
            }
1089
            support.firePropertyChange(PROP_PROBLEMS,null,null);
1090
        }
1091
1092
        void attachListeners() {
1093
            if (listenersInitialized.compareAndSet(false, true)) {
1094
                eval.addPropertyChangeListener(this);
1095
                JavaPlatformManager.getDefault().addPropertyChangeListener(WeakListeners.propertyChange(this, JavaPlatformManager.getDefault()));
1096
                LibraryManager.addOpenManagersPropertyChangeListener(new OpenManagersWeakListener(this));
1097
                addLibraryManagerListener();
1098
            } else {
1099
                throw new IllegalStateException();
1100
            }
1101
        }
1102
1103
        private void addLibraryManagerListener() {
1104
            final Map<URL,Object[]> oldLMs;
1105
            final boolean attachToDefault;
1106
            synchronized (this) {
1107
                attachToDefault = activeLibManLocs == null;
1108
                if (attachToDefault) {
1109
                    activeLibManLocs = new HashMap<URL,Object[]>();
1110
                }
1111
                oldLMs = new HashMap<URL,Object[]>(activeLibManLocs);
1112
            }
1113
            if (attachToDefault) {
1114
                final LibraryManager manager = LibraryManager.getDefault();
1115
                manager.addPropertyChangeListener(WeakListeners.propertyChange(this, manager));
1116
            }
1117
            final Collection<? extends LibraryManager> managers = LibraryManager.getOpenManagers();
1118
            final Map<URL,LibraryManager> managerByLocation = new HashMap<URL, LibraryManager>();
1119
            for (LibraryManager manager : managers) {
1120
                final URL url = manager.getLocation();
1121
                if (url != null) {
1122
                    managerByLocation.put(url, manager);
1123
                }
1124
            }
1125
            final HashMap<URL,Object[]> toRemove = new HashMap<URL,Object[]>(oldLMs);
1126
            toRemove.keySet().removeAll(managerByLocation.keySet());
1127
            for (Object[] pair : toRemove.values()) {
1128
                ((LibraryManager)pair[0]).removePropertyChangeListener((PropertyChangeListener)pair[1]);
1129
            }
1130
            managerByLocation.keySet().removeAll(oldLMs.keySet());
1131
            final HashMap<URL,Object[]> toAdd = new HashMap<URL,Object[]>();
1132
            for (Map.Entry<URL,LibraryManager> e : managerByLocation.entrySet()) {
1133
                final LibraryManager manager = e.getValue();
1134
                final PropertyChangeListener listener = WeakListeners.propertyChange(this, manager);
1135
                manager.addPropertyChangeListener(listener);
1136
                toAdd.put(e.getKey(), new Object[] {manager, listener});
1137
            }
1138
            synchronized (this) {
1139
                activeLibManLocs.keySet().removeAll(toRemove.keySet());
1140
                activeLibManLocs.putAll(toAdd);
1141
            }
1142
        }
1143
1144
    }
1145
1146
    private static final class PlatformVersionProblemProviderImpl implements ProjectProblemsProvider, PropertyChangeListener {
1147
1148
        private final PropertyChangeSupport support = new PropertyChangeSupport(this);
1149
        private final Object problemsLock = new Object();
1150
        //@GuardedBy("problemsLock")
1151
        private Collection<? extends ProjectProblem> problems;
1152
        //@GuardedBy("problemsLock")
1153
        private long eventId;
1154
        private final AtomicBoolean listenersInitialized = new AtomicBoolean();
1155
1156
        private final AntProjectHelper helper;
1157
        private final PropertyEvaluator eval;
1158
        private final Runnable hook;
1159
        private final String platformType;
1160
        private final String platformProp;
1161
        private final Set<String> versionProps;
1162
1163
        PlatformVersionProblemProviderImpl(
1164
                @NonNull final AntProjectHelper helper,
1165
                @NonNull final PropertyEvaluator eval,
1166
                @NullAllowed final Runnable hook,
1167
                @NonNull final String platformType,
1168
                @NonNull final String platformProp,
1169
                @NonNull final String... versionProps) {
1170
            assert helper != null;
1171
            assert eval != null;
1172
            assert platformType != null;
1173
            assert platformProp != null;
1174
            assert versionProps != null;
1175
            this.helper = helper;
1176
            this.eval = eval;
1177
            this.hook = hook;
1178
            this.platformType = platformType;
1179
            this.platformProp = platformProp;
1180
            this.versionProps = new HashSet<String>(Arrays.asList(versionProps));
1181
        }
1182
1183
        @Override
1184
        public void addPropertyChangeListener(@NonNull final PropertyChangeListener listener) {
1185
            Parameters.notNull("listener", listener);   //NOI18N
1186
            support.addPropertyChangeListener(listener);
1187
        }
1188
1189
        @Override
1190
        public void removePropertyChangeListener(@NonNull final PropertyChangeListener listener) {
1191
            Parameters.notNull("listener", listener);   //NOI18N
1192
            support.removePropertyChangeListener(listener);
1193
        }
1194
1195
        @Override
1196
        @NbBundle.Messages({
1197
            "LBL_Invalid_JDK_Version=Invalid Java Platform Version",
1198
            "HINT_Invalid_JDK_Vernsion=The active project platform is an older version than it's required by project source/binary format."
1199
        })
1200
        public Collection<? extends ProjectProblem> getProblems() {
1201
            Collection<? extends ProjectProblem> curProblems;
1202
            long curEventId;
1203
            synchronized (problemsLock) {
1204
                curProblems = problems;
1205
                curEventId = eventId;
1206
            }
1207
            if (curProblems != null) {
1208
                return curProblems;
1209
            }
1210
            curProblems = ProjectManager.mutex().readAccess(
1211
                new Mutex.Action<Collection<? extends ProjectProblem>>() {
1212
                    @Override
1213
                    public Collection<? extends ProjectProblem> run() {
1214
                        final JavaPlatform activePlatform = getActivePlatform();
1215
                        final SpecificationVersion platformVersion = activePlatform == null ?
1216
                            null:
1217
                            activePlatform.getSpecification().getVersion();
1218
                        final Collection<String> invalidVersionProps = new ArrayList<String>(versionProps.size());
1219
                        SpecificationVersion minVersion = getInvalidJdkVersion(
1220
                                platformVersion,
1221
                                invalidVersionProps);
1222
                        return minVersion != null ?
1223
                            Collections.singleton(ProjectProblem.createError(
1224
                                LBL_Invalid_JDK_Version(),
1225
                                HINT_Invalid_JDK_Vernsion(),
1226
                                new SourceTargetResolver(
1227
                                    helper,
1228
                                    hook,
1229
                                    platformType,
1230
                                    platformProp,
1231
                                    invalidVersionProps,
1232
                                    minVersion,
1233
                                    platformVersion))) :
1234
                            Collections.<ProjectProblem>emptySet();
1235
                    }
1236
            });
1237
            synchronized (problemsLock) {
1238
                if (curEventId == eventId) {
1239
                    //No canonical mapping needed
1240
                    problems = curProblems;
1241
                } else if (problems != null) {
1242
                    curProblems = problems;
1243
                }
1244
            }
1245
            assert curProblems != null;
1246
            return curProblems;
1247
        }
1248
1249
        @Override
1250
        public void propertyChange(PropertyChangeEvent evt) {
1251
            final String propName = evt.getPropertyName();
1252
            if (propName == null || platformProp.equals(propName) || versionProps.contains(propName)) {
1253
                synchronized (problemsLock) {
1254
                    problems = null;
1255
                    eventId++;
1256
                }
1257
                support.firePropertyChange(PROP_PROBLEMS,null,null);
1258
            }
1259
        }
1260
1261
        void attachListeners() {
1262
            if (listenersInitialized.compareAndSet(false, true)) {
1263
                eval.addPropertyChangeListener(this);
1264
            } else {
1265
                throw new IllegalStateException();
1266
            }
1267
        }
1268
1269
        /**
1270
         * Gets minimal required JDK version or null if the project platform
1271
         * satisfy the required JDK versions.
1272
         * @return The minimal {@link SpecificationVersion} of platform or null.
1273
         */
1274
        @CheckForNull
1275
        private SpecificationVersion getInvalidJdkVersion (
1276
                @NullAllowed final SpecificationVersion platformVersion,
1277
                @NonNull final Collection<? super String> invalidVersionProps) {
1278
            SpecificationVersion minVersion = null;
1279
            if (platformVersion != null) {
1280
                for (String vp : versionProps) {
1281
                    final String value = this.eval.getProperty(vp);
1282
                    if (value == null || value.isEmpty()) {
1283
                        continue;
1284
                    }
1285
                    try {
1286
                        final SpecificationVersion vpVersion = new SpecificationVersion (value);
1287
                        if (vpVersion.compareTo(platformVersion) > 0) {
1288
                            invalidVersionProps.add(vp);
1289
                            minVersion = max(minVersion,vpVersion);
1290
                        }
1291
                    } catch (NumberFormatException nfe) {
1292
                        LOG.log(
1293
                            Level.WARNING,
1294
                            "Property: {0} holds non valid version: {1}",  //NOI18N
1295
                            new Object[]{
1296
                                vp,
1297
                                value
1298
                            });
1299
                    }
1300
                }
1301
            }
1302
            return minVersion;
1303
        }
1304
1305
        @CheckForNull
1306
        private SpecificationVersion max (
1307
            @NullAllowed final SpecificationVersion a,
1308
            @NullAllowed final SpecificationVersion b) {
1309
            if (a == null) {
1310
                return b;
1311
            } else if (b == null) {
1312
                return a;
1313
            } else if (a.compareTo(b)>=0) {
1314
                return a;
1315
            } else {
1316
                return b;
1317
            }
1318
        }
1319
1320
        @CheckForNull
1321
        private JavaPlatform getActivePlatform() {
1322
            final String activePlatformId = this.eval.getProperty(platformProp);
1323
            final JavaPlatformManager pm = JavaPlatformManager.getDefault();
1324
            if (activePlatformId == null) {
1325
                return pm.getDefaultPlatform();
1326
            }
1327
            final JavaPlatform[] installedPlatforms = pm.getPlatforms(
1328
                    null,
1329
                    new Specification(platformType, null));
1330
            for (JavaPlatform javaPlatform : installedPlatforms) {
1331
                final String antName = javaPlatform.getProperties().get("platform.ant.name"); //NOI18N
1332
                if (activePlatformId.equals(antName)) {
1333
                    return javaPlatform;
1334
                }
1335
            }
1336
            return null;
1337
        }
1338
    }
1339
1340
    private static class OpenManagersWeakListener extends WeakReference<PropertyChangeListener> implements Runnable, PropertyChangeListener {
1341
1342
        public OpenManagersWeakListener(final PropertyChangeListener listener) {
1343
            super(listener, Utilities.activeReferenceQueue());
1344
        }
1345
1346
        @Override
1347
        public void run() {
1348
            LibraryManager.removeOpenManagersPropertyChangeListener(this);
1349
        }
1350
1351
        @Override
1352
        public void propertyChange(PropertyChangeEvent evt) {
1353
            final PropertyChangeListener listener = get();
1354
            if (listener != null) {
1355
                listener.propertyChange(evt);
1356
            }
1357
        }
1358
1359
    }
1360
    //</editor-fold>
1361
}
(-)a/java.project/src/org/netbeans/spi/java/project/support/ui/BrokenReferencesSupport.java (-155 / +179 lines)
Lines 44-77 Link Here
44
44
45
package org.netbeans.spi.java.project.support.ui;
45
package org.netbeans.spi.java.project.support.ui;
46
46
47
import javax.swing.event.ChangeEvent;
48
import javax.swing.event.ChangeListener;
49
import java.awt.Dialog;
50
import java.io.IOException;
51
import java.util.concurrent.Callable;
47
import java.util.concurrent.Callable;
52
import javax.swing.JButton;
53
import org.netbeans.api.annotations.common.CheckForNull;
48
import org.netbeans.api.annotations.common.CheckForNull;
54
import org.netbeans.api.annotations.common.NonNull;
49
import org.netbeans.api.annotations.common.NonNull;
55
import org.netbeans.api.annotations.common.NullAllowed;
50
import org.netbeans.api.annotations.common.NullAllowed;
51
import org.netbeans.api.project.FileOwnerQuery;
56
import org.netbeans.api.project.Project;
52
import org.netbeans.api.project.Project;
57
import org.netbeans.api.project.ProjectManager;
53
import org.netbeans.api.project.ProjectManager;
58
import org.netbeans.api.project.ProjectUtils;
59
import org.netbeans.api.project.libraries.Library;
54
import org.netbeans.api.project.libraries.Library;
60
import org.netbeans.modules.java.project.BrokenReferencesAlertPanel;
55
import org.netbeans.modules.java.project.ProjectProblemsProviders;
61
import org.netbeans.modules.java.project.BrokenReferencesCustomizer;
62
import org.netbeans.modules.java.project.BrokenReferencesModel;
63
import org.netbeans.modules.java.project.JavaProjectSettings;
64
import org.netbeans.spi.project.support.ant.AntProjectHelper;
56
import org.netbeans.spi.project.support.ant.AntProjectHelper;
65
import org.netbeans.spi.project.support.ant.PropertyEvaluator;
57
import org.netbeans.spi.project.support.ant.PropertyEvaluator;
66
import org.netbeans.spi.project.support.ant.ReferenceHelper;
58
import org.netbeans.spi.project.support.ant.ReferenceHelper;
67
import org.openide.DialogDescriptor;
68
import org.openide.DialogDisplayer;
69
import org.openide.util.Exceptions;
70
import org.openide.util.NbBundle.Messages;
71
import org.openide.util.Parameters;
59
import org.openide.util.Parameters;
72
import org.openide.util.RequestProcessor;
60
import org.netbeans.spi.project.ui.ProjectProblemsProvider;
73
import org.openide.windows.WindowManager;
61
import org.netbeans.api.project.ui.ProjectProblems;
74
import static org.netbeans.spi.java.project.support.ui.Bundle.*;
62
import org.openide.filesystems.FileObject;
63
import org.openide.util.Lookup;
64
import org.openide.util.lookup.Lookups;
65
import org.openide.util.lookup.ProxyLookup;
75
66
76
/**
67
/**
77
 * Support for managing broken project references. Project freshly checkout from
68
 * Support for managing broken project references. Project freshly checkout from
Lines 86-106 Link Here
86
 * customizer.
77
 * customizer.
87
 * </div>
78
 * </div>
88
 * @author David Konecny
79
 * @author David Konecny
80
 * @author Tomas Zezula
89
 */
81
 */
90
public class BrokenReferencesSupport {
82
public class BrokenReferencesSupport {
91
83
        
92
    private static final RequestProcessor RP = new RequestProcessor(BrokenReferencesSupport.class);
93
94
    private static final boolean suppressBrokenRefAlert = Boolean.getBoolean("BrokenReferencesSupport.suppressBrokenRefAlert"); //NOI18N
95
    
96
    /** Is Broken References alert shown now? */
97
    private static BrokenReferencesModel.Context context;
98
99
    private static RequestProcessor.Task rpTask;
100
101
    /** Timeout within which request to show alert will be ignored. */
102
    private static int BROKEN_ALERT_TIMEOUT = 1000;
103
    
104
    private BrokenReferencesSupport() {}
84
    private BrokenReferencesSupport() {}
105
85
106
    /**
86
    /**
Lines 118-132 Link Here
118
     *    always exists.
98
     *    always exists.
119
     * @return true if some problem was found and it is necessary to give
99
     * @return true if some problem was found and it is necessary to give
120
     *    user a chance to fix them
100
     *    user a chance to fix them
101
     *
102
     * @deprecated Add {@link ProjectProblemsProvider} into project lookup,
103
     * use {@link BrokenReferencesSupport#createReferenceProblemsProvider} as default
104
     * implementation, and use {@link ProjectProblems#isBroken}
121
     */
105
     */
106
    @Deprecated
122
    public static boolean isBroken(AntProjectHelper projectHelper, 
107
    public static boolean isBroken(AntProjectHelper projectHelper, 
123
            ReferenceHelper referenceHelper, String[] properties, String[] platformProperties) {
108
            ReferenceHelper referenceHelper, String[] properties, String[] platformProperties) {
124
        Parameters.notNull("projectHelper", projectHelper);             //NOI18N
109
        Parameters.notNull("projectHelper", projectHelper);             //NOI18N
125
        Parameters.notNull("referenceHelper", referenceHelper);         //NOI18N
110
        Parameters.notNull("referenceHelper", referenceHelper);         //NOI18N
126
        Parameters.notNull("properties", properties);                   //NOI18N
111
        Parameters.notNull("properties", properties);                   //NOI18N
127
        Parameters.notNull("platformProperties", platformProperties);   //NOI18N
112
        Parameters.notNull("platformProperties", platformProperties);   //NOI18N
128
        return BrokenReferencesModel.isBroken(projectHelper, referenceHelper,
113
        return ProjectProblems.isBroken(ProjectDecorator.create(
129
            projectHelper.getStandardPropertyEvaluator(), properties, platformProperties);
114
                projectHelper,
115
                referenceHelper,
116
                projectHelper.getStandardPropertyEvaluator(),
117
                properties,
118
                platformProperties,
119
                true));
130
    }
120
    }
131
    
121
    
132
    /**
122
    /**
Lines 143-181 Link Here
143
     *    platform is expected to be "default_platform" and this platform
133
     *    platform is expected to be "default_platform" and this platform
144
     *    always exists.
134
     *    always exists.
145
     * @see LibraryDefiner
135
     * @see LibraryDefiner
136
     *
137
     * @deprecated Add {@link ProjectProblemsProvider} into project lookup,
138
     * use {@link BrokenReferencesSupport#createReferenceProblemsProvider} as default
139
     * implementation, and use {@link ProjectProblems#showCustomizer}
146
     */
140
     */
147
    @Messages({
141
    @Deprecated
148
        "LBL_BrokenLinksCustomizer_Close=Close",
149
        "ACSD_BrokenLinksCustomizer_Close=N/A",
150
        "LBL_BrokenLinksCustomizer_Title=Resolve Reference Problems - \"{0}\" Project"
151
    })
152
    public static void showCustomizer(AntProjectHelper projectHelper, 
142
    public static void showCustomizer(AntProjectHelper projectHelper, 
153
            ReferenceHelper referenceHelper, String[] properties, String[] platformProperties) {
143
            ReferenceHelper referenceHelper, String[] properties, String[] platformProperties) {
154
        BrokenReferencesModel model = new BrokenReferencesModel(projectHelper, referenceHelper, properties, platformProperties);
144
        ProjectProblems.showCustomizer(ProjectDecorator.create(
155
        BrokenReferencesCustomizer customizer = new BrokenReferencesCustomizer(model);
145
                projectHelper,
156
        JButton close = new JButton (LBL_BrokenLinksCustomizer_Close()); // NOI18N
146
                referenceHelper,
157
        close.getAccessibleContext ().setAccessibleDescription (ACSD_BrokenLinksCustomizer_Close()); // NOI18N
147
                projectHelper.getStandardPropertyEvaluator(),
158
        String projectDisplayName = "???"; // NOI18N
148
                properties,
159
        try {
149
                platformProperties,
160
            Project project = ProjectManager.getDefault().findProject(projectHelper.getProjectDirectory());
150
                false));
161
            if (project != null) {
162
                projectDisplayName = ProjectUtils.getInformation(project).getDisplayName();
163
            }
164
        } catch (IOException e) {
165
            Exceptions.printStackTrace(e);
166
        }
167
        DialogDescriptor dd = new DialogDescriptor(customizer, 
168
            LBL_BrokenLinksCustomizer_Title(projectDisplayName), // NOI18N
169
            true, new Object[] {close}, close, DialogDescriptor.DEFAULT_ALIGN, null, null);
170
        Dialog dlg = null;
171
        try {
172
            dlg = DialogDisplayer.getDefault().createDialog(dd);
173
            dlg.setVisible(true);
174
        } finally {
175
            if (dlg != null) {
176
                dlg.dispose();
177
            }
178
        }
179
    }
151
    }
180
152
181
    /**
153
    /**
Lines 184-192 Link Here
184
     * the project opening, and it will take care about showing message box only
156
     * the project opening, and it will take care about showing message box only
185
     * once for several subsequent calls during a timeout.
157
     * once for several subsequent calls during a timeout.
186
     * The alert box has also "show this warning again" check box.
158
     * The alert box has also "show this warning again" check box.
159
     *
160
     * @deprecated Add {@link ProjectProblemsProvider} into project lookup,
161
     * use {@link BrokenReferencesSupport#createReferenceProblemsProvider} as default
162
     * implementation, and use {@link ProjectProblems#showAlert}
187
     */
163
     */
164
    @Deprecated
188
    public static void showAlert() {
165
    public static void showAlert() {
189
        showAlertImpl(null);
166
        ProjectProblems.showCustomizer(ProjectDecorator.create());
190
    }
167
    }
191
168
192
    /**
169
    /**
Lines 209-216 Link Here
209
     *    platform is expected to be "default_platform" and this platform
186
     *    platform is expected to be "default_platform" and this platform
210
     *    always exists.
187
     *    always exists.
211
     * @since 1.37
188
     * @since 1.37
189
     *
190
     * @deprecated Add {@link ProjectProblemsProvider} into project lookup,
191
     * use {@link BrokenReferencesSupport#createReferenceProblemsProvider} as default
192
     * implementation, and use {@link ProjectProblems#showAlert}
212
     */
193
     */
213
    
194
    @Deprecated
214
    public static void showAlert(
195
    public static void showAlert(
215
            @NonNull final AntProjectHelper projectHelper,
196
            @NonNull final AntProjectHelper projectHelper,
216
            @NonNull final ReferenceHelper referenceHelper,
197
            @NonNull final ReferenceHelper referenceHelper,
Lines 222-320 Link Here
222
        Parameters.notNull("evaluator", evaluator);                     //NOI18N
203
        Parameters.notNull("evaluator", evaluator);                     //NOI18N
223
        Parameters.notNull("properties", properties);                   //NOI18N
204
        Parameters.notNull("properties", properties);                   //NOI18N
224
        Parameters.notNull("platformProperties", platformProperties);   //NOI18N
205
        Parameters.notNull("platformProperties", platformProperties);   //NOI18N
225
        showAlertImpl(new BrokenReferencesModel.BrokenProject(projectHelper, referenceHelper, evaluator, properties, platformProperties));
206
        ProjectProblems.showAlert(ProjectDecorator.create(
207
                projectHelper,
208
                referenceHelper,
209
                evaluator,
210
                properties,
211
                platformProperties,
212
                false));
226
    }
213
    }
227
214
215
    /**
216
     * Creates a {@link ProjectProblemsProvider} creating broken references
217
     * problems.
218
     * @param projectHelper AntProjectHelper associated with the project.
219
     * @param referenceHelper ReferenceHelper associated with the project.
220
     * @param evaluator the {@link PropertyEvaluator} used to resolve broken references
221
     * @param properties array of property names which values hold
222
     *    references which may be broken. For example for J2SE project
223
     *    the property names will be: "javac.classpath", "run.classpath", etc.
224
     * @param platformProperties array of property names which values hold
225
     *    name of the platform(s) used by the project. These platforms will be
226
     *    checked for existence. For example for J2SE project the property
227
     *    name is one and it is "platform.active". The name of the default
228
     *    platform is expected to be "default_platform" and this platform
229
     *    always exists.
230
     * @return the {@link ProjectProblemsProvider} to be laced into project lookup.
231
     * @see ProjectProblemsProvider
232
     * @since 1.48
233
     */
234
    public static ProjectProblemsProvider createReferenceProblemsProvider(
235
            @NonNull final AntProjectHelper projectHelper,
236
            @NonNull final ReferenceHelper referenceHelper,
237
            @NonNull final PropertyEvaluator evaluator,
238
            @NonNull final String[] properties,
239
            @NonNull final String[] platformProperties) {
240
        Parameters.notNull("projectHelper", projectHelper);             //NOI18N
241
        Parameters.notNull("referenceHelper", referenceHelper);         //NOI18N
242
        Parameters.notNull("evaluator", evaluator);                     //NOI18N
243
        Parameters.notNull("properties", properties);                   //NOI18N
244
        Parameters.notNull("platformProperties", platformProperties);   //NOI18N
245
        return ProjectProblemsProviders.createReferenceProblemProvider(
246
                projectHelper,
247
                referenceHelper,
248
                evaluator,
249
                properties,
250
                platformProperties);
251
    }
228
252
229
    @Messages({
253
    /**
230
        "CTL_Broken_References_Resolve=Resolve Problems...",
254
     * Creates a {@link ProjectProblemsProvider} creating wrong Java platform
231
        "AD_Broken_References_Resolve=N/A",
255
     * version problems.
232
        "CTL_Broken_References_Close=Close",
256
     * @param projectHelper AntProjectHelper associated with the project.
233
        "AD_Broken_References_Close=N/A",
257
     * @param evaluator the {@link PropertyEvaluator} used to resolve broken references
234
        "MSG_Broken_References_Title=Open Project",
258
     * @param postPlatformSetHook called by problem resolution after the platform property has changed
235
        "LBL_Broken_References_Resolve_Panel_Close=Close",
259
     * to a new platform. The project type can do project specific changes like updating project.xml file.
236
        "AD_Broken_References_Resolve_Panel_Close=N/A",
260
     * The hook is called under {@link ProjectManager#mutex} write access before the project is saved.
237
        "LBL_Broken_References_Resolve_Panel_Title=Resolve Reference Problems"
261
     * @param platformType the type of platform, for example j2se
238
    })
262
     * @param platformProperty a property holding the active platform id.
239
    private static synchronized void showAlertImpl(@NullAllowed final BrokenReferencesModel.BrokenProject broken) {        
263
     * @param versionProperties array of property names which values hold the source,
240
        if (!JavaProjectSettings.isShowAgainBrokenRefAlert() || suppressBrokenRefAlert) {
264
     * target level.
241
            return;
265
     * @return {@link ProjectProblemsProvider} to be laced into project lookup.
242
        } else if (context == null) {
266
     * 
243
            assert rpTask == null;
267
     * @see ProjectProblemsProvider
244
268
     * @since 1.48
245
            final Runnable task = new Runnable() {
269
     */
246
                public @Override void run() {
270
    public static ProjectProblemsProvider createPlatformVersionProblemProvider(
247
                    final BrokenReferencesModel.Context ctx;
271
            @NonNull final AntProjectHelper projectHelper,
248
                    synchronized (BrokenReferencesSupport.class) {
272
            @NonNull final PropertyEvaluator evaluator,
249
                        rpTask = null;
273
            @NullAllowed final Runnable postPlatformSetHook,
250
                        ctx = context;
274
            @NonNull final String platformType,
251
                    }
275
            @NonNull final String platformProperty,
252
                    if (ctx == null) {
276
            @NonNull final String... versionProperties) {
253
                        return;
277
        Parameters.notNull("projectHelper", projectHelper);             //NOI18N
254
                    }
278
        Parameters.notNull("evaluator", evaluator);                     //NOI18N
255
                    try {
279
        Parameters.notNull("platformProperty", platformProperty);       //NOI18N
256
                        final JButton resolveOption = new JButton(CTL_Broken_References_Resolve());
280
        Parameters.notNull("versionProperties", versionProperties);     //NOI18N
257
                        resolveOption.getAccessibleContext().setAccessibleDescription(AD_Broken_References_Resolve());
281
        return ProjectProblemsProviders.createPlatformVersionProblemProvider(
258
                        JButton closeOption = new JButton (CTL_Broken_References_Close());
282
                projectHelper,
259
                        closeOption.getAccessibleContext().setAccessibleDescription(AD_Broken_References_Close());
283
                evaluator,
260
                        DialogDescriptor dd = new DialogDescriptor(new BrokenReferencesAlertPanel(),
284
                postPlatformSetHook,
261
                            MSG_Broken_References_Title(),
285
                platformType,
262
                            true,
286
                platformProperty,
263
                            new Object[] {resolveOption, closeOption},
287
                versionProperties);
264
                            closeOption,
288
    }
265
                            DialogDescriptor.DEFAULT_ALIGN,
289
        
266
                            null,
267
                            null);
268
                        dd.setMessageType(DialogDescriptor.WARNING_MESSAGE);
269
                        ctx.addChangeListener(new ChangeListener() {
270
                            @Override
271
                            public void stateChanged(ChangeEvent e) {
272
                                resolveOption.setVisible(!ctx.isEmpty());
273
                            }
274
                        });
275
                        resolveOption.setVisible(!ctx.isEmpty());
276
                        if (DialogDisplayer.getDefault().notify(dd) == resolveOption) {
277
                            final BrokenReferencesModel model = new BrokenReferencesModel(ctx, true);
278
                            final BrokenReferencesCustomizer customizer = new BrokenReferencesCustomizer(model);
279
                            JButton close = new JButton (Bundle.LBL_Broken_References_Resolve_Panel_Close());
280
                            close.getAccessibleContext ().setAccessibleDescription (Bundle.AD_Broken_References_Resolve_Panel_Close());
281
                            dd = new DialogDescriptor(customizer,
282
                                Bundle.LBL_Broken_References_Resolve_Panel_Title(),
283
                                true,
284
                                new Object[] {closeOption},
285
                                closeOption,
286
                                DialogDescriptor.DEFAULT_ALIGN,
287
                                null,
288
                                null);
289
                            DialogDisplayer.getDefault().notify(dd);
290
                        }
291
                    } finally {
292
                        synchronized (BrokenReferencesSupport.class) {
293
                            //Clean seen references and start from empty list
294
                            context = null;
295
                        }
296
                    }
297
                }
298
            };
299
300
            context = new BrokenReferencesModel.Context();
301
            rpTask = RP.create(new Runnable() {
302
                @Override
303
                public void run() {
304
                    WindowManager.getDefault().invokeWhenUIReady(task);
305
                }
306
            });
307
        }
308
309
        assert context != null;        
310
        if (broken != null) {
311
            context.offer(broken);
312
        }
313
        if (rpTask != null) {
314
            //Not yet shown, move
315
            rpTask.schedule(BROKEN_ALERT_TIMEOUT);
316
        }
317
    }            
318
    /**
290
    /**
319
     * Service which may be {@linkplain ServiceProvider registered} to download remote libraries or otherwise define them.
291
     * Service which may be {@linkplain ServiceProvider registered} to download remote libraries or otherwise define them.
320
     * @since org.netbeans.modules.java.project/1 1.35
292
     * @since org.netbeans.modules.java.project/1 1.35
Lines 329-333 Link Here
329
        @CheckForNull Callable<Library> missingLibrary(String name);
301
        @CheckForNull Callable<Library> missingLibrary(String name);
330
302
331
    }
303
    }
332
    
304
305
306
    private static final class ProjectDecorator implements Project {
307
308
        private final Project delegate;
309
        private final Lookup lookup;
310
311
        private ProjectDecorator(
312
                @NonNull final Project delegate,
313
                @NonNull final ProjectProblemsProvider  provider) {
314
            assert delegate != null;
315
            this.delegate = delegate;
316
            this.lookup = new ProxyLookup(delegate.getLookup(),Lookups.singleton(provider));
317
        }
318
319
        private ProjectDecorator() {
320
            this.delegate = null;
321
            this.lookup = Lookup.EMPTY;
322
        }
323
324
        @Override
325
        public FileObject getProjectDirectory() {
326
            return delegate.getProjectDirectory();
327
        }
328
329
        @Override
330
        public Lookup getLookup() {
331
            return lookup;
332
        }        
333
334
        static ProjectDecorator create(
335
            @NonNull final AntProjectHelper projectHelper,
336
            @NonNull final ReferenceHelper referenceHelper,
337
            @NonNull final PropertyEvaluator evaluator,
338
            @NonNull final String[] properties,
339
            @NonNull final String[] platformProperties,
340
            final boolean abortAfterFirstProblem) {
341
            final Project prj = FileOwnerQuery.getOwner(projectHelper.getProjectDirectory());
342
            return new ProjectDecorator(
343
                prj,
344
                ProjectProblemsProviders.createReferenceProblemProvider(
345
                    projectHelper,
346
                    referenceHelper,
347
                    evaluator,
348
                    properties,
349
                    platformProperties));
350
        }
351
352
        static ProjectDecorator create() {
353
            return new ProjectDecorator();
354
        }
355
    }
356
            
333
}
357
}
(-)a/projectui/src/org/netbeans/modules/project/ui/problems/BrokenProjectActionFactory.java (+109 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2012 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 2012 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.modules.project.ui.problems;
43
44
import java.awt.event.ActionEvent;
45
import java.util.Collection;
46
import javax.swing.AbstractAction;
47
import javax.swing.Action;
48
import org.netbeans.api.annotations.common.NonNull;
49
import org.netbeans.api.project.Project;
50
import org.netbeans.api.project.ui.ProjectProblems;
51
import org.openide.awt.ActionID;
52
import org.openide.awt.ActionRegistration;
53
import org.openide.awt.DynamicMenuContent;
54
import org.openide.util.ContextAwareAction;
55
import org.openide.util.Lookup;
56
import org.openide.util.NbBundle;
57
58
/**
59
 *
60
 * @author Tomas Zezula
61
 */
62
@ActionID(id = "org.netbeans.modules.project.ui.problems.BrokenProjectActionFactory", category = "Project")
63
@ActionRegistration(displayName = "#LBL_Fix_Broken_Links_Action", lazy=false)
64
public class BrokenProjectActionFactory extends AbstractAction implements ContextAwareAction {
65
66
    public BrokenProjectActionFactory() {
67
        putValue(Action.NAME, NbBundle.getMessage(BrokenProjectActionFactory.class, "LBL_Fix_Broken_Links_Action"));
68
        setEnabled(false);
69
        putValue(DynamicMenuContent.HIDE_WHEN_DISABLED, true);
70
    }
71
72
    @Override
73
    public void actionPerformed(ActionEvent e) {
74
        throw new IllegalStateException();
75
    }
76
77
    @Override
78
    public Action createContextAwareInstance(Lookup actionContext) {
79
        final Collection<? extends Project> p = actionContext.lookupAll(Project.class);
80
        if (p.size() != 1) {
81
            return this;
82
        }
83
        return new BrokenProjectAction(p.iterator().next());
84
    }
85
86
87
    /** This action is created only when project has broken references.
88
     * Once these are resolved the action is disabled.
89
     */
90
    private static class BrokenProjectAction extends AbstractAction {
91
92
        private final Project prj;
93
94
        BrokenProjectAction(@NonNull final Project prj) {
95
            this.prj = prj;        
96
            putValue(Action.NAME, NbBundle.getMessage(BrokenProjectActionFactory.class, "LBL_Fix_Broken_Links_Action"));
97
            setEnabled(ProjectProblems.isBroken(prj));
98
            putValue(DynamicMenuContent.HIDE_WHEN_DISABLED, true);
99
        }
100
101
        @Override
102
        public void actionPerformed(ActionEvent e) {
103
//                helper.requestUpdate();
104
            ProjectProblems.showCustomizer(prj);
105
        }
106
107
    }
108
109
}
(-)a/projectui/src/org/netbeans/modules/project/ui/problems/BrokenProjectAnnotator.java (+237 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2012 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 2012 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.modules.project.ui.problems;
43
44
import java.awt.Image;
45
import java.beans.PropertyChangeEvent;
46
import java.beans.PropertyChangeListener;
47
import java.lang.ref.Reference;
48
import java.lang.ref.WeakReference;
49
import java.net.URL;
50
import java.util.Collection;
51
import java.util.Collections;
52
import java.util.HashSet;
53
import java.util.Iterator;
54
import java.util.Map;
55
import java.util.Set;
56
import java.util.WeakHashMap;
57
import java.util.concurrent.ExecutionException;
58
import java.util.concurrent.Future;
59
import java.util.logging.Level;
60
import java.util.logging.Logger;
61
import javax.swing.event.ChangeListener;
62
import org.netbeans.api.annotations.common.NonNull;
63
import org.netbeans.api.annotations.common.StaticResource;
64
import org.netbeans.api.project.Project;
65
import org.netbeans.api.project.ui.OpenProjects;
66
import org.netbeans.api.project.ui.ProjectProblems;
67
import org.netbeans.spi.project.ProjectIconAnnotator;
68
import org.netbeans.spi.project.ui.ProjectProblemsProvider;
69
import org.openide.util.ChangeSupport;
70
import org.openide.util.Exceptions;
71
import org.openide.util.ImageUtilities;
72
import org.openide.util.NbBundle;
73
import org.openide.util.Parameters;
74
import org.openide.util.RequestProcessor;
75
import org.openide.util.lookup.ServiceProvider;
76
77
/**
78
 *
79
 * @author Tomas Zezula
80
 */
81
@ServiceProvider(service=ProjectIconAnnotator.class)
82
public class BrokenProjectAnnotator implements ProjectIconAnnotator, PropertyChangeListener {
83
84
85
    @StaticResource
86
    private static final String BROKEN_PROJECT_BADGE_PATH = "org/netbeans/modules/project/ui/resources/brokenProjectBadge.gif";    //NOI18N
87
    private static final URL BROKEN_PROJECT_BADGE_URL = BrokenProjectAnnotator.class.getClassLoader().getResource(BROKEN_PROJECT_BADGE_PATH);
88
    private static final Image BROKEN_PROJECT_BADGE = ImageUtilities.loadImage(BROKEN_PROJECT_BADGE_PATH, true);
89
    private static final int FIRE_DELAY = 500;
90
    private static final RequestProcessor FIRER = new RequestProcessor(BrokenProjectAnnotator.class.getName()+".fire", 1, false, false);   //NOI18N
91
    private static final RequestProcessor NOTIFIER = new RequestProcessor(BrokenProjectAnnotator.class.getName()+".alert", 1, false, false);   //NOI18N
92
    private static final Logger LOG = Logger.getLogger(BrokenProjectAnnotator.class.getName());
93
94
    private final ChangeSupport changeSupport = new ChangeSupport(this);
95
    private final RequestProcessor.Task task = FIRER.create(new Runnable(){
96
        @Override
97
        public void run() {
98
            changeSupport.fireChange();
99
            LOG.fine("Fire.");
100
        }
101
    });
102
    private final Object cacheLock = new Object();
103
    //@GuardedBy("cacheLock")
104
    private final Map<Project, Integer> brokenCache = new WeakHashMap<Project, Integer>();
105
    //@GuardedBy("cacheLock")
106
    private final Map<ProjectProblemsProvider, Set<Reference<Project>>> problemsProvider2prj =
107
            new WeakHashMap<ProjectProblemsProvider, Set<Reference<Project>>>();
108
109
110
    @NonNull
111
    @Override
112
    public Image annotateIcon(
113
            @NonNull final Project project,
114
            @NonNull Image original,
115
            final boolean openedNode) {
116
        LOG.log(Level.FINE, "The annotateIcon called for project: {0}.", project);  //NOI18N
117
        Integer problemsCount = null;
118
        boolean firstTime = false;
119
        synchronized (cacheLock) {
120
            if (brokenCache.containsKey(project)) {
121
                problemsCount = brokenCache.get(project);
122
                LOG.log(Level.FINE, "In cache: {0}.", problemsCount);   //NOI18N
123
            } else {
124
                firstTime = true;
125
                brokenCache.put(project, problemsCount);
126
                final ProjectProblemsProvider ppp = project.getLookup().lookup(ProjectProblemsProvider.class);
127
                if (ppp != null) {
128
                    ppp.addPropertyChangeListener(this);
129
                    Set<Reference<Project>> projects = problemsProvider2prj.get(ppp);
130
                    if (projects == null) {
131
                        projects = new HashSet<Reference<Project>>();
132
                        problemsProvider2prj.put(ppp, projects);
133
                    }
134
                    projects.add(new WeakReference<Project>(project));
135
                }
136
                LOG.fine("Added listeners.");    //NOI18N
137
            }
138
        }
139
140
        if (problemsCount == null) {
141
            final ProjectProblemsProvider provider = project.getLookup().lookup(ProjectProblemsProvider.class);
142
            final Collection<? extends ProjectProblemsProvider.ProjectProblem> problems =
143
                provider == null?
144
                Collections.<ProjectProblemsProvider.ProjectProblem>emptySet():
145
                provider.getProblems();
146
            problemsCount = problems.size();
147
            if (firstTime) {
148
                for (ProjectProblemsProvider.ProjectProblem p : problems) {
149
                    if (p.getSeverity() == ProjectProblemsProvider.Severity.ERROR) {
150
                        scheduleAlert(project);
151
                        break;
152
                    }
153
                }
154
            }
155
            synchronized (cacheLock) {
156
                brokenCache.put(project, problemsCount);
157
                LOG.log(Level.FINE, "Set {0} to cache.", problemsCount);   //NOI18N
158
            }
159
        }
160
        if (problemsCount > 0) {            
161
            final String message = problemsCount == 1 ?
162
                    NbBundle.getMessage(BrokenProjectAnnotator.class, "MSG_OneProblem") :
163
                    NbBundle.getMessage(BrokenProjectAnnotator.class, "MSG_MoreProblems", problemsCount);
164
            final String messageHtml = String.format(
165
                "<img src=\"%s\">&nbsp;%s",   //NOI18N
166
                BROKEN_PROJECT_BADGE_URL.toExternalForm(),
167
                message);
168
            original = ImageUtilities.mergeImages(
169
                    original,
170
                    ImageUtilities.assignToolTipToImage(BROKEN_PROJECT_BADGE, messageHtml),
171
                    8,
172
                    0);
173
        }
174
        return original;
175
    }
176
177
    @Override
178
    public void addChangeListener(@NonNull final ChangeListener listener) {
179
        Parameters.notNull("listener", listener);   //NOI18N
180
        changeSupport.addChangeListener(listener);
181
182
    }
183
184
    @Override
185
    public void removeChangeListener(ChangeListener listener) {
186
        Parameters.notNull("listener", listener);   //NOI18N
187
        changeSupport.removeChangeListener(listener);
188
    }
189
190
    @Override
191
    public void propertyChange(@NonNull final PropertyChangeEvent evt) {
192
        if (ProjectProblemsProvider.PROP_PROBLEMS.equals(evt.getPropertyName())) {
193
            synchronized (cacheLock) {
194
                final Set<Reference<Project>> toRefresh = problemsProvider2prj.get(evt.getSource());
195
                if (toRefresh != null) {
196
                    LOG.fine("Event from known ProjectProblemsProvider -> clearing cache for:");  //NOI18N
197
                    for (final Iterator<Reference<Project>> it = toRefresh.iterator();it.hasNext();) {
198
                        final Reference<Project> ref = it.next();
199
                        final Project prj = ref.get();
200
                        if (prj != null) {
201
                            brokenCache.put(prj, null);
202
                            LOG.log(Level.FINE, "Project: {0}", prj);  //NOI18N
203
                        } else {
204
                            it.remove();
205
                        }
206
                    }
207
                } else {
208
                    LOG.fine("Event from unknown ProjectProblemsProvider -> clearing all caches.");  //NOI18N
209
                }
210
            }
211
            task.schedule(FIRE_DELAY);
212
        }
213
    }
214
215
    /**
216
     * Shows alert for given project.
217
     * Todo: Better would be to use ProjectOpenHook, however there is no way
218
     * how to register the POH for all project types, see issue #215687.
219
     * @param prj
220
     */
221
    private void scheduleAlert(@NonNull final Project prj) {
222
        NOTIFIER.post(new Runnable() {
223
            @Override
224
            public void run() {
225
                final Future<Project[]> projects = OpenProjects.getDefault().openProjects();
226
                try {
227
                    projects.get();
228
                } catch (ExecutionException ex) {
229
                    Exceptions.printStackTrace(ex);
230
                } catch (InterruptedException ie) {
231
                    Exceptions.printStackTrace(ie);
232
                }
233
                ProjectProblems.showAlert(prj);
234
            }
235
        });
236
    }
237
}
(-)a/java.project/src/org/netbeans/modules/java/project/BrokenReferencesAlertPanel.form (-2 / +6 lines)
Lines 1-6 Link Here
1
<?xml version="1.0" encoding="UTF-8" ?>
1
<?xml version="1.0" encoding="UTF-8" ?>
2
2
3
<Form version="1.2" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
3
<Form version="1.2" maxVersion="1.2" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
4
  <AccessibilityProperties>
4
  <AccessibilityProperties>
5
    <Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
5
    <Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
6
      <ResourceString bundle="org/netbeans/modules/java/project/Bundle.properties" key="ACSN_BrokenReferencesAlertPanel" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
6
      <ResourceString bundle="org/netbeans/modules/java/project/Bundle.properties" key="ACSN_BrokenReferencesAlertPanel" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
Lines 10-16 Link Here
10
    </Property>
10
    </Property>
11
  </AccessibilityProperties>
11
  </AccessibilityProperties>
12
  <AuxValues>
12
  <AuxValues>
13
    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
14
    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
15
    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
13
    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
16
    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
17
    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
14
    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
18
    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
15
    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
19
    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
16
    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
20
    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
Lines 22-28 Link Here
22
    <Component class="javax.swing.JLabel" name="jLabel1">
26
    <Component class="javax.swing.JLabel" name="jLabel1">
23
      <Properties>
27
      <Properties>
24
        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
28
        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
25
          <ResourceString bundle="org/netbeans/modules/java/project/Bundle.properties" key="MSG_Broken_References_Label" replaceFormat="java.util.ResourceBundle.getBundle(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
29
          <ResourceString bundle="org/netbeans/modules/project/ui/problems/Bundle.properties" key="MSG_Broken_References_Label" replaceFormat="java.util.ResourceBundle.getBundle(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
26
        </Property>
30
        </Property>
27
      </Properties>
31
      </Properties>
28
      <Constraints>
32
      <Constraints>
(-)a/java.project/src/org/netbeans/modules/java/project/BrokenReferencesAlertPanel.java (-12 / +12 lines)
Lines 42-48 Link Here
42
 * made subject to such option by the copyright holder.
42
 * made subject to such option by the copyright holder.
43
 */
43
 */
44
44
45
package org.netbeans.modules.java.project;
45
package org.netbeans.modules.project.ui.problems;
46
46
47
import javax.swing.JPanel;
47
import javax.swing.JPanel;
48
48
Lines 50-56 Link Here
50
50
51
    public BrokenReferencesAlertPanel() {
51
    public BrokenReferencesAlertPanel() {
52
        initComponents();
52
        initComponents();
53
        notAgain.setSelected(!JavaProjectSettings.isShowAgainBrokenRefAlert());
53
        notAgain.setSelected(!BrokenReferencesSettings.isShowAgainBrokenRefAlert());
54
    }
54
    }
55
55
56
    /** This method is called from within the constructor to
56
    /** This method is called from within the constructor to
Lines 58-64 Link Here
58
     * WARNING: Do NOT modify this code. The content of this method is
58
     * WARNING: Do NOT modify this code. The content of this method is
59
     * always regenerated by the Form Editor.
59
     * always regenerated by the Form Editor.
60
     */
60
     */
61
    // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
61
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
62
    private void initComponents() {
62
    private void initComponents() {
63
        java.awt.GridBagConstraints gridBagConstraints;
63
        java.awt.GridBagConstraints gridBagConstraints;
64
64
Lines 69-77 Link Here
69
69
70
        setLayout(new java.awt.GridBagLayout());
70
        setLayout(new java.awt.GridBagLayout());
71
71
72
        getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(BrokenReferencesAlertPanel.class, "ACSN_BrokenReferencesAlertPanel"));
72
        java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("org/netbeans/modules/project/ui/problems/Bundle"); // NOI18N
73
        getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(BrokenReferencesAlertPanel.class, "ACSD_BrokenReferencesAlertPanel"));
73
        org.openide.awt.Mnemonics.setLocalizedText(jLabel1, bundle.getString("MSG_Broken_References_Label")); // NOI18N
74
        org.openide.awt.Mnemonics.setLocalizedText(jLabel1, java.util.ResourceBundle.getBundle("org/netbeans/modules/java/project/Bundle").getString("MSG_Broken_References_Label"));
75
        gridBagConstraints = new java.awt.GridBagConstraints();
74
        gridBagConstraints = new java.awt.GridBagConstraints();
76
        gridBagConstraints.gridx = 0;
75
        gridBagConstraints.gridx = 0;
77
        gridBagConstraints.gridy = 0;
76
        gridBagConstraints.gridy = 0;
Lines 80-93 Link Here
80
        gridBagConstraints.insets = new java.awt.Insets(11, 11, 0, 0);
79
        gridBagConstraints.insets = new java.awt.Insets(11, 11, 0, 0);
81
        add(jLabel1, gridBagConstraints);
80
        add(jLabel1, gridBagConstraints);
82
81
83
        org.openide.awt.Mnemonics.setLocalizedText(notAgain, org.openide.util.NbBundle.getMessage(BrokenReferencesAlertPanel.class, "MSG_BrokenReferencesAlertPanel_notAgain"));
82
        org.openide.awt.Mnemonics.setLocalizedText(notAgain, org.openide.util.NbBundle.getMessage(BrokenReferencesAlertPanel.class, "MSG_BrokenReferencesAlertPanel_notAgain")); // NOI18N
84
        notAgain.setMargin(new java.awt.Insets(0, 0, 0, 0));
83
        notAgain.setMargin(new java.awt.Insets(0, 0, 0, 0));
85
        notAgain.addActionListener(new java.awt.event.ActionListener() {
84
        notAgain.addActionListener(new java.awt.event.ActionListener() {
86
            public void actionPerformed(java.awt.event.ActionEvent evt) {
85
            public void actionPerformed(java.awt.event.ActionEvent evt) {
87
                notAgainActionPerformed(evt);
86
                notAgainActionPerformed(evt);
88
            }
87
            }
89
        });
88
        });
90
91
        gridBagConstraints = new java.awt.GridBagConstraints();
89
        gridBagConstraints = new java.awt.GridBagConstraints();
92
        gridBagConstraints.gridx = 0;
90
        gridBagConstraints.gridx = 0;
93
        gridBagConstraints.gridy = 2;
91
        gridBagConstraints.gridy = 2;
Lines 95-104 Link Here
95
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
93
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
96
        gridBagConstraints.insets = new java.awt.Insets(6, 11, 0, 0);
94
        gridBagConstraints.insets = new java.awt.Insets(6, 11, 0, 0);
97
        add(notAgain, gridBagConstraints);
95
        add(notAgain, gridBagConstraints);
98
        notAgain.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(BrokenReferencesAlertPanel.class, "ACSN_BrokenReferencesAlertPanel_notAgain"));
96
        notAgain.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(BrokenReferencesAlertPanel.class, "ACSN_BrokenReferencesAlertPanel_notAgain")); // NOI18N
99
        notAgain.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(BrokenReferencesAlertPanel.class, "ACSD_BrokenReferencesAlertPanel_notAgain"));
97
        notAgain.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(BrokenReferencesAlertPanel.class, "ACSD_BrokenReferencesAlertPanel_notAgain")); // NOI18N
100
98
101
        org.openide.awt.Mnemonics.setLocalizedText(message, org.openide.util.NbBundle.getMessage(BrokenReferencesAlertPanel.class, "MSG_Broken_References"));
99
        org.openide.awt.Mnemonics.setLocalizedText(message, org.openide.util.NbBundle.getMessage(BrokenReferencesAlertPanel.class, "MSG_Broken_References")); // NOI18N
102
        gridBagConstraints = new java.awt.GridBagConstraints();
100
        gridBagConstraints = new java.awt.GridBagConstraints();
103
        gridBagConstraints.gridx = 0;
101
        gridBagConstraints.gridx = 0;
104
        gridBagConstraints.gridy = 1;
102
        gridBagConstraints.gridy = 1;
Lines 117-126 Link Here
117
        gridBagConstraints.weighty = 1.0;
115
        gridBagConstraints.weighty = 1.0;
118
        add(jPanel1, gridBagConstraints);
116
        add(jPanel1, gridBagConstraints);
119
117
118
        getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(BrokenReferencesAlertPanel.class, "ACSN_BrokenReferencesAlertPanel")); // NOI18N
119
        getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(BrokenReferencesAlertPanel.class, "ACSD_BrokenReferencesAlertPanel")); // NOI18N
120
    }// </editor-fold>//GEN-END:initComponents
120
    }// </editor-fold>//GEN-END:initComponents
121
121
122
    private void notAgainActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_notAgainActionPerformed
122
    private void notAgainActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_notAgainActionPerformed
123
        JavaProjectSettings.setShowAgainBrokenRefAlert(!notAgain.isSelected());
123
        BrokenReferencesSettings.setShowAgainBrokenRefAlert(!notAgain.isSelected());
124
    }//GEN-LAST:event_notAgainActionPerformed
124
    }//GEN-LAST:event_notAgainActionPerformed
125
125
126
126
(-)a/java.project/src/org/netbeans/modules/java/project/BrokenReferencesCustomizer.form (-1 / +1 lines)
Lines 1-4 Link Here
1
<?xml version="1.1" encoding="UTF-8" ?>
1
<?xml version="1.0" encoding="UTF-8" ?>
2
2
3
<Form version="1.2" maxVersion="1.2" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
3
<Form version="1.2" maxVersion="1.2" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
4
  <Properties>
4
  <Properties>
(-)a/java.project/src/org/netbeans/modules/java/project/BrokenReferencesCustomizer.java (-169 / +131 lines)
Lines 42-82 Link Here
42
 * made subject to such option by the copyright holder.
42
 * made subject to such option by the copyright holder.
43
 */
43
 */
44
44
45
package org.netbeans.modules.java.project;
45
package org.netbeans.modules.project.ui.problems;
46
46
47
import org.netbeans.api.project.libraries.Library;
48
import java.util.logging.Level;
49
import java.util.logging.Logger;
50
import java.awt.Component;
47
import java.awt.Component;
51
import java.awt.EventQueue;
48
import java.util.concurrent.ExecutionException;
52
import java.io.File;
49
import java.util.concurrent.Future;
50
import java.util.concurrent.atomic.AtomicReference;
53
import javax.swing.DefaultListCellRenderer;
51
import javax.swing.DefaultListCellRenderer;
54
import javax.swing.JFileChooser;
55
import javax.swing.JList;
52
import javax.swing.JList;
56
import javax.swing.SwingUtilities;
53
import javax.swing.SwingUtilities;
54
import org.netbeans.api.annotations.common.NonNull;
55
import org.netbeans.api.annotations.common.NullAllowed;
57
import org.netbeans.api.annotations.common.StaticResource;
56
import org.netbeans.api.annotations.common.StaticResource;
57
import org.netbeans.spi.project.ui.ProjectProblemsProvider;
58
import org.openide.NotificationLineSupport;
58
59
59
import org.netbeans.api.java.platform.PlatformsCustomizer;
60
import org.openide.util.Exceptions;
60
import org.netbeans.api.project.libraries.LibrariesCustomizer;
61
import org.netbeans.api.project.libraries.LibraryManager;
62
import org.netbeans.spi.project.support.ant.ui.VariablesSupport;
63
import org.netbeans.spi.project.ui.support.ProjectChooser;
64
import org.openide.util.ImageUtilities;
61
import org.openide.util.ImageUtilities;
65
import static org.netbeans.modules.java.project.Bundle.*;
66
import org.openide.util.NbBundle.Messages;
62
import org.openide.util.NbBundle.Messages;
63
import org.openide.util.Parameters;
67
import org.openide.util.RequestProcessor;
64
import org.openide.util.RequestProcessor;
68
65
69
/**
66
/**
70
 *
67
 *
71
 * @author  David Konecny
68
 * @author David Konecny
69
 * @author Tomas Zezula
72
 */
70
 */
73
public class BrokenReferencesCustomizer extends javax.swing.JPanel {
71
public class BrokenReferencesCustomizer extends javax.swing.JPanel {
74
72
75
    private static final RequestProcessor RP = new RequestProcessor(BrokenReferencesCustomizer.class);
73
    private static final RequestProcessor RP = new RequestProcessor(BrokenReferencesCustomizer.class);
76
    private static final Logger LOG = Logger.getLogger(BrokenReferencesCustomizer.class.getName());
77
74
78
    private BrokenReferencesModel model;
75
    private final BrokenReferencesModel model;
79
    private File lastSelectedFile;
76
    //@GuardedBy("this")
77
    private NotificationLineSupport nls;
80
    
78
    
81
    /** Creates new form BrokenReferencesCustomizer */
79
    /** Creates new form BrokenReferencesCustomizer */
82
    public BrokenReferencesCustomizer(BrokenReferencesModel model) {
80
    public BrokenReferencesCustomizer(BrokenReferencesModel model) {
Lines 84-90 Link Here
84
        this.model = model;
82
        this.model = model;
85
        errorList.setModel(model);
83
        errorList.setModel(model);
86
        errorList.setSelectedIndex(0);
84
        errorList.setSelectedIndex(0);
87
        errorList.setCellRenderer(new ListCellRendererImpl(model));
85
        errorList.setCellRenderer(new ListCellRendererImpl());
88
    }
86
    }
89
    
87
    
90
    /** This method is called from within the constructor to
88
    /** This method is called from within the constructor to
Lines 92-98 Link Here
92
     * WARNING: Do NOT modify this code. The content of this method is
90
     * WARNING: Do NOT modify this code. The content of this method is
93
     * always regenerated by the Form Editor.
91
     * always regenerated by the Form Editor.
94
     */
92
     */
95
    private void initComponents() {//GEN-BEGIN:initComponents
93
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
94
    private void initComponents() {
96
        java.awt.GridBagConstraints gridBagConstraints;
95
        java.awt.GridBagConstraints gridBagConstraints;
97
96
98
        errorListLabel = new javax.swing.JLabel();
97
        errorListLabel = new javax.swing.JLabel();
Lines 103-120 Link Here
103
        jScrollPane2 = new javax.swing.JScrollPane();
102
        jScrollPane2 = new javax.swing.JScrollPane();
104
        description = new javax.swing.JTextArea();
103
        description = new javax.swing.JTextArea();
105
104
105
        setPreferredSize(new java.awt.Dimension(450, 300));
106
        setLayout(new java.awt.GridBagLayout());
106
        setLayout(new java.awt.GridBagLayout());
107
107
108
        setPreferredSize(new java.awt.Dimension(450, 300));
109
        getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(BrokenReferencesCustomizer.class, "ACSN_BrokenReferencesCustomizer"));
110
        getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(BrokenReferencesCustomizer.class, "ACSD_BrokenReferencesCustomizer"));
111
        errorListLabel.setLabelFor(errorList);
108
        errorListLabel.setLabelFor(errorList);
112
        org.openide.awt.Mnemonics.setLocalizedText(errorListLabel, org.openide.util.NbBundle.getMessage(BrokenReferencesCustomizer.class, "LBL_BrokenLinksCustomizer_List"));
109
        org.openide.awt.Mnemonics.setLocalizedText(errorListLabel, org.openide.util.NbBundle.getMessage(BrokenReferencesCustomizer.class, "LBL_BrokenLinksCustomizer_List")); // NOI18N
113
        gridBagConstraints = new java.awt.GridBagConstraints();
110
        gridBagConstraints = new java.awt.GridBagConstraints();
114
        gridBagConstraints.anchor = java.awt.GridBagConstraints.SOUTHWEST;
111
        gridBagConstraints.anchor = java.awt.GridBagConstraints.SOUTHWEST;
115
        gridBagConstraints.insets = new java.awt.Insets(6, 12, 3, 0);
112
        gridBagConstraints.insets = new java.awt.Insets(6, 12, 3, 0);
116
        add(errorListLabel, gridBagConstraints);
113
        add(errorListLabel, gridBagConstraints);
117
        errorListLabel.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(BrokenReferencesCustomizer.class, "ACSD_BrokenLinksCustomizer_List"));
114
        errorListLabel.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(BrokenReferencesCustomizer.class, "ACSD_BrokenLinksCustomizer_List")); // NOI18N
118
115
119
        errorList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
116
        errorList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
120
        errorList.addListSelectionListener(new javax.swing.event.ListSelectionListener() {
117
        errorList.addListSelectionListener(new javax.swing.event.ListSelectionListener() {
Lines 122-128 Link Here
122
                errorListValueChanged(evt);
119
                errorListValueChanged(evt);
123
            }
120
            }
124
        });
121
        });
125
126
        jScrollPane1.setViewportView(errorList);
122
        jScrollPane1.setViewportView(errorList);
127
123
128
        gridBagConstraints = new java.awt.GridBagConstraints();
124
        gridBagConstraints = new java.awt.GridBagConstraints();
Lines 135-147 Link Here
135
        gridBagConstraints.insets = new java.awt.Insets(0, 12, 0, 0);
131
        gridBagConstraints.insets = new java.awt.Insets(0, 12, 0, 0);
136
        add(jScrollPane1, gridBagConstraints);
132
        add(jScrollPane1, gridBagConstraints);
137
133
138
        org.openide.awt.Mnemonics.setLocalizedText(fix, org.openide.util.NbBundle.getMessage(BrokenReferencesCustomizer.class, "LBL_BrokenLinksCustomizer_Fix"));
134
        org.openide.awt.Mnemonics.setLocalizedText(fix, org.openide.util.NbBundle.getMessage(BrokenReferencesCustomizer.class, "LBL_BrokenLinksCustomizer_Fix")); // NOI18N
139
        fix.addActionListener(new java.awt.event.ActionListener() {
135
        fix.addActionListener(new java.awt.event.ActionListener() {
140
            public void actionPerformed(java.awt.event.ActionEvent evt) {
136
            public void actionPerformed(java.awt.event.ActionEvent evt) {
141
                fixActionPerformed(evt);
137
                fixActionPerformed(evt);
142
            }
138
            }
143
        });
139
        });
144
145
        gridBagConstraints = new java.awt.GridBagConstraints();
140
        gridBagConstraints = new java.awt.GridBagConstraints();
146
        gridBagConstraints.gridx = 1;
141
        gridBagConstraints.gridx = 1;
147
        gridBagConstraints.gridy = 1;
142
        gridBagConstraints.gridy = 1;
Lines 149-165 Link Here
149
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
144
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
150
        gridBagConstraints.insets = new java.awt.Insets(0, 6, 0, 12);
145
        gridBagConstraints.insets = new java.awt.Insets(0, 6, 0, 12);
151
        add(fix, gridBagConstraints);
146
        add(fix, gridBagConstraints);
152
        fix.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(BrokenReferencesCustomizer.class, "ACSD_BrokenLinksCustomizer_Fix"));
147
        fix.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(BrokenReferencesCustomizer.class, "ACSD_BrokenLinksCustomizer_Fix")); // NOI18N
153
148
154
        descriptionLabel.setLabelFor(description);
149
        descriptionLabel.setLabelFor(description);
155
        org.openide.awt.Mnemonics.setLocalizedText(descriptionLabel, org.openide.util.NbBundle.getMessage(BrokenReferencesCustomizer.class, "LBL_BrokenLinksCustomizer_Description"));
150
        org.openide.awt.Mnemonics.setLocalizedText(descriptionLabel, org.openide.util.NbBundle.getMessage(BrokenReferencesCustomizer.class, "LBL_BrokenLinksCustomizer_Description")); // NOI18N
156
        gridBagConstraints = new java.awt.GridBagConstraints();
151
        gridBagConstraints = new java.awt.GridBagConstraints();
157
        gridBagConstraints.gridx = 0;
152
        gridBagConstraints.gridx = 0;
158
        gridBagConstraints.gridy = 2;
153
        gridBagConstraints.gridy = 2;
159
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
154
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
160
        gridBagConstraints.insets = new java.awt.Insets(6, 12, 3, 0);
155
        gridBagConstraints.insets = new java.awt.Insets(6, 12, 3, 0);
161
        add(descriptionLabel, gridBagConstraints);
156
        add(descriptionLabel, gridBagConstraints);
162
        descriptionLabel.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(BrokenReferencesCustomizer.class, "ACSD_BrokenLinksCustomizer_Description"));
157
        descriptionLabel.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(BrokenReferencesCustomizer.class, "ACSD_BrokenLinksCustomizer_Description")); // NOI18N
163
158
164
        description.setEditable(false);
159
        description.setEditable(false);
165
        description.setLineWrap(true);
160
        description.setLineWrap(true);
Lines 174-291 Link Here
174
        gridBagConstraints.insets = new java.awt.Insets(0, 12, 5, 0);
169
        gridBagConstraints.insets = new java.awt.Insets(0, 12, 5, 0);
175
        add(jScrollPane2, gridBagConstraints);
170
        add(jScrollPane2, gridBagConstraints);
176
171
177
    }//GEN-END:initComponents
172
        getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(BrokenReferencesCustomizer.class, "ACSN_BrokenReferencesCustomizer")); // NOI18N
173
        getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(BrokenReferencesCustomizer.class, "ACSD_BrokenReferencesCustomizer")); // NOI18N
174
    }// </editor-fold>//GEN-END:initComponents
178
175
179
    private void errorListValueChanged(javax.swing.event.ListSelectionEvent evt) {//GEN-FIRST:event_errorListValueChanged
176
    private void errorListValueChanged(javax.swing.event.ListSelectionEvent evt) {//GEN-FIRST:event_errorListValueChanged
180
        updateSelection();
177
        updateSelection();
181
    }//GEN-LAST:event_errorListValueChanged
178
    }//GEN-LAST:event_errorListValueChanged
182
179
183
    @Messages({
180
    
184
        "LBL_BrokenLinksCustomizer_Resolve_Project=Browse Project \"{0}\"",
185
        "LBL_BrokenLinksCustomizer_Resolve_File=Browse \"{0}\""
186
    })
187
    private void fixActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fixActionPerformed
181
    private void fixActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fixActionPerformed
188
        int index = errorList.getSelectedIndex();
182
        final Object value = errorList.getSelectedValue();
189
        if (index==-1) {
183
        if (!(value instanceof BrokenReferencesModel.ProblemReference)) {
190
            return;
184
            return;
191
        }
185
        }
192
        final BrokenReferencesModel.OneReference or = model.getOneReference(index);
186
        final BrokenReferencesModel.ProblemReference or = (BrokenReferencesModel.ProblemReference) value;
193
        if (or.getType() == BrokenReferencesModel.RefType.LIBRARY ||
187
        errorList.setEnabled(false);
194
            or.getType() == BrokenReferencesModel.RefType.LIBRARY_CONTENT) {
188
        fix.setEnabled(false);
195
            fix.setEnabled(false);
189
        Future<ProjectProblemsProvider.Result> becomesResult = null;
196
            try {
190
        try {
197
                final LibraryManager lm = model.getProjectLibraryManager(or);
191
            becomesResult = or.problem.resolve();
198
                if (lm == null) {
192
            assert becomesResult != null;
199
                    //Closed and freed project
193
        } finally {
200
                    return;
194
            if (becomesResult == null) {
195
                updateAfterResolve(null);
196
            } else if (becomesResult.isDone()) {
197
                ProjectProblemsProvider.Result result = null;
198
                try {
199
                    result = becomesResult.get();
200
                } catch (InterruptedException ex) {
201
                    Exceptions.printStackTrace(ex);
202
                } catch (ExecutionException ex) {
203
                    Exceptions.printStackTrace(ex);
204
                } finally {
205
                    updateAfterResolve(result);
201
                }
206
                }
202
                LibrariesCustomizer.showCustomizer(null,lm);
207
            } else {
203
            } finally {
208
                final Future<ProjectProblemsProvider.Result> becomesResultFin = becomesResult;
204
                fix.setEnabled(true);
209
                RP.post(new Runnable() {
205
            }
210
                    @Override
206
        } else if (or.getType() == BrokenReferencesModel.RefType.DEFINABLE_LIBRARY) {
211
                    public void run() {
207
            fix.setEnabled(false);
212
                        final AtomicReference<ProjectProblemsProvider.Result> result = new AtomicReference<ProjectProblemsProvider.Result>();
208
            RP.post(new Runnable() {
213
                        try {
209
                public @Override void run() {
214
                            result.set(becomesResultFin.get());
210
                    try {
215
                        } catch (InterruptedException ie) {
211
                        Library lib = or.define();
216
                            Exceptions.printStackTrace(ie);
212
                        LOG.log(Level.FINE, "found {0}", lib);
217
                        } catch (ExecutionException ee) {
213
                        EventQueue.invokeLater(new Runnable() {
218
                            Exceptions.printStackTrace(ee);
214
                            public @Override void run() {
219
                        } finally {
215
                                model.refresh();
220
                            SwingUtilities.invokeLater(new Runnable() {
216
                                updateSelection();
221
                                @Override
217
                            }
222
                                public void run() {
218
                        });
223
                                    updateAfterResolve(result.get());
219
                    } catch (Exception x) {
224
                                }
220
                        LOG.log(Level.INFO, null, x);
225
                            });
221
                        // fallback: user may need to create library manually
222
                        final LibraryManager lm = model.getProjectLibraryManager(or);
223
                        if (lm == null) {
224
                            //Closed and freed project
225
                            return;
226
                        }
226
                        }
227
                        LibrariesCustomizer.showCustomizer(null, lm);
228
                    } finally {
229
                        SwingUtilities.invokeLater(new Runnable() {
230
                            @Override
231
                            public void run() {
232
                                fix.setEnabled(true);
233
                            }
234
                        });
235
                    }
227
                    }
236
                }
228
                });
237
            });
238
            return;
239
        } else if (or.getType() == BrokenReferencesModel.RefType.PLATFORM) {
240
            fix.setEnabled(false);
241
            try {
242
                PlatformsCustomizer.showCustomizer(null);
243
            } finally {
244
                fix.setEnabled(true);
245
            }
246
        } else if (or.getType() == BrokenReferencesModel.RefType.VARIABLE || or.getType() == BrokenReferencesModel.RefType.VARIABLE_CONTENT) {
247
            fix.setEnabled(false);
248
            try {
249
                VariablesSupport.showVariablesCustomizer();
250
            } finally {
251
                fix.setEnabled(true);
252
            }
253
        } else {
254
            fix.setEnabled(false);
255
            try {
256
                JFileChooser chooser;
257
                if (or.getType() == BrokenReferencesModel.RefType.PROJECT) {
258
                    chooser = ProjectChooser.projectChooser();
259
                    chooser.setDialogTitle(LBL_BrokenLinksCustomizer_Resolve_Project(or.getDisplayID()));
260
                } else {
261
                    chooser = new JFileChooser();
262
                    chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
263
                    chooser.setDialogTitle(LBL_BrokenLinksCustomizer_Resolve_File(or.getDisplayID()));
264
                }
265
                if (lastSelectedFile != null) {
266
                    chooser.setSelectedFile(lastSelectedFile);
267
                }
268
                int option = chooser.showOpenDialog(null);
269
                if (option == JFileChooser.APPROVE_OPTION) {
270
                    model.updateReference(errorList.getSelectedIndex(), chooser.getSelectedFile());
271
                    lastSelectedFile = chooser.getSelectedFile();
272
                }
273
            } finally {
274
                fix.setEnabled(true);
275
            }
229
            }
276
        }
230
        }
231
        
232
    }//GEN-LAST:event_fixActionPerformed
233
234
    private void updateAfterResolve(@NullAllowed final ProjectProblemsProvider.Result result) {
235
        if (!SwingUtilities.isEventDispatchThread()) {
236
            throw new IllegalStateException();
237
        }
277
        model.refresh();
238
        model.refresh();
239
        errorList.setEnabled(true);
278
        updateSelection();
240
        updateSelection();
279
    }//GEN-LAST:event_fixActionPerformed
241
        if (result != null) {
242
            notify(result.getStatus(), result.getMessage());
243
        }
244
    }
280
245
281
    @Messages("LBL_BrokenLinksCustomizer_Problem_Was_Resolved=This problem was resolved")
246
    @Messages("LBL_BrokenLinksCustomizer_Problem_Was_Resolved=This problem was resolved")
282
    private void updateSelection() {
247
    private void updateSelection() {
283
        if (errorList.getSelectedIndex() != -1 && errorList.getSelectedIndex() < model.getSize()) {
248
        final Object value = errorList.getSelectedValue();
284
            if (model.isBroken(errorList.getSelectedIndex())) {
249
        if (value instanceof BrokenReferencesModel.ProblemReference) {
285
                description.setText(getDescription(errorList.getSelectedIndex()));
250
            final BrokenReferencesModel.ProblemReference reference = (BrokenReferencesModel.ProblemReference) value;
251
            if (!reference.resolved) {
252
                description.setText(reference.problem.getDescription());
286
                fix.setEnabled(true);
253
                fix.setEnabled(true);
287
            } else {
254
            } else {
288
                description.setText(LBL_BrokenLinksCustomizer_Problem_Was_Resolved());
255
                description.setText(Bundle.LBL_BrokenLinksCustomizer_Problem_Was_Resolved());
289
                // Leave the button always enabled so that user can alter 
256
                // Leave the button always enabled so that user can alter 
290
                // resolved reference. Especially needed for automatically
257
                // resolved reference. Especially needed for automatically
291
                // resolved JAR references.
258
                // resolved JAR references.
Lines 295-336 Link Here
295
            description.setText("");
262
            description.setText("");
296
            fix.setEnabled(false);
263
            fix.setEnabled(false);
297
        }
264
        }
265
        clearNotification();
298
    }
266
    }
299
    
267
    
300
    @Messages({
301
        "LBL_BrokenLinksCustomizer_BrokenLibraryDesc=Problem: The project uses a class library called \"{0}\", but this class library was not found.\nSolution: Click Resolve to open the Library Manager and create a new class library called \"{0}\".",
302
        "LBL_BrokenLinksCustomizer_BrokenDefinableLibraryDesc=Problem: The project uses a class library called \"{0}\", but this class library is not currently defined locally.\nSolution: Click Resolve to download or otherwise automatically define this library.",
303
        "LBL_BrokenLinksCustomizer_BrokenLibraryContentDesc=Problem: The project uses the class library called \"{0}\" but the classpath items of this library are missing.\nSolution: Click Resolve to open the Library Manager and locate the missing classpath items of \"{0}\" library.",
304
        "LBL_BrokenLinksCustomizer_BrokenProjectReferenceDesc=Problem: The project classpath includes a reference to the project called \"{0}\", but this project was not found.\nSolution: Click Resolve and locate the missing project.",
305
        "LBL_BrokenLinksCustomizer_BrokenFileReferenceDesc=Problem: The project uses the file/folder called \"{0}\", but this file/folder was not found.\nSolution: Click Resolve and locate the missing file/folder.",
306
        "LBL_BrokenLinksCustomizer_BrokenVariableReferenceDesc=Problem: The project uses the variable called \"{0}\", but this variable was not found.\nSolution: Click Resolve and setup this variable there.",
307
        "LBL_BrokenLinksCustomizer_BrokenVariableContentDesc=Problem: The project uses the variable based file/folder \"{0}\", but this file/folder was not found.\nSolution: Click Resolve and update your variable to point to correct location.",
308
        "LBL_BrokenLinksCustomizer_BrokenPlatformDesc=Problem: The project uses the Java Platform called \"{0}\", but this platform was not found.\nSolution: Click Resolve and create new platform called \"{0}\"."
309
    })
310
    private String getDescription(int index) {
311
        BrokenReferencesModel.OneReference or = model.getOneReference(index);
312
        switch (or.getType()) {
313
            case LIBRARY:
314
                return LBL_BrokenLinksCustomizer_BrokenLibraryDesc(or.getDisplayID());
315
            case DEFINABLE_LIBRARY:
316
                return LBL_BrokenLinksCustomizer_BrokenDefinableLibraryDesc(or.getDisplayID());
317
            case LIBRARY_CONTENT:
318
                return LBL_BrokenLinksCustomizer_BrokenLibraryContentDesc(or.getDisplayID());
319
            case PROJECT:
320
                return LBL_BrokenLinksCustomizer_BrokenProjectReferenceDesc(or.getDisplayID());
321
            case FILE:
322
                return LBL_BrokenLinksCustomizer_BrokenFileReferenceDesc(or.getDisplayID());
323
            case VARIABLE:
324
                return LBL_BrokenLinksCustomizer_BrokenVariableReferenceDesc(or.getDisplayID());
325
            case VARIABLE_CONTENT:
326
                return LBL_BrokenLinksCustomizer_BrokenVariableContentDesc(or.getDisplayID());
327
            case PLATFORM:
328
                return LBL_BrokenLinksCustomizer_BrokenPlatformDesc(or.getDisplayID());
329
            default:
330
                assert false;
331
                return null;
332
        }
333
    }
334
    
268
    
335
    // Variables declaration - do not modify//GEN-BEGIN:variables
269
    // Variables declaration - do not modify//GEN-BEGIN:variables
336
    private javax.swing.JTextArea description;
270
    private javax.swing.JTextArea description;
Lines 342-366 Link Here
342
    private javax.swing.JScrollPane jScrollPane2;
276
    private javax.swing.JScrollPane jScrollPane2;
343
    // End of variables declaration//GEN-END:variables
277
    // End of variables declaration//GEN-END:variables
344
278
345
    private static final @StaticResource String BROKEN_REF = "org/netbeans/modules/java/project/resources/broken-reference.gif";
279
    private static final @StaticResource String BROKEN_REF = "org/netbeans/modules/project/ui/resources/broken-reference.gif";
346
    private static final @StaticResource String RESOLVED_REF = "org/netbeans/modules/java/project/resources/resolved-reference.gif";
280
    private static final @StaticResource String RESOLVED_REF = "org/netbeans/modules/project/ui/resources/resolved-reference.gif";
347
281
348
    private static class ListCellRendererImpl extends DefaultListCellRenderer {
282
    synchronized void setNotificationLineSupport(@NullAllowed final NotificationLineSupport notificationLineSupport) {
283
        Parameters.notNull("notificationLineSupport", notificationLineSupport); //NOI18N
284
        nls = notificationLineSupport;
285
    }
349
286
350
        private BrokenReferencesModel model;
287
    private synchronized void notify (
351
        
288
        @NonNull final ProjectProblemsProvider.Status status,
352
        public ListCellRendererImpl(BrokenReferencesModel model) {
289
        @NullAllowed final String message) {
353
            this.model = model;
290
        if (message != null) {
291
            switch (status) {
292
                case RESOLVED:
293
                    nls.setInformationMessage(message);
294
                    break;
295
                case RESOLVED_WITH_WARNING:
296
                    nls.setWarningMessage(message);
297
                    break;
298
                case UNRESOLVED:
299
                    nls.setErrorMessage(message);
300
                    break;
301
                default:
302
                    throw new IllegalStateException(status.name());
303
            }
354
        }
304
        }
305
    }
306
307
    private synchronized void clearNotification() {
308
        //May be null when called from the constructor, before initialization
309
        if (nls != null) {
310
            nls.clearMessages();
311
        }
312
    }
313
314
    private static class ListCellRendererImpl extends DefaultListCellRenderer {                        
355
        
315
        
356
        public @Override Component getListCellRendererComponent( JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
316
        public @Override Component getListCellRendererComponent( JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
357
            super.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus );            
317
            if (value instanceof BrokenReferencesModel.ProblemReference) {
358
            if (model.isBroken(index)) {
318
                BrokenReferencesModel.ProblemReference problemRef = (BrokenReferencesModel.ProblemReference) value;
359
                setIcon(ImageUtilities.loadImageIcon(BROKEN_REF, false));
319
                super.getListCellRendererComponent( list, problemRef.getDisplayName(), index, isSelected, cellHasFocus );
360
            } else {
320
                if (problemRef.resolved) {
361
                setIcon(ImageUtilities.loadImageIcon(RESOLVED_REF, false));
321
                    setIcon(ImageUtilities.loadImageIcon(RESOLVED_REF, false));
322
                } else {
323
                    setIcon(ImageUtilities.loadImageIcon(BROKEN_REF, false));
324
                }
362
            }
325
            }
363
            
364
            return this;
326
            return this;
365
        }
327
        }
366
    }
328
    }
(-)a/projectui/src/org/netbeans/modules/project/ui/problems/BrokenReferencesImpl.java (+199 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2012 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 2012 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.modules.project.ui.problems;
43
44
import java.awt.Dialog;
45
import javax.swing.JButton;
46
import javax.swing.event.ChangeEvent;
47
import javax.swing.event.ChangeListener;
48
import org.netbeans.api.annotations.common.NonNull;
49
import org.netbeans.api.project.Project;
50
import org.netbeans.api.project.ProjectUtils;
51
import org.netbeans.modules.project.uiapi.BrokenReferencesImplementation;
52
import org.openide.DialogDescriptor;
53
import org.openide.DialogDisplayer;
54
import org.openide.util.NbBundle;
55
import org.openide.util.RequestProcessor;
56
import org.openide.util.lookup.ServiceProvider;
57
import org.openide.windows.WindowManager;
58
import static org.netbeans.modules.project.ui.problems.Bundle.*;
59
import org.openide.util.Parameters;
60
61
/**
62
 *
63
 * @author Tomas Zezula
64
 */
65
@ServiceProvider(service=BrokenReferencesImplementation.class)
66
public class BrokenReferencesImpl implements BrokenReferencesImplementation {
67
68
    private static final boolean suppressBrokenRefAlert = Boolean.getBoolean("BrokenReferencesSupport.suppressBrokenRefAlert"); //NOI18N
69
    private static final RequestProcessor RP = new RequestProcessor(BrokenReferencesImpl.class);
70
    private static int BROKEN_ALERT_TIMEOUT = 1000;
71
72
    private BrokenReferencesModel.Context context;
73
    private RequestProcessor.Task rpTask;
74
    
75
76
    @Override
77
    @NbBundle.Messages({
78
        "CTL_Broken_References_Resolve=Resolve Problems...",
79
        "AD_Broken_References_Resolve=N/A",
80
        "CTL_Broken_References_Close=Close",
81
        "AD_Broken_References_Close=N/A",
82
        "MSG_Broken_References_Title=Open Project",
83
        "LBL_Broken_References_Resolve_Panel_Close=Close",
84
        "AD_Broken_References_Resolve_Panel_Close=N/A",
85
        "LBL_Broken_References_Resolve_Panel_Title=Resolve Project Problems"
86
    })
87
    public void showAlert(Project project) {
88
        Parameters.notNull("project", project); //NOI18N
89
        if (!BrokenReferencesSettings.isShowAgainBrokenRefAlert() || suppressBrokenRefAlert) {
90
            return;
91
        } else if (context == null) {
92
            assert rpTask == null;
93
94
            final Runnable task = new Runnable() {
95
                @Override
96
                public void run() {
97
                    final BrokenReferencesModel.Context ctx;
98
                    synchronized (BrokenReferencesImpl.this) {
99
                        rpTask = null;
100
                        ctx = context;
101
                    }
102
                    if (ctx == null) {
103
                        return;
104
                    }
105
                    try {
106
                        final JButton resolveOption = new JButton(CTL_Broken_References_Resolve());
107
                        resolveOption.getAccessibleContext().setAccessibleDescription(AD_Broken_References_Resolve());
108
                        JButton closeOption = new JButton (CTL_Broken_References_Close());
109
                        closeOption.getAccessibleContext().setAccessibleDescription(AD_Broken_References_Close());
110
                        DialogDescriptor dd = new DialogDescriptor(new BrokenReferencesAlertPanel(),
111
                            MSG_Broken_References_Title(),
112
                            true,
113
                            new Object[] {resolveOption, closeOption},
114
                            closeOption,
115
                            DialogDescriptor.DEFAULT_ALIGN,
116
                            null,
117
                            null);
118
                        dd.setMessageType(DialogDescriptor.WARNING_MESSAGE);
119
                        ctx.addChangeListener(new ChangeListener() {
120
                            @Override
121
                            public void stateChanged(ChangeEvent e) {
122
                                resolveOption.setVisible(!ctx.isEmpty());
123
                            }
124
                        });
125
                        resolveOption.setVisible(!ctx.isEmpty());
126
                        if (DialogDisplayer.getDefault().notify(dd) == resolveOption) {
127
                            final BrokenReferencesModel model = new BrokenReferencesModel(ctx, true);
128
                            final BrokenReferencesCustomizer customizer = new BrokenReferencesCustomizer(model);
129
                            JButton close = new JButton (Bundle.LBL_Broken_References_Resolve_Panel_Close());
130
                            close.getAccessibleContext ().setAccessibleDescription (Bundle.AD_Broken_References_Resolve_Panel_Close());
131
                            dd = new DialogDescriptor(customizer,
132
                                Bundle.LBL_Broken_References_Resolve_Panel_Title(),
133
                                true,
134
                                new Object[] {closeOption},
135
                                closeOption,
136
                                DialogDescriptor.DEFAULT_ALIGN,
137
                                null,
138
                                null);
139
                            customizer.setNotificationLineSupport(dd.createNotificationLineSupport());
140
                            DialogDisplayer.getDefault().notify(dd);
141
                        }
142
                    } finally {
143
                        synchronized (BrokenReferencesImpl.this) {
144
                            //Clean seen references and start from empty list
145
                            context = null;
146
                        }
147
                    }
148
                }
149
            };
150
151
            context = new BrokenReferencesModel.Context();
152
            rpTask = RP.create(new Runnable() {
153
                @Override
154
                public void run() {
155
                    WindowManager.getDefault().invokeWhenUIReady(task);
156
                }
157
            });
158
        }
159
160
        assert context != null;
161
        if (project != null) {
162
            context.offer(project);
163
        }
164
        if (rpTask != null) {
165
            //Not yet shown, move
166
            rpTask.schedule(BROKEN_ALERT_TIMEOUT);
167
        }
168
    }
169
170
171
    @NbBundle.Messages({
172
        "LBL_BrokenLinksCustomizer_Close=Close",
173
        "ACSD_BrokenLinksCustomizer_Close=N/A",
174
        "LBL_BrokenLinksCustomizer_Title=Resolve Project Problems - \"{0}\" Project"
175
    })
176
    @Override
177
    public void showCustomizer(@NonNull Project project) {
178
        Parameters.notNull("project", project); //NOI18N
179
        BrokenReferencesModel model = new BrokenReferencesModel(project);
180
        BrokenReferencesCustomizer customizer = new BrokenReferencesCustomizer(model);
181
        JButton close = new JButton (LBL_BrokenLinksCustomizer_Close()); // NOI18N
182
        close.getAccessibleContext ().setAccessibleDescription (ACSD_BrokenLinksCustomizer_Close()); // NOI18N
183
        String projectDisplayName = ProjectUtils.getInformation(project).getDisplayName();
184
        DialogDescriptor dd = new DialogDescriptor(customizer,
185
            LBL_BrokenLinksCustomizer_Title(projectDisplayName), // NOI18N
186
            true, new Object[] {close}, close, DialogDescriptor.DEFAULT_ALIGN, null, null);
187
        customizer.setNotificationLineSupport(dd.createNotificationLineSupport());
188
        Dialog dlg = null;
189
        try {
190
            dlg = DialogDisplayer.getDefault().createDialog(dd);
191
            dlg.setVisible(true);
192
        } finally {
193
            if (dlg != null) {
194
                dlg.dispose();
195
            }
196
        }
197
    }
198
199
}
(-)a/java.project/src/org/netbeans/modules/java/project/BrokenReferencesModel.java (-616 / +99 lines)
Lines 42-94 Link Here
42
 * made subject to such option by the copyright holder.
42
 * made subject to such option by the copyright holder.
43
 */
43
 */
44
44
45
package org.netbeans.modules.java.project;
45
package org.netbeans.modules.project.ui.problems;
46
46
47
import java.io.File;
48
import java.io.IOException;
49
import java.lang.ref.Reference;
50
import java.lang.ref.WeakReference;
51
import java.net.URI;
52
import java.net.URL;
53
import java.util.ArrayList;
47
import java.util.ArrayList;
54
import java.util.Arrays;
55
import java.util.Collections;
48
import java.util.Collections;
56
import java.util.HashSet;
57
import java.util.LinkedHashSet;
49
import java.util.LinkedHashSet;
58
import java.util.LinkedList;
50
import java.util.LinkedList;
59
import java.util.List;
51
import java.util.List;
60
import java.util.Map;
61
import java.util.Set;
52
import java.util.Set;
62
import java.util.concurrent.Callable;
63
import java.util.logging.Level;
53
import java.util.logging.Level;
64
import java.util.logging.Logger;
54
import java.util.logging.Logger;
65
import java.util.regex.Matcher;
66
import java.util.regex.Pattern;
67
import javax.swing.AbstractListModel;
55
import javax.swing.AbstractListModel;
68
import javax.swing.event.ChangeEvent;
56
import javax.swing.event.ChangeEvent;
69
import javax.swing.event.ChangeListener;
57
import javax.swing.event.ChangeListener;
70
import org.netbeans.api.annotations.common.NonNull;
58
import org.netbeans.api.annotations.common.NonNull;
71
import org.netbeans.api.java.platform.JavaPlatform;
72
import org.netbeans.api.java.platform.JavaPlatformManager;
73
import org.netbeans.api.project.FileOwnerQuery;
74
import org.netbeans.api.project.Project;
59
import org.netbeans.api.project.Project;
75
import org.netbeans.api.project.ProjectManager;
76
import org.netbeans.api.project.ProjectUtils;
60
import org.netbeans.api.project.ProjectUtils;
77
import org.netbeans.api.project.libraries.Library;
78
import org.netbeans.api.project.libraries.LibraryManager;
79
import org.netbeans.spi.java.project.support.ui.BrokenReferencesSupport.LibraryDefiner;
80
import org.netbeans.spi.project.libraries.support.LibrariesSupport;
81
import org.netbeans.spi.project.support.ant.AntProjectHelper;
82
import org.netbeans.spi.project.support.ant.EditableProperties;
83
import org.netbeans.spi.project.support.ant.PropertyEvaluator;
84
import org.netbeans.spi.project.support.ant.PropertyUtils;
85
import org.netbeans.spi.project.support.ant.ReferenceHelper;
86
import org.openide.filesystems.FileObject;
87
import org.openide.util.ChangeSupport;
61
import org.openide.util.ChangeSupport;
88
import org.openide.util.Exceptions;
62
import org.netbeans.spi.project.ui.ProjectProblemsProvider;
89
import org.openide.util.Lookup;
63
import org.netbeans.spi.project.ui.ProjectProblemsProvider.ProjectProblem;
90
import static org.netbeans.modules.java.project.Bundle.*;
64
import org.openide.util.NbBundle;
91
import org.openide.util.NbBundle.Messages;
92
65
93
public final class BrokenReferencesModel extends AbstractListModel {
66
public final class BrokenReferencesModel extends AbstractListModel {
94
67
Lines 96-113 Link Here
96
69
97
    private final Context ctx;
70
    private final Context ctx;
98
    private final boolean global;
71
    private final boolean global;
99
    private List<OneReference> references;
72
    private List<ProblemReference> problems;
100
73
101
    public BrokenReferencesModel(AntProjectHelper helper, 
74
    BrokenReferencesModel(@NonNull final Context ctx, boolean global) {
102
            ReferenceHelper resolver, String[] props, String[] platformsProps) {
103
        this(new Context(new BrokenProject(helper, resolver, helper.getStandardPropertyEvaluator(), props, platformsProps)),false);
104
    }
105
106
    public BrokenReferencesModel(final @NonNull Context ctx, boolean global) {
107
        assert ctx != null;
75
        assert ctx != null;
108
        this.ctx = ctx;
76
        this.ctx = ctx;
109
        this.global = global;
77
        this.global = global;
110
        references = new ArrayList<OneReference>();
78
        problems = new ArrayList<ProblemReference>();
111
        refresh();
79
        refresh();
112
        ctx.addChangeListener(new ChangeListener() {
80
        ctx.addChangeListener(new ChangeListener() {
113
            @Override
81
            @Override
Lines 117-743 Link Here
117
        });
85
        });
118
    }
86
    }
119
87
120
    public void refresh() {
88
    BrokenReferencesModel(@NonNull final Project project) {
121
        Set<OneReference> all = new LinkedHashSet<OneReference>();
89
        this(new Context(), false);
122
        for (BrokenProject bprj : ctx.getBrokenProjects()) {
90
        this.ctx.offer(project);
123
            Set<OneReference> s = getReferences(bprj, false);
91
    }
124
            all.addAll(s);
92
125
            s = getPlatforms(bprj, false);
93
    @Override
126
            all.addAll(s);
94
    public Object getElementAt(int index) {
95
        return getOneReference(index);
96
    }    
97
98
    @Override
99
    public int getSize() {
100
        return problems.size();
101
    }
102
103
    void refresh() {
104
        final Set<ProblemReference> all = new LinkedHashSet<ProblemReference>();
105
        for (Project bprj : ctx.getBrokenProjects()) {
106
            final ProjectProblemsProvider ppp = bprj.getLookup().lookup(ProjectProblemsProvider.class);
107
            if (ppp != null) {
108
                for (ProjectProblem problem : ppp.getProblems()) {
109
                    all.add(new ProblemReference(problem, bprj, global));
110
                }
111
            }
127
        }
112
        }
128
        updateReferencesList(references, all);
113
        updateReferencesList(problems, all);
129
        this.fireContentsChanged(this, 0, getSize());
114
        this.fireContentsChanged(this, 0, getSize());
130
    }
115
    }
131
116
132
    @Messages({
117
    private ProblemReference getOneReference(int index) {
133
        "LBL_BrokenLinksCustomizer_BrokenLibrary=\"{0}\" library could not be found",
118
        assert index>=0 && index<problems.size();
134
        "LBL_BrokenLinksCustomizer_BrokenDefinableLibrary=\"{0}\" library must be defined",
119
        return problems.get(index);
135
        "LBL_BrokenLinksCustomizer_BrokenLibraryContent=\"{0}\" library has missing items",
136
        "LBL_BrokenLinksCustomizer_BrokenProjectReference=\"{0}\" project could not be found",
137
        "LBL_BrokenLinksCustomizer_BrokenFileReference=\"{0}\" file/folder could not be found",
138
        "LBL_BrokenLinksCustomizer_BrokenVariable=\"{0}\" variable could not be found",
139
        "LBL_BrokenLinksCustomizer_BrokenVariableContent=\"{0}\" variable based file/folder could not be found",
140
        "LBL_BrokenLinksCustomizer_BrokenPlatform=\"{0}\" platform could not be found",
141
        "LBL_BrokenLinksCustomizer_BrokenLibrary_In_Project=\"{0}\" library (in {1}) could not be found",
142
        "LBL_BrokenLinksCustomizer_BrokenDefinableLibrary_In_Project=\"{0}\" library (in {1}) must be defined",
143
        "LBL_BrokenLinksCustomizer_BrokenLibraryContent_In_Project=\"{0}\" library (in {1}) has missing items",
144
        "LBL_BrokenLinksCustomizer_BrokenProjectReference_In_Project=\"{0}\" project (in {1}) could not be found",
145
        "LBL_BrokenLinksCustomizer_BrokenFileReference_In_Project=\"{0}\" file/folder (in {1}) could not be found",
146
        "LBL_BrokenLinksCustomizer_BrokenVariable_In_Project=\"{0}\" variable (in {1}) could not be found",
147
        "LBL_BrokenLinksCustomizer_BrokenVariableContent_In_Project=\"{0}\" variable based file/folder (in {1}) could not be found",
148
        "LBL_BrokenLinksCustomizer_BrokenPlatform_In_Project=\"{0}\" platform (in {1}) could not be found"
149
    })
150
    public @Override Object getElementAt(int index) {
151
        final OneReference or = getOneReference(index);
152
        final Project prj = or.bprj.getProject();
153
        if (global && prj != null) {
154
            final String projectName = ProjectUtils.getInformation(prj).getDisplayName();
155
            switch (or.type) {
156
                case LIBRARY:
157
                    return LBL_BrokenLinksCustomizer_BrokenLibrary_In_Project(or.getDisplayID(), projectName);
158
                case DEFINABLE_LIBRARY:
159
                    return LBL_BrokenLinksCustomizer_BrokenDefinableLibrary_In_Project(or.getDisplayID(), projectName);
160
                case LIBRARY_CONTENT:
161
                    return LBL_BrokenLinksCustomizer_BrokenLibraryContent_In_Project(or.getDisplayID(), projectName);
162
                case PROJECT:
163
                    return LBL_BrokenLinksCustomizer_BrokenProjectReference_In_Project(or.getDisplayID(), projectName);
164
                case FILE:
165
                    return LBL_BrokenLinksCustomizer_BrokenFileReference_In_Project(or.getDisplayID(), projectName);
166
                case VARIABLE:
167
                    return LBL_BrokenLinksCustomizer_BrokenVariable_In_Project(or.getDisplayID(), projectName);
168
                case VARIABLE_CONTENT:
169
                    return LBL_BrokenLinksCustomizer_BrokenVariableContent_In_Project(or.getDisplayID(), projectName);
170
                case PLATFORM:
171
                    return LBL_BrokenLinksCustomizer_BrokenPlatform_In_Project(or.getDisplayID(), projectName);
172
                default:
173
                    assert false;
174
                    return null;
175
            }
176
        } else {
177
            switch (or.type) {
178
                case LIBRARY:
179
                    return LBL_BrokenLinksCustomizer_BrokenLibrary(or.getDisplayID());
180
                case DEFINABLE_LIBRARY:
181
                    return LBL_BrokenLinksCustomizer_BrokenDefinableLibrary(or.getDisplayID());
182
                case LIBRARY_CONTENT:
183
                    return LBL_BrokenLinksCustomizer_BrokenLibraryContent(or.getDisplayID());
184
                case PROJECT:
185
                    return LBL_BrokenLinksCustomizer_BrokenProjectReference(or.getDisplayID());
186
                case FILE:
187
                    return LBL_BrokenLinksCustomizer_BrokenFileReference(or.getDisplayID());
188
                case VARIABLE:
189
                    return LBL_BrokenLinksCustomizer_BrokenVariable(or.getDisplayID());
190
                case VARIABLE_CONTENT:
191
                    return LBL_BrokenLinksCustomizer_BrokenVariableContent(or.getDisplayID());
192
                case PLATFORM:
193
                    return LBL_BrokenLinksCustomizer_BrokenPlatform(or.getDisplayID());
194
                default:
195
                    assert false;
196
                    return null;
197
            }
198
        }
199
    }
200
201
    public OneReference getOneReference(int index) {
202
        assert index>=0 && index<references.size();
203
        return references.get(index);
204
    }
120
    }
205
    
121
    
206
    public boolean isBroken(int index) {
122
    private static void updateReferencesList(List<ProblemReference> oldBroken, Set<ProblemReference> newBroken) {
207
        return references.get(index).broken;
123
        LOG.log(Level.FINE, "References updated from {0} to {1}", new Object[] {oldBroken, newBroken});
208
    }
124
        for (ProblemReference or : oldBroken) {
209
    
125
            or.resolved = !newBroken.contains(or);
210
    public @Override int getSize() {
211
        return references.size();
212
    }
213
214
    public static boolean isBroken(AntProjectHelper helper, ReferenceHelper refHelper, PropertyEvaluator evaluator, String[] props, String[] platformsProps) {
215
        final BrokenProject bprj = new BrokenProject(helper, refHelper, evaluator, props, platformsProps);
216
        Set<OneReference> s = getReferences(bprj, true);
217
        if (s.size() > 0) {
218
            return true;
219
        }
126
        }
220
        s = getPlatforms(bprj, true);
127
        for (ProblemReference or : newBroken) {
221
        return s.size() > 0;
222
    }
223
224
    private static Set<OneReference> getReferences(final BrokenProject bprj, final boolean abortAfterFirstProblem) {
225
        Set<OneReference> set = new LinkedHashSet<OneReference>();
226
        StringBuilder all = new StringBuilder();
227
        // this call waits for list of libraries to be refreshhed
228
        LibraryManager.getDefault().getLibraries();
229
        final AntProjectHelper helper = bprj.getAntProjectHelper();
230
        final PropertyEvaluator evaluator = bprj.getEvaluator();
231
        final ReferenceHelper refHelper = bprj.getReferenceHelper();
232
        if (helper == null || evaluator == null || refHelper == null) {
233
            return set;
234
        }
235
        final String[] ps = bprj.getProperties();
236
        EditableProperties ep = helper.getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
237
        for (String p : ps) {
238
            // evaluate given property and tokenize it
239
            
240
            String prop = evaluator.getProperty(p);
241
            if (prop == null) {
242
                continue;
243
            }
244
            LOG.log(Level.FINE, "Evaluated {0}={1}", new Object[] {p, prop});
245
            String[] vals = PropertyUtils.tokenizePath(prop);
246
                        
247
            // no check whether after evaluating there are still some 
248
            // references which could not be evaluated
249
            for (String v : vals) {
250
                // we are checking only: project reference, file reference, library reference
251
                if (!(v.startsWith("${file.reference.") || v.startsWith("${project.") || v.startsWith("${libs.") || v.startsWith("${var."))) { // NOI18N
252
                    all.append(v);
253
                    continue;
254
                }
255
                if (v.startsWith("${project.")) { // NOI18N
256
                    // something in the form: "${project.<projID>}/dist/foo.jar"
257
                    String val = v.substring(2, v.indexOf('}')); // NOI18N
258
                    set.add(new OneReference(bprj, RefType.PROJECT, val, true));
259
                } else {
260
                    RefType type = RefType.LIBRARY;
261
                    if (v.startsWith("${file.reference")) { // NOI18N
262
                        type = RefType.FILE;
263
                    } else if (v.startsWith("${var")) { // NOI18N
264
                        type = RefType.VARIABLE;
265
                    }
266
                    String val = v.substring(2, v.length() - 1);
267
                    set.add(new OneReference(bprj, type, val, true));
268
                }
269
                if (abortAfterFirstProblem) {
270
                    break;
271
                }
272
            }
273
            if (set.size() > 0 && abortAfterFirstProblem) {
274
                break;
275
            }
276
            
277
            // test that resolved variable based property points to an existing file
278
            String path = ep.getProperty(p);
279
            if (path != null) {
280
                for (String v : PropertyUtils.tokenizePath(path)) {
281
                    if (v.startsWith("${file.reference.")) {    //NOI18N
282
                        v = ep.getProperty(v.substring(2, v.length() - 1));
283
                    }
284
                    if (v != null && v.startsWith("${var.")) {    //NOI18N
285
                        String value = evaluator.evaluate(v);
286
                        if (value.startsWith("${var.")) { // NOI18N
287
                            // this problem was already reported
288
                            continue;
289
                        }
290
                        File f = getFile(helper, evaluator, value);
291
                        if (f.exists()) {
292
                            continue;
293
                        }
294
                        set.add(new OneReference(bprj, RefType.VARIABLE_CONTENT, v, true));
295
                    }
296
                }
297
            }
298
            
299
        }
300
        
301
        // Check also that all referenced project really exist and are reachable.
302
        // If they are not report them as broken reference.
303
        // XXX: there will be API in PropertyUtils for listing of Ant 
304
        // prop names in String. Consider using it here.
305
        final Map<String, String> entries = evaluator.getProperties();
306
        if (entries == null) {
307
            throw new IllegalArgumentException("Properies mapping could not be computed (e.g. due to a circular definition). Evaluator: "+evaluator.toString());  //NOI18N
308
        }        
309
        for (Map.Entry<String, String> entry : entries.entrySet()) {
310
            String key = entry.getKey();
311
            String value = entry.getValue();
312
            if (key.startsWith("project.")) { // NOI18N
313
                if ("project.license".equals(key)) {    //NOI18N
314
                    continue;
315
                }
316
                File f = getFile(helper, evaluator, value);
317
                if (f.exists()) {
318
                    continue;
319
                }
320
                // Check that the value is really used by some property.
321
                // If it is not then ignore such a project.
322
                if (all.indexOf(value) == -1) {
323
                    continue;
324
                }
325
                set.add(new OneReference(bprj, RefType.PROJECT, key, true));
326
            }
327
            else if (key.startsWith("file.reference")) {    //NOI18N
328
                File f = getFile(helper, evaluator, value);
329
                String unevaluatedValue = ep.getProperty(key);
330
                boolean alreadyChecked = unevaluatedValue != null ? unevaluatedValue.startsWith("${var.") : false; // NOI18N
331
                if (f.exists() || all.indexOf(value) == -1 || alreadyChecked) { // NOI18N
332
                    continue;
333
                }
334
                set.add(new OneReference(bprj, RefType.FILE, key, true));
335
            }
336
        }
337
        
338
        //Check for libbraries with broken classpath content
339
        Set<String> usedLibraries = new HashSet<String>();
340
        Pattern libPattern = Pattern.compile("\\$\\{(libs\\.[-._a-zA-Z0-9]+\\.classpath)\\}"); //NOI18N
341
        for (String p : ps) {
342
            String propertyValue = ep.getProperty(p);
343
            if (propertyValue != null) {
344
                for (String v : PropertyUtils.tokenizePath(propertyValue)) {
345
                    Matcher m = libPattern.matcher(v);
346
                    if (m.matches()) {
347
                        usedLibraries.add (m.group(1));
348
                    }
349
                }
350
            }
351
        }
352
        for (String libraryRef : usedLibraries) {
353
            String libraryName = libraryRef.substring(5,libraryRef.length()-10);
354
            Library lib = refHelper.findLibrary(libraryName);
355
            if (lib == null) {
356
                // Should already have been caught before?
357
                set.add(new OneReference(bprj, RefType.LIBRARY, libraryRef, true));
358
            }
359
            else {
360
                //XXX: Should check all the volumes (sources, javadoc, ...)?
361
                for (URI uri : lib.getURIContent("classpath")) { // NOI18N
362
                    URI uri2 = LibrariesSupport.getArchiveFile(uri);
363
                    if (uri2 == null) {
364
                        uri2 = uri;
365
                    }
366
                    FileObject fo = LibrariesSupport.resolveLibraryEntryFileObject(lib.getManager().getLocation(), uri2);
367
                    if (null == fo && !canResolveEvaluatedUri(helper.getStandardPropertyEvaluator(), lib.getManager().getLocation(), uri2)) {
368
                        set.add(new OneReference(bprj, RefType.LIBRARY_CONTENT, libraryRef, true));
369
                        break;
370
                    }
371
                }
372
            }
373
        }
374
        
375
        return set;
376
    }
377
    
378
    /** Tests whether evaluated URI can be resolved. To support library entry 
379
     * like "${MAVEN_REPO}/struts/struts.jar".
380
     */
381
    private static boolean canResolveEvaluatedUri(PropertyEvaluator eval, URL libBase, URI libUri) {
382
        if (libUri.isAbsolute()) {
383
            return false;
384
        }
385
        String path = LibrariesSupport.convertURIToFilePath(libUri);
386
        String newPath = eval.evaluate(path);
387
        if (newPath.equals(path)) {
388
            return false;
389
        }
390
        URI newUri = LibrariesSupport.convertFilePathToURI(newPath);
391
        return null != LibrariesSupport.resolveLibraryEntryFileObject(libBase, newUri);
392
    }
393
394
    private static File getFile (AntProjectHelper helper, PropertyEvaluator evaluator, String name) {
395
        if (helper != null) {
396
            return new File(helper.resolvePath(name));
397
        } else {
398
            File f = new File(name);
399
            if (!f.exists()) {
400
                // perhaps the file is relative?
401
                String basedir = evaluator.getProperty("basedir"); // NOI18N
402
                assert basedir != null;
403
                f = new File(new File(basedir), name);
404
            }
405
            return f;
406
        }
407
    }
408
409
    private static Set<OneReference> getPlatforms(final BrokenProject bprj, boolean abortAfterFirstProblem) {
410
        final Set<OneReference> set = new LinkedHashSet<OneReference>();
411
        final PropertyEvaluator evaluator = bprj.getEvaluator();
412
        if (evaluator == null) {
413
            return set;
414
        }
415
        for (String pprop : bprj.getPlatformProperties()) {
416
            String prop = evaluator.getProperty(pprop);
417
            if (prop == null) {
418
                continue;
419
            }
420
            if (!existPlatform(prop)) {
421
                
422
                // XXX: the J2ME stores in project.properties also platform 
423
                // display name and so show this display name instead of just
424
                // prop ID if available.
425
                if (evaluator.getProperty(pprop + ".description") != null) { // NOI18N
426
                    prop = evaluator.getProperty(pprop + ".description"); // NOI18N
427
                }
428
                
429
                set.add(new OneReference(bprj, RefType.PLATFORM, prop, true));
430
            }
431
            if (set.size() > 0 && abortAfterFirstProblem) {
432
                break;
433
            }
434
        }
435
        return set;
436
    }
437
    
438
    private static void updateReferencesList(List<OneReference> oldBroken, Set<OneReference> newBroken) {
439
        LOG.log(Level.FINE, "References updated from {0} to {1}", new Object[] {oldBroken, newBroken});
440
        for (OneReference or : oldBroken) {
441
            if (newBroken.contains(or)) {
442
                or.broken = true;
443
            } else {
444
                or.broken = false;
445
            }
446
        }
447
        for (OneReference or : newBroken) {
448
            if (!oldBroken.contains(or)) {
128
            if (!oldBroken.contains(or)) {
449
                oldBroken.add(or);
129
                oldBroken.add(or);
450
            }
130
            }
451
        }
131
        }
452
    }
132
    }
453
    
454
    private static boolean existPlatform(String platform) {
455
        if (platform.equals("default_platform")) { // NOI18N
456
            return true;
457
        }
458
        for (JavaPlatform plat : JavaPlatformManager.getDefault().getInstalledPlatforms()) {
459
            // XXX: this should be defined as PROPERTY somewhere
460
            if (platform.equals(plat.getProperties().get("platform.ant.name")) && // NOI18N
461
                    plat.getInstallFolders().size() > 0) {
462
                return true;
463
            }
464
        }
465
        return false;
466
    }
467
133
468
    // XXX: perhaps could be moved to ReferenceResolver. 
134
    static final class ProblemReference {
469
    // But nobody should need it so it is here for now.
135
        private final boolean global;
470
    void updateReference(int index, File file) {
136
        private final Project project;
471
        updateReference0(index, file);
137
        final ProjectProblem problem;
472
        // #48210 - check whether the folder does not contain other jars
138
        
473
        // which could auto resolve some broken links:
474
        OneReference or = getOneReference(index);
475
        if (or.getType() != RefType.FILE) {
476
            return;
477
        }
478
        for (int i=0; i<getSize(); i++) {
479
            if (!isBroken(i) || i == index) {
480
                continue;
481
            }
482
            or = getOneReference(i);
483
            if (or.getType() != RefType.FILE) {
484
                continue;
485
            }
486
            File f = new File(file.getParentFile(), or.getDisplayID());
487
            if (f.exists()) {
488
                updateReference0(i, f);
489
            }
490
        }
491
    }
492
    
493
    private void updateReference0(int index, File file) {
494
        final OneReference ref = getOneReference(index);
495
        final String reference = ref.ID;
496
        final AntProjectHelper helper = ref.bprj.getAntProjectHelper();
497
        if (helper == null) {
498
            //Closed and freed project, ignore
499
            return;
500
        }
501
        FileObject myProjDirFO = helper.getProjectDirectory();
502
        final String propertiesFile = AntProjectHelper.PRIVATE_PROPERTIES_PATH;
503
        final String path = file.getAbsolutePath();
504
        Project p;
505
        try {
506
            p = ProjectManager.getDefault().findProject(myProjDirFO);
507
        } catch (IOException ex) {
508
            Exceptions.printStackTrace(ex);
509
            p = null;
510
        }
511
        final Project proj = p;
512
        ProjectManager.mutex().postWriteRequest(new Runnable() {
513
                public @Override void run() {
514
                    EditableProperties props = helper.getProperties(propertiesFile);
515
                    if (!path.equals(props.getProperty(reference))) {
516
                        props.setProperty(reference, path);
517
                        helper.putProperties(propertiesFile, props);
518
                    }
519
                                        
520
                    if (proj != null) {
521
                        try {
522
                            ProjectManager.getDefault().saveProject(proj);
523
                        } catch (IOException ex) {
524
                            Exceptions.printStackTrace(ex);
525
                        }
526
                    }
527
                }
528
            });
529
    }
530
    
531
    /** @return non-null library manager */
532
    LibraryManager getProjectLibraryManager(@NonNull final OneReference or) {
533
        assert or != null;
534
        final ReferenceHelper resolver = or.bprj.getReferenceHelper();
535
        return resolver == null ? null :
536
                resolver.getProjectLibraryManager() != null ?
537
                    resolver.getProjectLibraryManager() : LibraryManager.getDefault();
538
    }
539
139
540
    enum RefType {
140
        volatile boolean resolved;
541
        PROJECT,
542
        FILE,
543
        PLATFORM,
544
        LIBRARY,
545
        DEFINABLE_LIBRARY,
546
        LIBRARY_CONTENT,
547
        VARIABLE,
548
        VARIABLE_CONTENT,
549
    }
550
141
551
    public static final class OneReference {
552
142
553
        private final BrokenProject bprj;
143
        ProblemReference(
554
        private final RefType type;
144
                @NonNull final ProjectProblem problem,
555
        private boolean broken;
145
                @NonNull final Project project,
556
        private final String ID;
146
                final boolean global) {
557
        private final Callable<Library> definer;
147
            assert problem != null;
558
148
            this.problem = problem;
559
        OneReference(
149
            this.project = project;
560
            @NonNull final BrokenProject bprj,
150
            this.global = global;
561
            @NonNull RefType type,
562
            @NonNull final String ID,
563
            final boolean broken) {
564
            assert bprj != null;
565
            Callable<Library> _definer = null;
566
            if (type == RefType.LIBRARY) {
567
                String name = ID.substring(5, ID.length() - 10);
568
                for (LibraryDefiner ld : Lookup.getDefault().lookupAll(LibraryDefiner.class)) {
569
                    _definer = ld.missingLibrary(name);
570
                    if (_definer != null) {
571
                        type = RefType.DEFINABLE_LIBRARY;
572
                        break;
573
                    }
574
                }
575
            }
576
            this.bprj = bprj;
577
            this.type = type;
578
            this.ID = ID;
579
            this.broken = broken;
580
            definer = _definer;
581
        }
582
        
583
        public RefType getType() {
584
            return type;
585
        }
586
        
587
        public String getDisplayID() {
588
            switch (type) {
589
                
590
                case LIBRARY:
591
                case DEFINABLE_LIBRARY:
592
                case LIBRARY_CONTENT:
593
                    // libs.<name>.classpath
594
                    return ID.substring(5, ID.length()-10);
595
                    
596
                case PROJECT:
597
                    // project.<name>
598
                    return ID.substring(8);
599
                    
600
                case FILE:
601
                    // file.reference.<name>
602
                    return ID.substring(15);
603
                    
604
                case PLATFORM:
605
                    return ID;
606
                    
607
                case VARIABLE:
608
                    return ID.substring(4, ID.indexOf('}')); // NOI18N
609
                    
610
                case VARIABLE_CONTENT:
611
                    return ID.substring(6, ID.indexOf('}')) + ID.substring(ID.indexOf('}')+1); // NOI18N
612
                    
613
                default:
614
                    assert false;
615
                    return ID;
616
            }
617
        }
151
        }
618
152
619
        public Library define() throws Exception {
620
            return definer.call();
621
        }
622
153
623
        public @Override String toString() {
154
        String getDisplayName() {
624
            return type + ":" + ID + (broken ? "" : "[fixed]");
155
            final String displayName = problem.getDisplayName();
625
        }
156
            String message;
157
            if (global) {
158
                final String projectName = ProjectUtils.getInformation(project).getDisplayName();
159
                message = NbBundle.getMessage(
160
                        BrokenReferencesModel.class,
161
                        "FMT_ProblemInProject",
162
                        projectName,
163
                        displayName);
626
164
627
        public @Override boolean equals(Object o) {
165
            } else {
628
            if (o == this) {
166
                message = displayName;
629
                return true;
630
            }
167
            }
631
            if (!(o instanceof OneReference)) {
168
            return message;
632
                return false;
633
            }
634
            OneReference or = (OneReference)o;
635
            return (this.type == or.type && this.ID.equals(or.ID) && this.bprj.equals(or.bprj));
636
        }
637
        
638
        public @Override int hashCode() {
639
            int result = 7 * type.hashCode();
640
            result = 31*result + ID.hashCode();
641
            return result;
642
        }
643
        
644
    }
645
646
    public static final class BrokenProject {
647
        private final Reference<AntProjectHelper> helper;
648
        private final Reference<ReferenceHelper> referenceHelper;
649
        private final Reference<PropertyEvaluator> evaluator;
650
        private final String[] properties;
651
        private final String[] platformProperties;
652
653
        public BrokenProject(
654
            @NonNull final AntProjectHelper helper,
655
            @NonNull final ReferenceHelper referenceHelper,
656
            @NonNull final PropertyEvaluator evaluator,
657
            @NonNull final String[] properties,
658
            @NonNull final String[] platformProperties) {
659
            assert helper != null;
660
            assert referenceHelper != null;
661
            assert properties != null;
662
            assert platformProperties != null;
663
            this.helper = new WeakReference<AntProjectHelper>(helper);
664
            this.referenceHelper = new WeakReference<ReferenceHelper>(referenceHelper);
665
            this.evaluator = new WeakReference<PropertyEvaluator>(evaluator);
666
            this.properties = Arrays.copyOf(properties, properties.length);
667
            this.platformProperties = Arrays.copyOf(platformProperties, platformProperties.length);
668
        }
669
670
        AntProjectHelper getAntProjectHelper() {
671
            return helper.get();
672
        }
673
674
        Project getProject() {
675
            final AntProjectHelper h = getAntProjectHelper();
676
            return h == null ? null : FileOwnerQuery.getOwner(h.getProjectDirectory());
677
        }
678
679
        ReferenceHelper getReferenceHelper() {
680
            return referenceHelper.get();
681
        }
682
683
        PropertyEvaluator getEvaluator() {            
684
            return evaluator.get();
685
        }
686
687
        String[] getProperties() {
688
            return this.properties;
689
        }
690
691
        String[] getPlatformProperties() {
692
            return this.platformProperties;
693
        }
169
        }
694
170
695
        @Override
171
        @Override
696
        public boolean equals(final Object other) {
172
        @NonNull
697
            if (!(other instanceof BrokenProject)) {
173
        public String toString() {
698
                return false;
174
            return String.format(
699
            }
175
              "Problem: %s %s", //NOI18N
700
            final AntProjectHelper myAPH = getAntProjectHelper();
176
              problem,
701
            final AntProjectHelper otherAPH = ((BrokenProject)other).getAntProjectHelper();
177
              resolved ? "resolved" : "unresolved");    //NOI18N
702
            final FileObject myDir = myAPH == null ? null : myAPH.getProjectDirectory();
703
            final FileObject otherDir = otherAPH == null ? null : otherAPH.getProjectDirectory();
704
            return myDir == null ? otherDir == null : myDir.equals(otherDir);
705
        }
178
        }
706
179
707
        @Override
180
        @Override
708
        public int hashCode() {
181
        public int hashCode() {
709
            final AntProjectHelper h = getAntProjectHelper();
182
            int hash = 17;
710
            return h == null ? 0 : h.getProjectDirectory().hashCode();
183
            hash = 31 * hash + problem.hashCode();
184
            hash = 31 * hash + project.hashCode();
185
            return hash;
711
        }
186
        }
187
188
        @Override
189
        public boolean equals (Object other) {
190
            if (!(other instanceof ProblemReference)) {
191
                return false;
192
            }
193
            final ProblemReference otherRef = (ProblemReference) other;
194
            return problem.equals(otherRef.problem) &&
195
                   project.equals(otherRef.project);
196
        }
197
712
    }
198
    }
713
199
714
    public static final class Context {
200
    static final class Context {
715
        private final List<BrokenProject> toResolve;
201
        private final List<Project> toResolve;
716
        private final ChangeSupport support;
202
        private final ChangeSupport support;
717
203
718
        public Context() {
204
        public Context() {
719
            toResolve = Collections.synchronizedList(new LinkedList<BrokenProject>());
205
            toResolve = Collections.synchronizedList(new LinkedList<Project>());
720
            support = new ChangeSupport(this);
206
            support = new ChangeSupport(this);
721
        }
207
        }        
722
208
723
        private Context(final @NonNull BrokenProject broken) {
209
        public void offer(@NonNull final Project broken) {
724
            this();
725
            this.offer(broken);
726
        }
727
728
        public void offer(final BrokenProject broken) {
729
            assert broken != null;
210
            assert broken != null;
730
            this.toResolve.add(broken);
211
            if (broken.getLookup().lookup(ProjectProblemsProvider.class) != null) {
731
            support.fireChange();
212
                this.toResolve.add(broken);
213
                support.fireChange();
214
            }
732
        }
215
        }
733
216
734
        public boolean isEmpty() {
217
        public boolean isEmpty() {
735
            return this.toResolve.isEmpty();
218
            return this.toResolve.isEmpty();
736
        }
219
        }
737
220
738
        public BrokenProject[] getBrokenProjects() {
221
        public Project[] getBrokenProjects() {
739
            synchronized (toResolve) {
222
            synchronized (toResolve) {
740
                return toResolve.toArray(new BrokenProject[toResolve.size()]);
223
                return toResolve.toArray(new Project[toResolve.size()]);
741
            }
224
            }
742
        }
225
        }
743
226
(-)a/projectui/src/org/netbeans/modules/project/ui/problems/BrokenReferencesSettings.java (+68 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2012 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 2012 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.modules.project.ui.problems;
43
44
import java.util.prefs.Preferences;
45
import org.openide.util.NbPreferences;
46
47
/**
48
 *
49
 * @author Tomas Zezula
50
 */
51
public class BrokenReferencesSettings {
52
53
    private static final String PROP_SHOW_AGAIN_BROKEN_REF_ALERT = "showAgainBrokenRefAlert"; //NOI18N
54
55
    public static boolean isShowAgainBrokenRefAlert() {
56
        return prefs().getBoolean(PROP_SHOW_AGAIN_BROKEN_REF_ALERT, true);
57
    }
58
59
    public static void setShowAgainBrokenRefAlert(boolean again) {
60
        prefs().putBoolean(PROP_SHOW_AGAIN_BROKEN_REF_ALERT, again);
61
    }
62
63
64
    private static Preferences prefs() {
65
        return NbPreferences.forModule(BrokenReferencesSettings.class);
66
    }
67
68
}
(-)a/projectui/src/org/netbeans/modules/project/ui/problems/Bundle.properties (+21 lines)
Line 0 Link Here
1
MSG_Broken_References_Label=<html><b>Project Problems</b></html>
2
MSG_BrokenReferencesAlertPanel_notAgain=&Do not show this message again
3
ACSN_BrokenReferencesAlertPanel_notAgain=Do not show this message again
4
ACSD_BrokenReferencesAlertPanel_notAgain=N/A
5
MSG_Broken_References=<html>One or more project resources could not be found.<br>Right-click the project in the Projects window and choose<br><b>Resolve Project Problems</b> to find the missing resources.</html>
6
ACSN_BrokenReferencesAlertPanel=Broken Project Panel
7
ACSD_BrokenReferencesAlertPanel=N/A
8
FMT_ProblemInProject={1} (in {0})
9
10
LBL_BrokenLinksCustomizer_List=Project &Problems:
11
ACSD_BrokenLinksCustomizer_List=N/A
12
LBL_BrokenLinksCustomizer_Fix=&Resolve...
13
ACSD_BrokenLinksCustomizer_Fix=N/A
14
LBL_BrokenLinksCustomizer_Description=&Description\:
15
ACSD_BrokenLinksCustomizer_Description=N/A
16
ACSN_BrokenReferencesCustomizer=Broken Project Customizer
17
ACSD_BrokenReferencesCustomizer=N/A
18
19
LBL_Fix_Broken_Links_Action=Resolve Project Problems...
20
MSG_OneProblem= Unresolved project problem, fix by "Resolve Project Problems".
21
MSG_MoreProblems= {0} unresolved project problems, fix by "Resolve Project Problems".
(-)a/projectuiapi/apichanges.xml (+19 lines)
Lines 107-112 Link Here
107
    <!-- ACTUAL CHANGES BEGIN HERE: -->
107
    <!-- ACTUAL CHANGES BEGIN HERE: -->
108
108
109
    <changes>
109
    <changes>
110
        <change id="ProjectProblems">
111
            <api name="general"/>
112
            <summary>Added an SPI to provide project metadata problems.</summary>
113
            <version major="1" minor="60"/>
114
            <date day="24" month="7" year="2012"/>
115
            <author login="tzezula"/>
116
            <compatibility addition="yes"/>
117
            <description>
118
                <p>
119
                    Added a SPI allowing project to provide project metadata problems.
120
                    These problems are shown in the Resolve Project Problems Customizer,
121
                    project with project problems are annotated by broken project badge.
122
                </p>
123
            </description>
124
            <class package="org.netbeans.api.project.ui" name="ProjectProblems"/>
125
            <class package="org.netbeans.spi.project.ui" name="ProjectProblemsProvider"/>
126
            <class package="org.netbeans.spi.project.ui" name="ProjectProblemResolver"/>
127
            <issue number="215628"/>
128
        </change>
110
        <change id="PathFinder">
129
        <change id="PathFinder">
111
            <api name="general"/>
130
            <api name="general"/>
112
            <summary>Extracted <code>findPath</code> method into a new interface <code>PathFinder</code></summary>
131
            <summary>Extracted <code>findPath</code> method into a new interface <code>PathFinder</code></summary>
(-)a/projectuiapi/nbproject/project.properties (-1 / +1 lines)
Lines 42-48 Link Here
42
42
43
javac.compilerargs=-Xlint -Xlint:-serial
43
javac.compilerargs=-Xlint -Xlint:-serial
44
javac.source=1.6
44
javac.source=1.6
45
spec.version.base=1.59.0
45
spec.version.base=1.60.0
46
is.autoload=true
46
is.autoload=true
47
javadoc.arch=${basedir}/arch.xml
47
javadoc.arch=${basedir}/arch.xml
48
javadoc.apichanges=${basedir}/apichanges.xml
48
javadoc.apichanges=${basedir}/apichanges.xml
(-)a/projectuiapi/src/org/netbeans/api/project/ui/ProjectProblems.java (+112 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2012 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 2012 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.api.project.ui;
43
44
import org.netbeans.api.annotations.common.NonNull;
45
import org.netbeans.api.project.Project;
46
import org.netbeans.modules.project.uiapi.BrokenReferencesImplementation;
47
import org.netbeans.spi.project.ui.ProjectProblemsProvider;
48
import org.openide.util.Lookup;
49
import org.openide.util.Parameters;
50
51
/**
52
 * Support for managing project problems. Project freshly checkout from
53
 * VCS can has broken references of several types: reference to other project,
54
 * reference to a foreign file, reference to an external source root, reference
55
 * to a library, etc. This class has helper methods for detection of these problems
56
 * and for fixing them.
57
 *
58
 * @see ProjectProblemsProvider
59
 * 
60
 * @since 1.60
61
 * @author Tomas Zezula
62
 */
63
public class ProjectProblems {
64
65
    private ProjectProblems() {
66
        throw new IllegalStateException();
67
    }
68
69
70
    /**
71
     * Checks whether the project has some project problem.
72
     * @param project the project to test for existence of project problems like broken references, missing server, etc.
73
     * @return true if some problem was found and it is necessary to give
74
     *  user a chance to fix them.
75
     */
76
    public static boolean isBroken(@NonNull final Project project) {
77
        Parameters.notNull("project", project); //NOI18N
78
        final ProjectProblemsProvider provider = project.getLookup().lookup(ProjectProblemsProvider.class);
79
        return provider !=null && !provider.getProblems().isEmpty();
80
    }
81
82
83
    /**
84
     * Show alert message box informing user that a project has broken
85
     * references. This method can be safely called from any thread, e.g. during
86
     * the project opening, and it will take care about showing message box only
87
     * once for several subsequent calls during a timeout.
88
     * The alert box has also "show this warning again" check box and provides resolve
89
     * broken references option
90
     * @param project to show the alert for.
91
     */
92
    public static void showAlert(@NonNull final Project project) {
93
        Parameters.notNull("project", project); //NOI18N
94
        final BrokenReferencesImplementation impl = Lookup.getDefault().lookup(BrokenReferencesImplementation.class);
95
        if (impl != null) {
96
            impl.showAlert(project);
97
        }
98
    }
99
100
    /**
101
     * Shows an UI customizer which gives users chance to fix encountered problems.
102
     * @param project the project for which the customizer should be shown.
103
     */
104
    public static void showCustomizer(@NonNull final Project project) {
105
        Parameters.notNull("project", project); //NOI18N
106
        final BrokenReferencesImplementation impl = Lookup.getDefault().lookup(BrokenReferencesImplementation.class);
107
        if (impl != null) {
108
            impl.showCustomizer(project);
109
        }
110
    }
111
112
}
(-)a/projectuiapi/src/org/netbeans/modules/project/uiapi/BrokenReferencesImplementation.java (+57 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2012 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 2012 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.modules.project.uiapi;
43
44
import org.netbeans.api.annotations.common.NonNull;
45
import org.netbeans.api.project.Project;
46
47
/**
48
 *
49
 * @author Tomas Zezula
50
 */
51
public interface BrokenReferencesImplementation {
52
    
53
    void showAlert(@NonNull Project project);
54
55
    void showCustomizer(@NonNull Project project);
56
57
}
(-)a/projectuiapi/src/org/netbeans/spi/project/ui/ProjectProblemResolver.java (+82 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2012 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 2012 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.spi.project.ui;
43
44
import java.util.concurrent.Future;
45
46
/**
47
 * A fix of the {@link ProjectProblemsProvider.ProjectProblem}.
48
 * As the {@link ProjectProblemResolver} is the only project specific
49
 * part of the {@link ProjectProblemsProvider.ProjectProblem} it's used
50
 * by {@link ProjectProblemsProvider.ProjectProblem#hashCode} and
51
 * {@link ProjectProblemsProvider.ProjectProblem#equals} and therefore
52
 * it should provide reasonable {@link Object#hashCode} and {@link Object#equals}
53
 * implementation, for example based on project property which should be fixed.
54
 *
55
 * @since 1.60
56
 * @author Tomas Zezula
57
 */
58
public interface ProjectProblemResolver {
59
60
    /**
61
     * Resolves the project problem.
62
     * Resolves the {@link ProjectProblemsProvider.ProjectProblem}
63
     * returning the {@link Future} holding the resolution result.
64
     * The method is called by the Event Dispatch Thread. When the
65
     * resolution needs to be done by a background thread, eg. downloading
66
     * an archive from repository, the implementation directly returns
67
     * a {@link Future} which is completed by the background thread.
68
     * @return the {@link Future} holding the resolution result.
69
     */
70
    Future<ProjectProblemsProvider.Result> resolve();
71
72
    /**
73
     * {@inheritDoc}
74
     */
75
    boolean equals(Object other);
76
77
    /**
78
     * {@inheritDoc}
79
     */
80
    int hashCode();
81
82
}
(-)a/projectuiapi/src/org/netbeans/spi/project/ui/ProjectProblemsProvider.java (+338 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2012 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 2012 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.spi.project.ui;
43
44
import java.beans.PropertyChangeListener;
45
import java.util.Collection;
46
import java.util.concurrent.Future;
47
import org.netbeans.api.annotations.common.CheckForNull;
48
import org.netbeans.api.annotations.common.NonNull;
49
import org.netbeans.api.annotations.common.NullAllowed;
50
import org.netbeans.spi.project.ui.support.UILookupMergerSupport;
51
import org.openide.util.Parameters;
52
53
/**
54
 * Provider of project metadata problems.
55
 * The provider of various project metadata problems like broken reference to source root,
56
 * broken reference to a library, wrong compiler options, etc.
57
 * The provider implementation(s) are registered into the project lookup as well as
58
 * the {@link org.netbeans.spi.project.LookupMerger} for them
59
 * {@link UILookupMergerSupport#createProjectProblemsProviderMerger}.
60
 *
61
 * <div class="nonnormative">
62
 * <p>The presence of the {@link ProjectProblemsProvider} in the project lookup
63
 * automatically enable the broken project metadata badge on the project.
64
 * If the project wants to provide the "Resolve Broken Project" action it needs
65
 * to add a reference to the "org.netbeans.modules.project.ui.problems.BrokenProjectActionFactory"
66
 * action with required position, for example using the ActionRefrecne annotation:
67
 * <pre>
68
 * &#64;ActionReferences({
69
    &#64;ActionReference(
70
        id=&#64;ActionID(id="org.netbeans.modules.project.ui.problems.BrokenProjectActionFactory",category="Project"),
71
        position = 2600,
72
        path = "Projects/org-netbeans-modules-myproject/Actions")
73
})
74
 * </pre>
75
 * </p>
76
 * </div>
77
 *
78
 * @author Tomas Zezula
79
 * @since 1.60
80
 */
81
public interface ProjectProblemsProvider {
82
83
    /**
84
     * Name of the problems property.
85
     */
86
    String PROP_PROBLEMS = "problems";  //NOI18N
87
88
89
    /**
90
     * Adds a {@link PropertyChangeListener} listening on change of project
91
     * metadata problems.
92
     * @param listener the listener to be added.
93
     */
94
    void addPropertyChangeListener(@NonNull PropertyChangeListener listener);
95
96
    /**
97
     * Removes a {@link PropertyChangeListener} listening on change of project
98
     * metadata problems.
99
     * @param listener the listener to be removed.
100
     */
101
    void removePropertyChangeListener (@NonNull PropertyChangeListener listener);
102
103
    /**
104
     * Returns project metadata problems found by this {@link ProjectProblemsProvider}.
105
     * @return the problems
106
     */
107
    @NonNull
108
    Collection<? extends ProjectProblem> getProblems();
109
110
111
    /**
112
     * The {@link ProjectProblem} resolution status.
113
     */
114
    enum Status {
115
        RESOLVED,
116
        RESOLVED_WITH_WARNING,
117
        UNRESOLVED
118
    }
119
120
121
    /**
122
     * The {@link ProjectProblem} severity.
123
     */
124
    enum Severity {
125
        ERROR,
126
        WARNING
127
    }
128
129
    /**
130
     * Result of the project metadata problem resolution.
131
     */
132
    public final class Result {
133
        private final Status status;
134
        private final String message;
135
136
        private Result(
137
            @NonNull final Status status,
138
            @NullAllowed final String message) {
139
            this.status = status;
140
            this.message = message;
141
        }
142
143
        /**
144
         * Returns true if the problem was resolved.
145
         * @return true if the problem was successfully resolved.
146
         */
147
        public boolean isResolved() {
148
            return status != Status.UNRESOLVED;
149
        }
150
151
        /**
152
         * Returns status of the resolution.
153
         * @return the {@link ProjectProblemsProvider.Status}
154
         */
155
        @NonNull
156
        public Status getStatus() {
157
            return status;
158
        }
159
160
        /**
161
         * Returns possible error or warning message.
162
         * @return the message which should be presented to the user.
163
         */
164
        @CheckForNull
165
        public String getMessage() {
166
            return message;
167
        }
168
169
        /**
170
         * Creates a new instance of the {@link Result}.
171
         * @param status the status of the project problem resolution.
172
         * @return the new {@link Result} instance.
173
         */
174
        public static Result create(
175
            @NonNull final Status status) {
176
            Parameters.notNull("status", status);   //NOI18N
177
            return new Result(status, null);
178
        }
179
180
        /**
181
         * Creates a new instance of the {@link Result}.
182
         * @param status the status of the project problem resolution.
183
         * @param message the message which should be presented to the user.
184
         * @return the new {@link Result} instance.
185
         */
186
        public static Result create(
187
            @NonNull final Status status,
188
            @NonNull final String message) {
189
            Parameters.notNull("status", status);   //NOI18N
190
            Parameters.notNull("message", message);   //NOI18N
191
            return new Result(status, message);
192
        }
193
194
    }
195
196
197
    /**
198
     * Project metadata problem.
199
     * Represents a problem in the project metadata which should be presented
200
     * to the user and resolved.
201
     */
202
    public final class ProjectProblem {
203
204
        private final Severity severity;
205
        private final String displayName;
206
        private final String description;
207
        private final ProjectProblemResolver resolver;
208
209
        private ProjectProblem(
210
                @NonNull final Severity severity,
211
                @NonNull final String displayName,
212
                @NonNull final String description,
213
                @NonNull final ProjectProblemResolver resolver) {
214
            Parameters.notNull("severity", severity); //NOI18N
215
            Parameters.notNull("displayName", displayName); //NOI18N
216
            Parameters.notNull("description", description); //NOI18N
217
            Parameters.notNull("resolver", resolver);   //NOI18N
218
            this.severity = severity;
219
            this.displayName = displayName;
220
            this.description = description;
221
            this.resolver = resolver;
222
        }
223
224
        /**
225
         * Returns a {@link ProjectProblem} severity.
226
         * @return the {@link Severity}
227
         */
228
        @NonNull
229
        public Severity getSeverity() {
230
            return severity;
231
        }
232
233
        /**
234
         * Returns a display name of the problem.
235
         * The display name is presented to the user in the UI.
236
         * @return the display name.
237
         */
238
        @NonNull
239
        public String getDisplayName() {
240
            return displayName;
241
        }        
242
243
        /**
244
         * Returns the description of the problem.
245
         * The description is shown in the project problems details.
246
         * @return project problem description.
247
         */
248
        @NonNull
249
        public String getDescription() {
250
            return description;
251
        }
252
253
        /**
254
         * Resolves the problem.
255
         * Called by the Event Dispatch Thread.
256
         * When the resolution needs to be done by a background thread, eg. downloading
257
         * an archive from repository, the implementation directly returns
258
         * a {@link Future} which is completed by the background thread.
259
         * @return the {@link Future} holding the problem resolution status.
260
         */
261
        public Future<Result> resolve() {
262
            return resolver.resolve();
263
        }
264
265
        /**
266
         * {@inheritDoc}
267
         */
268
        @Override
269
        public boolean equals(final Object other) {
270
            if (other == this) {
271
                return true;
272
            }
273
            if (!(other instanceof ProjectProblem)) {
274
                return false;
275
            }
276
            final  ProjectProblem otherProblem = (ProjectProblem) other;
277
            return displayName.equals(otherProblem.displayName) &&
278
                description.equals(otherProblem.description) &&
279
                resolver.equals(otherProblem.resolver);
280
        }
281
282
        /**
283
         * {@inheritdoc}
284
         */
285
        @Override
286
        public int hashCode() {
287
            int result = 17;
288
            result = 31 * result + displayName.hashCode();
289
            result = 31 * result + description.hashCode();
290
            result = 31 * result + resolver.hashCode();
291
            return result;
292
        }
293
294
        /**
295
         * {@inheritdoc}
296
         */
297
        @Override
298
        public String toString() {
299
            return String.format(
300
             "Project Problem: %s, resolvable by: %s",  //NOI18N
301
             displayName,
302
             resolver);
303
        }
304
305
306
        /**
307
         * Creates a new instance of the {@link ProjectProblem} with error {@link Severity}.
308
         * @param displayName the project problem display name.
309
         * @param description the project problem description.
310
         * @param resolver the {@link ProjectProblemResolver} to resolve the problem.
311
         * @return a new instance of {@link ProjectProblem}
312
         */
313
        @NonNull
314
        public static ProjectProblem createError(
315
                @NonNull final String displayName,
316
                @NonNull final String description,
317
                @NonNull final ProjectProblemResolver resolver) {            
318
            return new ProjectProblem(Severity.ERROR, displayName,description,resolver);
319
        }
320
321
        /**
322
         * Creates a new instance of the {@link ProjectProblem} with warning {@link Severity}.
323
         * @param displayName the project problem display name.
324
         * @param description the project problem description.
325
         * @param resolver the {@link ProjectProblemResolver} to resolve the problem.
326
         * @return a new instance of {@link ProjectProblem}
327
         */
328
        @NonNull
329
        public static ProjectProblem createWarning(
330
                @NonNull final String displayName,
331
                @NonNull final String description,
332
                @NonNull final ProjectProblemResolver resolver) {
333
            return new ProjectProblem(Severity.WARNING, displayName,description,resolver);
334
        }
335
336
    }
337
338
}
(-)a/projectuiapi/src/org/netbeans/spi/project/ui/support/UILookupMergerSupport.java (-1 / +96 lines)
Lines 45-59 Link Here
45
45
46
package org.netbeans.spi.project.ui.support;
46
package org.netbeans.spi.project.ui.support;
47
47
48
import java.beans.PropertyChangeEvent;
49
import java.beans.PropertyChangeListener;
50
import java.beans.PropertyChangeSupport;
51
import java.lang.ref.Reference;
52
import java.lang.ref.WeakReference;
48
import java.util.Arrays;
53
import java.util.Arrays;
54
import java.util.Collection;
55
import java.util.Collections;
49
import java.util.LinkedHashSet;
56
import java.util.LinkedHashSet;
50
import java.util.Set;
57
import java.util.Set;
58
import org.netbeans.api.annotations.common.NonNull;
51
import org.netbeans.modules.project.uiapi.ProjectOpenedTrampoline;
59
import org.netbeans.modules.project.uiapi.ProjectOpenedTrampoline;
52
import org.netbeans.spi.project.LookupMerger;
60
import org.netbeans.spi.project.LookupMerger;
53
import org.netbeans.spi.project.ui.PrivilegedTemplates;
61
import org.netbeans.spi.project.ui.PrivilegedTemplates;
54
import org.netbeans.spi.project.ui.ProjectOpenedHook;
62
import org.netbeans.spi.project.ui.ProjectOpenedHook;
63
import org.netbeans.spi.project.ui.ProjectProblemsProvider;
55
import org.netbeans.spi.project.ui.RecommendedTemplates;
64
import org.netbeans.spi.project.ui.RecommendedTemplates;
56
import org.openide.util.Lookup;
65
import org.openide.util.Lookup;
66
import org.openide.util.Parameters;
67
import org.openide.util.WeakListeners;
57
68
58
/**
69
/**
59
 * Factory class for creation of {@link org.netbeans.spi.project.LookupMerger} instances.
70
 * Factory class for creation of {@link org.netbeans.spi.project.LookupMerger} instances.
Lines 98-103 Link Here
98
    public static LookupMerger<ProjectOpenedHook> createProjectOpenHookMerger(ProjectOpenedHook defaultInstance) {
109
    public static LookupMerger<ProjectOpenedHook> createProjectOpenHookMerger(ProjectOpenedHook defaultInstance) {
99
        return new OpenMerger(defaultInstance);
110
        return new OpenMerger(defaultInstance);
100
    }
111
    }
112
113
114
    /**
115
     * Create a {@link org.netbeans.spi.project.LookupMerger} instance
116
     * for {@link org.netbeans.spi.project.ui.ProjectProblemsProvider}. The merger
117
     * collects all {@link org.netbeans.spi.project.ui.ProjectProblemsProvider.ProjectProblem}s
118
     * from {@link org.netbeans.spi.project.ui.ProjectProblemsProvider}s registered in the
119
     * project lookup.
120
     * @return instance to include in project lookup
121
     * @since  1.60
122
     */
123
    public static LookupMerger<ProjectProblemsProvider> createProjectProblemsProviderMerger() {
124
        return new ProjectProblemsProviderMerger();
125
    }
101
    
126
    
102
    private static class PrivilegedMerger implements LookupMerger<PrivilegedTemplates> {
127
    private static class PrivilegedMerger implements LookupMerger<PrivilegedTemplates> {
103
        public Class<PrivilegedTemplates> getMergeableClass() {
128
        public Class<PrivilegedTemplates> getMergeableClass() {
Lines 135-140 Link Here
135
        }
160
        }
136
        
161
        
137
    }
162
    }
163
164
    private static class ProjectProblemsProviderMerger implements LookupMerger<ProjectProblemsProvider> {
165
166
        @Override
167
        public Class<ProjectProblemsProvider> getMergeableClass() {
168
            return ProjectProblemsProvider.class;
169
        }
170
171
        @Override
172
        public ProjectProblemsProvider merge(Lookup lookup) {
173
            return new ProjectProblemsProviderImpl(lookup);
174
        }
175
176
    }
138
    
177
    
139
    private static class PrivilegedTemplatesImpl implements PrivilegedTemplates {
178
    private static class PrivilegedTemplatesImpl implements PrivilegedTemplates {
140
        
179
        
Lines 215-219 Link Here
215
        
254
        
216
    }
255
    }
217
    
256
    
218
    
257
    private static class ProjectProblemsProviderImpl implements ProjectProblemsProvider, PropertyChangeListener {
258
259
        private final Lookup lkp;
260
        private final PropertyChangeSupport support;
261
        //@GuardedBy("this")
262
        private Iterable<? extends Reference<ProjectProblemsProvider>> providers;
263
264
265
        ProjectProblemsProviderImpl(@NonNull final Lookup lkp) {
266
            Parameters.notNull("lkp", lkp); //NOI18N
267
            this.lkp = lkp;
268
            this.support = new PropertyChangeSupport(this);
269
        }
270
271
        @Override
272
        public void addPropertyChangeListener(PropertyChangeListener listener) {
273
            Parameters.notNull("listener", listener);   //NOI18N
274
            support.addPropertyChangeListener(listener);
275
        }
276
277
        @Override
278
        public void removePropertyChangeListener(PropertyChangeListener listener) {
279
            Parameters.notNull("listener", listener);   //NOI18N
280
            support.removePropertyChangeListener(listener);
281
        }
282
283
        @Override
284
        public Collection<? extends ProjectProblem> getProblems() {
285
            final Collection<ProjectProblem> problems = new LinkedHashSet<ProjectProblem>();
286
            for (Reference<ProjectProblemsProvider> providerRef : getProviders()) {
287
                final ProjectProblemsProvider provider = providerRef.get();
288
                if (provider != null) {
289
                    problems.addAll(provider.getProblems());
290
                }
291
            }
292
            return Collections.unmodifiableCollection(problems);
293
        }
294
295
        @Override
296
        public void propertyChange(PropertyChangeEvent evt) {
297
            if (PROP_PROBLEMS.equals(evt.getPropertyName())) {
298
                support.firePropertyChange(PROP_PROBLEMS,null,null);
299
            }
300
        }
301
302
        private synchronized Iterable<? extends Reference<ProjectProblemsProvider>> getProviders() {
303
            if (providers == null) {
304
                final Collection<Reference<ProjectProblemsProvider>> newProviders = new LinkedHashSet<Reference<ProjectProblemsProvider>>();
305
                for (ProjectProblemsProvider provider : lkp.lookupAll(ProjectProblemsProvider.class)) {
306
                    provider.addPropertyChangeListener(WeakListeners.propertyChange(this, provider));
307
                    newProviders.add(new WeakReference<ProjectProblemsProvider>(provider));
308
                }
309
                providers = newProviders;
310
            }
311
            return providers;
312
        }
313
    }
219
}
314
}

Return to bug 215628