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

(-)arch.xml (-3 / +108 lines)
Lines 1035-1043 Link Here
1035
        </question>
1035
        </question>
1036
-->
1036
-->
1037
 <answer id="resources-read">
1037
 <answer id="resources-read">
1038
  <p>
1038
    <api group="layer" name="ModuleProjectLookup" type="export" category="official">
1039
   No.
1039
     <p>
1040
  </p>
1040
       "Projects/org-netbeans-modules-apisupport-project/Lookup" folder's content is used to construct the project's additional lookup.
1041
       It's content is expected to be <code>LookupProvider</code> instances. Apisupport project provides <code>LookupMerger</code>s 
1042
       for <code>Sources</code>, <code>PrivilegedTemplates</code> and <code>RecommendedTemplates</code>. Implementations added by 3rd parties 
1043
       will be merged into a single instance in the project's lookup.
1044
     </p>
1045
    </api>
1046
    <api group="layer" name="SuiteProjectLookup" type="export" category="official">
1047
     <p>
1048
       "Projects/org-netbeans-modules-apisupport-project-suite/Lookup" folder's content is used to construct the project's additional lookup.
1049
       It's content is expected to be <code>LookupProvider</code> instances. 
1050
     </p>
1051
    </api>
1052
   <api group="layer" name="ModuleLogicalViewProvider" type="export" category="official">
1053
     <p>
1054
       "Projects/org-netbeans-modules-apisupport-project/Nodes" folder's content is used to construct the project's child nodes.
1055
       It's content is expected to be <code>NodeFactory</code> instances.
1056
     </p>
1057
    </api>
1058
   <api group="layer" name="SuiteLogicalViewProvider" type="export" category="official">
1059
     <p>
1060
       "Projects/org-netbeans-modules-apisupport-project-suite/Nodes" folder's content is used to construct the project's child nodes.
1061
       It's content is expected to be <code>NodeFactory</code> instances.
1062
     </p>
1063
    </api>
1064
     
1041
 </answer>
1065
 </answer>
1042
1066
1043
1067
Lines 1072-1077 Link Here
1072
 <answer id="security-policy">
1096
 <answer id="security-policy">
1073
  <p>
1097
  <p>
1074
   No.
1098
   No.
1099
  </p>
1100
 </answer>
1101
1102
1103
1104
1105
<!--
1106
        <question id="arch-where" when="impl">
1107
            Where one can find sources for your module?
1108
            <hint>
1109
                Please provide link to the CVS web client at
1110
                http://www.netbeans.org/download/source_browse.html
1111
                or just use tag defaultanswer generate='here'
1112
            </hint>
1113
        </question>
1114
-->
1115
 <answer id="arch-where">
1116
  <defaultanswer generate='here' />
1117
 </answer>
1118
1119
1120
1121
<!--
1122
        <question id="compat-deprecation" when="init">
1123
            How the introduction of your project influences functionality
1124
            provided by previous version of the product?
1125
            <hint>
1126
            If you are planning to deprecate/remove/change any existing APIs,
1127
            list them here accompanied with the reason explaining why you
1128
            are doing so.
1129
            </hint>
1130
        </question>
1131
-->
1132
 <answer id="compat-deprecation">
1133
  <p>
1134
   XXX no answer for compat-deprecation
1135
  </p>
1136
 </answer>
1137
1138
1139
1140
<!--
1141
        <question id="exec-ant-tasks" when="impl">
1142
            Do you define or register any ant tasks that other can use?
1143
            
1144
            <hint>
1145
            If you provide an ant task that users can use, you need to be very
1146
            careful about its syntax and behaviour, as it most likely forms an
1147
	          API for end users and as there is a lot of end users, their reaction
1148
            when such API gets broken can be pretty strong.
1149
            </hint>
1150
        </question>
1151
-->
1152
 <answer id="exec-ant-tasks">
1153
  <p>
1154
   XXX no answer for exec-ant-tasks
1155
  </p>
1156
 </answer>
1157
1158
1159
1160
<!--
1161
        <question id="resources-preferences" when="final">
1162
            Does your module uses preferences via Preferences API? Does your module use NbPreferences or
1163
            or regular JDK Preferences ? Does it read, write or both ? 
1164
            Does it share preferences with other modules ? If so, then why ?
1165
            <hint>
1166
                You may use
1167
                    &lt;api type="export" group="preferences"
1168
                    name="preference node name" category="private"&gt;
1169
                    description of individual keys, where it is used, what it
1170
                    influences, whether the module reads/write it, etc.
1171
                    &lt;/api&gt;
1172
                Due to XML ID restrictions, rather than /org/netbeans/modules/foo give the "name" as org.netbeans.modules.foo.
1173
                Note that if you use NbPreferences this name will then be the same as the code name base of the module.
1174
            </hint>
1175
        </question>
1176
-->
1177
 <answer id="resources-preferences">
1178
  <p>
1179
   XXX no answer for resources-preferences
1075
  </p>
1180
  </p>
1076
 </answer>
1181
 </answer>
1077
1182
(-)nbproject/project.properties (-1 / +1 lines)
Lines 17-23 Link Here
17
17
18
javac.compilerargs=-Xlint -Xlint:-serial
18
javac.compilerargs=-Xlint -Xlint:-serial
19
javac.source=1.5
19
javac.source=1.5
20
spec.version.base=1.13.0
20
spec.version.base=1.14.0
21
21
22
ant.jar=${ant.home}/lib/ant.jar
22
ant.jar=${ant.home}/lib/ant.jar
23
antsrc.cp=\
23
antsrc.cp=\
(-)src/org/netbeans/modules/apisupport/project/NbModuleProject.java (-5 / +13 lines)
Lines 85-91 Link Here
85
import org.netbeans.modules.apisupport.project.ui.ModuleLogicalView;
85
import org.netbeans.modules.apisupport.project.ui.ModuleLogicalView;
86
import org.netbeans.modules.apisupport.project.ui.ModuleOperations;
86
import org.netbeans.modules.apisupport.project.ui.ModuleOperations;
87
import org.netbeans.modules.apisupport.project.universe.LocalizedBundleInfo;
87
import org.netbeans.modules.apisupport.project.universe.LocalizedBundleInfo;
88
import org.netbeans.spi.project.support.LookupProviderSupport;
88
import org.netbeans.spi.project.ui.RecommendedTemplates;
89
import org.netbeans.spi.project.ui.RecommendedTemplates;
90
import org.netbeans.spi.project.ui.support.UILookupMergerSupport;
89
91
90
/**
92
/**
91
 * A NetBeans module project.
93
 * A NetBeans module project.
Lines 103-109 Link Here
103
    
105
    
104
    private final AntProjectHelper helper;
106
    private final AntProjectHelper helper;
105
    private final Evaluator eval;
107
    private final Evaluator eval;
106
    private final Lookup lookup;
108
    private Lookup lookup;
107
    private Map<FileObject,Element> extraCompilationUnits;
109
    private Map<FileObject,Element> extraCompilationUnits;
108
    private final GeneratedFilesHelper genFilesHelper;
110
    private final GeneratedFilesHelper genFilesHelper;
109
    private final NbModuleTypeProviderImpl typeProvider;
111
    private final NbModuleTypeProviderImpl typeProvider;
Lines 190-195 Link Here
190
            }
192
            }
191
        });
193
        });
192
        lookup = Lookups.fixed(new Object[] {
194
        lookup = Lookups.fixed(new Object[] {
195
            this,
193
            new Info(),
196
            new Info(),
194
            helper.createAuxiliaryConfiguration(),
197
            helper.createAuxiliaryConfiguration(),
195
            helper.createCacheDirectoryProvider(),
198
            helper.createCacheDirectoryProvider(),
Lines 209-215 Link Here
209
                // currently these are hardcoded
212
                // currently these are hardcoded
210
                "build", // NOI18N
213
                "build", // NOI18N
211
            }),
214
            }),
212
            sourcesHelper.createSources(),
215
            sourcesHelper.createSources(),                    
213
            new AntArtifactProviderImpl(this, helper, evaluator()),
216
            new AntArtifactProviderImpl(this, helper, evaluator()),
214
            new CustomizerProviderImpl(this, getHelper(), evaluator()),
217
            new CustomizerProviderImpl(this, getHelper(), evaluator()),
215
            new SuiteProviderImpl(),
218
            new SuiteProviderImpl(),
Lines 218-225 Link Here
218
            new ModuleProjectClassPathExtender(this),
221
            new ModuleProjectClassPathExtender(this),
219
            new LocalizedBundleInfoProvider(),
222
            new LocalizedBundleInfoProvider(),
220
            new ModuleOperations(this),
223
            new ModuleOperations(this),
221
            new ServiceNodeHandler(this)
224
            new ServiceNodeHandler(this),
225
            LookupProviderSupport.createSourcesMerger(),
226
            UILookupMergerSupport.createPrivilegedTemplatesMerger(),
227
            UILookupMergerSupport.createRecommendedTemplatesMerger(),
228
222
        });
229
        });
230
        lookup = LookupProviderSupport.createCompositeLookup(lookup, "Projects/org-netbeans-modules-apisupport-project/Lookup"); //NOI18N
223
    }
231
    }
224
    
232
    
225
    public String toString() {
233
    public String toString() {
Lines 627-633 Link Here
627
    public void setRunInAtomicAction(boolean runInAtomicAction) {
635
    public void setRunInAtomicAction(boolean runInAtomicAction) {
628
        eval.setRunInAtomicAction(runInAtomicAction);
636
        eval.setRunInAtomicAction(runInAtomicAction);
629
    }
637
    }
630
    
638
        
631
    private final class Info implements ProjectInformation, PropertyChangeListener {
639
    private final class Info implements ProjectInformation, PropertyChangeListener {
632
        
640
        
633
        private final PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
641
        private final PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
Lines 764-770 Link Here
764
    public void notifyDeleting() {
772
    public void notifyDeleting() {
765
        eval.removeListeners();
773
        eval.removeListeners();
766
    }
774
    }
767
    
775
        
768
    private final class SavedHook extends ProjectXmlSavedHook {
776
    private final class SavedHook extends ProjectXmlSavedHook {
769
        
777
        
770
        SavedHook() {}
778
        SavedHook() {}
(-)src/org/netbeans/modules/apisupport/project/suite/SuiteProject.java (-1 / +5 lines)
Lines 42-47 Link Here
42
import org.netbeans.modules.apisupport.project.ui.customizer.SuiteCustomizer;
42
import org.netbeans.modules.apisupport.project.ui.customizer.SuiteCustomizer;
43
import org.netbeans.modules.apisupport.project.ui.customizer.SuiteProperties;
43
import org.netbeans.modules.apisupport.project.ui.customizer.SuiteProperties;
44
import org.netbeans.modules.apisupport.project.universe.NbPlatform;
44
import org.netbeans.modules.apisupport.project.universe.NbPlatform;
45
import org.netbeans.spi.project.support.LookupProviderSupport;
45
import org.netbeans.spi.project.support.ant.AntProjectEvent;
46
import org.netbeans.spi.project.support.ant.AntProjectEvent;
46
import org.netbeans.spi.project.support.ant.AntProjectHelper;
47
import org.netbeans.spi.project.support.ant.AntProjectHelper;
47
import org.netbeans.spi.project.support.ant.AntProjectListener;
48
import org.netbeans.spi.project.support.ant.AntProjectListener;
Lines 54-59 Link Here
54
import org.netbeans.spi.project.ui.PrivilegedTemplates;
55
import org.netbeans.spi.project.ui.PrivilegedTemplates;
55
import org.netbeans.spi.project.ui.ProjectOpenedHook;
56
import org.netbeans.spi.project.ui.ProjectOpenedHook;
56
import org.netbeans.spi.project.ui.RecommendedTemplates;
57
import org.netbeans.spi.project.ui.RecommendedTemplates;
58
import org.netbeans.spi.project.ui.support.UILookupMergerSupport;
57
import org.openide.ErrorManager;
59
import org.openide.ErrorManager;
58
import org.openide.filesystems.FileObject;
60
import org.openide.filesystems.FileObject;
59
import org.openide.filesystems.FileUtil;
61
import org.openide.filesystems.FileUtil;
Lines 73-79 Link Here
73
            "org/netbeans/modules/apisupport/project/suite/resources/suite.gif"; // NOI18N
75
            "org/netbeans/modules/apisupport/project/suite/resources/suite.gif"; // NOI18N
74
    
76
    
75
    private final AntProjectHelper helper;
77
    private final AntProjectHelper helper;
76
    private final Lookup lookup;
78
    private Lookup lookup;
77
    private final PropertyEvaluator eval;
79
    private final PropertyEvaluator eval;
78
    private final GeneratedFilesHelper genFilesHelper;
80
    private final GeneratedFilesHelper genFilesHelper;
79
    
81
    
Lines 83-88 Link Here
83
        genFilesHelper = new GeneratedFilesHelper(helper);
85
        genFilesHelper = new GeneratedFilesHelper(helper);
84
        Util.err.log("Loading suite project in " + getProjectDirectory());
86
        Util.err.log("Loading suite project in " + getProjectDirectory());
85
        lookup = Lookups.fixed(new Object[] {
87
        lookup = Lookups.fixed(new Object[] {
88
            this, 
86
            new Info(),
89
            new Info(),
87
            helper.createAuxiliaryConfiguration(),
90
            helper.createAuxiliaryConfiguration(),
88
            helper.createCacheDirectoryProvider(),
91
            helper.createCacheDirectoryProvider(),
Lines 97-102 Link Here
97
            new PrivilegedTemplatesImpl(),
100
            new PrivilegedTemplatesImpl(),
98
            new SuiteOperations(this),
101
            new SuiteOperations(this),
99
        });
102
        });
103
        lookup = LookupProviderSupport.createCompositeLookup(lookup, "Projects/org-netbeans-modules-apisupport-project-suite/Lookup");
100
    }
104
    }
101
    
105
    
102
    public String toString() {
106
    public String toString() {
(-)src/org/netbeans/modules/apisupport/project/ui/ImportantFilesNodeFactory.java (+460 lines)
Added Link Here
1
/*
2
 * The contents of this file are subject to the terms of the Common Development
3
 * and Distribution License (the License). You may not use this file except in
4
 * compliance with the License.
5
 *
6
 * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7
 * or http://www.netbeans.org/cddl.txt.
8
 *
9
 * When distributing Covered Code, include this CDDL Header Notice in each file
10
 * and include the License file at http://www.netbeans.org/cddl.txt.
11
 * If applicable, add the following below the CDDL Header, with the fields
12
 * enclosed by brackets [] replaced by your own identifying information:
13
 * "Portions Copyrighted [year] [name of copyright owner]"
14
 *
15
 * The Original Software is NetBeans. The Initial Developer of the Original
16
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
17
 * Microsystems, Inc. All Rights Reserved.
18
 */
19
20
package org.netbeans.modules.apisupport.project.ui;
21
22
import java.awt.Image;
23
import java.util.ArrayList;
24
import java.util.Collections;
25
import java.util.HashSet;
26
import java.util.Iterator;
27
import java.util.LinkedHashMap;
28
import java.util.List;
29
import java.util.Set;
30
import javax.swing.event.ChangeListener;
31
import org.netbeans.api.project.Project;
32
import org.netbeans.modules.apisupport.project.NbModuleProject;
33
import org.netbeans.modules.apisupport.project.layers.LayerNode;
34
import org.netbeans.modules.apisupport.project.layers.LayerUtils;
35
import org.netbeans.modules.apisupport.project.metainf.ServiceNodeHandler;
36
import org.netbeans.modules.apisupport.project.suite.SuiteProject;
37
import org.netbeans.spi.project.ui.support.NodeFactory;
38
import org.netbeans.spi.project.ui.support.NodeList;
39
import org.openide.ErrorManager;
40
import org.openide.filesystems.FileChangeAdapter;
41
import org.openide.filesystems.FileChangeListener;
42
import org.openide.filesystems.FileEvent;
43
import org.openide.filesystems.FileObject;
44
import org.openide.filesystems.FileStateInvalidException;
45
import org.openide.filesystems.FileSystem;
46
import org.openide.loaders.DataObject;
47
import org.openide.loaders.DataObjectNotFoundException;
48
import org.openide.nodes.Children;
49
import org.openide.nodes.FilterNode;
50
import org.openide.nodes.Node;
51
import org.openide.util.NbBundle;
52
import org.openide.util.RequestProcessor;
53
import org.openide.util.Utilities;
54
55
/**
56
 *
57
 * @author mkleint
58
 */
59
public class ImportantFilesNodeFactory implements NodeFactory {
60
    /** Package private for unit tests. */
61
    static final String IMPORTANT_FILES_NAME = "important.files"; // NOI18N
62
    /** Package private for unit tests only. */
63
    static final RequestProcessor RP = new RequestProcessor();
64
    
65
    /** Creates a new instance of ImportantFilesNodeFactory */
66
    public ImportantFilesNodeFactory() {
67
    }
68
    
69
    public NodeList createNodes(Project p) {
70
        return new ImpFilesNL(p);
71
    }
72
73
    private static class ImpFilesNL implements NodeList<String> {
74
        private Project project;
75
        
76
        public ImpFilesNL(Project p) {
77
            project = p;
78
        }
79
    
80
        public List<String> keys() {
81
            return Collections.singletonList(IMPORTANT_FILES_NAME);
82
        }
83
84
        public void addChangeListener(ChangeListener l) {
85
            //ignore, doesn't change
86
        }
87
88
        public void removeChangeListener(ChangeListener l) {
89
            //ignore, doesn't change
90
        }
91
92
        public Node node(String key) {
93
            assert key == IMPORTANT_FILES_NAME;
94
            if (project instanceof NbModuleProject) {
95
                return new ImportantFilesNode((NbModuleProject)project);
96
            }
97
            if (project instanceof SuiteProject) {
98
                return new ImportantFilesNode(new SuiteImportantFilesChildren((SuiteProject)project));
99
            }
100
            return null;
101
        }
102
103
        public void addNotify() {
104
        }
105
106
        public void removeNotify() {
107
        }
108
    }
109
    /**
110
     * Show node "Important Files" with various config and docs files beneath it.
111
     */
112
    static final class ImportantFilesNode extends AnnotatedNode {
113
        
114
        public ImportantFilesNode(NbModuleProject project) {
115
            super(new ImportantFilesChildren(project));
116
        }
117
        
118
        ImportantFilesNode(Children ch) {
119
            super(ch);
120
        }
121
        
122
        public String getName() {
123
            return IMPORTANT_FILES_NAME;
124
        }
125
        
126
        private Image getIcon(boolean opened) {
127
            Image badge = Utilities.loadImage("org/netbeans/modules/apisupport/project/resources/config-badge.gif", true);
128
            return Utilities.mergeImages(UIUtil.getTreeFolderIcon(opened), badge, 8, 8);
129
        }
130
        
131
        private static final String DISPLAY_NAME = NbBundle.getMessage(ModuleLogicalView.class, "LBL_important_files");
132
        
133
        public String getDisplayName() {
134
            return annotateName(DISPLAY_NAME);
135
        }
136
        
137
        public String getHtmlDisplayName() {
138
            return computeAnnotatedHtmlDisplayName(DISPLAY_NAME, getFiles());
139
        }
140
        
141
        public Image getIcon(int type) {
142
            return annotateIcon(getIcon(false), type);
143
        }
144
        
145
        public Image getOpenedIcon(int type) {
146
            return annotateIcon(getIcon(true), type);
147
        }
148
        
149
    }
150
    
151
    /**
152
     * Actual list of important files.
153
     */
154
    private static final class ImportantFilesChildren extends Children.Keys {
155
        
156
        private List visibleFiles = new ArrayList();
157
        private FileChangeListener fcl;
158
        
159
        /** Abstract location to display name. */
160
        private static final java.util.Map<String,String> FILES = new LinkedHashMap();
161
        static {
162
            FILES.put("${manifest.mf}", NbBundle.getMessage(ModuleLogicalView.class, "LBL_module_manifest"));
163
            FILES.put("${javadoc.arch}", NbBundle.getMessage(ModuleLogicalView.class, "LBL_arch_desc"));
164
            FILES.put("${javadoc.apichanges}", NbBundle.getMessage(ModuleLogicalView.class, "LBL_api_changes"));
165
            FILES.put("${javadoc.overview}", NbBundle.getMessage(ModuleLogicalView.class, "LBL_javadoc_overview"));
166
            FILES.put("build.xml", NbBundle.getMessage(ModuleLogicalView.class, "LBL_build.xml"));
167
            FILES.put("nbproject/project.xml", NbBundle.getMessage(ModuleLogicalView.class, "LBL_project.xml"));
168
            FILES.put("nbproject/project.properties", NbBundle.getMessage(ModuleLogicalView.class, "LBL_project.properties"));
169
            FILES.put("nbproject/private/private.properties", NbBundle.getMessage(ModuleLogicalView.class, "LBL_private.properties"));
170
            FILES.put("nbproject/suite.properties", NbBundle.getMessage(ModuleLogicalView.class, "LBL_suite.properties"));
171
            FILES.put("nbproject/private/suite-private.properties", NbBundle.getMessage(ModuleLogicalView.class, "LBL_suite-private.properties"));
172
            FILES.put("nbproject/platform.properties", NbBundle.getMessage(ModuleLogicalView.class, "LBL_platform.properties"));
173
            FILES.put("nbproject/private/platform-private.properties", NbBundle.getMessage(ModuleLogicalView.class, "LBL_platform-private.properties"));
174
        }
175
        
176
        private final NbModuleProject project;
177
        
178
        public ImportantFilesChildren(NbModuleProject project) {
179
            this.project = project;
180
        }
181
        
182
        protected void addNotify() {
183
            super.addNotify();
184
            attachListeners();
185
            refreshKeys();
186
        }
187
        
188
        protected void removeNotify() {
189
            setKeys(Collections.EMPTY_SET);
190
            removeListeners();
191
            super.removeNotify();
192
        }
193
        
194
        protected Node[] createNodes(Object key) {
195
            if (key instanceof String) {
196
                String loc = (String) key;
197
                String locEval = project.evaluator().evaluate(loc);
198
                FileObject file = project.getHelper().resolveFileObject(locEval);
199
                try {
200
                    Node orig = DataObject.find(file).getNodeDelegate();
201
                    return new Node[] {new SpecialFileNode(orig, (String) FILES.get(loc))};
202
                } catch (DataObjectNotFoundException e) {
203
                    throw new AssertionError(e);
204
                }
205
            } else if (key instanceof LayerUtils.LayerHandle) {
206
                return new Node[] {/* #68240 */ new SpecialFileNode(new LayerNode((LayerUtils.LayerHandle) key), null)};
207
            } else if (key instanceof ServiceNodeHandler) {
208
                return new Node[]{((ServiceNodeHandler)key).createServiceRootNode()};
209
            } else {
210
                throw new AssertionError(key);
211
            } 
212
        }
213
        
214
        private void refreshKeys() {
215
            Set<FileObject> files = new HashSet();
216
            List newVisibleFiles = new ArrayList();
217
            LayerUtils.LayerHandle handle = LayerUtils.layerForProject(project);
218
            FileObject layerFile = handle.getLayerFile();
219
            if (layerFile != null) {
220
                newVisibleFiles.add(handle);
221
                files.add(layerFile);
222
            }
223
            Iterator it = FILES.keySet().iterator();
224
            while (it.hasNext()) {
225
                String loc = (String) it.next();
226
                String locEval = project.evaluator().evaluate(loc);
227
                if (locEval == null) {
228
                    newVisibleFiles.remove(loc); // XXX why?
229
                    continue;
230
                }
231
                FileObject file = project.getHelper().resolveFileObject(locEval);
232
                if (file != null) {
233
                    newVisibleFiles.add(loc);
234
                    files.add(file);
235
                }
236
            }
237
            if (!isInitialized() || !newVisibleFiles.equals(visibleFiles)) {
238
                visibleFiles = newVisibleFiles;
239
                visibleFiles.add(project.getLookup().lookup(ServiceNodeHandler.class));
240
                RP.post(new Runnable() { // #72471
241
                    public void run() {
242
                        setKeys(visibleFiles);
243
                    }
244
                });
245
                ((ImportantFilesNode) getNode()).setFiles(files);
246
            }
247
        }
248
        
249
        private void attachListeners() {
250
            try {
251
                if (fcl == null) {
252
                    fcl = new FileChangeAdapter() {
253
                        public void fileDataCreated(FileEvent fe) {
254
                            refreshKeys();
255
                        }
256
                        public void fileDeleted(FileEvent fe) {
257
                            refreshKeys();
258
                        }
259
                    };
260
                    project.getProjectDirectory().getFileSystem().addFileChangeListener(fcl);
261
                }
262
            } catch (FileStateInvalidException e) {
263
                assert false : e;
264
            }
265
        }
266
        
267
        private void removeListeners() {
268
            if (fcl != null) {
269
                try {
270
                    project.getProjectDirectory().getFileSystem().removeFileChangeListener(fcl);
271
                } catch (FileStateInvalidException e) {
272
                    assert false : e;
273
                }
274
                fcl = null;
275
            }
276
        }
277
        
278
    }
279
    /**
280
     * Node to represent some special file in a project.
281
     * Mostly just a wrapper around the normal data node.
282
     */
283
    static final class SpecialFileNode extends FilterNode {
284
        
285
        private final String displayName;
286
        
287
        public SpecialFileNode(Node orig, String displayName) {
288
            super(orig);
289
            this.displayName = displayName;
290
        }
291
        
292
        public String getDisplayName() {
293
            if (displayName != null) {
294
                return displayName;
295
            } else {
296
                return super.getDisplayName();
297
            }
298
        }
299
        
300
        public boolean canRename() {
301
            return false;
302
        }
303
        
304
        public boolean canDestroy() {
305
            return false;
306
        }
307
        
308
        public boolean canCut() {
309
            return false;
310
        }
311
        
312
        public String getHtmlDisplayName() {
313
            String result = null;
314
            DataObject dob = (DataObject) getLookup().lookup(DataObject.class);
315
            if (dob != null) {
316
                Set files = dob.files();
317
                result = computeAnnotatedHtmlDisplayName(getDisplayName(), files);
318
            }
319
            return result;
320
        }
321
        
322
    }
323
    
324
    /**
325
     * Annotates <code>htmlDisplayName</code>, if it is needed, and returns the
326
     * result; <code>null</code> otherwise.
327
     */
328
    private static String computeAnnotatedHtmlDisplayName(
329
            final String htmlDisplayName, final Set files) {
330
        
331
        String result = null;
332
        if (files != null && files.iterator().hasNext()) {
333
            try {
334
                FileObject fo = (FileObject) files.iterator().next();
335
                FileSystem.Status stat = fo.getFileSystem().getStatus();
336
                if (stat instanceof FileSystem.HtmlStatus) {
337
                    FileSystem.HtmlStatus hstat = (FileSystem.HtmlStatus) stat;
338
                    
339
                    String annotated = hstat.annotateNameHtml(htmlDisplayName, files);
340
                    
341
                    // Make sure the super string was really modified (XXX why?)
342
                    if (!htmlDisplayName.equals(annotated)) {
343
                        result = annotated;
344
                    }
345
                }
346
            } catch (FileStateInvalidException e) {
347
                ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
348
            }
349
        }
350
        return result;
351
    }
352
    
353
    /**
354
     * Actual list of important files.
355
     */
356
    private static final class SuiteImportantFilesChildren extends Children.Keys<String> {
357
        
358
        private List<String> visibleFiles = new ArrayList();
359
        private FileChangeListener fcl;
360
        
361
        /** Abstract location to display name. */
362
        private static final java.util.Map<String,String> FILES = new LinkedHashMap();
363
        static {
364
            FILES.put("master.jnlp", NbBundle.getMessage(SuiteLogicalView.class,"LBL_jnlp_master"));
365
            FILES.put("build.xml", NbBundle.getMessage(SuiteLogicalView.class,"LBL_build.xml"));
366
            FILES.put("nbproject/project.properties", NbBundle.getMessage(SuiteLogicalView.class,"LBL_project.properties"));
367
            FILES.put("nbproject/private/private.properties", NbBundle.getMessage(SuiteLogicalView.class,"LBL_private.properties"));
368
            FILES.put("nbproject/platform.properties", NbBundle.getMessage(SuiteLogicalView.class,"LBL_platform.properties"));
369
            FILES.put("nbproject/private/platform-private.properties", NbBundle.getMessage(SuiteLogicalView.class,"LBL_platform-private.properties"));
370
        }
371
        
372
        private final SuiteProject project;
373
        
374
        public SuiteImportantFilesChildren(SuiteProject project) {
375
            this.project = project;
376
        }
377
        
378
        protected void addNotify() {
379
            super.addNotify();
380
            attachListeners();
381
            refreshKeys();
382
        }
383
        
384
        protected void removeNotify() {
385
            setKeys(Collections.EMPTY_SET);
386
            removeListeners();
387
            super.removeNotify();
388
        }
389
        
390
        protected Node[] createNodes(String loc) {
391
            String locEval = project.getEvaluator().evaluate(loc);
392
            FileObject file = project.getHelper().resolveFileObject(locEval);
393
            
394
            try {
395
                Node orig = DataObject.find(file).getNodeDelegate();
396
                return new Node[] {new SpecialFileNode(orig, (String) FILES.get(loc))};
397
            } catch (DataObjectNotFoundException e) {
398
                throw new AssertionError(e);
399
            }
400
        }
401
        
402
        private void refreshKeys() {
403
            List<String> newVisibleFiles = new ArrayList();
404
            Iterator it = FILES.keySet().iterator();
405
            Set files = new HashSet();
406
            while (it.hasNext()) {
407
                String loc = (String) it.next();
408
                String locEval = project.getEvaluator().evaluate(loc);
409
                if (locEval == null) {
410
                    continue;
411
                }
412
                FileObject file = project.getHelper().resolveFileObject(locEval);
413
                if (file != null) {
414
                    newVisibleFiles.add(loc);
415
                    files.add(file);
416
                }
417
            }
418
            if (!isInitialized() || !newVisibleFiles.equals(visibleFiles)) {
419
                visibleFiles = newVisibleFiles;
420
                RP.post(new Runnable() { // #72471
421
                    public void run() {
422
                        setKeys(visibleFiles);
423
                    }
424
                });
425
                ((ImportantFilesNode) getNode()).setFiles(files); // #72439
426
            }
427
        }
428
        
429
        private void attachListeners() {
430
            try {
431
                if (fcl == null) {
432
                    fcl = new FileChangeAdapter() {
433
                        public void fileDataCreated(FileEvent fe) {
434
                            refreshKeys();
435
                        }
436
                        public void fileDeleted(FileEvent fe) {
437
                            refreshKeys();
438
                        }
439
                    };
440
                    project.getProjectDirectory().getFileSystem().addFileChangeListener(fcl);
441
                }
442
            } catch (FileStateInvalidException e) {
443
                assert false : e;
444
            }
445
        }
446
        
447
        private void removeListeners() {
448
            if (fcl != null) {
449
                try {
450
                    project.getProjectDirectory().getFileSystem().removeFileChangeListener(fcl);
451
                } catch (FileStateInvalidException e) {
452
                    assert false : e;
453
                }
454
                fcl = null;
455
            }
456
        }
457
        
458
    }
459
    
460
}
(-)src/org/netbeans/modules/apisupport/project/ui/LibrariesNode.java (-1 / +2 lines)
Lines 43-48 Link Here
43
import org.netbeans.modules.apisupport.project.NbModuleTypeProvider;
43
import org.netbeans.modules.apisupport.project.NbModuleTypeProvider;
44
import org.netbeans.modules.apisupport.project.ProjectXMLManager;
44
import org.netbeans.modules.apisupport.project.ProjectXMLManager;
45
import org.netbeans.modules.apisupport.project.Util;
45
import org.netbeans.modules.apisupport.project.Util;
46
import org.netbeans.modules.apisupport.project.ui.ModulesNodeFactory.AddNewLibraryWrapperAction;
46
import org.netbeans.modules.apisupport.project.ui.customizer.AddModulePanel;
47
import org.netbeans.modules.apisupport.project.ui.customizer.AddModulePanel;
47
import org.netbeans.modules.apisupport.project.ui.customizer.EditDependencyPanel;
48
import org.netbeans.modules.apisupport.project.ui.customizer.EditDependencyPanel;
48
import org.netbeans.modules.apisupport.project.ui.customizer.ModuleDependency;
49
import org.netbeans.modules.apisupport.project.ui.customizer.ModuleDependency;
Lines 100-106 Link Here
100
        if (Util.getModuleType(project) == NbModuleTypeProvider.SUITE_COMPONENT) {
101
        if (Util.getModuleType(project) == NbModuleTypeProvider.SUITE_COMPONENT) {
101
            actions = new Action[] {
102
            actions = new Action[] {
102
                new AddModuleDependencyAction(project),
103
                new AddModuleDependencyAction(project),
103
                new SuiteLogicalView.AddNewLibraryWrapperAction(project, project)
104
                new AddNewLibraryWrapperAction(project, project)
104
            };
105
            };
105
        } else {
106
        } else {
106
            actions = new Action[] {
107
            actions = new Action[] {
(-)src/org/netbeans/modules/apisupport/project/ui/LibrariesNodeFactory.java (+97 lines)
Added Link Here
1
/*
2
 * The contents of this file are subject to the terms of the Common Development
3
 * and Distribution License (the License). You may not use this file except in
4
 * compliance with the License.
5
 *
6
 * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7
 * or http://www.netbeans.org/cddl.txt.
8
 *
9
 * When distributing Covered Code, include this CDDL Header Notice in each file
10
 * and include the License file at http://www.netbeans.org/cddl.txt.
11
 * If applicable, add the following below the CDDL Header, with the fields
12
 * enclosed by brackets [] replaced by your own identifying information:
13
 * "Portions Copyrighted [year] [name of copyright owner]"
14
 *
15
 * The Original Software is NetBeans. The Initial Developer of the Original
16
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
17
 * Microsystems, Inc. All Rights Reserved.
18
 */
19
20
package org.netbeans.modules.apisupport.project.ui;
21
22
import java.util.ArrayList;
23
import java.util.List;
24
import javax.swing.event.ChangeListener;
25
import org.netbeans.api.project.Project;
26
import org.netbeans.modules.apisupport.project.NbModuleProject;
27
import org.netbeans.spi.project.ui.support.NodeFactory;
28
import org.netbeans.spi.project.ui.support.NodeList;
29
import org.openide.filesystems.FileObject;
30
import org.openide.nodes.Node;
31
32
/**
33
 *
34
 * @author mkleint
35
 */
36
public class LibrariesNodeFactory implements NodeFactory {
37
    
38
    /** Creates a new instance of LibrariesNodeFactory */
39
    public LibrariesNodeFactory() {
40
    }
41
    
42
    public NodeList createNodes(Project p) {
43
        NbModuleProject proj =  p.getLookup().lookup(NbModuleProject.class);
44
        assert proj != null;
45
        return new LibraryNL(proj);
46
    }
47
    
48
    private static class LibraryNL implements NodeList<String> {
49
        
50
        private NbModuleProject project;
51
        
52
        LibraryNL(NbModuleProject prj) {
53
            project = prj;
54
        }
55
    
56
        public List<String> keys() {
57
            List<String> toRet = new ArrayList<String>();
58
            toRet.add(LibrariesNode.LIBRARIES_NAME);
59
            if(resolveFileObjectFromProperty("test.unit.src.dir") != null) { //NOI18N
60
                toRet.add(UnitTestLibrariesNode.UNIT_TEST_LIBRARIES_NAME);
61
            }
62
            return toRet;
63
        }
64
        
65
        private FileObject resolveFileObjectFromProperty(String property){
66
            String filename = project.evaluator().getProperty(property);
67
            if (filename == null) {
68
                return null;
69
            }
70
            return project.getHelper().resolveFileObject(filename);
71
        }
72
73
        public void addChangeListener(ChangeListener l) {
74
        }
75
76
        public void removeChangeListener(ChangeListener l) {
77
        }
78
79
        public Node node(String key) {
80
            if (key == LibrariesNode.LIBRARIES_NAME) {
81
                return  new LibrariesNode(project);
82
            } else if (key == UnitTestLibrariesNode.UNIT_TEST_LIBRARIES_NAME) {
83
                return new UnitTestLibrariesNode(project);
84
            }
85
            throw new AssertionError("Unknown key: " + key);
86
        }
87
88
        public void addNotify() {
89
            //TODO shall we somehow listen on project and ech for the 
90
            // test.unit.src.dir prop appearance/disappearance ??
91
        }
92
93
        public void removeNotify() {
94
        }
95
}
96
97
}
(-)src/org/netbeans/modules/apisupport/project/ui/ModuleLogicalView.java (-367 / +4 lines)
Lines 22-67 Link Here
22
import java.awt.Image;
22
import java.awt.Image;
23
import java.beans.PropertyChangeEvent;
23
import java.beans.PropertyChangeEvent;
24
import java.beans.PropertyChangeListener;
24
import java.beans.PropertyChangeListener;
25
import java.util.ArrayList;
26
import java.util.Arrays;
27
import java.util.Collections;
28
import java.util.HashSet;
25
import java.util.HashSet;
29
import java.util.Iterator;
30
import java.util.LinkedHashMap;
31
import java.util.List;
32
import java.util.Set;
26
import java.util.Set;
33
import javax.swing.Action;
27
import javax.swing.Action;
34
import javax.swing.event.ChangeEvent;
35
import javax.swing.event.ChangeListener;
36
import org.netbeans.modules.apisupport.project.metainf.ServiceNodeHandler;
37
import org.netbeans.api.java.project.JavaProjectConstants;
38
import org.netbeans.api.project.ProjectUtils;
28
import org.netbeans.api.project.ProjectUtils;
39
import org.netbeans.spi.java.project.support.ui.PackageView;
29
import org.netbeans.spi.java.project.support.ui.PackageView;
40
import org.netbeans.api.project.ProjectInformation;
30
import org.netbeans.api.project.ProjectInformation;
41
import org.netbeans.api.project.SourceGroup;
31
import org.netbeans.api.project.SourceGroup;
42
import org.netbeans.api.project.Sources;
32
import org.netbeans.api.project.Sources;
43
import org.netbeans.modules.apisupport.project.NbModuleProject;
33
import org.netbeans.modules.apisupport.project.NbModuleProject;
44
import org.netbeans.modules.apisupport.project.layers.LayerNode;
45
import org.netbeans.modules.apisupport.project.layers.LayerUtils;
46
import org.netbeans.spi.project.support.GenericSources;
47
import org.netbeans.spi.project.ui.LogicalViewProvider;
34
import org.netbeans.spi.project.ui.LogicalViewProvider;
48
import org.netbeans.spi.project.ui.support.DefaultProjectOperations;
35
import org.netbeans.spi.project.ui.support.DefaultProjectOperations;
49
import org.openide.ErrorManager;
36
import org.netbeans.spi.project.ui.support.NodeFactorySupport;
50
import org.openide.filesystems.FileChangeAdapter;
51
import org.openide.filesystems.FileChangeListener;
52
import org.openide.filesystems.FileEvent;
53
import org.openide.filesystems.FileObject;
37
import org.openide.filesystems.FileObject;
54
import org.openide.filesystems.FileStateInvalidException;
55
import org.openide.filesystems.FileSystem;
56
import org.openide.filesystems.FileUtil;
38
import org.openide.filesystems.FileUtil;
57
import org.openide.loaders.DataObject;
39
import org.openide.loaders.DataObject;
58
import org.openide.loaders.DataObjectNotFoundException;
40
import org.openide.loaders.DataObjectNotFoundException;
59
import org.openide.nodes.Children;
60
import org.openide.nodes.FilterNode;
61
import org.openide.nodes.Node;
41
import org.openide.nodes.Node;
62
import org.openide.util.NbBundle;
42
import org.openide.util.NbBundle;
63
import org.openide.util.RequestProcessor;
64
import org.openide.util.Utilities;
65
import org.openide.util.lookup.Lookups;
43
import org.openide.util.lookup.Lookups;
66
44
67
/**
45
/**
Lines 70-77 Link Here
70
 */
48
 */
71
public final class ModuleLogicalView implements LogicalViewProvider {
49
public final class ModuleLogicalView implements LogicalViewProvider {
72
    
50
    
73
    /** Package private for unit tests only. */
74
    static final RequestProcessor RP = new RequestProcessor();
75
    
51
    
76
    private final NbModuleProject project;
52
    private final NbModuleProject project;
77
    
53
    
Lines 113-119 Link Here
113
                return found;
89
                return found;
114
            }
90
            }
115
            // For Important Files node:
91
            // For Important Files node:
116
            if (rootChildren[i].getName().equals(IMPORTANT_FILES_NAME)) {
92
            if (rootChildren[i].getName().equals(ImportantFilesNodeFactory.IMPORTANT_FILES_NAME)) {
117
                Node[] ifChildren = rootChildren[i].getChildren().getNodes(true);
93
                Node[] ifChildren = rootChildren[i].getChildren().getNodes(true);
118
                for (int j = 0; j < ifChildren.length; j++) {
94
                for (int j = 0; j < ifChildren.length; j++) {
119
                    if (ifChildren[j].getCookie(DataObject.class) == file) {
95
                    if (ifChildren[j].getCookie(DataObject.class) == file) {
Lines 133-139 Link Here
133
        public RootNode(NbModuleProject project) {
109
        public RootNode(NbModuleProject project) {
134
            
110
            
135
            // XXX add a NodePathResolver impl to lookup
111
            // XXX add a NodePathResolver impl to lookup
136
            super(new RootChildren(project), Lookups.fixed(new Object[] {project}));
112
            super(NodeFactorySupport.createCompositeChildren(project, "Projects/org-netbeans-modules-apisupport-project/Nodes"), 
113
                  Lookups.fixed(new Object[] {project}));
137
            this.project = project;
114
            this.project = project;
138
            setForceAnnotation(true);
115
            setForceAnnotation(true);
139
            setIconBaseWithExtension(NbModuleProject.NB_PROJECT_ICON_PATH);
116
            setIconBaseWithExtension(NbModuleProject.NB_PROJECT_ICON_PATH);
Lines 199-542 Link Here
199
        }
176
        }
200
        
177
        
201
    }
178
    }
202
    
203
    /** Package private for unit tests. */
204
    static final String IMPORTANT_FILES_NAME = "important.files"; // NOI18N
205
    
206
    /** Accessor for SuiteLogicalView to create its important node which shall
207
     * be as similar as module's project one
208
     */
209
    static Node createImportantFilesNode(Children ch) {
210
        return new ImportantFilesNode(ch);
211
    }
212
    
213
    private static final class RootChildren extends Children.Keys implements ChangeListener {
214
        
215
        private static final String[] SOURCE_GROUP_TYPES = {
216
            JavaProjectConstants.SOURCES_TYPE_JAVA,
217
            NbModuleProject.SOURCES_TYPE_JAVAHELP,
218
        };
219
        
220
        private final NbModuleProject project;
221
        
222
        RootChildren(NbModuleProject project) {
223
            this.project = project;
224
        }
225
        
226
        protected void addNotify() {
227
            super.addNotify();
228
            refreshKeys();
229
            ProjectUtils.getSources(project).addChangeListener(this);
230
        }
231
        
232
        private void refreshKeys() {
233
            List l = new ArrayList();
234
            Sources s = ProjectUtils.getSources(project);
235
            for (int i = 0; i < SOURCE_GROUP_TYPES.length; i++) {
236
                SourceGroup[] groups = s.getSourceGroups(SOURCE_GROUP_TYPES[i]);
237
                l.addAll(Arrays.asList(groups));
238
            }
239
            SourceGroup javadocDocfiles = makeJavadocDocfilesSourceGroup();
240
            if (javadocDocfiles != null) {
241
                l.add(javadocDocfiles);
242
            }
243
            l.add(IMPORTANT_FILES_NAME);
244
            l.add(LibrariesNode.LIBRARIES_NAME);
245
            if(resolveFileObjectFromProperty("test.unit.src.dir") != null) { //NOI18N
246
                l.add(UnitTestLibrariesNode.UNIT_TEST_LIBRARIES_NAME);
247
            }
248
            setKeys(l);
249
        }
250
        
251
        private FileObject resolveFileObjectFromProperty(String property){
252
            String filename = project.evaluator().getProperty(property);
253
            if (filename == null) {
254
                return null;
255
            }
256
            return project.getHelper().resolveFileObject(filename);
257
        }
258
259
        protected void removeNotify() {
260
            ProjectUtils.getSources(project).removeChangeListener(this);
261
            setKeys(Collections.EMPTY_SET);
262
            super.removeNotify();
263
        }
264
        
265
        protected Node[] createNodes(Object key) {
266
            Node n;
267
            if (key instanceof SourceGroup) {
268
                n = PackageView.createPackageView((SourceGroup) key);
269
            } else if (key == IMPORTANT_FILES_NAME) {
270
                n = new ImportantFilesNode(project);
271
            } else if (key == LibrariesNode.LIBRARIES_NAME) {
272
                n = new LibrariesNode(project);
273
            } else if (key == UnitTestLibrariesNode.UNIT_TEST_LIBRARIES_NAME) {
274
                n = new UnitTestLibrariesNode(project);
275
            } else {
276
                throw new AssertionError("Unknown key: " + key);
277
            }
278
            return new Node[] {n};
279
        }
280
        
281
       
282
        private SourceGroup makeJavadocDocfilesSourceGroup() {
283
            String propname = "javadoc.docfiles"; // NOI18N
284
            FileObject root = resolveFileObjectFromProperty(propname);
285
            if(root == null) {
286
                return null;
287
            }
288
            return GenericSources.group(project, root, propname, NbBundle.getMessage(ModuleLogicalView.class, "LBL_extra_javadoc_files"), null, null);
289
        }
290
291
        public void stateChanged(ChangeEvent e) {
292
            refreshKeys();
293
        }
294
        
295
    }
296
    
297
    /**
298
     * Show node "Important Files" with various config and docs files beneath it.
299
     */
300
    static final class ImportantFilesNode extends AnnotatedNode {
301
        
302
        public ImportantFilesNode(NbModuleProject project) {
303
            super(new ImportantFilesChildren(project));
304
        }
305
        
306
        ImportantFilesNode(Children ch) {
307
            super(ch);
308
        }
309
        
310
        public String getName() {
311
            return IMPORTANT_FILES_NAME;
312
        }
313
        
314
        private Image getIcon(boolean opened) {
315
            Image badge = Utilities.loadImage("org/netbeans/modules/apisupport/project/resources/config-badge.gif", true);
316
            return Utilities.mergeImages(UIUtil.getTreeFolderIcon(opened), badge, 8, 8);
317
        }
318
        
319
        private static final String DISPLAY_NAME = NbBundle.getMessage(ModuleLogicalView.class, "LBL_important_files");
320
        
321
        public String getDisplayName() {
322
            return annotateName(DISPLAY_NAME);
323
        }
324
        
325
        public String getHtmlDisplayName() {
326
            return computeAnnotatedHtmlDisplayName(DISPLAY_NAME, getFiles());
327
        }
328
        
329
        public Image getIcon(int type) {
330
            return annotateIcon(getIcon(false), type);
331
        }
332
        
333
        public Image getOpenedIcon(int type) {
334
            return annotateIcon(getIcon(true), type);
335
        }
336
        
337
    }
338
    
339
    /**
340
     * Actual list of important files.
341
     */
342
    private static final class ImportantFilesChildren extends Children.Keys {
343
        
344
        private List visibleFiles = new ArrayList();
345
        private FileChangeListener fcl;
346
        
347
        /** Abstract location to display name. */
348
        private static final java.util.Map<String,String> FILES = new LinkedHashMap();
349
        static {
350
            FILES.put("${manifest.mf}", NbBundle.getMessage(ModuleLogicalView.class, "LBL_module_manifest"));
351
            FILES.put("${javadoc.arch}", NbBundle.getMessage(ModuleLogicalView.class, "LBL_arch_desc"));
352
            FILES.put("${javadoc.apichanges}", NbBundle.getMessage(ModuleLogicalView.class, "LBL_api_changes"));
353
            FILES.put("${javadoc.overview}", NbBundle.getMessage(ModuleLogicalView.class, "LBL_javadoc_overview"));
354
            FILES.put("build.xml", NbBundle.getMessage(ModuleLogicalView.class, "LBL_build.xml"));
355
            FILES.put("nbproject/project.xml", NbBundle.getMessage(ModuleLogicalView.class, "LBL_project.xml"));
356
            FILES.put("nbproject/project.properties", NbBundle.getMessage(ModuleLogicalView.class, "LBL_project.properties"));
357
            FILES.put("nbproject/private/private.properties", NbBundle.getMessage(ModuleLogicalView.class, "LBL_private.properties"));
358
            FILES.put("nbproject/suite.properties", NbBundle.getMessage(ModuleLogicalView.class, "LBL_suite.properties"));
359
            FILES.put("nbproject/private/suite-private.properties", NbBundle.getMessage(ModuleLogicalView.class, "LBL_suite-private.properties"));
360
            FILES.put("nbproject/platform.properties", NbBundle.getMessage(ModuleLogicalView.class, "LBL_platform.properties"));
361
            FILES.put("nbproject/private/platform-private.properties", NbBundle.getMessage(ModuleLogicalView.class, "LBL_platform-private.properties"));
362
        }
363
        
364
        private final NbModuleProject project;
365
        
366
        public ImportantFilesChildren(NbModuleProject project) {
367
            this.project = project;
368
        }
369
        
370
        protected void addNotify() {
371
            super.addNotify();
372
            attachListeners();
373
            refreshKeys();
374
        }
375
        
376
        protected void removeNotify() {
377
            setKeys(Collections.EMPTY_SET);
378
            removeListeners();
379
            super.removeNotify();
380
        }
381
        
382
        protected Node[] createNodes(Object key) {
383
            if (key instanceof String) {
384
                String loc = (String) key;
385
                String locEval = project.evaluator().evaluate(loc);
386
                FileObject file = project.getHelper().resolveFileObject(locEval);
387
                try {
388
                    Node orig = DataObject.find(file).getNodeDelegate();
389
                    return new Node[] {new SpecialFileNode(orig, (String) FILES.get(loc))};
390
                } catch (DataObjectNotFoundException e) {
391
                    throw new AssertionError(e);
392
                }
393
            } else if (key instanceof LayerUtils.LayerHandle) {
394
                return new Node[] {/* #68240 */ new SpecialFileNode(new LayerNode((LayerUtils.LayerHandle) key), null)};
395
            } else if (key instanceof ServiceNodeHandler) {
396
                return new Node[]{((ServiceNodeHandler)key).createServiceRootNode()};
397
            } else {
398
                throw new AssertionError(key);
399
            } 
400
        }
401
        
402
        private void refreshKeys() {
403
            Set<FileObject> files = new HashSet();
404
            List newVisibleFiles = new ArrayList();
405
            LayerUtils.LayerHandle handle = LayerUtils.layerForProject(project);
406
            FileObject layerFile = handle.getLayerFile();
407
            if (layerFile != null) {
408
                newVisibleFiles.add(handle);
409
                files.add(layerFile);
410
            }
411
            Iterator it = FILES.keySet().iterator();
412
            while (it.hasNext()) {
413
                String loc = (String) it.next();
414
                String locEval = project.evaluator().evaluate(loc);
415
                if (locEval == null) {
416
                    newVisibleFiles.remove(loc); // XXX why?
417
                    continue;
418
                }
419
                FileObject file = project.getHelper().resolveFileObject(locEval);
420
                if (file != null) {
421
                    newVisibleFiles.add(loc);
422
                    files.add(file);
423
                }
424
            }
425
            if (!isInitialized() || !newVisibleFiles.equals(visibleFiles)) {
426
                visibleFiles = newVisibleFiles;
427
                visibleFiles.add(project.getLookup().lookup(ServiceNodeHandler.class));
428
                RP.post(new Runnable() { // #72471
429
                    public void run() {
430
                        setKeys(visibleFiles);
431
                    }
432
                });
433
                ((ImportantFilesNode) getNode()).setFiles(files);
434
            }
435
        }
436
        
437
        private void attachListeners() {
438
            try {
439
                if (fcl == null) {
440
                    fcl = new FileChangeAdapter() {
441
                        public void fileDataCreated(FileEvent fe) {
442
                            refreshKeys();
443
                        }
444
                        public void fileDeleted(FileEvent fe) {
445
                            refreshKeys();
446
                        }
447
                    };
448
                    project.getProjectDirectory().getFileSystem().addFileChangeListener(fcl);
449
                }
450
            } catch (FileStateInvalidException e) {
451
                assert false : e;
452
            }
453
        }
454
        
455
        private void removeListeners() {
456
            if (fcl != null) {
457
                try {
458
                    project.getProjectDirectory().getFileSystem().removeFileChangeListener(fcl);
459
                } catch (FileStateInvalidException e) {
460
                    assert false : e;
461
                }
462
                fcl = null;
463
            }
464
        }
465
        
466
    }
467
    
468
    /**
469
     * Node to represent some special file in a project.
470
     * Mostly just a wrapper around the normal data node.
471
     */
472
    static final class SpecialFileNode extends FilterNode {
473
        
474
        private final String displayName;
475
        
476
        public SpecialFileNode(Node orig, String displayName) {
477
            super(orig);
478
            this.displayName = displayName;
479
        }
480
        
481
        public String getDisplayName() {
482
            if (displayName != null) {
483
                return displayName;
484
            } else {
485
                return super.getDisplayName();
486
            }
487
        }
488
        
489
        public boolean canRename() {
490
            return false;
491
        }
492
        
493
        public boolean canDestroy() {
494
            return false;
495
        }
496
        
497
        public boolean canCut() {
498
            return false;
499
        }
500
        
501
        public String getHtmlDisplayName() {
502
            String result = null;
503
            DataObject dob = (DataObject) getLookup().lookup(DataObject.class);
504
            if (dob != null) {
505
                Set files = dob.files();
506
                result = computeAnnotatedHtmlDisplayName(getDisplayName(), files);
507
            }
508
            return result;
509
        }
510
        
511
    }
512
    
513
    /**
514
     * Annotates <code>htmlDisplayName</code>, if it is needed, and returns the
515
     * result; <code>null</code> otherwise.
516
     */
517
    private static String computeAnnotatedHtmlDisplayName(
518
            final String htmlDisplayName, final Set files) {
519
        
520
        String result = null;
521
        if (files != null && files.iterator().hasNext()) {
522
            try {
523
                FileObject fo = (FileObject) files.iterator().next();
524
                FileSystem.Status stat = fo.getFileSystem().getStatus();
525
                if (stat instanceof FileSystem.HtmlStatus) {
526
                    FileSystem.HtmlStatus hstat = (FileSystem.HtmlStatus) stat;
527
                    
528
                    String annotated = hstat.annotateNameHtml(htmlDisplayName, files);
529
                    
530
                    // Make sure the super string was really modified (XXX why?)
531
                    if (!htmlDisplayName.equals(annotated)) {
532
                        result = annotated;
533
                    }
534
                }
535
            } catch (FileStateInvalidException e) {
536
                ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
537
            }
538
        }
539
        return result;
540
    }
541
    
542
}
179
}
(-)src/org/netbeans/modules/apisupport/project/ui/ModulesNodeFactory.java (+363 lines)
Added Link Here
1
/*
2
 * The contents of this file are subject to the terms of the Common Development
3
 * and Distribution License (the License). You may not use this file except in
4
 * compliance with the License.
5
 *
6
 * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7
 * or http://www.netbeans.org/cddl.txt.
8
 *
9
 * When distributing Covered Code, include this CDDL Header Notice in each file
10
 * and include the License file at http://www.netbeans.org/cddl.txt.
11
 * If applicable, add the following below the CDDL Header, with the fields
12
 * enclosed by brackets [] replaced by your own identifying information:
13
 * "Portions Copyrighted [year] [name of copyright owner]"
14
 *
15
 * The Original Software is NetBeans. The Initial Developer of the Original
16
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
17
 * Microsystems, Inc. All Rights Reserved.
18
 */
19
20
package org.netbeans.modules.apisupport.project.ui;
21
22
import java.awt.EventQueue;
23
import java.awt.Image;
24
import java.awt.event.ActionEvent;
25
import java.beans.PropertyChangeEvent;
26
import java.beans.PropertyChangeListener;
27
import java.io.IOException;
28
import java.util.Collections;
29
import java.util.SortedSet;
30
import java.util.TreeSet;
31
import javax.swing.AbstractAction;
32
import javax.swing.Action;
33
import javax.swing.event.ChangeEvent;
34
import javax.swing.event.ChangeListener;
35
import org.netbeans.api.project.Project;
36
import org.netbeans.api.project.ProjectInformation;
37
import org.netbeans.api.project.ProjectManager;
38
import org.netbeans.api.project.ProjectUtils;
39
import org.netbeans.api.project.ui.OpenProjects;
40
import org.netbeans.modules.apisupport.project.NbModuleProject;
41
import org.netbeans.modules.apisupport.project.Util;
42
import org.netbeans.modules.apisupport.project.suite.SuiteProject;
43
import org.netbeans.modules.apisupport.project.ui.customizer.SuiteUtils;
44
import org.netbeans.modules.apisupport.project.ui.wizard.NewNbModuleWizardIterator;
45
import org.netbeans.spi.project.SubprojectProvider;
46
import org.netbeans.spi.project.ui.support.NodeFactory;
47
import org.netbeans.spi.project.ui.support.NodeFactorySupport;
48
import org.netbeans.spi.project.ui.support.NodeList;
49
import org.openide.DialogDisplayer;
50
import org.openide.ErrorManager;
51
import org.openide.NotifyDescriptor;
52
import org.openide.awt.StatusDisplayer;
53
import org.openide.nodes.AbstractNode;
54
import org.openide.nodes.Children;
55
import org.openide.nodes.Node;
56
import org.openide.util.HelpCtx;
57
import org.openide.util.NbBundle;
58
import org.openide.util.RequestProcessor;
59
import org.openide.util.Utilities;
60
import org.openide.util.actions.CookieAction;
61
import org.openide.util.actions.NodeAction;
62
import org.openide.util.lookup.Lookups;
63
import org.openide.windows.WindowManager;
64
65
/**
66
 *
67
 * @author mkleint
68
 */
69
public class ModulesNodeFactory implements NodeFactory {
70
    
71
    /** Creates a new instance of ImportantFilesNodeFactory */
72
    public ModulesNodeFactory() {
73
    }
74
    
75
    public NodeList createNodes(Project p) {
76
        SuiteProject prj = p.getLookup().lookup(SuiteProject.class);
77
        assert prj != null;
78
        return NodeFactorySupport.fixedNodeList(new ModulesNode(prj));
79
    }
80
    
81
    private static String getMessage(final String key) {
82
        return NbBundle.getMessage(SuiteLogicalView.class, key);
83
    }
84
    
85
86
    /** Represent <em>Modules</em> node in the Suite Logical View. */
87
    static final class ModulesNode extends AbstractNode {
88
        
89
        private SuiteProject suite;
90
        
91
        ModulesNode(final SuiteProject suite) {
92
            super(new ModuleChildren(suite));
93
            this.suite = suite;
94
            setName("modules"); // NOI18N
95
            setDisplayName(getMessage("CTL_Modules"));
96
        }
97
        
98
        public Action[] getActions(boolean context) {
99
            return new Action[] {
100
                new AddNewSuiteComponentAction(suite),
101
                new AddNewLibraryWrapperAction(suite),
102
                new AddSuiteComponentAction(suite),
103
            };
104
        }
105
        
106
        private Image getIcon(boolean opened) {
107
            Image badge = Utilities.loadImage("org/netbeans/modules/apisupport/project/suite/resources/module-badge.gif", true);
108
            return Utilities.mergeImages(UIUtil.getTreeFolderIcon(opened), badge, 9, 9);
109
        }
110
        
111
        public Image getIcon(int type) {
112
            return getIcon(false);
113
        }
114
        
115
        public Image getOpenedIcon(int type) {
116
            return getIcon(true);
117
        }
118
        
119
        static final class ModuleChildren extends Children.Keys<NbModuleProject> implements ChangeListener {
120
            
121
            private final SuiteProject suite;
122
            
123
            public ModuleChildren(SuiteProject suite) {
124
                suite.getLookup().lookup(SubprojectProvider.class).addChangeListener(this);
125
                this.suite = suite;
126
            }
127
            
128
            protected void addNotify() {
129
                updateKeys();
130
            }
131
            
132
            private void updateKeys() {
133
                // e.g.(?) Explorer view under Children.MUTEX subsequently calls e.g.
134
                // SuiteProject$Info.getSimpleName() which acquires ProjectManager.mutex(). And
135
                // since this method might be called under ProjectManager.mutex() write access
136
                // and updateKeys() --> setKeys() in turn calls Children.MUTEX write access,
137
                // deadlock is here, so preventing it... (also got this under read access)
138
                EventQueue.invokeLater(new Runnable() {
139
                    public void run() {
140
                        // #70112: sort them.
141
                        SortedSet<NbModuleProject> subModules = new TreeSet(Util.projectDisplayNameComparator());
142
                        subModules.addAll(SuiteUtils.getSubProjects(suite));
143
                        setKeys(subModules);
144
                    }
145
                });
146
            }
147
            
148
            protected void removeNotify() {
149
                suite.getLookup().lookup(SubprojectProvider.class).removeChangeListener(this);
150
                setKeys(Collections.EMPTY_SET);
151
            }
152
            
153
            protected Node[] createNodes(NbModuleProject p) {
154
                return new Node[] {new SuiteComponentNode(p)};
155
            }
156
            
157
            public void stateChanged(ChangeEvent ev) {
158
                updateKeys();
159
            }
160
            
161
        }
162
        
163
    }
164
    
165
    private static final class AddSuiteComponentAction extends AbstractAction {
166
        
167
        private final SuiteProject suite;
168
        
169
        public AddSuiteComponentAction(final SuiteProject suite) {
170
            super(getMessage("CTL_AddModule"));
171
            this.suite = suite;
172
        }
173
        
174
        public void actionPerformed(ActionEvent evt) {
175
            NbModuleProject project = UIUtil.chooseSuiteComponent(
176
                    WindowManager.getDefault().getMainWindow(),
177
                    suite);
178
            if (project != null) {
179
                if (!SuiteUtils.contains(suite, project)) {
180
                    try {
181
                        SuiteUtils.addModule(suite, (NbModuleProject) project);
182
                        ProjectManager.getDefault().saveProject(suite);
183
                    } catch (IOException ex) {
184
                        ErrorManager.getDefault().notify(ex);
185
                    }
186
                } else {
187
                    DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(
188
                            NbBundle.getMessage(SuiteLogicalView.class, "MSG_SuiteAlreadyContainsCNB", project.getCodeNameBase())));
189
                }
190
            }
191
        }
192
        
193
    }
194
    
195
    private static final class AddNewSuiteComponentAction extends AbstractAction {
196
        
197
        private final SuiteProject suite;
198
        
199
        public AddNewSuiteComponentAction(final SuiteProject suite) {
200
            super(getMessage("CTL_AddNewModule"));
201
            this.suite = suite;
202
        }
203
        
204
        public void actionPerformed(ActionEvent evt) {
205
            NewNbModuleWizardIterator iterator = NewNbModuleWizardIterator.createSuiteComponentIterator(suite);
206
            UIUtil.runProjectWizard(iterator, "CTL_NewModuleProject"); // NOI18N
207
        }
208
        
209
    }
210
    
211
    static final class AddNewLibraryWrapperAction extends AbstractAction {
212
        
213
        private final Project suiteProvider;
214
        private final NbModuleProject target;
215
        
216
        public AddNewLibraryWrapperAction(final Project suiteProvider, final NbModuleProject target) {
217
            super(getMessage("CTL_AddNewLibrary"));
218
            this.suiteProvider = suiteProvider;
219
            this.target = target;
220
        }
221
        
222
        public AddNewLibraryWrapperAction(final Project suiteProvider) {
223
            this(suiteProvider, null);
224
        }
225
        
226
        public void actionPerformed(ActionEvent evt) {
227
            NbModuleProject project = UIUtil.runLibraryWrapperWizard(suiteProvider);
228
            if (project != null && target != null) {
229
                try {
230
                    Util.addDependency(target, project);
231
                    ProjectManager.getDefault().saveProject(target);
232
                } catch (IOException e) {
233
                    assert false : e;
234
                }
235
            }
236
        }
237
        
238
    }
239
    
240
    /** Represent one module (a suite component) node. */
241
    private static final class SuiteComponentNode extends AbstractNode {
242
        
243
        private final static Action REMOVE_ACTION = new RemoveSuiteComponentAction();
244
        private final static Action OPEN_ACTION = new OpenProjectAction();
245
        
246
        public SuiteComponentNode(final NbModuleProject suiteComponent) {
247
            super(Children.LEAF, Lookups.fixed(new Object[] {suiteComponent}));
248
            ProjectInformation info = ProjectUtils.getInformation(suiteComponent);
249
            setName(info.getName());
250
            setDisplayName(info.getDisplayName());
251
            setIconBaseWithExtension(NbModuleProject.NB_PROJECT_ICON_PATH);
252
            info.addPropertyChangeListener(new PropertyChangeListener() {
253
                public void propertyChange(PropertyChangeEvent evt) {
254
                    if (evt.getPropertyName() == ProjectInformation.PROP_DISPLAY_NAME) {
255
                        SuiteComponentNode.this.setDisplayName((String) evt.getNewValue());
256
                    } else if (evt.getPropertyName() == ProjectInformation.PROP_NAME) {
257
                        SuiteComponentNode.this.setName((String) evt.getNewValue());
258
                    }
259
                }
260
            });
261
        }
262
        
263
        public Action[] getActions(boolean context) {
264
            return new Action[] {
265
                OPEN_ACTION, REMOVE_ACTION
266
            };
267
        }
268
        
269
        public Action getPreferredAction() {
270
            return OPEN_ACTION;
271
        }
272
        
273
    }
274
    
275
    private static final class RemoveSuiteComponentAction extends NodeAction {
276
        
277
        protected void performAction(Node[] activatedNodes) {
278
            for (int i = 0; i < activatedNodes.length; i++) {
279
                final NbModuleProject suiteComponent =
280
                        (NbModuleProject) activatedNodes[i].getLookup().lookup(NbModuleProject.class);
281
                assert suiteComponent != null : "NbModuleProject in lookup"; // NOI18N
282
                try {
283
                    NbModuleProject[] modules = SuiteUtils.getDependentModules(suiteComponent);
284
                    boolean remove = true;
285
                    if (modules.length > 0) {
286
                        StringBuffer sb = new StringBuffer("<ul>"); // NOI18N
287
                        for (int j = 0; j < modules.length; j++) {
288
                            sb.append("<li>" + ProjectUtils.getInformation(modules[j]).getDisplayName() + "</li>"); // NOI18N
289
                        }
290
                        sb.append("</ul>"); // NOI18N
291
                        String displayName = ProjectUtils.getInformation(suiteComponent).getDisplayName();
292
                        String confirmMessage = NbBundle.getMessage(SuiteLogicalView.class,
293
                                "MSG_RemovingModuleMessage", displayName, sb.toString()); // NOI18N
294
                        remove = UIUtil.showAcceptCancelDialog(
295
                                NbBundle.getMessage(SuiteLogicalView.class, "CTL_RemovingModuleTitle", displayName),
296
                                confirmMessage, getMessage("CTL_RemoveDependency"), null, NotifyDescriptor.QUESTION_MESSAGE);
297
                    }
298
                    if (remove) {
299
                        SuiteUtils.removeModuleFromSuiteWithDependencies(suiteComponent);
300
                    }
301
                } catch (IOException ex) {
302
                    ErrorManager.getDefault().notify(ex);
303
                }
304
            }
305
        }
306
        
307
        protected boolean enable(Node[] activatedNodes) {
308
            return true;
309
        }
310
        
311
        public String getName() {
312
            return getMessage("CTL_RemoveModule");
313
        }
314
        
315
        public HelpCtx getHelpCtx() {
316
            return HelpCtx.DEFAULT_HELP;
317
        }
318
        
319
        protected boolean asynchronous() {
320
            return false;
321
        }
322
        
323
    }
324
    
325
    private static final class OpenProjectAction extends CookieAction {
326
        
327
        protected void performAction(Node[] activatedNodes) {
328
            final Project[] projects = new Project[activatedNodes.length];
329
            for (int i = 0; i < activatedNodes.length; i++) {
330
                Project project = (Project) activatedNodes[i].getLookup().lookup(Project.class);
331
                projects[i] = project;
332
            }
333
            RequestProcessor.getDefault().post(new Runnable() {
334
                public void run() {
335
                    StatusDisplayer.getDefault().setStatusText(getMessage("MSG_OpeningProjects"));
336
                    OpenProjects.getDefault().open(projects, false);
337
                }
338
            });
339
        }
340
        
341
        public String getName() {
342
            return getMessage("CTL_OpenProject");
343
        }
344
        
345
        public HelpCtx getHelpCtx() {
346
            return HelpCtx.DEFAULT_HELP;
347
        }
348
        
349
        protected boolean asynchronous() {
350
            return false;
351
        }
352
        
353
        protected int mode() {
354
            return CookieAction.MODE_ALL;
355
        }
356
        
357
        protected Class[] cookieClasses() {
358
            return new Class[] { Project.class };
359
        }
360
        
361
    }
362
    
363
}
(-)src/org/netbeans/modules/apisupport/project/ui/SourcesNodeFactory.java (+126 lines)
Added Link Here
1
/*
2
 * The contents of this file are subject to the terms of the Common Development
3
 * and Distribution License (the License). You may not use this file except in
4
 * compliance with the License.
5
 *
6
 * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7
 * or http://www.netbeans.org/cddl.txt.
8
 *
9
 * When distributing Covered Code, include this CDDL Header Notice in each file
10
 * and include the License file at http://www.netbeans.org/cddl.txt.
11
 * If applicable, add the following below the CDDL Header, with the fields
12
 * enclosed by brackets [] replaced by your own identifying information:
13
 * "Portions Copyrighted [year] [name of copyright owner]"
14
 *
15
 * The Original Software is NetBeans. The Initial Developer of the Original
16
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
17
 * Microsystems, Inc. All Rights Reserved.
18
 */
19
20
package org.netbeans.modules.apisupport.project.ui;
21
22
import java.util.ArrayList;
23
import java.util.Arrays;
24
import java.util.List;
25
import javax.swing.event.ChangeEvent;
26
import javax.swing.event.ChangeListener;
27
import org.netbeans.api.java.project.JavaProjectConstants;
28
import org.netbeans.api.project.Project;
29
import org.netbeans.api.project.ProjectUtils;
30
import org.netbeans.api.project.SourceGroup;
31
import org.netbeans.api.project.Sources;
32
import org.netbeans.modules.apisupport.project.NbModuleProject;
33
import org.netbeans.spi.java.project.support.ui.PackageView;
34
import org.netbeans.spi.project.support.GenericSources;
35
import org.netbeans.spi.project.ui.support.NodeFactory;
36
import org.netbeans.spi.project.ui.support.NodeList;
37
import org.openide.filesystems.FileObject;
38
import org.openide.nodes.Node;
39
import org.openide.util.NbBundle;
40
41
/**
42
 *
43
 * @author mkleint
44
 */
45
public class SourcesNodeFactory implements NodeFactory {
46
    
47
    /** Creates a new instance of SourcesNodeFactory */
48
    public SourcesNodeFactory() {
49
    }
50
    
51
    public NodeList createNodes(Project p) {
52
        NbModuleProject prj = p.getLookup().lookup(NbModuleProject.class);
53
        return new SourceNL(prj);
54
    }
55
56
    
57
    private static class SourceNL implements NodeList<SourceGroup>, ChangeListener {
58
        private List<ChangeListener> list = new ArrayList<ChangeListener>();
59
        private static final String[] SOURCE_GROUP_TYPES = {
60
            JavaProjectConstants.SOURCES_TYPE_JAVA,
61
            NbModuleProject.SOURCES_TYPE_JAVAHELP,
62
        };
63
        
64
        private final NbModuleProject project;
65
        
66
        SourceNL(NbModuleProject prj) {
67
            project = prj;
68
        }
69
        public void addChangeListener(ChangeListener l) {
70
            list.add(l);
71
        }
72
73
        public void removeChangeListener(ChangeListener l) {
74
            list.remove(l);
75
        }
76
77
        public void addNotify() {
78
            ProjectUtils.getSources(project).addChangeListener(this);
79
        }
80
81
        public void removeNotify() {
82
            ProjectUtils.getSources(project).removeChangeListener(this);
83
        }
84
85
        public Node node(SourceGroup key) {
86
            return PackageView.createPackageView(key);
87
        }
88
89
        public List<SourceGroup> keys() {
90
            List<SourceGroup> l = new ArrayList<SourceGroup>();
91
            Sources s = ProjectUtils.getSources(project);
92
            for (int i = 0; i < SOURCE_GROUP_TYPES.length; i++) {
93
                SourceGroup[] groups = s.getSourceGroups(SOURCE_GROUP_TYPES[i]);
94
                l.addAll(Arrays.asList(groups));
95
            }
96
            SourceGroup javadocDocfiles = makeJavadocDocfilesSourceGroup();
97
            if (javadocDocfiles != null) {
98
                l.add(javadocDocfiles);
99
            }
100
            return l;
101
        }
102
        
103
        private SourceGroup makeJavadocDocfilesSourceGroup() {
104
            String propname = "javadoc.docfiles"; // NOI18N
105
            FileObject root = resolveFileObjectFromProperty(propname);
106
            if(root == null) {
107
                return null;
108
            }
109
            return GenericSources.group(project, root, propname, NbBundle.getMessage(ModuleLogicalView.class, "LBL_extra_javadoc_files"), null, null);
110
        }
111
        
112
        private FileObject resolveFileObjectFromProperty(String property){
113
            String filename = project.evaluator().getProperty(property);
114
            if (filename == null) {
115
                return null;
116
            }
117
            return project.getHelper().resolveFileObject(filename);
118
        }
119
120
        public void stateChanged(ChangeEvent arg0) {
121
            for (ChangeListener lst : list) {
122
                lst.stateChanged(new ChangeEvent(this));
123
            }
124
        }
125
}
126
}
(-)src/org/netbeans/modules/apisupport/project/ui/SuiteLogicalView.java (-436 / +3 lines)
Lines 19-82 Link Here
19
19
20
package org.netbeans.modules.apisupport.project.ui;
20
package org.netbeans.modules.apisupport.project.ui;
21
21
22
import java.awt.EventQueue;
23
import java.awt.Image;
22
import java.awt.Image;
24
import java.awt.event.ActionEvent;
25
import java.beans.PropertyChangeEvent;
23
import java.beans.PropertyChangeEvent;
26
import java.beans.PropertyChangeListener;
24
import java.beans.PropertyChangeListener;
27
import java.io.IOException;
28
import java.util.ArrayList;
29
import java.util.Collections;
30
import java.util.Enumeration;
25
import java.util.Enumeration;
31
import java.util.HashSet;
26
import java.util.HashSet;
32
import java.util.Iterator;
33
import java.util.LinkedHashMap;
34
import java.util.List;
35
import java.util.Set;
27
import java.util.Set;
36
import java.util.SortedSet;
37
import java.util.TreeSet;
38
import javax.swing.AbstractAction;
39
import javax.swing.Action;
28
import javax.swing.Action;
40
import javax.swing.event.ChangeEvent;
41
import javax.swing.event.ChangeListener;
42
import org.netbeans.api.project.FileOwnerQuery;
29
import org.netbeans.api.project.FileOwnerQuery;
43
import org.netbeans.api.project.Project;
44
import org.netbeans.api.project.ProjectInformation;
30
import org.netbeans.api.project.ProjectInformation;
45
import org.netbeans.api.project.ProjectManager;
46
import org.netbeans.api.project.ProjectUtils;
31
import org.netbeans.api.project.ProjectUtils;
47
import org.netbeans.api.project.ui.OpenProjects;
48
import org.netbeans.modules.apisupport.project.NbModuleProject;
49
import org.netbeans.modules.apisupport.project.Util;
50
import org.netbeans.modules.apisupport.project.suite.SuiteProject;
32
import org.netbeans.modules.apisupport.project.suite.SuiteProject;
51
import org.netbeans.modules.apisupport.project.ui.customizer.SuiteUtils;
52
import org.netbeans.modules.apisupport.project.ui.wizard.NewNbModuleWizardIterator;
53
import org.netbeans.spi.project.SubprojectProvider;
54
import org.netbeans.spi.project.ui.LogicalViewProvider;
33
import org.netbeans.spi.project.ui.LogicalViewProvider;
55
import org.netbeans.spi.project.ui.support.DefaultProjectOperations;
34
import org.netbeans.spi.project.ui.support.DefaultProjectOperations;
56
import org.openide.DialogDisplayer;
35
import org.netbeans.spi.project.ui.support.NodeFactorySupport;
57
import org.openide.ErrorManager;
58
import org.openide.NotifyDescriptor;
59
import org.openide.awt.StatusDisplayer;
60
import org.openide.filesystems.FileChangeAdapter;
61
import org.openide.filesystems.FileObject;
36
import org.openide.filesystems.FileObject;
62
import org.openide.filesystems.FileUtil;
37
import org.openide.filesystems.FileUtil;
63
import org.openide.filesystems.FileChangeListener;
64
import org.openide.filesystems.FileEvent;
65
import org.openide.filesystems.FileStateInvalidException;
66
import org.openide.loaders.DataObject;
38
import org.openide.loaders.DataObject;
67
import org.openide.loaders.DataObjectNotFoundException;
39
import org.openide.loaders.DataObjectNotFoundException;
68
import org.openide.nodes.AbstractNode;
69
import org.openide.nodes.Children;
70
import org.openide.nodes.Node;
40
import org.openide.nodes.Node;
71
import org.openide.util.HelpCtx;
72
import org.openide.util.NbBundle;
41
import org.openide.util.NbBundle;
73
import org.openide.util.RequestProcessor;
74
import org.openide.util.Utilities;
42
import org.openide.util.Utilities;
75
import org.openide.util.WeakListeners;
43
import org.openide.util.WeakListeners;
76
import org.openide.util.actions.CookieAction;
77
import org.openide.util.actions.NodeAction;
78
import org.openide.util.lookup.Lookups;
44
import org.openide.util.lookup.Lookups;
79
import org.openide.windows.WindowManager;
80
45
81
/**
46
/**
82
 * Provides a logical view of a NetBeans suite project.
47
 * Provides a logical view of a NetBeans suite project.
Lines 128-137 Link Here
128
        return null;
93
        return null;
129
    }
94
    }
130
    
95
    
131
    private static String getMessage(final String key) {
132
        return NbBundle.getMessage(SuiteLogicalView.class, key);
133
    }
134
    
135
    /** Package private for unit test only. */
96
    /** Package private for unit test only. */
136
    static final class SuiteRootNode extends AnnotatedNode
97
    static final class SuiteRootNode extends AnnotatedNode
137
            implements PropertyChangeListener {
98
            implements PropertyChangeListener {
Lines 142-148 Link Here
142
        private final ProjectInformation info;
103
        private final ProjectInformation info;
143
        
104
        
144
        SuiteRootNode(final SuiteProject suite) {
105
        SuiteRootNode(final SuiteProject suite) {
145
            super(createRootChildren(suite), Lookups.fixed(new Object[] {suite}));
106
            super(NodeFactorySupport.createCompositeChildren(suite, "Projects/org-netbeans-modules-apisupport-project-suite/Nodes"), 
107
                  Lookups.fixed(new Object[] {suite}));
146
            this.suite = suite;
108
            this.suite = suite;
147
            info = ProjectUtils.getInformation(suite);
109
            info = ProjectUtils.getInformation(suite);
148
            info.addPropertyChangeListener(WeakListeners.propertyChange(this, info));
110
            info.addPropertyChangeListener(WeakListeners.propertyChange(this, info));
Lines 204-602 Link Here
204
        }
166
        }
205
        
167
        
206
    }
168
    }
207
    
208
    private static Children createRootChildren(final SuiteProject suite) {
209
        ImportantFilesChildren ch = new ImportantFilesChildren(suite);
210
        Node ifn = ModuleLogicalView.createImportantFilesNode(ch);
211
        
212
        Node[] nodes = new Node[] { new ModulesNode(suite), ifn,  };
213
        Children children = new Children.Array();
214
        children.add(nodes);
215
        return children;
216
    }
217
    
218
    /** Represent <em>Modules</em> node in the Suite Logical View. */
219
    static final class ModulesNode extends AbstractNode {
220
        
221
        private SuiteProject suite;
222
        
223
        ModulesNode(final SuiteProject suite) {
224
            super(new ModuleChildren(suite));
225
            this.suite = suite;
226
            setName("modules"); // NOI18N
227
            setDisplayName(getMessage("CTL_Modules"));
228
        }
229
        
230
        public Action[] getActions(boolean context) {
231
            return new Action[] {
232
                new AddNewSuiteComponentAction(suite),
233
                new AddNewLibraryWrapperAction(suite),
234
                new AddSuiteComponentAction(suite),
235
            };
236
        }
237
        
238
        private Image getIcon(boolean opened) {
239
            Image badge = Utilities.loadImage("org/netbeans/modules/apisupport/project/suite/resources/module-badge.gif", true);
240
            return Utilities.mergeImages(UIUtil.getTreeFolderIcon(opened), badge, 9, 9);
241
        }
242
        
243
        public Image getIcon(int type) {
244
            return getIcon(false);
245
        }
246
        
247
        public Image getOpenedIcon(int type) {
248
            return getIcon(true);
249
        }
250
        
251
        static final class ModuleChildren extends Children.Keys<NbModuleProject> implements ChangeListener {
252
            
253
            private final SuiteProject suite;
254
            
255
            public ModuleChildren(SuiteProject suite) {
256
                suite.getLookup().lookup(SubprojectProvider.class).addChangeListener(this);
257
                this.suite = suite;
258
            }
259
            
260
            protected void addNotify() {
261
                updateKeys();
262
            }
263
            
264
            private void updateKeys() {
265
                // e.g.(?) Explorer view under Children.MUTEX subsequently calls e.g.
266
                // SuiteProject$Info.getSimpleName() which acquires ProjectManager.mutex(). And
267
                // since this method might be called under ProjectManager.mutex() write access
268
                // and updateKeys() --> setKeys() in turn calls Children.MUTEX write access,
269
                // deadlock is here, so preventing it... (also got this under read access)
270
                EventQueue.invokeLater(new Runnable() {
271
                    public void run() {
272
                        // #70112: sort them.
273
                        SortedSet<NbModuleProject> subModules = new TreeSet(Util.projectDisplayNameComparator());
274
                        subModules.addAll(SuiteUtils.getSubProjects(suite));
275
                        setKeys(subModules);
276
                    }
277
                });
278
            }
279
            
280
            protected void removeNotify() {
281
                suite.getLookup().lookup(SubprojectProvider.class).removeChangeListener(this);
282
                setKeys(Collections.EMPTY_SET);
283
            }
284
            
285
            protected Node[] createNodes(NbModuleProject p) {
286
                return new Node[] {new SuiteComponentNode(p)};
287
            }
288
            
289
            public void stateChanged(ChangeEvent ev) {
290
                updateKeys();
291
            }
292
            
293
        }
294
        
295
    }
296
    
297
    private static final class AddSuiteComponentAction extends AbstractAction {
298
        
299
        private final SuiteProject suite;
300
        
301
        public AddSuiteComponentAction(final SuiteProject suite) {
302
            super(getMessage("CTL_AddModule"));
303
            this.suite = suite;
304
        }
305
        
306
        public void actionPerformed(ActionEvent evt) {
307
            NbModuleProject project = UIUtil.chooseSuiteComponent(
308
                    WindowManager.getDefault().getMainWindow(),
309
                    suite);
310
            if (project != null) {
311
                if (!SuiteUtils.contains(suite, project)) {
312
                    try {
313
                        SuiteUtils.addModule(suite, (NbModuleProject) project);
314
                        ProjectManager.getDefault().saveProject(suite);
315
                    } catch (IOException ex) {
316
                        ErrorManager.getDefault().notify(ex);
317
                    }
318
                } else {
319
                    DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(
320
                            NbBundle.getMessage(SuiteLogicalView.class, "MSG_SuiteAlreadyContainsCNB", project.getCodeNameBase())));
321
                }
322
            }
323
        }
324
        
325
    }
326
    
327
    private static final class AddNewSuiteComponentAction extends AbstractAction {
328
        
329
        private final SuiteProject suite;
330
        
331
        public AddNewSuiteComponentAction(final SuiteProject suite) {
332
            super(getMessage("CTL_AddNewModule"));
333
            this.suite = suite;
334
        }
335
        
336
        public void actionPerformed(ActionEvent evt) {
337
            NewNbModuleWizardIterator iterator = NewNbModuleWizardIterator.createSuiteComponentIterator(suite);
338
            UIUtil.runProjectWizard(iterator, "CTL_NewModuleProject"); // NOI18N
339
        }
340
        
341
    }
342
    
343
    static final class AddNewLibraryWrapperAction extends AbstractAction {
344
        
345
        private final Project suiteProvider;
346
        private final NbModuleProject target;
347
        
348
        public AddNewLibraryWrapperAction(final Project suiteProvider, final NbModuleProject target) {
349
            super(getMessage("CTL_AddNewLibrary"));
350
            this.suiteProvider = suiteProvider;
351
            this.target = target;
352
        }
353
        
354
        public AddNewLibraryWrapperAction(final Project suiteProvider) {
355
            this(suiteProvider, null);
356
        }
357
        
358
        public void actionPerformed(ActionEvent evt) {
359
            NbModuleProject project = UIUtil.runLibraryWrapperWizard(suiteProvider);
360
            if (project != null && target != null) {
361
                try {
362
                    Util.addDependency(target, project);
363
                    ProjectManager.getDefault().saveProject(target);
364
                } catch (IOException e) {
365
                    assert false : e;
366
                }
367
            }
368
        }
369
        
370
    }
371
    
372
    /** Represent one module (a suite component) node. */
373
    private static final class SuiteComponentNode extends AbstractNode {
374
        
375
        private final static Action REMOVE_ACTION = new RemoveSuiteComponentAction();
376
        private final static Action OPEN_ACTION = new OpenProjectAction();
377
        
378
        public SuiteComponentNode(final NbModuleProject suiteComponent) {
379
            super(Children.LEAF, Lookups.fixed(new Object[] {suiteComponent}));
380
            ProjectInformation info = ProjectUtils.getInformation(suiteComponent);
381
            setName(info.getName());
382
            setDisplayName(info.getDisplayName());
383
            setIconBaseWithExtension(NbModuleProject.NB_PROJECT_ICON_PATH);
384
            info.addPropertyChangeListener(new PropertyChangeListener() {
385
                public void propertyChange(PropertyChangeEvent evt) {
386
                    if (evt.getPropertyName() == ProjectInformation.PROP_DISPLAY_NAME) {
387
                        SuiteComponentNode.this.setDisplayName((String) evt.getNewValue());
388
                    } else if (evt.getPropertyName() == ProjectInformation.PROP_NAME) {
389
                        SuiteComponentNode.this.setName((String) evt.getNewValue());
390
                    }
391
                }
392
            });
393
        }
394
        
395
        public Action[] getActions(boolean context) {
396
            return new Action[] {
397
                OPEN_ACTION, REMOVE_ACTION
398
            };
399
        }
400
        
401
        public Action getPreferredAction() {
402
            return OPEN_ACTION;
403
        }
404
        
405
    }
406
    
407
    private static final class RemoveSuiteComponentAction extends NodeAction {
408
        
409
        protected void performAction(Node[] activatedNodes) {
410
            for (int i = 0; i < activatedNodes.length; i++) {
411
                final NbModuleProject suiteComponent =
412
                        (NbModuleProject) activatedNodes[i].getLookup().lookup(NbModuleProject.class);
413
                assert suiteComponent != null : "NbModuleProject in lookup"; // NOI18N
414
                try {
415
                    NbModuleProject[] modules = SuiteUtils.getDependentModules(suiteComponent);
416
                    boolean remove = true;
417
                    if (modules.length > 0) {
418
                        StringBuffer sb = new StringBuffer("<ul>"); // NOI18N
419
                        for (int j = 0; j < modules.length; j++) {
420
                            sb.append("<li>" + ProjectUtils.getInformation(modules[j]).getDisplayName() + "</li>"); // NOI18N
421
                        }
422
                        sb.append("</ul>"); // NOI18N
423
                        String displayName = ProjectUtils.getInformation(suiteComponent).getDisplayName();
424
                        String confirmMessage = NbBundle.getMessage(SuiteLogicalView.class,
425
                                "MSG_RemovingModuleMessage", displayName, sb.toString()); // NOI18N
426
                        remove = UIUtil.showAcceptCancelDialog(
427
                                NbBundle.getMessage(SuiteLogicalView.class, "CTL_RemovingModuleTitle", displayName),
428
                                confirmMessage, getMessage("CTL_RemoveDependency"), null, NotifyDescriptor.QUESTION_MESSAGE);
429
                    }
430
                    if (remove) {
431
                        SuiteUtils.removeModuleFromSuiteWithDependencies(suiteComponent);
432
                    }
433
                } catch (IOException ex) {
434
                    ErrorManager.getDefault().notify(ex);
435
                }
436
            }
437
        }
438
        
439
        protected boolean enable(Node[] activatedNodes) {
440
            return true;
441
        }
442
        
443
        public String getName() {
444
            return getMessage("CTL_RemoveModule");
445
        }
446
        
447
        public HelpCtx getHelpCtx() {
448
            return HelpCtx.DEFAULT_HELP;
449
        }
450
        
451
        protected boolean asynchronous() {
452
            return false;
453
        }
454
        
455
    }
456
    
457
    private static final class OpenProjectAction extends CookieAction {
458
        
459
        protected void performAction(Node[] activatedNodes) {
460
            final Project[] projects = new Project[activatedNodes.length];
461
            for (int i = 0; i < activatedNodes.length; i++) {
462
                Project project = (Project) activatedNodes[i].getLookup().lookup(Project.class);
463
                projects[i] = project;
464
            }
465
            RequestProcessor.getDefault().post(new Runnable() {
466
                public void run() {
467
                    StatusDisplayer.getDefault().setStatusText(getMessage("MSG_OpeningProjects"));
468
                    OpenProjects.getDefault().open(projects, false);
469
                }
470
            });
471
        }
472
        
473
        public String getName() {
474
            return getMessage("CTL_OpenProject");
475
        }
476
        
477
        public HelpCtx getHelpCtx() {
478
            return HelpCtx.DEFAULT_HELP;
479
        }
480
        
481
        protected boolean asynchronous() {
482
            return false;
483
        }
484
        
485
        protected int mode() {
486
            return CookieAction.MODE_ALL;
487
        }
488
        
489
        protected Class[] cookieClasses() {
490
            return new Class[] { Project.class };
491
        }
492
        
493
    }
494
    
495
    /**
496
     * Actual list of important files.
497
     */
498
    private static final class ImportantFilesChildren extends Children.Keys<String> {
499
        
500
        private List<String> visibleFiles = new ArrayList();
501
        private FileChangeListener fcl;
502
        
503
        /** Abstract location to display name. */
504
        private static final java.util.Map<String,String> FILES = new LinkedHashMap();
505
        static {
506
            FILES.put("master.jnlp", getMessage("LBL_jnlp_master"));
507
            FILES.put("build.xml", getMessage("LBL_build.xml"));
508
            FILES.put("nbproject/project.properties", getMessage("LBL_project.properties"));
509
            FILES.put("nbproject/private/private.properties", getMessage("LBL_private.properties"));
510
            FILES.put("nbproject/platform.properties", getMessage("LBL_platform.properties"));
511
            FILES.put("nbproject/private/platform-private.properties", getMessage("LBL_platform-private.properties"));
512
        }
513
        
514
        private final SuiteProject project;
515
        
516
        public ImportantFilesChildren(SuiteProject project) {
517
            this.project = project;
518
        }
519
        
520
        protected void addNotify() {
521
            super.addNotify();
522
            attachListeners();
523
            refreshKeys();
524
        }
525
        
526
        protected void removeNotify() {
527
            setKeys(Collections.EMPTY_SET);
528
            removeListeners();
529
            super.removeNotify();
530
        }
531
        
532
        protected Node[] createNodes(String loc) {
533
            String locEval = project.getEvaluator().evaluate(loc);
534
            FileObject file = project.getHelper().resolveFileObject(locEval);
535
            
536
            try {
537
                Node orig = DataObject.find(file).getNodeDelegate();
538
                return new Node[] {new ModuleLogicalView.SpecialFileNode(orig, (String) FILES.get(loc))};
539
            } catch (DataObjectNotFoundException e) {
540
                throw new AssertionError(e);
541
            }
542
        }
543
        
544
        private void refreshKeys() {
545
            List<String> newVisibleFiles = new ArrayList();
546
            Iterator it = FILES.keySet().iterator();
547
            Set files = new HashSet();
548
            while (it.hasNext()) {
549
                String loc = (String) it.next();
550
                String locEval = project.getEvaluator().evaluate(loc);
551
                if (locEval == null) {
552
                    continue;
553
                }
554
                FileObject file = project.getHelper().resolveFileObject(locEval);
555
                if (file != null) {
556
                    newVisibleFiles.add(loc);
557
                    files.add(file);
558
                }
559
            }
560
            if (!isInitialized() || !newVisibleFiles.equals(visibleFiles)) {
561
                visibleFiles = newVisibleFiles;
562
                RequestProcessor.getDefault().post(new Runnable() { // #72471
563
                    public void run() {
564
                        setKeys(visibleFiles);
565
                    }
566
                });
567
                ((ModuleLogicalView.ImportantFilesNode) getNode()).setFiles(files); // #72439
568
            }
569
        }
570
        
571
        private void attachListeners() {
572
            try {
573
                if (fcl == null) {
574
                    fcl = new FileChangeAdapter() {
575
                        public void fileDataCreated(FileEvent fe) {
576
                            refreshKeys();
577
                        }
578
                        public void fileDeleted(FileEvent fe) {
579
                            refreshKeys();
580
                        }
581
                    };
582
                    project.getProjectDirectory().getFileSystem().addFileChangeListener(fcl);
583
                }
584
            } catch (FileStateInvalidException e) {
585
                assert false : e;
586
            }
587
        }
588
        
589
        private void removeListeners() {
590
            if (fcl != null) {
591
                try {
592
                    project.getProjectDirectory().getFileSystem().removeFileChangeListener(fcl);
593
                } catch (FileStateInvalidException e) {
594
                    assert false : e;
595
                }
596
                fcl = null;
597
            }
598
        }
599
        
600
    }
601
    
602
}
169
}
(-)src/org/netbeans/modules/apisupport/project/ui/resources/layer.xml (+18 lines)
Lines 175-180 Link Here
175
    </folder>
175
    </folder>
176
176
177
    <folder name="Projects" >
177
    <folder name="Projects" >
178
        <folder name="org-netbeans-modules-apisupport-project">
179
            <folder name="Lookup"/>
180
            <folder name="Nodes">
181
                <file name="org-netbeans-modules-apisupport-project-ui-SourcesNodeFactory.instance"/>
182
                <attr name="org-netbeans-modules-apisupport-project-ui-SourcesNodeFactory.instance/org-netbeans-modules-apisupport-project-ui-ImportantFilesNodeFactory.instance" boolvalue="true"/>
183
                <file name="org-netbeans-modules-apisupport-project-ui-ImportantFilesNodeFactory.instance"/>
184
                <attr name="org-netbeans-modules-apisupport-project-ui-ImportantFilesNodeFactory.instance/org-netbeans-modules-apisupport-project-ui-LibrariesNodeFactory.instance" boolvalue="true"/>
185
                <file name="org-netbeans-modules-apisupport-project-ui-LibrariesNodeFactory.instance"/>
186
            </folder>
187
         </folder>
188
        <folder name="org-netbeans-modules-apisupport-project-suite">
189
            <folder name="Lookup"/>
190
            <folder name="Nodes">
191
                <file name="org-netbeans-modules-apisupport-project-ui-ModulesNodeFactory.instance"/>
192
                <attr name="org-netbeans-modules-apisupport-project-ui-ModulesNodeFactory.instance/org-netbeans-modules-apisupport-project-ui-ImportantFilesNodeFactory.instance" boolvalue="true"/>
193
                <file name="org-netbeans-modules-apisupport-project-ui-ImportantFilesNodeFactory.instance"/>
194
            </folder>
195
        </folder>
178
        <folder name="Actions" >
196
        <folder name="Actions" >
179
            <attr name="org-netbeans-modules-versioning-system-cvss-ui-actions-ProjectCvsMenuItem.instance/org-netbeans-modules-apisupport-project-ui-XTestProjectMenuItem.instance" boolvalue="true" />
197
            <attr name="org-netbeans-modules-versioning-system-cvss-ui-actions-ProjectCvsMenuItem.instance/org-netbeans-modules-apisupport-project-ui-XTestProjectMenuItem.instance" boolvalue="true" />
180
            <!-- Adds XTest sub menu to project root node -->
198
            <!-- Adds XTest sub menu to project root node -->

Return to bug 95366