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 / +7 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 88-94 Link Here
88
import org.netbeans.api.project.ant.AntBuildExtender;
84
import org.netbeans.api.project.ant.AntBuildExtender;
89
import org.netbeans.api.project.libraries.Library;
85
import org.netbeans.api.project.libraries.Library;
90
import org.netbeans.api.project.libraries.LibraryManager;
86
import org.netbeans.api.project.libraries.LibraryManager;
91
import org.netbeans.api.project.ui.OpenProjects;
92
import org.netbeans.api.queries.FileBuiltQuery.Status;
87
import org.netbeans.api.queries.FileBuiltQuery.Status;
93
import org.netbeans.modules.java.api.common.Roots;
88
import org.netbeans.modules.java.api.common.Roots;
94
import org.netbeans.modules.java.api.common.SourceRoots;
89
import org.netbeans.modules.java.api.common.SourceRoots;
Lines 139-145 Link Here
139
import org.openide.util.Mutex;
134
import org.openide.util.Mutex;
140
import org.openide.util.MutexException;
135
import org.openide.util.MutexException;
141
import org.openide.util.NbBundle;
136
import org.openide.util.NbBundle;
142
import org.openide.util.RequestProcessor;
143
import org.openide.util.lookup.Lookups;
137
import org.openide.util.lookup.Lookups;
144
import org.w3c.dom.Document;
138
import org.w3c.dom.Document;
145
import org.w3c.dom.Element;
139
import org.w3c.dom.Element;
Lines 151-157 Link Here
151
import static org.netbeans.spi.project.support.ant.GeneratedFilesHelper.FLAG_OLD_STYLESHEET;
145
import static org.netbeans.spi.project.support.ant.GeneratedFilesHelper.FLAG_OLD_STYLESHEET;
152
import static org.netbeans.spi.project.support.ant.GeneratedFilesHelper.FLAG_UNKNOWN;
146
import static org.netbeans.spi.project.support.ant.GeneratedFilesHelper.FLAG_UNKNOWN;
153
import org.netbeans.spi.whitelist.support.WhiteListQueryMergerSupport;
147
import org.netbeans.spi.whitelist.support.WhiteListQueryMergerSupport;
154
import org.openide.filesystems.FileLock;
155
import org.openide.filesystems.URLMapper;
148
import org.openide.filesystems.URLMapper;
156
import org.openide.modules.SpecificationVersion;
149
import org.openide.modules.SpecificationVersion;
157
/**
150
/**
Lines 189-195 Link Here
189
    };
182
    };
190
    private static final Icon J2SE_PROJECT_ICON = ImageUtilities.loadImageIcon("org/netbeans/modules/java/j2seproject/ui/resources/j2seProject.png", false); // NOI18N
183
    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());
184
    private static final Logger LOG = Logger.getLogger(J2SEProject.class.getName());
192
    private static final RequestProcessor RP = new RequestProcessor(J2SEProject.class.getName(), 1);
193
185
194
    private final AuxiliaryConfiguration aux;
186
    private final AuxiliaryConfiguration aux;
195
    private final AntProjectHelper helper;
187
    private final AntProjectHelper helper;
Lines 362-368 Link Here
362
    }
354
    }
363
355
364
    private Lookup createLookup(final AuxiliaryConfiguration aux) {
356
    private Lookup createLookup(final AuxiliaryConfiguration aux) {
365
        FileEncodingQueryImplementation encodingQuery = QuerySupport.createFileEncodingQuery(evaluator(), J2SEProjectProperties.SOURCE_ENCODING);
357
        final FileEncodingQueryImplementation encodingQuery = QuerySupport.createFileEncodingQuery(evaluator(), J2SEProjectProperties.SOURCE_ENCODING);
358
        final J2SELogicalViewProvider lvp = new J2SELogicalViewProvider(this, this.updateHelper, evaluator(), refHelper);
366
        final Lookup base = Lookups.fixed(
359
        final Lookup base = Lookups.fixed(
367
            J2SEProject.this,
360
            J2SEProject.this,
368
            QuerySupport.createProjectInformation(updateHelper, this, J2SE_PROJECT_ICON),
361
            QuerySupport.createProjectInformation(updateHelper, this, J2SE_PROJECT_ICON),
Lines 370-376 Link Here
370
            helper.createCacheDirectoryProvider(),
363
            helper.createCacheDirectoryProvider(),
371
            helper.createAuxiliaryProperties(),
364
            helper.createAuxiliaryProperties(),
372
            refHelper.createSubprojectProvider(),
365
            refHelper.createSubprojectProvider(),
373
            new J2SELogicalViewProvider(this, this.updateHelper, evaluator(), refHelper),
366
            lvp,
374
            // new J2SECustomizerProvider(this, this.updateHelper, evaluator(), refHelper),
367
            // new J2SECustomizerProvider(this, this.updateHelper, evaluator(), refHelper),
375
            new CustomizerProviderImpl(this, this.updateHelper, evaluator(), refHelper, this.genFilesHelper),        
368
            new CustomizerProviderImpl(this, this.updateHelper, evaluator(), refHelper, this.genFilesHelper),        
376
            LookupMergerSupport.createClassPathProviderMerger(cpProvider),
369
            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
397
            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),
398
            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(),
399
            LookupProviderSupport.createActionProviderMerger(),
407
            WhiteListQueryMergerSupport.createWhiteListQueryMerger()
400
            WhiteListQueryMergerSupport.createWhiteListQueryMerger(),
401
            BrokenReferencesSupport.createReferenceProblemsProvider(helper, refHelper, eval, lvp.getBreakableProperties(), lvp.getPlatformProperties()),
402
            BrokenReferencesSupport.createPlatformVersionProblemProvider(eval, J2SEProjectProperties.JAVA_PLATFORM, J2SEProjectProperties.JAVAC_SOURCE, J2SEProjectProperties.JAVAC_TARGET),
403
            UILookupMergerSupport.createProjectProblemsProviderMerger()
408
        );
404
        );
409
        lookup = base; // in case LookupProvider's call Project.getLookup
405
        lookup = base; // in case LookupProvider's call Project.getLookup
410
        return LookupProviderSupport.createCompositeLookup(base, "Projects/org-netbeans-modules-java-j2seproject/Lookup"); //NOI18N
406
        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
672
                    LOG.log(Level.WARNING, "Unsupported charset: {0} in project: {1}", new Object[]{prop, FileUtil.getFileDisplayName(getProjectDirectory())}); //NOI18N
677
                }
673
                }
678
            }
674
            }
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
675
            //Update per project CopyLibs if needed
703
            new UpdateCopyLibs(J2SEProject.this).run();
676
            new UpdateCopyLibs(J2SEProject.this).run();
704
            
677
            
(-)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 / +1 lines)
Lines 151-157 Link Here
151
                    <compile-dependency/>
151
                    <compile-dependency/>
152
                    <run-dependency>
152
                    <run-dependency>
153
                        <release-version>1</release-version>
153
                        <release-version>1</release-version>
154
                        <specification-version>1.59</specification-version>
154
                        <specification-version>1.60</specification-version>
155
                    </run-dependency>
155
                    </run-dependency>
156
                </dependency>
156
                </dependency>
157
                <dependency>
157
                <dependency>
(-)a/java.project/src/org/netbeans/modules/java/project/Bundle.properties (-28 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
(-)a/java.project/src/org/netbeans/modules/java/project/ProjectProblemsProviders.java (+1204 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.Arrays;
55
import java.util.Collection;
56
import java.util.Collections;
57
import java.util.HashMap;
58
import java.util.HashSet;
59
import java.util.LinkedHashSet;
60
import java.util.Map;
61
import java.util.Queue;
62
import java.util.Set;
63
import java.util.concurrent.Callable;
64
import java.util.concurrent.ExecutionException;
65
import java.util.concurrent.Future;
66
import java.util.concurrent.FutureTask;
67
import java.util.concurrent.RunnableFuture;
68
import java.util.concurrent.TimeUnit;
69
import java.util.concurrent.TimeoutException;
70
import java.util.concurrent.atomic.AtomicBoolean;
71
import java.util.logging.Level;
72
import java.util.logging.Logger;
73
import java.util.regex.Matcher;
74
import java.util.regex.Pattern;
75
import javax.swing.JFileChooser;
76
import org.netbeans.api.annotations.common.CheckForNull;
77
import org.netbeans.api.annotations.common.NonNull;
78
import org.netbeans.api.annotations.common.NullAllowed;
79
import org.netbeans.api.java.platform.JavaPlatform;
80
import org.netbeans.api.java.platform.JavaPlatformManager;
81
import org.netbeans.api.java.platform.PlatformsCustomizer;
82
import org.netbeans.api.project.Project;
83
import org.netbeans.api.project.ProjectManager;
84
import org.netbeans.api.project.libraries.LibrariesCustomizer;
85
import org.netbeans.api.project.libraries.Library;
86
import org.netbeans.api.project.libraries.LibraryManager;
87
import org.netbeans.spi.project.libraries.support.LibrariesSupport;
88
import org.netbeans.spi.project.support.ant.AntProjectHelper;
89
import org.netbeans.spi.project.support.ant.EditableProperties;
90
import org.netbeans.spi.project.support.ant.PropertyEvaluator;
91
import org.netbeans.spi.project.support.ant.PropertyUtils;
92
import org.netbeans.spi.project.support.ant.ReferenceHelper;
93
import org.netbeans.spi.project.ui.ProjectProblemsProvider;
94
import org.openide.filesystems.FileObject;
95
import org.openide.util.NbBundle;
96
import org.openide.util.Parameters;
97
98
import static org.netbeans.modules.java.project.Bundle.*;
99
import org.netbeans.spi.java.project.support.ui.BrokenReferencesSupport;
100
import org.netbeans.spi.project.support.ant.ui.VariablesSupport;
101
import org.netbeans.spi.project.ui.ProjectProblemResolver;
102
import org.netbeans.spi.project.ui.ProjectProblemsProvider.Result;
103
import org.netbeans.spi.project.ui.support.ProjectChooser;
104
import org.openide.modules.SpecificationVersion;
105
import org.openide.util.Exceptions;
106
import org.openide.util.Lookup;
107
import org.openide.util.RequestProcessor;
108
import org.openide.util.Utilities;
109
import org.openide.util.WeakListeners;
110
111
/**
112
 *
113
 * @author Tomas Zezula
114
 */
115
public class ProjectProblemsProviders {
116
117
    private static final Logger LOG = Logger.getLogger(ProjectProblemsProviders.class.getName());
118
    private static final RequestProcessor RP = new RequestProcessor(ProjectProblemsProviders.class);
119
120
    private ProjectProblemsProviders() {
121
        throw new IllegalStateException(String.format("The %s cannot be instantiated.",this.getClass().getName())); //NOI18N
122
    }
123
124
   
125
126
    public static ProjectProblemsProvider createReferenceProblemProvider(
127
            @NonNull final AntProjectHelper projectHelper,
128
            @NonNull final ReferenceHelper referenceHelper,
129
            @NonNull final PropertyEvaluator evaluator,
130
            @NonNull final String[] properties,
131
            @NonNull final String[] platformProperties) {
132
        final ReferenceProblemProviderImpl pp = new ReferenceProblemProviderImpl(projectHelper, evaluator, referenceHelper, properties, platformProperties);
133
        pp.attachListeners();
134
        return pp;
135
    }
136
    
137
    public static ProjectProblemsProvider createPlatformVersionProblemProvider(            
138
            @NonNull final PropertyEvaluator evaluator,
139
            @NonNull final String platformProperty,
140
            @NonNull final String... platformVersionProperties) {
141
        final PlatformVersionProblemProviderImpl pp = new PlatformVersionProblemProviderImpl(evaluator, platformProperty, platformVersionProperties);
142
        pp.attachListeners();
143
        return pp;
144
    }
145
146
    //<editor-fold defaultstate="collapsed" desc="Helper Methods & Types">
147
    @NonNull
148
    private static Set<? extends ProjectProblemsProvider.ProjectProblem> getReferenceProblems(
149
            @NullAllowed final AntProjectHelper helper,
150
            @NullAllowed final PropertyEvaluator evaluator,
151
            @NullAllowed final ReferenceHelper refHelper,
152
            @NonNull final String[] ps,
153
            final boolean abortAfterFirstProblem) {
154
        Set<ProjectProblemsProvider.ProjectProblem> set = new LinkedHashSet<ProjectProblemsProvider.ProjectProblem>();
155
        StringBuilder all = new StringBuilder();
156
        // this call waits for list of libraries to be refreshhed
157
        LibraryManager.getDefault().getLibraries();
158
        if (helper == null || evaluator == null || refHelper == null) {
159
            return set;
160
        }
161
        final Queue<FileResolver> fileReoslvers = new ArrayDeque<FileResolver>();
162
        EditableProperties ep = helper.getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
163
        for (String p : ps) {
164
            // evaluate given property and tokenize it
165
166
            String prop = evaluator.getProperty(p);
167
            if (prop == null) {
168
                continue;
169
            }
170
            LOG.log(Level.FINE, "Evaluated {0}={1}", new Object[] {p, prop});
171
            String[] vals = PropertyUtils.tokenizePath(prop);
172
173
            // no check whether after evaluating there are still some
174
            // references which could not be evaluated
175
            for (String v : vals) {
176
                // we are checking only: project reference, file reference, library reference
177
                if (!(v.startsWith("${file.reference.") || v.startsWith("${project.") || v.startsWith("${libs.") || v.startsWith("${var."))) { // NOI18N
178
                    all.append(v);
179
                    continue;
180
                }
181
                if (v.startsWith("${project.")) { // NOI18N
182
                    // something in the form: "${project.<projID>}/dist/foo.jar"
183
                    String val = v.substring(2, v.indexOf('}')); // NOI18N
184
                    set.add(
185
                        ProjectProblemsProvider.ProjectProblem.create(
186
                            getDisplayName(RefType.PROJECT, val),
187
                            getDescription(RefType.PROJECT, val),
188
                            new ProjectResolver(val, helper)));
189
                } else {
190
                    final String val = v.substring(2, v.length() - 1);
191
                    final ProjectProblemsProvider.ProjectProblem problem;
192
                    if (v.startsWith("${file.reference")) { // NOI18N
193
                        final FileResolver fr = new FileResolver(val, helper, fileReoslvers);
194
                        fileReoslvers.offer(fr);
195
                        problem = ProjectProblemsProvider.ProjectProblem.create(
196
                            getDisplayName(RefType.FILE, val),
197
                            getDescription(RefType.FILE, val),
198
                            fr);
199
200
                    } else if (v.startsWith("${var")) { // NOI18N
201
                        problem = ProjectProblemsProvider.ProjectProblem.create(
202
                            getDisplayName(RefType.VARIABLE, val),
203
                            getDescription(RefType.VARIABLE, val),
204
                            new VariableResolver(RefType.VARIABLE, val));
205
                    } else {
206
                        problem = ProjectProblemsProvider.ProjectProblem.create(
207
                            getDisplayName(RefType.LIBRARY, val),
208
                            getDescription(RefType.LIBRARY, val),
209
                            new LibraryResolver(RefType.LIBRARY, val, refHelper));
210
                    }
211
                    set.add(problem);
212
                }
213
                if (abortAfterFirstProblem) {
214
                    break;
215
                }
216
            }
217
            if (set.size() > 0 && abortAfterFirstProblem) {
218
                break;
219
            }
220
221
            // test that resolved variable based property points to an existing file
222
            String path = ep.getProperty(p);
223
            if (path != null) {
224
                for (String v : PropertyUtils.tokenizePath(path)) {
225
                    if (v.startsWith("${file.reference.")) {    //NOI18N
226
                        v = ep.getProperty(v.substring(2, v.length() - 1));
227
                    }
228
                    if (v != null && v.startsWith("${var.")) {    //NOI18N
229
                        String value = evaluator.evaluate(v);
230
                        if (value.startsWith("${var.")) { // NOI18N
231
                            // this problem was already reported
232
                            continue;
233
                        }
234
                        File f = getFile(helper, evaluator, value);
235
                        if (f.exists()) {
236
                            continue;
237
                        }
238
                        set.add(
239
                            ProjectProblemsProvider.ProjectProblem.create(
240
                                getDisplayName(RefType.VARIABLE_CONTENT, v),
241
                                getDescription(RefType.VARIABLE_CONTENT, v),
242
                                new VariableResolver(RefType.VARIABLE_CONTENT, v)));
243
                    }
244
                }
245
            }
246
247
        }
248
249
        // Check also that all referenced project really exist and are reachable.
250
        // If they are not report them as broken reference.
251
        // XXX: there will be API in PropertyUtils for listing of Ant
252
        // prop names in String. Consider using it here.
253
        final Map<String, String> entries = evaluator.getProperties();
254
        if (entries == null) {
255
            throw new IllegalArgumentException("Properies mapping could not be computed (e.g. due to a circular definition). Evaluator: "+evaluator.toString());  //NOI18N
256
        }
257
        for (Map.Entry<String, String> entry : entries.entrySet()) {
258
            String key = entry.getKey();
259
            String value = entry.getValue();
260
            if (key.startsWith("project.")) { // NOI18N
261
                if ("project.license".equals(key)) {    //NOI18N
262
                    continue;
263
                }
264
                File f = getFile(helper, evaluator, value);
265
                if (f.exists()) {
266
                    continue;
267
                }
268
                // Check that the value is really used by some property.
269
                // If it is not then ignore such a project.
270
                if (all.indexOf(value) == -1) {
271
                    continue;
272
                }
273
                set.add(
274
                    ProjectProblemsProvider.ProjectProblem.create(
275
                        getDisplayName(RefType.PROJECT, key),
276
                        getDescription(RefType.PROJECT, key),
277
                        new ProjectResolver(key, helper)));
278
            }
279
            else if (key.startsWith("file.reference")) {    //NOI18N
280
                File f = getFile(helper, evaluator, value);
281
                String unevaluatedValue = ep.getProperty(key);
282
                boolean alreadyChecked = unevaluatedValue != null ? unevaluatedValue.startsWith("${var.") : false; // NOI18N
283
                if (f.exists() || all.indexOf(value) == -1 || alreadyChecked) { // NOI18N
284
                    continue;
285
                }
286
                final FileResolver fr = new FileResolver(key, helper, fileReoslvers);
287
                fileReoslvers.offer(fr);
288
                set.add(
289
                    ProjectProblemsProvider.ProjectProblem.create(
290
                        getDisplayName(RefType.FILE, key),
291
                        getDescription(RefType.FILE, key),
292
                        fr));
293
            }
294
        }
295
296
        //Check for libbraries with broken classpath content
297
        Set<String> usedLibraries = new HashSet<String>();
298
        Pattern libPattern = Pattern.compile("\\$\\{(libs\\.[-._a-zA-Z0-9]+\\.classpath)\\}"); //NOI18N
299
        for (String p : ps) {
300
            String propertyValue = ep.getProperty(p);
301
            if (propertyValue != null) {
302
                for (String v : PropertyUtils.tokenizePath(propertyValue)) {
303
                    Matcher m = libPattern.matcher(v);
304
                    if (m.matches()) {
305
                        usedLibraries.add (m.group(1));
306
                    }
307
                }
308
            }
309
        }
310
        for (String libraryRef : usedLibraries) {
311
            String libraryName = libraryRef.substring(5,libraryRef.length()-10);
312
            Library lib = refHelper.findLibrary(libraryName);
313
            if (lib == null) {
314
                // Should already have been caught before?
315
                set.add(
316
                    ProjectProblemsProvider.ProjectProblem.create(
317
                        getDisplayName(RefType.LIBRARY, libraryRef),
318
                        getDescription(RefType.LIBRARY, libraryRef),
319
                        new LibraryResolver(RefType.LIBRARY, libraryRef, refHelper)));
320
            }
321
            else {
322
                //XXX: Should check all the volumes (sources, javadoc, ...)?
323
                for (URI uri : lib.getURIContent("classpath")) { // NOI18N
324
                    URI uri2 = LibrariesSupport.getArchiveFile(uri);
325
                    if (uri2 == null) {
326
                        uri2 = uri;
327
                    }
328
                    FileObject fo = LibrariesSupport.resolveLibraryEntryFileObject(lib.getManager().getLocation(), uri2);
329
                    if (null == fo && !canResolveEvaluatedUri(helper.getStandardPropertyEvaluator(), lib.getManager().getLocation(), uri2)) {
330
                        set.add(
331
                            ProjectProblemsProvider.ProjectProblem.create(
332
                                getDisplayName(RefType.LIBRARY_CONTENT, libraryRef),
333
                                getDescription(RefType.LIBRARY_CONTENT, libraryRef),
334
                                new LibraryResolver(RefType.LIBRARY_CONTENT, libraryRef, refHelper)));
335
                        break;
336
                    }
337
                }
338
            }
339
        }
340
341
        return set;
342
    }
343
344
    @NonNull
345
    private static Set<ProjectProblemsProvider.ProjectProblem> getPlatformProblems(
346
            @NullAllowed final PropertyEvaluator evaluator,
347
            @NonNull final String[] platformProperties,
348
            boolean abortAfterFirstProblem) {
349
        final Set<ProjectProblemsProvider.ProjectProblem> set = new LinkedHashSet<ProjectProblemsProvider.ProjectProblem>();
350
        if (evaluator == null) {
351
            return set;
352
        }
353
        for (String pprop : platformProperties) {
354
            String prop = evaluator.getProperty(pprop);
355
            if (prop == null) {
356
                continue;
357
            }
358
            if (!existPlatform(prop)) {
359
360
                // XXX: the J2ME stores in project.properties also platform
361
                // display name and so show this display name instead of just
362
                // prop ID if available.
363
                if (evaluator.getProperty(pprop + ".description") != null) { // NOI18N
364
                    prop = evaluator.getProperty(pprop + ".description"); // NOI18N
365
                }
366
367
                set.add(
368
                    ProjectProblemsProvider.ProjectProblem.create(
369
                        getDisplayName(RefType.PLATFORM, prop),
370
                        getDescription(RefType.PLATFORM, prop),
371
                        new PlatformResolver(prop)));
372
            }
373
            if (set.size() > 0 && abortAfterFirstProblem) {
374
                break;
375
            }
376
        }
377
        return set;
378
    }
379
380
    private static File getFile (AntProjectHelper helper, PropertyEvaluator evaluator, String name) {
381
        if (helper != null) {
382
            return new File(helper.resolvePath(name));
383
        } else {
384
            File f = new File(name);
385
            if (!f.exists()) {
386
                // perhaps the file is relative?
387
                String basedir = evaluator.getProperty("basedir"); // NOI18N
388
                assert basedir != null;
389
                f = new File(new File(basedir), name);
390
            }
391
            return f;
392
        }
393
    }
394
395
    /** Tests whether evaluated URI can be resolved. To support library entry
396
     * like "${MAVEN_REPO}/struts/struts.jar".
397
     */
398
    private static boolean canResolveEvaluatedUri(PropertyEvaluator eval, URL libBase, URI libUri) {
399
        if (libUri.isAbsolute()) {
400
            return false;
401
        }
402
        String path = LibrariesSupport.convertURIToFilePath(libUri);
403
        String newPath = eval.evaluate(path);
404
        if (newPath.equals(path)) {
405
            return false;
406
        }
407
        URI newUri = LibrariesSupport.convertFilePathToURI(newPath);
408
        return null != LibrariesSupport.resolveLibraryEntryFileObject(libBase, newUri);
409
    }
410
411
    private static boolean existPlatform(String platform) {
412
        if (platform.equals("default_platform")) { // NOI18N
413
            return true;
414
        }
415
        for (JavaPlatform plat : JavaPlatformManager.getDefault().getInstalledPlatforms()) {
416
            // XXX: this should be defined as PROPERTY somewhere
417
            if (platform.equals(plat.getProperties().get("platform.ant.name")) && // NOI18N
418
                    plat.getInstallFolders().size() > 0) {
419
                return true;
420
            }
421
        }
422
        return false;
423
    }
424
425
    @NonNull
426
    @NbBundle.Messages({
427
        "LBL_BrokenLinksCustomizer_BrokenLibrary=\"{0}\" library could not be found",
428
        "LBL_BrokenLinksCustomizer_BrokenDefinableLibrary=\"{0}\" library must be defined",
429
        "LBL_BrokenLinksCustomizer_BrokenLibraryContent=\"{0}\" library has missing items",
430
        "LBL_BrokenLinksCustomizer_BrokenProjectReference=\"{0}\" project could not be found",
431
        "LBL_BrokenLinksCustomizer_BrokenFileReference=\"{0}\" file/folder could not be found",
432
        "LBL_BrokenLinksCustomizer_BrokenVariable=\"{0}\" variable could not be found",
433
        "LBL_BrokenLinksCustomizer_BrokenVariableContent=\"{0}\" variable based file/folder could not be found",
434
        "LBL_BrokenLinksCustomizer_BrokenPlatform=\"{0}\" platform could not be found",
435
    })
436
    private static String getDisplayName(
437
            @NonNull final RefType type,
438
            @NonNull final String id) {
439
        switch (type) {
440
            case LIBRARY:
441
                return LBL_BrokenLinksCustomizer_BrokenLibrary(getDisplayId(type, id));
442
            case DEFINABLE_LIBRARY:
443
                return LBL_BrokenLinksCustomizer_BrokenDefinableLibrary(getDisplayId(type, id));
444
            case LIBRARY_CONTENT:
445
                return LBL_BrokenLinksCustomizer_BrokenLibraryContent(getDisplayId(type, id));
446
            case PROJECT:
447
                return LBL_BrokenLinksCustomizer_BrokenProjectReference(getDisplayId(type, id));
448
            case FILE:
449
                return LBL_BrokenLinksCustomizer_BrokenFileReference(getDisplayId(type, id));
450
            case PLATFORM:
451
                return LBL_BrokenLinksCustomizer_BrokenPlatform(getDisplayId(type, id));
452
            case VARIABLE:
453
                return LBL_BrokenLinksCustomizer_BrokenVariable(getDisplayId(type, id));
454
            case VARIABLE_CONTENT:
455
                return LBL_BrokenLinksCustomizer_BrokenVariableContent(getDisplayId(type, id));
456
            default:
457
                assert false;
458
                return id;
459
        }
460
    }
461
462
    @NbBundle.Messages({
463
        "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}\".",
464
        "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.",
465
        "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.",
466
        "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.",
467
        "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.",
468
        "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.",
469
        "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.",
470
        "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}\"."
471
    })
472
    private static String getDescription(
473
            @NonNull final RefType type,
474
            @NonNull final String id
475
            ) {
476
        switch (type) {
477
            case LIBRARY:
478
                return LBL_BrokenLinksCustomizer_BrokenLibraryDesc(getDisplayId(type, id));
479
            case DEFINABLE_LIBRARY:
480
                return LBL_BrokenLinksCustomizer_BrokenDefinableLibraryDesc(getDisplayId(type, id));
481
            case LIBRARY_CONTENT:
482
                return LBL_BrokenLinksCustomizer_BrokenLibraryContentDesc(getDisplayId(type, id));
483
            case PROJECT:
484
                return LBL_BrokenLinksCustomizer_BrokenProjectReferenceDesc(getDisplayId(type, id));
485
            case FILE:
486
                return LBL_BrokenLinksCustomizer_BrokenFileReferenceDesc(getDisplayId(type, id));
487
            case PLATFORM:
488
                return LBL_BrokenLinksCustomizer_BrokenPlatformDesc(getDisplayId(type, id));
489
            case VARIABLE:
490
                return LBL_BrokenLinksCustomizer_BrokenVariableReferenceDesc(getDisplayId(type, id));
491
            case VARIABLE_CONTENT:
492
                return LBL_BrokenLinksCustomizer_BrokenVariableContent(getDisplayId(type, id));
493
            default:
494
                assert false;
495
                return id;
496
        }
497
    }
498
499
    private static String getDisplayId(
500
            @NonNull final RefType type,
501
            @NonNull final String id) {
502
        switch (type) {
503
            case LIBRARY:
504
            case DEFINABLE_LIBRARY:
505
            case LIBRARY_CONTENT:
506
                // libs.<name>.classpath
507
                return id.substring(5, id.length()-10);
508
            case PROJECT:
509
                // project.<name>
510
                return id.substring(8);
511
            case FILE:
512
                // file.reference.<name>
513
                return id.substring(15);
514
            case PLATFORM:
515
                return id;
516
            case VARIABLE:
517
                return id.substring(4, id.indexOf('}')); // NOI18N
518
            case VARIABLE_CONTENT:
519
                return id.substring(6, id.indexOf('}')) + id.substring(id.indexOf('}')+1); // NOI18N
520
            default:
521
                assert false;
522
                return id;
523
        }
524
    }
525
526
    private enum RefType {
527
        PROJECT,
528
        FILE,
529
        PLATFORM,
530
        LIBRARY,
531
        DEFINABLE_LIBRARY,
532
        LIBRARY_CONTENT,
533
        VARIABLE,
534
        VARIABLE_CONTENT,
535
    }
536
    //</editor-fold>
537
538
    
539
    //<editor-fold defaultstate="collapsed" desc="Resolver implementations">
540
    private static abstract class BaseResolver implements ProjectProblemResolver {
541
542
        protected final RefType type;
543
        protected final String id;
544
545
        BaseResolver(
546
            @NonNull final RefType type,
547
            @NonNull final String id) {
548
            Parameters.notNull("type", type);   //NOI18N
549
            Parameters.notNull("id", id);   //NOI18N
550
            this.type = type;
551
            this.id = id;
552
        }
553
        
554
        @Override
555
        public final int hashCode() {
556
            int result = 17;
557
            result = 31 * result + type.hashCode();
558
            result = 31 * result + id.hashCode();
559
            return result;
560
        }
561
        
562
        @Override
563
        public final boolean equals(@NullAllowed final Object other) {
564
            if (!(other instanceof BaseResolver)) {
565
                return false;
566
            }
567
            final BaseResolver otherResolver = (BaseResolver) other;
568
            return type.equals(otherResolver.type) &&
569
                   id.equals(otherResolver.id);
570
        }
571
572
        @Override
573
        public String toString() {
574
            return String.format(
575
               "Resolver for %s %s",    //NOI18N
576
               type,
577
               id);
578
        }
579
580
581
582
    }
583
584
    private static class PlatformResolver extends BaseResolver {
585
586
        PlatformResolver(@NonNull final String id) {
587
            super(RefType.PLATFORM, id);
588
        }
589
590
        @Override
591
        @NonNull
592
        public Future<ProjectProblemsProvider.Result> resolve() {
593
            PlatformsCustomizer.showCustomizer(null);
594
            return new Done(ProjectProblemsProvider.Result.create(ProjectProblemsProvider.Status.RESOLVED));
595
        }
596
597
    }
598
    
599
    private static class LibraryResolver extends BaseResolver {
600
601
        private final Callable<Library> definer;
602
        private final Reference<ReferenceHelper> refHelper;
603
604
        LibraryResolver(
605
                @NonNull RefType type,
606
                @NonNull final String id,
607
                @NonNull final ReferenceHelper refHelper) {
608
            this(translate(type, id),id, refHelper);
609
        }
610
611
        private LibraryResolver(
612
                @NonNull final Object[] typeDefiner/*todo: replace by Pair*/,
613
                @NonNull final String id,
614
                @NonNull final ReferenceHelper refHelper) {
615
            super((RefType)typeDefiner[0],id);
616
            this.definer = (Callable<Library>)typeDefiner[1];
617
            this.refHelper = new WeakReference<ReferenceHelper>(refHelper);
618
        }
619
620
        @Override
621
        @NonNull
622
        public Future<ProjectProblemsProvider.Result> resolve() {
623
            if (type == RefType.DEFINABLE_LIBRARY) {
624
                return resolveByDefiner();
625
            } else {
626
                return new Done(ProjectProblemsProvider.Result.create(resolveByLibraryManager()));
627
            }
628
        }
629
630
        private ProjectProblemsProvider.Status resolveByLibraryManager() {
631
            final LibraryManager lm = getProjectLibraryManager();
632
            if (lm == null) {
633
                //Closed and freed project
634
                return ProjectProblemsProvider.Status.UNRESOLVED;
635
            }
636
            LibrariesCustomizer.showCustomizer(null,lm);
637
            return ProjectProblemsProvider.Status.RESOLVED;
638
        }
639
640
        private Future<ProjectProblemsProvider.Result> resolveByDefiner() {
641
            assert definer != null;
642
            final RunnableFuture<ProjectProblemsProvider.Result> future =
643
                    new FutureTask<ProjectProblemsProvider.Result>(
644
                    new Callable<ProjectProblemsProvider.Result>() {
645
                        @Override
646
                        public ProjectProblemsProvider.Result call() throws Exception {
647
                            ProjectProblemsProvider.Status result = ProjectProblemsProvider.Status.UNRESOLVED;
648
                            try {
649
                                Library lib = definer.call();
650
                                LOG.log(Level.FINE, "found {0}", lib);  //NOI18N
651
                                result = ProjectProblemsProvider.Status.RESOLVED;
652
                            } catch (Exception x) {
653
                                LOG.log(Level.INFO, null, x);
654
                                result = resolveByLibraryManager();
655
                            }
656
                            return ProjectProblemsProvider.Result.create(result);
657
                        }
658
                    });
659
            RP.post(future);
660
            return future;
661
        }
662
663
        @CheckForNull
664
        private LibraryManager getProjectLibraryManager() {
665
            final ReferenceHelper rh = refHelper.get();
666
            return rh == null ?
667
                null:
668
                rh.getProjectLibraryManager() != null ?
669
                    rh.getProjectLibraryManager():
670
                    LibraryManager.getDefault();
671
        }
672
673
        @NonNull
674
        private static Object[] translate(
675
           @NonNull RefType original,
676
           @NonNull String id) {
677
            Callable<Library> _definer = null;
678
            if (original == RefType.LIBRARY) {
679
                final String name = id.substring(5, id.length() - 10);
680
                for (BrokenReferencesSupport.LibraryDefiner ld : Lookup.getDefault().lookupAll(BrokenReferencesSupport.LibraryDefiner.class)) {
681
                    _definer = ld.missingLibrary(name);
682
                    if (_definer != null) {
683
                        return new Object[] {RefType.DEFINABLE_LIBRARY, _definer};
684
                    }
685
                }
686
            }
687
            return new Object[] {original, null};
688
        }
689
    }    
690
691
    private static class VariableResolver extends BaseResolver {
692
        VariableResolver(@NonNull final RefType type, @NonNull final String id) {
693
            super(type, id);
694
        }
695
696
        @Override
697
        @NonNull
698
        public Future<ProjectProblemsProvider.Result> resolve() {
699
            VariablesSupport.showVariablesCustomizer();
700
            return new Done(ProjectProblemsProvider.Result.create(ProjectProblemsProvider.Status.RESOLVED));
701
        }
702
    }
703
704
    private static abstract class ReferenceResolver extends BaseResolver {
705
706
        static File lastSelectedFile;
707
708
        private final Reference<AntProjectHelper> antProjectHelper;
709
710
        ReferenceResolver(
711
                @NonNull final RefType type,
712
                @NonNull final String id,
713
                @NonNull final AntProjectHelper antProjectHelper) {
714
            super (type, id);
715
            this.antProjectHelper = new WeakReference<AntProjectHelper>(antProjectHelper);
716
        }
717
718
        abstract void updateReference(@NonNull final File file);
719
720
        final void updateReferenceImpl(@NonNull final File file) {
721
            final String reference = id;
722
            final AntProjectHelper helper = antProjectHelper.get();
723
            if (helper == null) {
724
                //Closed and freed project, ignore
725
                return;
726
            }
727
            FileObject myProjDirFO = helper.getProjectDirectory();
728
            final String propertiesFile = AntProjectHelper.PRIVATE_PROPERTIES_PATH;
729
            final String path = file.getAbsolutePath();
730
            Project p;
731
            try {
732
                p = ProjectManager.getDefault().findProject(myProjDirFO);
733
            } catch (IOException ex) {
734
                Exceptions.printStackTrace(ex);
735
                p = null;
736
            }
737
            final Project proj = p;
738
            ProjectManager.mutex().postWriteRequest(new Runnable() {
739
                    public @Override void run() {
740
                        EditableProperties props = helper.getProperties(propertiesFile);
741
                        if (!path.equals(props.getProperty(reference))) {
742
                            props.setProperty(reference, path);
743
                            helper.putProperties(propertiesFile, props);
744
                        }
745
746
                        if (proj != null) {
747
                            try {
748
                                ProjectManager.getDefault().saveProject(proj);
749
                            } catch (IOException ex) {
750
                                Exceptions.printStackTrace(ex);
751
                            }
752
                        }
753
                    }
754
                });
755
        }
756
    }
757
758
    private static class ProjectResolver extends ReferenceResolver {
759
        ProjectResolver(@NonNull final String id, @NonNull AntProjectHelper antProjectHelper) {
760
            super (RefType.PROJECT, id, antProjectHelper);
761
        }
762
763
        @Override
764
        @NonNull
765
        @NbBundle.Messages({
766
            "LBL_BrokenLinksCustomizer_Resolve_Project=Browse Project \"{0}\"",
767
        })
768
        public Future<ProjectProblemsProvider.Result> resolve() {
769
            ProjectProblemsProvider.Status result = ProjectProblemsProvider.Status.UNRESOLVED;
770
            final JFileChooser chooser = ProjectChooser.projectChooser();
771
            chooser.setDialogTitle(LBL_BrokenLinksCustomizer_Resolve_Project(getDisplayId(type, id)));
772
            if (lastSelectedFile != null) {
773
                chooser.setSelectedFile(lastSelectedFile);
774
            }
775
            int option = chooser.showOpenDialog(null);
776
            if (option == JFileChooser.APPROVE_OPTION) {
777
                updateReference(chooser.getSelectedFile());
778
                lastSelectedFile = chooser.getSelectedFile();
779
                result = ProjectProblemsProvider.Status.RESOLVED;
780
            }
781
            return new Done(ProjectProblemsProvider.Result.create(result));
782
        }
783
784
        @Override
785
        void updateReference(@NonNull final File file) {
786
            updateReferenceImpl(file);
787
        }
788
789
    }
790
791
    private static class FileResolver extends ReferenceResolver {
792
793
        private final Queue<? extends FileResolver> peers;
794
        private ProjectProblemsProvider.Status resolved =
795
                ProjectProblemsProvider.Status.UNRESOLVED;
796
797
        FileResolver(
798
                @NonNull final String id,
799
                @NonNull final AntProjectHelper antProjectHelper,
800
                @NonNull final Queue<? extends FileResolver> peers) {
801
            super(RefType.FILE, id, antProjectHelper);
802
            this.peers = peers;
803
        }
804
805
        @Override
806
        @NonNull
807
        @NbBundle.Messages({
808
            "LBL_BrokenLinksCustomizer_Resolve_File=Browse \"{0}\""
809
        })
810
        public Future<ProjectProblemsProvider.Result> resolve() {
811
            final JFileChooser chooser = new JFileChooser();
812
            chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
813
            chooser.setDialogTitle(LBL_BrokenLinksCustomizer_Resolve_File(getDisplayId(type, id)));
814
            if (lastSelectedFile != null) {
815
                chooser.setSelectedFile(lastSelectedFile);
816
            }
817
            int option = chooser.showOpenDialog(null);
818
            if (option == JFileChooser.APPROVE_OPTION) {
819
                updateReference(chooser.getSelectedFile());
820
                lastSelectedFile = chooser.getSelectedFile();
821
                resolved = ProjectProblemsProvider.Status.RESOLVED;
822
            }
823
            return new Done(ProjectProblemsProvider.Result.create(resolved));
824
        }
825
826
        @Override
827
        void updateReference(@NonNull final File file) {
828
            updateReferenceImpl(file);
829
            final File parentFolder = file.getParentFile();
830
            for (FileResolver peer : peers) {
831
                if (this != peer && peer.resolved == ProjectProblemsProvider.Status.UNRESOLVED) {
832
                    final File f = new File(parentFolder, getDisplayId(type, id));
833
                    if (f.exists()) {
834
                        updateReferenceImpl(f);
835
                    }
836
                }
837
            }
838
        }
839
840
841
    }
842
843
    private static class SourceTargetResolver implements ProjectProblemResolver {
844
845
        @Override
846
        public Future<Result> resolve() {
847
            return new Done(ProjectProblemsProvider.Result.create(
848
                    ProjectProblemsProvider.Status.UNRESOLVED));
849
        }
850
851
        @Override
852
        public boolean equals(Object other) {
853
            if (!(other instanceof SourceTargetResolver)) {
854
                return false;
855
            }
856
            return true;
857
        }
858
859
        @Override
860
        public int hashCode() {
861
            return 17;
862
863
        }
864
865
866
    }
867
868
    private static final class Done implements Future<ProjectProblemsProvider.Result> {
869
870
        private final ProjectProblemsProvider.Result result;
871
872
        Done(@NonNull final ProjectProblemsProvider.Result result) {
873
            Parameters.notNull("result", result);   //NOI18N
874
            this.result = result;
875
        }
876
877
        @Override
878
        public boolean cancel(boolean mayInterruptIfRunning) {
879
            return false;
880
        }
881
882
        @Override
883
        public boolean isCancelled() {
884
            return false;
885
        }
886
887
        @Override
888
        public boolean isDone() {
889
            return true;
890
        }
891
892
        @Override
893
        public ProjectProblemsProvider.Result get() throws InterruptedException, ExecutionException {
894
            return result;
895
        }
896
897
        @Override
898
        public ProjectProblemsProvider.Result get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
899
            return get();
900
        }
901
902
    }
903
    //</editor-fold>
904
905
    //<editor-fold defaultstate="collapsed" desc="ProjectProblemsProvider implementations">
906
    private static final class ReferenceProblemProviderImpl implements ProjectProblemsProvider, PropertyChangeListener {
907
908
        private final PropertyChangeSupport support = new PropertyChangeSupport(this);
909
910
        private final Object problemsLock = new Object();
911
        //@GuardedBy("problemsLock")
912
        private Collection<? extends ProjectProblem> problems;
913
        //@GuardedBy("problemsLock")
914
        private long eventId;
915
        private final AtomicBoolean listenersInitialized = new AtomicBoolean();
916
917
        private final AntProjectHelper helper;
918
        private final PropertyEvaluator eval;
919
        private final ReferenceHelper refHelper;
920
        private final String[] refProps;
921
        private final String[] platformProps;
922
923
        private Map<URL,Object[]> activeLibManLocs;
924
925
926
        ReferenceProblemProviderImpl(
927
                @NonNull final AntProjectHelper helper,
928
                @NonNull final PropertyEvaluator eval,
929
                @NonNull final ReferenceHelper refHelper,
930
                @NonNull final String[] refProps,
931
                @NonNull final String[] platformProps) {
932
            assert helper != null;
933
            assert eval != null;
934
            assert refHelper != null;
935
            assert refProps != null;
936
            assert platformProps != null;
937
            this.helper = helper;
938
            this.eval = eval;
939
            this.refHelper = refHelper;
940
            this.refProps = Arrays.copyOf(refProps, refProps.length);
941
            this.platformProps = Arrays.copyOf(platformProps, platformProps.length);
942
        }
943
944
        @Override
945
        public void addPropertyChangeListener(@NonNull final PropertyChangeListener listener) {
946
            Parameters.notNull("listener", listener);   //NOI18N
947
            support.addPropertyChangeListener(listener);
948
        }
949
950
        @Override
951
        public void removePropertyChangeListener(@NonNull final PropertyChangeListener listener) {
952
            Parameters.notNull("listener", listener);   //NOI18N
953
            support.removePropertyChangeListener(listener);
954
        }
955
956
        @Override
957
        public Collection<? extends ProjectProblem> getProblems() {
958
            Collection<? extends ProjectProblem> curProblems;
959
            long curEventId;
960
            synchronized (problemsLock) {
961
                curProblems = problems;
962
                curEventId = eventId;
963
            }
964
            if (curProblems != null) {
965
                return curProblems;
966
            }
967
            final Set<ProjectProblem> newProblems = new LinkedHashSet<ProjectProblem>();
968
            newProblems.addAll(getReferenceProblems(helper,eval,refHelper,refProps,false));
969
            newProblems.addAll(getPlatformProblems(eval,platformProps,false));
970
            curProblems = Collections.unmodifiableSet(newProblems);
971
            synchronized (problemsLock){
972
                if (curEventId == eventId) {
973
                    //No canonical mapping needed
974
                    problems = curProblems;
975
                } else if (problems != null) {
976
                    curProblems = problems;
977
                }
978
            }
979
            assert curProblems != null;
980
            return curProblems;
981
        }
982
983
        @Override
984
        public void propertyChange(PropertyChangeEvent evt) {
985
            if (LibraryManager.PROP_OPEN_LIBRARY_MANAGERS.equals(evt.getPropertyName())) {
986
                addLibraryManagerListener();
987
            }
988
            synchronized (problemsLock) {
989
                problems = null;
990
                eventId++;
991
            }
992
            support.firePropertyChange(PROP_PROBLEMS,null,null);
993
        }
994
995
        void attachListeners() {
996
            if (listenersInitialized.compareAndSet(false, true)) {
997
                eval.addPropertyChangeListener(this);
998
                JavaPlatformManager.getDefault().addPropertyChangeListener(WeakListeners.propertyChange(this, JavaPlatformManager.getDefault()));
999
                LibraryManager.addOpenManagersPropertyChangeListener(new OpenManagersWeakListener(this));
1000
                addLibraryManagerListener();
1001
            } else {
1002
                throw new IllegalStateException();
1003
            }
1004
        }
1005
1006
        private void addLibraryManagerListener() {
1007
            final Map<URL,Object[]> oldLMs;
1008
            final boolean attachToDefault;
1009
            synchronized (this) {
1010
                attachToDefault = activeLibManLocs == null;
1011
                if (attachToDefault) {
1012
                    activeLibManLocs = new HashMap<URL,Object[]>();
1013
                }
1014
                oldLMs = new HashMap<URL,Object[]>(activeLibManLocs);
1015
            }
1016
            if (attachToDefault) {
1017
                final LibraryManager manager = LibraryManager.getDefault();
1018
                manager.addPropertyChangeListener(WeakListeners.propertyChange(this, manager));
1019
            }
1020
            final Collection<? extends LibraryManager> managers = LibraryManager.getOpenManagers();
1021
            final Map<URL,LibraryManager> managerByLocation = new HashMap<URL, LibraryManager>();
1022
            for (LibraryManager manager : managers) {
1023
                final URL url = manager.getLocation();
1024
                if (url != null) {
1025
                    managerByLocation.put(url, manager);
1026
                }
1027
            }
1028
            final HashMap<URL,Object[]> toRemove = new HashMap<URL,Object[]>(oldLMs);
1029
            toRemove.keySet().removeAll(managerByLocation.keySet());
1030
            for (Object[] pair : toRemove.values()) {
1031
                ((LibraryManager)pair[0]).removePropertyChangeListener((PropertyChangeListener)pair[1]);
1032
            }
1033
            managerByLocation.keySet().removeAll(oldLMs.keySet());
1034
            final HashMap<URL,Object[]> toAdd = new HashMap<URL,Object[]>();
1035
            for (Map.Entry<URL,LibraryManager> e : managerByLocation.entrySet()) {
1036
                final LibraryManager manager = e.getValue();
1037
                final PropertyChangeListener listener = WeakListeners.propertyChange(this, manager);
1038
                manager.addPropertyChangeListener(listener);
1039
                toAdd.put(e.getKey(), new Object[] {manager, listener});
1040
            }
1041
            synchronized (this) {
1042
                activeLibManLocs.keySet().removeAll(toRemove.keySet());
1043
                activeLibManLocs.putAll(toAdd);
1044
            }
1045
        }
1046
1047
    }
1048
1049
    private static final class PlatformVersionProblemProviderImpl implements ProjectProblemsProvider, PropertyChangeListener {
1050
1051
        private final PropertyChangeSupport support = new PropertyChangeSupport(this);
1052
        private final Object problemsLock = new Object();
1053
        //@GuardedBy("problemsLock")
1054
        private Collection<? extends ProjectProblem> problems;
1055
        //@GuardedBy("problemsLock")
1056
        private long eventId;
1057
        private final AtomicBoolean listenersInitialized = new AtomicBoolean();
1058
        
1059
        private final PropertyEvaluator eval;
1060
        private final String platformProp;
1061
        private final Set<String> versionProps;
1062
1063
        PlatformVersionProblemProviderImpl(
1064
                @NonNull final PropertyEvaluator eval,
1065
                @NonNull final String platformProp,
1066
                @NonNull final String... versionProps) {
1067
            assert eval != null;
1068
            assert platformProp != null;
1069
            assert versionProps != null;
1070
            this.eval = eval;
1071
            this.platformProp = platformProp;
1072
            this.versionProps = new HashSet<String>(Arrays.asList(versionProps));
1073
        }
1074
1075
        @Override
1076
        public void addPropertyChangeListener(@NonNull final PropertyChangeListener listener) {
1077
            Parameters.notNull("listener", listener);   //NOI18N
1078
            support.addPropertyChangeListener(listener);
1079
        }
1080
1081
        @Override
1082
        public void removePropertyChangeListener(@NonNull final PropertyChangeListener listener) {
1083
            Parameters.notNull("listener", listener);   //NOI18N
1084
            support.removePropertyChangeListener(listener);
1085
        }
1086
1087
        @Override
1088
        @NbBundle.Messages({
1089
            "LBL_Invalid_JDK_Version=Invalid Java Platform Verion",
1090
            "HINT_Invalid_JDK_Vernsion=The active project platform  is an older version than it's required by project source/binary format."
1091
        })
1092
        public Collection<? extends ProjectProblem> getProblems() {
1093
            Collection<? extends ProjectProblem> curProblems;
1094
            long curEventId;
1095
            synchronized (problemsLock) {
1096
                curProblems = problems;
1097
                curEventId = eventId;
1098
            }
1099
            if (curProblems != null) {
1100
                return curProblems;
1101
            }
1102
            curProblems =
1103
                    hasInvalidJdkVersion() ?
1104
                        Collections.singleton(ProjectProblem.create(
1105
                            LBL_Invalid_JDK_Version(),
1106
                            HINT_Invalid_JDK_Vernsion(),
1107
                            new SourceTargetResolver())) :
1108
                        Collections.<ProjectProblem>emptySet();
1109
            synchronized (problemsLock) {
1110
                if (curEventId == eventId) {
1111
                    //No canonical mapping needed
1112
                    problems = curProblems;
1113
                } else if (problems != null) {
1114
                    curProblems = problems;
1115
                }
1116
            }
1117
            assert curProblems != null;
1118
            return curProblems;
1119
        }
1120
1121
        @Override
1122
        public void propertyChange(PropertyChangeEvent evt) {
1123
            final String propName = evt.getPropertyName();
1124
            if (propName == null || platformProp.equals(propName) || versionProps.contains(propName)) {
1125
                synchronized (problemsLock) {
1126
                    problems = null;
1127
                    eventId++;
1128
                }
1129
                support.firePropertyChange(PROP_PROBLEMS,null,null);
1130
            }
1131
        }
1132
1133
        void attachListeners() {
1134
            if (listenersInitialized.compareAndSet(false, true)) {
1135
                eval.addPropertyChangeListener(this);
1136
            } else {
1137
                throw new IllegalStateException();
1138
            }
1139
        }
1140
1141
        private boolean hasInvalidJdkVersion () {
1142
            final String platformId = this.eval.getProperty(platformProp);
1143
            final JavaPlatform activePlatform = getActivePlatform(platformId);
1144
            if (activePlatform == null) {
1145
                return false;
1146
            }
1147
            final SpecificationVersion platformVersion = activePlatform.getSpecification().getVersion();
1148
            for (String vp : versionProps) {
1149
                final String value = this.eval.getProperty(vp);
1150
                if (value == null) {
1151
                    continue;
1152
                }
1153
                try {
1154
                    final SpecificationVersion vpVersion = new SpecificationVersion (value);
1155
                    if (vpVersion.compareTo(platformVersion) > 0) {
1156
                        return true;
1157
                    }
1158
                } catch (NumberFormatException nfe) {
1159
1160
                }
1161
            }
1162
            return false;
1163
    }
1164
1165
        @CheckForNull
1166
        private static JavaPlatform getActivePlatform(@NullAllowed final String activePlatformId) {
1167
            final JavaPlatformManager pm = JavaPlatformManager.getDefault();
1168
            if (activePlatformId == null) {
1169
                return pm.getDefaultPlatform();
1170
            }
1171
            final JavaPlatform[] installedPlatforms = pm.getPlatforms(null, null);
1172
            for (JavaPlatform javaPlatform : installedPlatforms) {
1173
                final String antName = javaPlatform.getProperties().get("platform.ant.name"); //NOI18N
1174
                if (activePlatformId.equals(antName)) {
1175
                    return javaPlatform;
1176
                }
1177
            }
1178
            return null;
1179
        }
1180
1181
    }
1182
1183
    private static class OpenManagersWeakListener extends WeakReference<PropertyChangeListener> implements Runnable, PropertyChangeListener {
1184
1185
        public OpenManagersWeakListener(final PropertyChangeListener listener) {
1186
            super(listener, Utilities.activeReferenceQueue());
1187
        }
1188
1189
        @Override
1190
        public void run() {
1191
            LibraryManager.removeOpenManagersPropertyChangeListener(this);
1192
        }
1193
1194
        @Override
1195
        public void propertyChange(PropertyChangeEvent evt) {
1196
            final PropertyChangeListener listener = get();
1197
            if (listener != null) {
1198
                listener.propertyChange(evt);
1199
            }
1200
        }
1201
1202
    }
1203
    //</editor-fold>
1204
}
(-)a/java.project/src/org/netbeans/spi/java/project/support/ui/BrokenReferencesSupport.java (-157 / +167 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.project.FileOwnerQuery;
56
import org.netbeans.api.project.Project;
51
import org.netbeans.api.project.Project;
57
import org.netbeans.api.project.ProjectManager;
58
import org.netbeans.api.project.ProjectUtils;
59
import org.netbeans.api.project.libraries.Library;
52
import org.netbeans.api.project.libraries.Library;
60
import org.netbeans.modules.java.project.BrokenReferencesAlertPanel;
53
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;
54
import org.netbeans.spi.project.support.ant.AntProjectHelper;
65
import org.netbeans.spi.project.support.ant.PropertyEvaluator;
55
import org.netbeans.spi.project.support.ant.PropertyEvaluator;
66
import org.netbeans.spi.project.support.ant.ReferenceHelper;
56
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;
57
import org.openide.util.Parameters;
72
import org.openide.util.RequestProcessor;
58
import org.netbeans.spi.project.ui.ProjectProblemsProvider;
73
import org.openide.windows.WindowManager;
59
import org.netbeans.api.project.ui.ProjectProblems;
74
import static org.netbeans.spi.java.project.support.ui.Bundle.*;
60
import org.openide.filesystems.FileObject;
61
import org.openide.util.Lookup;
62
import org.openide.util.lookup.Lookups;
63
import org.openide.util.lookup.ProxyLookup;
75
64
76
/**
65
/**
77
 * Support for managing broken project references. Project freshly checkout from
66
 * Support for managing broken project references. Project freshly checkout from
Lines 86-106 Link Here
86
 * customizer.
75
 * customizer.
87
 * </div>
76
 * </div>
88
 * @author David Konecny
77
 * @author David Konecny
78
 * @author Tomas Zezula
89
 */
79
 */
90
public class BrokenReferencesSupport {
80
public class BrokenReferencesSupport {
91
81
        
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() {}
82
    private BrokenReferencesSupport() {}
105
83
106
    /**
84
    /**
Lines 118-132 Link Here
118
     *    always exists.
96
     *    always exists.
119
     * @return true if some problem was found and it is necessary to give
97
     * @return true if some problem was found and it is necessary to give
120
     *    user a chance to fix them
98
     *    user a chance to fix them
99
     *
100
     * @deprecated Add {@link ProjectProblemsProvider} into project lookup,
101
     * use {@link BrokenReferencesSupport#createReferenceProblemsProvider} as default
102
     * implementation, and use {@link ProjectProblems#isBroken}
121
     */
103
     */
104
    @Deprecated
122
    public static boolean isBroken(AntProjectHelper projectHelper, 
105
    public static boolean isBroken(AntProjectHelper projectHelper, 
123
            ReferenceHelper referenceHelper, String[] properties, String[] platformProperties) {
106
            ReferenceHelper referenceHelper, String[] properties, String[] platformProperties) {
124
        Parameters.notNull("projectHelper", projectHelper);             //NOI18N
107
        Parameters.notNull("projectHelper", projectHelper);             //NOI18N
125
        Parameters.notNull("referenceHelper", referenceHelper);         //NOI18N
108
        Parameters.notNull("referenceHelper", referenceHelper);         //NOI18N
126
        Parameters.notNull("properties", properties);                   //NOI18N
109
        Parameters.notNull("properties", properties);                   //NOI18N
127
        Parameters.notNull("platformProperties", platformProperties);   //NOI18N
110
        Parameters.notNull("platformProperties", platformProperties);   //NOI18N
128
        return BrokenReferencesModel.isBroken(projectHelper, referenceHelper,
111
        return ProjectProblems.isBroken(ProjectDecorator.create(
129
            projectHelper.getStandardPropertyEvaluator(), properties, platformProperties);
112
                projectHelper,
113
                referenceHelper,
114
                projectHelper.getStandardPropertyEvaluator(),
115
                properties,
116
                platformProperties,
117
                true));
130
    }
118
    }
131
    
119
    
132
    /**
120
    /**
Lines 143-181 Link Here
143
     *    platform is expected to be "default_platform" and this platform
131
     *    platform is expected to be "default_platform" and this platform
144
     *    always exists.
132
     *    always exists.
145
     * @see LibraryDefiner
133
     * @see LibraryDefiner
134
     *
135
     * @deprecated Add {@link ProjectProblemsProvider} into project lookup,
136
     * use {@link BrokenReferencesSupport#createReferenceProblemsProvider} as default
137
     * implementation, and use {@link ProjectProblems#showCustomizer}
146
     */
138
     */
147
    @Messages({
139
    @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, 
140
    public static void showCustomizer(AntProjectHelper projectHelper, 
153
            ReferenceHelper referenceHelper, String[] properties, String[] platformProperties) {
141
            ReferenceHelper referenceHelper, String[] properties, String[] platformProperties) {
154
        BrokenReferencesModel model = new BrokenReferencesModel(projectHelper, referenceHelper, properties, platformProperties);
142
        ProjectProblems.showCustomizer(ProjectDecorator.create(
155
        BrokenReferencesCustomizer customizer = new BrokenReferencesCustomizer(model);
143
                projectHelper,
156
        JButton close = new JButton (LBL_BrokenLinksCustomizer_Close()); // NOI18N
144
                referenceHelper,
157
        close.getAccessibleContext ().setAccessibleDescription (ACSD_BrokenLinksCustomizer_Close()); // NOI18N
145
                projectHelper.getStandardPropertyEvaluator(),
158
        String projectDisplayName = "???"; // NOI18N
146
                properties,
159
        try {
147
                platformProperties,
160
            Project project = ProjectManager.getDefault().findProject(projectHelper.getProjectDirectory());
148
                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
    }
149
    }
180
150
181
    /**
151
    /**
Lines 184-192 Link Here
184
     * the project opening, and it will take care about showing message box only
154
     * the project opening, and it will take care about showing message box only
185
     * once for several subsequent calls during a timeout.
155
     * once for several subsequent calls during a timeout.
186
     * The alert box has also "show this warning again" check box.
156
     * The alert box has also "show this warning again" check box.
157
     *
158
     * @deprecated Add {@link ProjectProblemsProvider} into project lookup,
159
     * use {@link BrokenReferencesSupport#createReferenceProblemsProvider} as default
160
     * implementation, and use {@link ProjectProblems#showAlert}
187
     */
161
     */
162
    @Deprecated
188
    public static void showAlert() {
163
    public static void showAlert() {
189
        showAlertImpl(null);
164
        ProjectProblems.showCustomizer(ProjectDecorator.create());
190
    }
165
    }
191
166
192
    /**
167
    /**
Lines 209-216 Link Here
209
     *    platform is expected to be "default_platform" and this platform
184
     *    platform is expected to be "default_platform" and this platform
210
     *    always exists.
185
     *    always exists.
211
     * @since 1.37
186
     * @since 1.37
187
     *
188
     * @deprecated Add {@link ProjectProblemsProvider} into project lookup,
189
     * use {@link BrokenReferencesSupport#createReferenceProblemsProvider} as default
190
     * implementation, and use {@link ProjectProblems#showAlert}
212
     */
191
     */
213
    
192
    @Deprecated
214
    public static void showAlert(
193
    public static void showAlert(
215
            @NonNull final AntProjectHelper projectHelper,
194
            @NonNull final AntProjectHelper projectHelper,
216
            @NonNull final ReferenceHelper referenceHelper,
195
            @NonNull final ReferenceHelper referenceHelper,
Lines 222-320 Link Here
222
        Parameters.notNull("evaluator", evaluator);                     //NOI18N
201
        Parameters.notNull("evaluator", evaluator);                     //NOI18N
223
        Parameters.notNull("properties", properties);                   //NOI18N
202
        Parameters.notNull("properties", properties);                   //NOI18N
224
        Parameters.notNull("platformProperties", platformProperties);   //NOI18N
203
        Parameters.notNull("platformProperties", platformProperties);   //NOI18N
225
        showAlertImpl(new BrokenReferencesModel.BrokenProject(projectHelper, referenceHelper, evaluator, properties, platformProperties));
204
        ProjectProblems.showAlert(ProjectDecorator.create(
205
                projectHelper,
206
                referenceHelper,
207
                evaluator,
208
                properties,
209
                platformProperties,
210
                false));
226
    }
211
    }
227
212
213
    /**
214
     * Creates a {@link ProjectProblemsProvider} creating broken references
215
     * problems.
216
     * @param projectHelper AntProjectHelper associated with the project.
217
     * @param referenceHelper ReferenceHelper associated with the project.
218
     * @param evaluator the {@link PropertyEvaluator} used to resolve broken references
219
     * @param properties array of property names which values hold
220
     *    references which may be broken. For example for J2SE project
221
     *    the property names will be: "javac.classpath", "run.classpath", etc.
222
     * @param platformProperties array of property names which values hold
223
     *    name of the platform(s) used by the project. These platforms will be
224
     *    checked for existence. For example for J2SE project the property
225
     *    name is one and it is "platform.active". The name of the default
226
     *    platform is expected to be "default_platform" and this platform
227
     *    always exists.
228
     * @return the {@link ProjectProblemsProvider} to be laced into project lookup.
229
     * @see ProjectProblemsProvider
230
     * @since 1.48
231
     */
232
    public static ProjectProblemsProvider createReferenceProblemsProvider(
233
            @NonNull final AntProjectHelper projectHelper,
234
            @NonNull final ReferenceHelper referenceHelper,
235
            @NonNull final PropertyEvaluator evaluator,
236
            @NonNull final String[] properties,
237
            @NonNull final String[] platformProperties) {
238
        Parameters.notNull("projectHelper", projectHelper);             //NOI18N
239
        Parameters.notNull("referenceHelper", referenceHelper);         //NOI18N
240
        Parameters.notNull("evaluator", evaluator);                     //NOI18N
241
        Parameters.notNull("properties", properties);                   //NOI18N
242
        Parameters.notNull("platformProperties", platformProperties);   //NOI18N
243
        return ProjectProblemsProviders.createReferenceProblemProvider(
244
                projectHelper,
245
                referenceHelper,
246
                evaluator,
247
                properties,
248
                platformProperties);
249
    }
228
250
229
    @Messages({
251
    /**
230
        "CTL_Broken_References_Resolve=Resolve Problems...",
252
     * Creates a {@link ProjectProblemsProvider} creating wrong Java platform
231
        "AD_Broken_References_Resolve=N/A",
253
     * version problems.
232
        "CTL_Broken_References_Close=Close",
254
     * @param evaluator the {@link PropertyEvaluator} used to resolve broken references
233
        "AD_Broken_References_Close=N/A",
255
     * @param platformProperty a property holding the active platform id.
234
        "MSG_Broken_References_Title=Open Project",
256
     * @param versionProperties array of property names which values hold the source,
235
        "LBL_Broken_References_Resolve_Panel_Close=Close",
257
     * target level.
236
        "AD_Broken_References_Resolve_Panel_Close=N/A",
258
     * @return {@link ProjectProblemsProvider} to be laced into project lookup.
237
        "LBL_Broken_References_Resolve_Panel_Title=Resolve Reference Problems"
259
     * 
238
    })
260
     * @see ProjectProblemsProvider
239
    private static synchronized void showAlertImpl(@NullAllowed final BrokenReferencesModel.BrokenProject broken) {        
261
     * @since 1.48
240
        if (!JavaProjectSettings.isShowAgainBrokenRefAlert() || suppressBrokenRefAlert) {
262
     */
241
            return;
263
    public static ProjectProblemsProvider createPlatformVersionProblemProvider(
242
        } else if (context == null) {
264
            @NonNull final PropertyEvaluator evaluator,
243
            assert rpTask == null;
265
            @NonNull final String platformProperty,
244
266
            @NonNull final String... versionProperties) {
245
            final Runnable task = new Runnable() {
267
        Parameters.notNull("evaluator", evaluator);                     //NOI18N
246
                public @Override void run() {
268
        Parameters.notNull("platformProperty", platformProperty);       //NOI18N
247
                    final BrokenReferencesModel.Context ctx;
269
        Parameters.notNull("versionProperties", versionProperties);     //NOI18N
248
                    synchronized (BrokenReferencesSupport.class) {
270
        return ProjectProblemsProviders.createPlatformVersionProblemProvider(
249
                        rpTask = null;
271
                evaluator,
250
                        ctx = context;
272
                platformProperty,
251
                    }
273
                versionProperties);
252
                    if (ctx == null) {
274
    }
253
                        return;
275
        
254
                    }
255
                    try {
256
                        final JButton resolveOption = new JButton(CTL_Broken_References_Resolve());
257
                        resolveOption.getAccessibleContext().setAccessibleDescription(AD_Broken_References_Resolve());
258
                        JButton closeOption = new JButton (CTL_Broken_References_Close());
259
                        closeOption.getAccessibleContext().setAccessibleDescription(AD_Broken_References_Close());
260
                        DialogDescriptor dd = new DialogDescriptor(new BrokenReferencesAlertPanel(),
261
                            MSG_Broken_References_Title(),
262
                            true,
263
                            new Object[] {resolveOption, closeOption},
264
                            closeOption,
265
                            DialogDescriptor.DEFAULT_ALIGN,
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
    /**
276
    /**
319
     * Service which may be {@linkplain ServiceProvider registered} to download remote libraries or otherwise define them.
277
     * 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
278
     * @since org.netbeans.modules.java.project/1 1.35
Lines 329-333 Link Here
329
        @CheckForNull Callable<Library> missingLibrary(String name);
287
        @CheckForNull Callable<Library> missingLibrary(String name);
330
288
331
    }
289
    }
332
    
290
291
292
    private static final class ProjectDecorator implements Project {
293
294
        private final Project delegate;
295
        private final Lookup lookup;
296
297
        private ProjectDecorator(
298
                @NonNull final Project delegate,
299
                @NonNull final ProjectProblemsProvider  provider) {
300
            assert delegate != null;
301
            this.delegate = delegate;
302
            this.lookup = new ProxyLookup(delegate.getLookup(),Lookups.singleton(provider));
303
        }
304
305
        private ProjectDecorator() {
306
            this.delegate = null;
307
            this.lookup = Lookup.EMPTY;
308
        }
309
310
        @Override
311
        public FileObject getProjectDirectory() {
312
            return delegate.getProjectDirectory();
313
        }
314
315
        @Override
316
        public Lookup getLookup() {
317
            return lookup;
318
        }        
319
320
        static ProjectDecorator create(
321
            @NonNull final AntProjectHelper projectHelper,
322
            @NonNull final ReferenceHelper referenceHelper,
323
            @NonNull final PropertyEvaluator evaluator,
324
            @NonNull final String[] properties,
325
            @NonNull final String[] platformProperties,
326
            final boolean abortAfterFirstProblem) {
327
            final Project prj = FileOwnerQuery.getOwner(projectHelper.getProjectDirectory());
328
            return new ProjectDecorator(
329
                prj,
330
                ProjectProblemsProviders.createReferenceProblemProvider(
331
                    projectHelper,
332
                    referenceHelper,
333
                    evaluator,
334
                    properties,
335
                    platformProperties));
336
        }
337
338
        static ProjectDecorator create() {
339
            return new ProjectDecorator();
340
        }
341
    }
342
            
333
}
343
}
(-)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 (+225 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.HashSet;
51
import java.util.Iterator;
52
import java.util.Map;
53
import java.util.Set;
54
import java.util.WeakHashMap;
55
import java.util.concurrent.ExecutionException;
56
import java.util.concurrent.Future;
57
import java.util.logging.Level;
58
import java.util.logging.Logger;
59
import javax.swing.event.ChangeListener;
60
import org.netbeans.api.annotations.common.NonNull;
61
import org.netbeans.api.annotations.common.StaticResource;
62
import org.netbeans.api.project.Project;
63
import org.netbeans.api.project.ui.OpenProjects;
64
import org.netbeans.api.project.ui.ProjectProblems;
65
import org.netbeans.spi.project.ProjectIconAnnotator;
66
import org.netbeans.spi.project.ui.ProjectProblemsProvider;
67
import org.openide.util.ChangeSupport;
68
import org.openide.util.Exceptions;
69
import org.openide.util.ImageUtilities;
70
import org.openide.util.NbBundle;
71
import org.openide.util.Parameters;
72
import org.openide.util.RequestProcessor;
73
import org.openide.util.lookup.ServiceProvider;
74
75
/**
76
 *
77
 * @author Tomas Zezula
78
 */
79
@ServiceProvider(service=ProjectIconAnnotator.class)
80
public class BrokenProjectAnnotator implements ProjectIconAnnotator, PropertyChangeListener {
81
82
83
    @StaticResource
84
    private static final String BROKEN_PROJECT_BADGE_PATH = "org/netbeans/modules/project/ui/resources/brokenProjectBadge.gif";    //NOI18N
85
    private static final URL BROKEN_PROJECT_BADGE_URL = BrokenProjectAnnotator.class.getClassLoader().getResource(BROKEN_PROJECT_BADGE_PATH);
86
    private static final Image BROKEN_PROJECT_BADGE = ImageUtilities.loadImage(BROKEN_PROJECT_BADGE_PATH, true);
87
    private static final int FIRE_DELAY = 500;
88
    private static final RequestProcessor FIRER = new RequestProcessor(BrokenProjectAnnotator.class.getName()+".fire", 1, false, false);   //NOI18N
89
    private static final RequestProcessor NOTIFIER = new RequestProcessor(BrokenProjectAnnotator.class.getName()+".alert", 1, false, false);   //NOI18N
90
    private static final Logger LOG = Logger.getLogger(BrokenProjectAnnotator.class.getName());
91
92
    private final ChangeSupport changeSupport = new ChangeSupport(this);
93
    private final RequestProcessor.Task task = FIRER.create(new Runnable(){
94
        @Override
95
        public void run() {
96
            changeSupport.fireChange();
97
            LOG.fine("Fire.");
98
        }
99
    });
100
    private final Object cacheLock = new Object();
101
    //@GuardedBy("cacheLock")
102
    private final Map<Project, Integer> brokenCache = new WeakHashMap<Project, Integer>();
103
    //@GuardedBy("cacheLock")
104
    private final Map<ProjectProblemsProvider, Set<Reference<Project>>> problemsProvider2prj =
105
            new WeakHashMap<ProjectProblemsProvider, Set<Reference<Project>>>();
106
107
108
    @NonNull
109
    @Override
110
    public Image annotateIcon(
111
            @NonNull final Project project,
112
            @NonNull Image original,
113
            final boolean openedNode) {
114
        LOG.log(Level.FINE, "The annotateIcon called for project: {0}.", project);  //NOI18N
115
        Integer problemsCount = null;
116
        boolean firstTime = false;
117
        synchronized (cacheLock) {
118
            if (brokenCache.containsKey(project)) {
119
                problemsCount = brokenCache.get(project);
120
                LOG.log(Level.FINE, "In cache: {0}.", problemsCount);   //NOI18N
121
            } else {
122
                firstTime = true;
123
                brokenCache.put(project, problemsCount);
124
                final ProjectProblemsProvider ppp = project.getLookup().lookup(ProjectProblemsProvider.class);
125
                if (ppp != null) {
126
                    ppp.addPropertyChangeListener(this);
127
                    Set<Reference<Project>> projects = problemsProvider2prj.get(ppp);
128
                    if (projects == null) {
129
                        projects = new HashSet<Reference<Project>>();
130
                        problemsProvider2prj.put(ppp, projects);
131
                    }
132
                    projects.add(new WeakReference<Project>(project));
133
                }
134
                LOG.fine("Added listeners.");    //NOI18N
135
            }
136
        }
137
        if (problemsCount == null) {
138
            final ProjectProblemsProvider provider = project.getLookup().lookup(ProjectProblemsProvider.class);
139
            problemsCount = provider !=null ? provider.getProblems().size() : 0;
140
            synchronized (cacheLock) {
141
                brokenCache.put(project, problemsCount);
142
                LOG.log(Level.FINE, "Set {0} to cache.", problemsCount);   //NOI18N
143
            }
144
        }
145
        if (problemsCount > 0) {
146
            if (firstTime) {
147
                scheduleAlert(project);
148
            }
149
            final String message = problemsCount == 1 ?
150
                    NbBundle.getMessage(BrokenProjectAnnotator.class, "MSG_OneProblem") :
151
                    NbBundle.getMessage(BrokenProjectAnnotator.class, "MSG_MoreProblems", problemsCount);
152
            final String messageHtml = String.format(
153
                "<img src=\"%s\">&nbsp;%s",   //NOI18N
154
                BROKEN_PROJECT_BADGE_URL.toExternalForm(),
155
                message);
156
            original = ImageUtilities.mergeImages(
157
                    original,
158
                    ImageUtilities.assignToolTipToImage(BROKEN_PROJECT_BADGE, messageHtml),
159
                    8,
160
                    0);
161
        }
162
        return original;
163
    }
164
165
    @Override
166
    public void addChangeListener(@NonNull final ChangeListener listener) {
167
        Parameters.notNull("listener", listener);   //NOI18N
168
        changeSupport.addChangeListener(listener);
169
170
    }
171
172
    @Override
173
    public void removeChangeListener(ChangeListener listener) {
174
        Parameters.notNull("listener", listener);   //NOI18N
175
        changeSupport.removeChangeListener(listener);
176
    }
177
178
    @Override
179
    public void propertyChange(@NonNull final PropertyChangeEvent evt) {
180
        if (ProjectProblemsProvider.PROP_PROBLEMS.equals(evt.getPropertyName())) {
181
            synchronized (cacheLock) {
182
                final Set<Reference<Project>> toRefresh = problemsProvider2prj.get(evt.getSource());
183
                if (toRefresh != null) {
184
                    LOG.fine("Event from known ProjectProblemsProvider -> clearing cache for:");  //NOI18N
185
                    for (final Iterator<Reference<Project>> it = toRefresh.iterator();it.hasNext();) {
186
                        final Reference<Project> ref = it.next();
187
                        final Project prj = ref.get();
188
                        if (prj != null) {
189
                            brokenCache.put(prj, null);
190
                            LOG.log(Level.FINE, "Project: {0}", prj);  //NOI18N
191
                        } else {
192
                            it.remove();
193
                        }
194
                    }
195
                } else {
196
                    LOG.fine("Event from unknown ProjectProblemsProvider -> clearing all caches.");  //NOI18N
197
                }
198
            }
199
            task.schedule(FIRE_DELAY);
200
        }
201
    }
202
203
    /**
204
     * Shows alert for given project.
205
     * Todo: Better would be to use ProjectOpenHook, however there is no way
206
     * how to register the POH for all project types, see issue #215687.
207
     * @param prj
208
     */
209
    private void scheduleAlert(@NonNull final Project prj) {
210
        NOTIFIER.post(new Runnable() {
211
            @Override
212
            public void run() {
213
                final Future<Project[]> projects = OpenProjects.getDefault().openProjects();
214
                try {
215
                    projects.get();
216
                } catch (ExecutionException ex) {
217
                    Exceptions.printStackTrace(ex);
218
                } catch (InterruptedException ie) {
219
                    Exceptions.printStackTrace(ie);
220
                }
221
                ProjectProblems.showAlert(prj);
222
            }
223
        });
224
    }
225
}
(-)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 (+298 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
    enum Status {
112
        RESOLVED,
113
        RESOLVED_WITH_WARNING,
114
        UNRESOLVED
115
    }
116
117
    /**
118
     * Result of the project metadata problem resolution.
119
     */
120
    public final class Result {
121
        private final Status status;
122
        private final String message;
123
124
        private Result(
125
            @NonNull final Status status,
126
            @NullAllowed final String message) {
127
            this.status = status;
128
            this.message = message;
129
        }
130
131
        /**
132
         * Returns true if the problem was resolved.
133
         * @return true if the problem was successfully resolved.
134
         */
135
        public boolean isResolved() {
136
            return status != Status.UNRESOLVED;
137
        }
138
139
        /**
140
         * Returns status of the resolution.
141
         * @return the {@link ProjectProblemsProvider.Status}
142
         */
143
        @NonNull
144
        public Status getStatus() {
145
            return status;
146
        }
147
148
        /**
149
         * Returns possible error or warning message.
150
         * @return the message which should be presented to the user.
151
         */
152
        @CheckForNull
153
        public String getMessage() {
154
            return message;
155
        }
156
157
        /**
158
         * Creates a new instance of the {@link Result}.
159
         * @param status the status of the project problem resolution.
160
         * @return the new {@link Result} instance.
161
         */
162
        public static Result create(
163
            @NonNull final Status status) {
164
            Parameters.notNull("status", status);   //NOI18N
165
            return new Result(status, null);
166
        }
167
168
        /**
169
         * Creates a new instance of the {@link Result}.
170
         * @param status the status of the project problem resolution.
171
         * @param message the message which should be presented to the user.
172
         * @return the new {@link Result} instance.
173
         */
174
        public static Result create(
175
            @NonNull final Status status,
176
            @NonNull final String message) {
177
            Parameters.notNull("status", status);   //NOI18N
178
            Parameters.notNull("message", message);   //NOI18N
179
            return new Result(status, message);
180
        }
181
182
    }
183
184
185
    /**
186
     * Project metadata problem.
187
     * Represents a problem in the project metadata which should be presented
188
     * to the user and resolved.
189
     */
190
    public final class ProjectProblem {
191
192
        private final String displayName;
193
        private final String description;
194
        private final ProjectProblemResolver resolver;
195
196
        private ProjectProblem(
197
                @NonNull final String displayName,
198
                @NonNull final String description,
199
                @NonNull final ProjectProblemResolver resolver) {
200
            this.displayName = displayName;
201
            this.description = description;
202
            this.resolver = resolver;
203
        }
204
205
        /**
206
         * Returns a display name of the problem.
207
         * The display name is presented to the user in the UI.
208
         * @return the display name.
209
         */
210
        @NonNull
211
        public String getDisplayName() {
212
            return displayName;
213
        }        
214
215
        /**
216
         * Returns the description of the problem.
217
         * The description is shown in the project problems details.
218
         * @return project problem description.
219
         */
220
        @NonNull
221
        public String getDescription() {
222
            return description;
223
        }
224
225
        /**
226
         * Resolves the problem.
227
         * Called by the Event Dispatch Thread.
228
         * When the resolution needs to be done by a background thread, eg. downloading
229
         * an archive from repository, the implementation directly returns
230
         * a {@link Future} which is completed by the background thread.
231
         * @return the {@link Future} holding the problem resolution status.
232
         */
233
        public Future<Result> resolve() {
234
            return resolver.resolve();
235
        }
236
237
        /**
238
         * {@inheritDoc}
239
         */
240
        @Override
241
        public boolean equals(final Object other) {
242
            if (other == this) {
243
                return true;
244
            }
245
            if (!(other instanceof ProjectProblem)) {
246
                return false;
247
            }
248
            final  ProjectProblem otherProblem = (ProjectProblem) other;
249
            return displayName.equals(otherProblem.displayName) &&
250
                description.equals(otherProblem.description) &&
251
                resolver.equals(otherProblem.resolver);
252
        }
253
254
        /**
255
         * {@inheritdoc}
256
         */
257
        @Override
258
        public int hashCode() {
259
            int result = 17;
260
            result = 31 * result + displayName.hashCode();
261
            result = 31 * result + description.hashCode();
262
            result = 31 * result + resolver.hashCode();
263
            return result;
264
        }
265
266
        /**
267
         * {@inheritdoc}
268
         */
269
        @Override
270
        public String toString() {
271
            return String.format(
272
             "Project Problem: %s, resolvable by: %s",  //NOI18N
273
             displayName,
274
             resolver);
275
        }
276
277
278
        /**
279
         * Creates a new instance of the {@link ProjectProblem}.
280
         * @param displayName the project problem display name.
281
         * @param description the project problem description.
282
         * @param resolver the {@link ProjectProblemResolver} to resolve the problem.
283
         * @return a new instance of {@link ProjectProblem}
284
         */
285
        @NonNull
286
        public static ProjectProblem create(
287
                @NonNull final String displayName,
288
                @NonNull final String description,
289
                @NonNull final ProjectProblemResolver resolver) {
290
            Parameters.notNull("displayName", displayName); //NOI18N
291
            Parameters.notNull("description", description); //NOI18N
292
            Parameters.notNull("resolver", resolver);   //NOI18N
293
            return new ProjectProblem(displayName,description,resolver);
294
        }
295
296
    }
297
298
}
(-)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