/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
*
* Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common
* Development and Distribution License("CDDL") (collectively, the
* "License"). You may not use this file except in compliance with the
* License. You can obtain a copy of the License at
* http://www.netbeans.org/cddl-gplv2.html
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
* specific language governing permissions and limitations under the
* License. When distributing the software, include this License Header
* Notice in each file and include the License file at
* nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the
* License Header, with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* Contributor(s):
*
* The Original Software is NetBeans. The Initial Developer of the Original
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2008 Sun
* Microsystems, Inc. All Rights Reserved.
*
* If you wish your version of this file to be governed by only the CDDL
* or only the GPL Version 2, indicate your decision by adding
* "[Contributor] elects to include this software in this distribution
* under the [CDDL or GPL Version 2] license." If you do not indicate a
* single choice of license, a recipient has the option to distribute
* your version of this file under either the CDDL, the GPL Version 2 or
* to extend the choice of license to its licensees as provided above.
* However, if you add GPL Version 2 code and therefore, elected the GPL
* Version 2 license, then the option applies only if the new code is
* made subject to such option by the copyright holder.
*/
package org.netbeans.modules.ruby.merbproject.ui;
import java.awt.Image;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.StringTokenizer;
import javax.swing.Icon;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.EventListenerList;
import org.netbeans.api.project.SourceGroup;
import org.netbeans.api.queries.VisibilityQuery;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.loaders.ChangeableDataFilter;
import org.openide.loaders.DataFilter;
import org.openide.loaders.DataFolder;
import org.openide.loaders.DataObject;
import org.openide.nodes.FilterNode;
import org.openide.nodes.Node;
import org.openide.nodes.NodeNotFoundException;
import org.openide.nodes.NodeOp;
import org.openide.util.ImageUtilities;
import org.openide.util.WeakListeners;
import org.openide.util.lookup.Lookups;
import org.openide.util.lookup.ProxyLookup;
// XXX need unit test
/**
* (Copied from Java Project Source (org.netbeans.modules.gsfpath.spi.project.support.ui)
* Displays a package root in a tree.
* @see "#42151"
* @author Jesse Glick
*/
public final class TreeRootNode extends FilterNode implements PropertyChangeListener {
private static final DataFilter VISIBILITY_QUERY_FILTER = new VisibilityQueryDataFilter();
private final SourceGroup g;
public TreeRootNode(SourceGroup g) {
this(DataFolder.findFolder(g.getRootFolder()), g);
}
private TreeRootNode(DataFolder folder, SourceGroup g) {
this (new FilterNode (folder.getNodeDelegate(), folder.createNodeChildren(VISIBILITY_QUERY_FILTER)),g);
}
private TreeRootNode (Node originalNode, SourceGroup g) {
super(originalNode, new PackageFilterChildren(originalNode),
new ProxyLookup(
originalNode.getLookup(),
Lookups.singleton(new PathFinder(g))
// no need for explicit search info
));
this.g = g;
g.addPropertyChangeListener(WeakListeners.propertyChange(this, g));
}
private final static Image PACKAGE_BADGE = ImageUtilities.loadImage("org/netbeans/modules/ruby/merbproject/ui/packageBadge.gif"); // NOI18N
/** Copied from PackageRootNode with modifications. */
private Image computeIcon(boolean opened, int type) {
Icon icon = g.getIcon(opened);
if (icon == null) {
Image image = opened ? super.getOpenedIcon(type) : super.getIcon(type);
return ImageUtilities.mergeImages(image, /*PackageRootNode.*/PACKAGE_BADGE, 7, 7);
} else {
return ImageUtilities.icon2Image(icon);
}
}
public @Override Image getIcon(int type) {
return computeIcon(false, type);
}
public @Override Image getOpenedIcon(int type) {
return computeIcon(true, type);
}
public @Override String getName() {
return g.getName();
}
public @Override String getDisplayName() {
return g.getDisplayName();
}
public @Override boolean canRename() {
return false;
}
public @Override boolean canDestroy() {
return false;
}
public @Override boolean canCut() {
return false;
}
public void propertyChange(PropertyChangeEvent ev) {
// XXX handle SourceGroup.rootFolder change too
fireNameChange(null, null);
fireDisplayNameChange(null, null);
fireIconChange();
fireOpenedIconChange();
}
/** Copied from PhysicalView and PackageRootNode. */
public static final class PathFinder {
private final SourceGroup g;
PathFinder(SourceGroup g) {
this.g = g;
}
public Node findPath(Node rootNode, Object o) {
FileObject fo;
if (o instanceof FileObject) {
fo = (FileObject) o;
} else if (o instanceof DataObject) {
fo = ((DataObject) o).getPrimaryFile();
} else {
return null;
}
FileObject groupRoot = g.getRootFolder();
if (FileUtil.isParentOf(groupRoot, fo) /* && group.contains(fo) */) {
FileObject folder = fo.isFolder() ? fo : fo.getParent();
String relPath = FileUtil.getRelativePath(groupRoot, folder);
List<String> path = new ArrayList<String>();
StringTokenizer strtok = new StringTokenizer(relPath, "/"); // NOI18N
while (strtok.hasMoreTokens()) {
String token = strtok.nextToken();
path.add(token);
}
try {
Node folderNode = folder.equals(groupRoot) ? rootNode : NodeOp.findPath(rootNode, Collections.enumeration(path));
if (fo.isFolder()) {
return folderNode;
} else {
Node[] childs = folderNode.getChildren().getNodes(true);
for (int i = 0; i < childs.length; i++) {
DataObject dobj = childs[i].getLookup().lookup(DataObject.class);
if (dobj != null && dobj.getPrimaryFile().getNameExt().equals(fo.getNameExt())) {
return childs[i];
}
}
}
} catch (NodeNotFoundException e) {
e.printStackTrace();
}
} else if (groupRoot.equals(fo)) {
return rootNode;
}
return null;
}
}
/** Copied from PhysicalView. */
private static final class VisibilityQueryDataFilter implements ChangeListener, ChangeableDataFilter {
private static final long serialVersionUID = 1L; // in case a DataFolder.ClonedFilterHandle saves me
private final EventListenerList ell = new EventListenerList();
public VisibilityQueryDataFilter() {
VisibilityQuery.getDefault().addChangeListener(this);
}
public boolean acceptDataObject(DataObject obj) {
FileObject fo = obj.getPrimaryFile();
return VisibilityQuery.getDefault().isVisible(fo);
}
public void stateChanged(ChangeEvent e) {
Object[] listeners = ell.getListenerList();
ChangeEvent event = null;
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == ChangeListener.class) {
if (event == null) {
event = new ChangeEvent(this);
}
((ChangeListener) listeners[i+1]).stateChanged(event);
}
}
}
public void addChangeListener(ChangeListener listener) {
ell.add(ChangeListener.class, listener);
}
public void removeChangeListener(ChangeListener listener) {
ell.remove(ChangeListener.class, listener);
}
}
private static final class PackageFilterChildren extends FilterNode.Children {
public PackageFilterChildren (final Node originalNode) {
super (originalNode);
}
@Override
protected Node copyNode(final Node originalNode) {
DataObject dobj = originalNode.getLookup().lookup(DataObject.class);
return (dobj instanceof DataFolder)
? new FilterNode(originalNode, new PackageFilterChildren(originalNode))
: super.copyNode(originalNode);
}
}
}