001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.mobility.end2end.util;
043:
044: import java.beans.PropertyChangeEvent;
045: import java.util.Arrays;
046: import java.util.Collections;
047: import javax.swing.event.ChangeEvent;
048: import javax.swing.event.ChangeListener;
049: import org.netbeans.api.java.project.JavaProjectConstants;
050: import org.netbeans.api.java.source.ClasspathInfo;
051: import org.netbeans.api.project.ProjectUtils;
052: import org.netbeans.api.project.SourceGroup;
053: import org.netbeans.api.project.Sources;
054: import org.netbeans.modules.mobility.e2e.classdata.ClassData;
055: import org.netbeans.modules.mobility.e2e.classdata.ClassDataRegistry;
056: import org.netbeans.modules.mobility.e2e.classdata.MethodData;
057: import org.netbeans.modules.mobility.end2end.classdata.TypeData;
058: import org.netbeans.modules.mobility.end2end.client.config.Configuration;
059: import org.openide.filesystems.FileAttributeEvent;
060: import org.openide.filesystems.FileEvent;
061: import org.openide.filesystems.FileObject;
062: import org.openide.filesystems.FileRenameEvent;
063: import org.openide.nodes.AbstractNode;
064: import org.openide.nodes.Children;
065: import org.openide.nodes.Children;
066: import org.openide.nodes.Node;
067: import java.awt.*;
068: import java.beans.PropertyChangeListener;
069: import java.util.ArrayList;
070: import java.util.Comparator;
071: import java.util.Enumeration;
072: import java.util.HashMap;
073: import java.util.HashSet;
074: import java.util.List;
075: import java.util.WeakHashMap;
076: import org.netbeans.modules.mobility.e2e.classdata.MethodParameter;
077: import org.netbeans.modules.mobility.end2end.classdata.OperationData;
078: import org.netbeans.modules.mobility.end2end.ui.treeview.MethodCheckedTreeBeanView;
079: import org.netbeans.modules.mobility.end2end.ui.treeview.MultiStateCheckBox;
080: import org.openide.filesystems.FileChangeListener;
081: import org.openide.filesystems.FileUtil;
082: import org.openide.util.NbBundle;
083: import org.openide.util.RequestProcessor;
084: import org.openide.util.RequestProcessor.Task;
085: import org.openide.util.WeakListeners;
086: import org.openide.util.lookup.Lookups;
087:
088: /**
089: *
090: * @author Adam
091: */
092:
093: public class ServiceNodeManager {
094:
095: static final String PACKAGE_ICON = "org/netbeans/spi/java/project/support/ui/packageBadge.gif"; //NOI18N
096: static final String CLASS_ICON = "org/netbeans/spi/java/project/support/ui/packageBadge.gif"; //NOI18N
097: static final String METHOD_ICON = "org/netbeans/spi/java/project/support/ui/packageBadge.gif"; //NIOI18N
098: public final static String NODE_VALIDITY_ATTRIBUTE = "isValid"; //NOI18N
099: public final static String NODE_SELECTION_ATTRIBUTE = "isSelected"; //NOI18N
100: private static final WeakHashMap<MethodCheckedTreeBeanView, ProjectChildren> oldNodes = new WeakHashMap<MethodCheckedTreeBeanView, ProjectChildren>();
101:
102: public static Node getRootNode(Configuration cfg,
103: MethodCheckedTreeBeanView tree) {
104: synchronized (oldNodes) {
105: ProjectChildren ch = oldNodes.get(tree);
106: if (ch != null) {
107: ch.removeNotify();
108: }
109: }
110:
111: ProjectChildren ch = new ProjectChildren(cfg, tree);
112: synchronized (oldNodes) {
113: oldNodes.put(tree, ch);
114: }
115: return new AbstractNode(ch);
116: }
117:
118: private static String getActiveProfile() {
119: return ClassDataRegistry.DEFAULT_PROFILE;
120: }
121:
122: private static class ProjectChildren extends Children.Keys<String>
123: implements ChangeListener, PropertyChangeListener,
124: FileChangeListener, Runnable {
125:
126: private final Configuration cfg;
127: private final MethodCheckedTreeBeanView tree;
128: private final Sources s;
129: private ClassDataRegistry activeProfileRegistry, allRegistry;
130: private ChangeListener ref1;
131: private final HashMap<Object, Object> hookedListeners = new HashMap(); // FileObject or SourceGroup -> listener
132: private final Task refreshTask = RequestProcessor.getDefault()
133: .create(this );
134: private final HashSet<String> selectionSource = new HashSet();
135:
136: public ProjectChildren(Configuration cfg,
137: MethodCheckedTreeBeanView tree) {
138: this .cfg = cfg;
139: this .tree = tree;
140: this .s = ProjectUtils
141: .getSources(Util.getServerProject(cfg));
142: run();
143: }
144:
145: protected void addNotify() {
146: ref1 = WeakListeners.change(this , s);
147: s.addChangeListener(ref1);
148: }
149:
150: protected synchronized void removeNotify() {
151: s.removeChangeListener(ref1);
152: synchronized (hookedListeners) {
153: removeListeners();
154: }
155: refreshTask.cancel();
156: }
157:
158: private void removeListeners() {
159: for (java.util.Map.Entry en : hookedListeners.entrySet()) {
160: Object o = en.getKey();
161: if (o instanceof SourceGroup)
162: ((SourceGroup) o)
163: .removePropertyChangeListener((PropertyChangeListener) en
164: .getValue());
165: else
166: ((FileObject) o)
167: .removeFileChangeListener((FileChangeListener) en
168: .getValue());
169: }
170: hookedListeners.clear();
171: }
172:
173: public void propertyChange(PropertyChangeEvent evt) {
174: refreshTask.schedule(200);
175: }
176:
177: public void stateChanged(ChangeEvent e) {
178: refreshTask.schedule(200);
179: }
180:
181: public void fileFolderCreated(FileEvent fe) {
182: refreshTask.schedule(200);
183: }
184:
185: public void fileDataCreated(FileEvent fe) {
186: refreshTask.schedule(200);
187: }
188:
189: public void fileChanged(FileEvent fe) {
190: refreshTask.schedule(200);
191: }
192:
193: public void fileDeleted(FileEvent fe) {
194: refreshTask.schedule(200);
195: }
196:
197: public void fileRenamed(FileRenameEvent fe) {
198: refreshTask.schedule(200);
199: }
200:
201: public void fileAttributeChanged(FileAttributeEvent fe) {
202: }
203:
204: public void run() {
205: SourceGroup[] groups = s
206: .getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA);
207: // Add all paths to the ClasspathInfo structure
208: List<ClasspathInfo> classpaths = new ArrayList();
209: HashMap<Object, Object> newHooks = new HashMap();
210: for (SourceGroup sg : s
211: .getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA)) {
212: if (!sg.getName().equals("${test.src.dir}")) {
213: classpaths.add(ClasspathInfo.create(sg
214: .getRootFolder())); //NOI18N
215: synchronized (hookedListeners) {
216: PropertyChangeListener l = (PropertyChangeListener) hookedListeners
217: .get(sg);
218: if (l == null) {
219: l = WeakListeners.propertyChange(this , sg);
220: sg.addPropertyChangeListener(l);
221: hookedListeners.put(sg, l);
222: }
223: newHooks.put(sg, l);
224: }
225: FileObject root = sg.getRootFolder();
226: addFCListener(root, newHooks);
227: Enumeration<? extends FileObject> en = root
228: .getChildren(true);
229: while (en.hasMoreElements()) {
230: FileObject fo = en.nextElement();
231: if (fo.isFolder() || fo.getExt().equals("java"))
232: addFCListener(fo, newHooks); //NOI18N
233: }
234: }
235: }
236: synchronized (hookedListeners) {
237: hookedListeners.keySet().removeAll(newHooks.keySet());
238: removeListeners();
239: hookedListeners.putAll(newHooks);
240: }
241: // Get the registry for all available classes
242: allRegistry = ClassDataRegistry.getRegistry(
243: ClassDataRegistry.ALL_JAVA_PROFILE, classpaths);
244: activeProfileRegistry = ClassDataRegistry.getRegistry(
245: getActiveProfile(), classpaths);
246: synchronized (selectionSource) {
247: selectionSource.clear();
248: List<org.netbeans.modules.mobility.end2end.classdata.ClassData> data = cfg
249: .getServices().get(0).getData();
250: if (data != null)
251: for (org.netbeans.modules.mobility.end2end.classdata.ClassData cd : data) {
252: String fqn = cd.getPackageName();
253: if (fqn.length() > 0)
254: fqn = fqn + '.';
255: fqn = fqn + cd.getClassName();
256: for (OperationData od : cd.getOperations()) {
257: StringBuffer sb = new StringBuffer(fqn);
258: sb.append('.').append(od.getName());
259: for (TypeData td : od.getParameterTypes()) {
260: sb.append(',').append(td.getType());
261: }
262: selectionSource.add(sb.toString());
263: }
264: }
265: }
266: String packages[] = allRegistry.getBasePackages().toArray(
267: new String[0]);
268: Arrays.sort(packages);
269: setKeys(packages);
270: for (Node n : getNodes())
271: ((PackageChildren) n.getChildren()).notifyChange();
272: tree.updateTreeNodeStates(null);
273: }
274:
275: private void addFCListener(FileObject fo,
276: HashMap<Object, Object> newHooks) {
277: synchronized (hookedListeners) {
278: FileChangeListener l = (FileChangeListener) hookedListeners
279: .get(fo);
280: if (l == null) {
281: l = FileUtil.weakFileChangeListener(this , fo);
282: fo.addFileChangeListener(l);
283: hookedListeners.put(fo, l);
284: }
285: newHooks.put(fo, l);
286: }
287: }
288:
289: protected Node[] createNodes(String packageName) {
290: AbstractNode n = new AbstractNode(new PackageChildren(
291: packageName));
292: n.setName(packageName);
293: n.setDisplayName(packageName.length() == 0 ? NbBundle
294: .getMessage(ServiceNodeManager.class,
295: "LBL_DefaultPackage") : packageName); //NOI18N
296: n.setIconBaseWithExtension(PACKAGE_ICON);
297: n.setValue(NODE_VALIDITY_ATTRIBUTE, activeProfileRegistry
298: .getBasePackages().contains(packageName));
299: return new Node[] { n };
300: }
301:
302: private class PackageChildren extends Children.Keys<ClassData> {
303:
304: private final String packageName;
305:
306: public PackageChildren(String packageName) {
307: this .packageName = packageName;
308: notifyChange();
309: }
310:
311: public void notifyChange() {
312: ClassData cd[] = allRegistry.getBaseClassesForPackage(
313: packageName).toArray(new ClassData[0]);
314: Arrays.sort(cd, new Comparator<ClassData>() {
315: public int compare(ClassData o1, ClassData o2) {
316: return o1.getClassName().compareTo(
317: o2.getClassName());
318: }
319: });
320: setKeys(cd);
321: for (Node n : getNodes())
322: ((ClassChildren) n.getChildren()).notifyChange();
323: }
324:
325: protected Node[] createNodes(ClassData classData) {
326: AbstractNode n = new AbstractNode(new ClassChildren(
327: classData), Lookups.singleton(classData));
328: n.setName(classData.getName());
329: StringBuffer nodeText = new StringBuffer();
330: createDisplayName(nodeText, classData);
331: n.setDisplayName(nodeText.toString());
332: n.setIconBaseWithExtension(CLASS_ICON);
333: n.setValue(NODE_VALIDITY_ATTRIBUTE,
334: activeProfileRegistry.getClassData(classData
335: .getFullyQualifiedName()) != null);
336: return new Node[] { n };
337: }
338: }
339:
340: private void createDisplayName(StringBuffer sb, ClassData cl) {
341: sb.append(cl.getName());
342: List<ClassData> gTypes = cl.getParameterTypes();
343: if (gTypes.size() > 0) {
344: sb.append('<');
345: boolean first = true;
346: for (ClassData param : gTypes) {
347: if (first)
348: first = false;
349: else
350: sb.append(',');
351: createDisplayName(sb, param);
352: }
353: sb.append('>');
354: }
355: }
356:
357: private class ClassChildren extends Children.Keys<MethodData> {
358:
359: private ClassData classData;
360: private final String fqn;
361:
362: public ClassChildren(ClassData classData) {
363: this .classData = classData;
364: this .fqn = classData.getFullyQualifiedName();
365: notifyChange();
366: }
367:
368: public void notifyChange() {
369: classData = allRegistry.getClassData(fqn);
370: setKeys(classData == null ? Collections.EMPTY_LIST
371: : classData.getMethods());
372: }
373:
374: protected Node[] createNodes(MethodData methodData) {
375: StringBuffer nodeText = new StringBuffer();
376: createDisplayName(nodeText, methodData.getReturnType());
377: nodeText.append(' ').append(methodData.getName())
378: .append('(');
379: boolean first = true;
380: for (MethodParameter param : methodData.getParameters()) {
381: if (first)
382: first = false;
383: else
384: nodeText.append(',');
385: createDisplayName(nodeText, param.getType());
386: nodeText.append(' ').append(param.getName());
387: }
388: nodeText.append(')');
389: AbstractNode n = new AbstractNode(Children.LEAF,
390: Lookups.singleton(methodData));
391: n.setName(methodData.getName());
392: n.setDisplayName(nodeText.toString());
393: n.setIconBaseWithExtension(METHOD_ICON);
394: ClassData cd = activeProfileRegistry
395: .getClassData(methodData.getParentClassName());
396: n.setValue(NODE_VALIDITY_ATTRIBUTE, cd != null
397: && cd.getMethods().contains(methodData));
398: StringBuffer sb = new StringBuffer(methodData
399: .getParentClassName());
400: sb.append('.').append(methodData.getName());
401: for (MethodParameter mp : methodData.getParameters()) {
402: sb.append(',').append(
403: mp.getType().getFullyQualifiedName());
404: }
405: n
406: .setValue(
407: NODE_SELECTION_ATTRIBUTE,
408: selectionSource.contains(sb.toString()) ? MultiStateCheckBox.State.SELECTED
409: : MultiStateCheckBox.State.UNSELECTED);
410: return new Node[] { n };
411: }
412: }
413:
414: }
415: }
|