All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.netbeans.modules.csl.navigation.ElementNode Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.netbeans.modules.csl.navigation;


import java.awt.Image;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.text.Document;
import org.netbeans.editor.BaseDocument;
import org.netbeans.modules.csl.api.ElementHandle;
import org.netbeans.modules.csl.api.ElementKind;
import org.netbeans.modules.csl.api.Modifier;
import org.netbeans.modules.csl.api.StructureItem;
import org.netbeans.modules.csl.core.GsfHtmlFormatter;
import org.netbeans.modules.csl.core.Language;
import org.netbeans.modules.csl.core.LanguageRegistry;
import org.netbeans.modules.csl.navigation.actions.OpenAction;
import org.netbeans.modules.csl.spi.ParserResult;
import org.openide.filesystems.FileObject;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.util.ImageUtilities;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;

/** 
 * This file is originally from Retouche, the Java Support 
 * infrastructure in NetBeans. I have modified the file as little
 * as possible to make merging Retouche fixes back as simple as
 * possible. 
 * 

* GSF changes made: Instead of accessing fields on Description object, * replace references to Description with StructureItem interface (descriptions * supplied by language plugins), make method calls on this interface rather * than accessing fields directly. Some data such as the "ui" field was moved * into ElementNode itself rather than sitting on the description object which * is no longer under our control. *

* Node representing an element * * * @author Petr Hrebejk */ public class ElementNode extends AbstractNode { private static Node WAIT_NODE; private OpenAction openAction; private StructureItem description; private ClassMemberPanelUI ui; private final FileObject fileObject; // For the root description /** Creates a new instance of TreeNode */ public ElementNode( StructureItem description, ClassMemberPanelUI ui, FileObject fileObject) { super(description.isLeaf() ? Children.LEAF: new ElementChildren(description, ui, fileObject), Lookups.fixed(fileObject)); this.description = description; setDisplayName( description.getName() ); this.ui = ui; this.fileObject = fileObject; } @Override public Image getIcon(int type) { if (description.getCustomIcon() != null) { return ImageUtilities.icon2Image(description.getCustomIcon()); } Icon icon = Icons.getElementIcon(description.getKind(), description.getModifiers()); if (icon != null) { return ImageUtilities.icon2Image(icon); } else { return super.getIcon(type); } } @Override public Image getOpenedIcon(int type) { return getIcon(type); } @Override public java.lang.String getDisplayName() { if (description.getName() == null) { return fileObject.getNameExt(); } else { return description.getName(); } } @Override public String getHtmlDisplayName() { return description.getHtml(new NavigatorFormatter()); } @Override public Action[] getActions( boolean context ) { if ( context || description.getName() == null ) { return ui.getActions(); } else { Action panelActions[] = ui.getActions(); Action actions[] = new Action[ 2 + panelActions.length ]; actions[0] = getOpenAction(); actions[1] = null; for( int i = 0; i < panelActions.length; i++ ){ actions[2 + i] = panelActions[i]; } return actions; } } @Override public Action getPreferredAction() { return getOpenAction(); } private synchronized Action getOpenAction() { if ( openAction == null ) { FileObject fo = ui.getFileObject(); try { openAction = new OpenAction(description.getElementHandle(), fo, description.getPosition()); } catch (UnsupportedOperationException uo) { return null; // root node does not have element handle } } return openAction; } static synchronized Node getWaitNode() { if ( WAIT_NODE == null ) { WAIT_NODE = new WaitNode(); } return WAIT_NODE; } /** * Refreshes the Node recursively. Only initiates the refresh; the refresh * itself may happen asynchronously. */ public void refreshRecursively() { List toExpand = new ArrayList(); refreshRecursively(Collections.singleton(this), toExpand); ui.performExpansion(toExpand, Collections.emptyList()); } private void refreshRecursively(Collection toDo, final Collection toExpand) { for (ElementNode elnod : toDo) { final Children ch = elnod.getChildren(); if ( ch instanceof ElementChildren ) { ((ElementChildren)ch).resetKeys((List)elnod.description.getNestedItems(), elnod.ui.getFilters()); Collection children = (Collection)(List)Arrays.asList((Node[])ch.getNodes()); toExpand.addAll(children); refreshRecursively(children, toExpand); } } } public ElementNode getMimeRootNodeForOffset(ParserResult info, int offset) { if (getDescription().getPosition() > offset) { return null; } // Look up the current mime type Document document = info.getSnapshot().getSource().getDocument(false); if (document == null) { return null; } BaseDocument doc = (BaseDocument)document; return getMimeRootNodeForOffset(doc, offset); } ElementNode getMimeRootNodeForOffset(BaseDocument doc, int offset) { List languages = LanguageRegistry.getInstance().getEmbeddedLanguages(doc, offset); // Look specifically within the if (languages.size() > 0) { Children ch = getChildren(); if ( ch instanceof ElementChildren ) { Node[] children = ch.getNodes(); for (Language language : languages) { // Inefficient linear search because the children may not be // ordered according to the source for (int i = 0; i < children.length; i++) { ElementNode c = (ElementNode) children[i]; if (c.getDescription() instanceof ElementScanningTask.MimetypeRootNode) { ElementScanningTask.MimetypeRootNode mr = (ElementScanningTask.MimetypeRootNode)c.getDescription(); if (mr.language == language) { return c.getNodeForOffset(offset); } } } } } } // No match in embedded languages - do normal offset search return getNodeForOffset(offset); } public ElementNode getNodeForOffset(int offset) { if (getDescription().getPosition() > offset) { return null; } // Inefficient linear search because the children may not be // ordered according to the source Children ch = getChildren(); if ( ch instanceof ElementChildren ) { Node[] children = ch.getNodes(); for (int i = 0; i < children.length; i++) { ElementNode c = (ElementNode) children[i]; // The promise of the API is broken at several places in the // codebase and thus this needs to be guarded. The assert is in // place to find the violating places. assert c.getDescription().getElementHandle() != null; @SuppressWarnings("null") FileObject childFileObject = c.getDescription().getElementHandle() != null ? c.getDescription().getElementHandle().getFileObject() : null; if (! Objects.equals(this.getFileObject(), childFileObject)) { // e.g. inherited items may be in another file // in such a case, incorrect item is highlighted on the navigator window if the FileObjects are not checked continue; } long start = c.getDescription().getPosition(); if (start <= offset) { long end = c.getDescription().getEndPosition(); if (end >= offset) { return c.getNodeForOffset(offset); } } } } return this; } public void updateRecursively( StructureItem newDescription ) { List nodesToExpand = new LinkedList(); List nodesToExpandRec = new LinkedList(); updateRecursively(newDescription, nodesToExpand, nodesToExpandRec); ui.performExpansion(nodesToExpand, nodesToExpandRec); } private void updateRecursively( StructureItem newDescription, List nodesToExpand, List nodesToExpandRec ) { Children ch = getChildren(); //If a node that was a LEAF now has children the child type has to be changed from Children.LEAF //to ElementChildren to be able to hold the new child data if(!(ch instanceof ElementChildren) && newDescription.getNestedItems() != null && newDescription.getNestedItems().size()>0) { ch=new ElementChildren(ui, fileObject); setChildren(ch); } if ( ch instanceof ElementChildren ) { HashSet oldSubs = new HashSet( description.getNestedItems() ); // Create a hashtable which maps StructureItem to node. // We will then identify the nodes by the description. The trick is // that the new and old description are equal and have the same hashcode Node[] nodes = ch.getNodes( true ); HashMap oldD2node = new HashMap(); for (Node node : nodes) { oldD2node.put(((ElementNode)node).description, (ElementNode)node); } // Now refresh keys ((ElementChildren)ch).resetKeys((List)newDescription.getNestedItems(), ui.getFilters()); // Reread nodes nodes = ch.getNodes( true ); boolean alreadyExpanded = false; for( StructureItem newSub : newDescription.getNestedItems() ) { ElementNode node = oldD2node.get(newSub); if ( node != null ) { // filtered out if ( !oldSubs.contains(newSub)) { nodesToExpand.add(node); } node.updateRecursively( newSub, nodesToExpand, nodesToExpandRec ); // update the node recursively } else { // a new node if (! alreadyExpanded) { alreadyExpanded = true; if (ui.isExpandedByDefault(this)) { nodesToExpand.add(this); } } for (Node newNode : nodes) { if (newNode instanceof ElementNode && ((ElementNode) newNode).getDescription() == newSub) { nodesToExpandRec.add(newNode); break; } } } } } StructureItem oldDescription = description; // Remember old description description = newDescription; // set new descrioption to the new node String oldHtml = oldDescription.getHtml(new NavigatorFormatter()); String descHtml = description.getHtml(new NavigatorFormatter()); if ( oldHtml != null && !oldHtml.equals(descHtml)) { // Different headers => we need to fire displayname change fireDisplayNameChange(oldHtml, descHtml); } if( oldDescription.getModifiers() != null && !oldDescription.getModifiers().equals(newDescription.getModifiers())) { fireIconChange(); fireOpenedIconChange(); } } public StructureItem getDescription() { return description; } public FileObject getFileObject() { return fileObject; } private static final class ElementChildren extends Children.Keys { private ClassMemberPanelUI ui; private FileObject fileObject; private StructureItem parent; @Override protected void addNotify() { super.addNotify(); if (parent != null) { resetKeys((List)parent.getNestedItems(), ui.getFilters()); } } public ElementChildren(ClassMemberPanelUI ui, FileObject fileObject) { this.ui = ui; this.fileObject = fileObject; } public ElementChildren(StructureItem parent, ClassMemberPanelUI ui, FileObject fileObject) { this.parent = parent; this.ui = ui; this.fileObject = fileObject; } protected Node[] createNodes(StructureItem key) { return new Node[] {new ElementNode(key, ui, fileObject)}; } void resetKeys( List descriptions, ClassMemberFilters filters ) { setKeys( filters.filter(descriptions) ); } } /** Stores all interesting data about given element. */ static class Description { public static final Comparator ALPHA_COMPARATOR = new DescriptionComparator(true); public static final Comparator POSITION_COMPARATOR = new DescriptionComparator(false); ClassMemberPanelUI ui; //FileObject fileObject; // For the root description String name; ElementHandle elementHandle; ElementKind kind; Set modifiers; List subs; String htmlHeader; long pos; Description( ClassMemberPanelUI ui ) { this.ui = ui; } @Override public boolean equals(Object o) { if ( o == null ) { //System.out.println("- f nul"); return false; } if ( !(o instanceof Description)) { // System.out.println("- not a desc"); return false; } Description d = (Description)o; if ( kind != d.kind ) { // System.out.println("- kind"); return false; } // Findbugs warns about this field being uninitialized on the following line! if ( !name.equals(d.name) ) { // System.out.println("- name"); return false; } // if ( !this.elementHandle.signatureEquals(d.elementHandle) ) { // return false; // } /* if ( !modifiers.equals(d.modifiers)) { // E.println("- modifiers"); return false; } */ // System.out.println("Equals called"); return true; } @Override public int hashCode() { int hash = 7; hash = 29 * hash + (this.name != null ? this.name.hashCode() : 0); hash = 29 * hash + (this.kind != null ? this.kind.hashCode() : 0); // hash = 29 * hash + (this.modifiers != null ? this.modifiers.hashCode() : 0); return hash; } private static class DescriptionComparator implements Comparator { boolean alpha; DescriptionComparator( boolean alpha ) { this.alpha = alpha; } public int compare(StructureItem d1, StructureItem d2) { if ( alpha ) { if ( k2i(d1.getKind()) != k2i(d2.getKind()) ) { return k2i(d1.getKind()) - k2i(d2.getKind()); } return d1.getSortText().compareTo(d2.getSortText()); } else { return d1.getPosition() == d2.getPosition() ? 0 : d1.getPosition() < d2.getPosition() ? -1 : 1; } } int k2i( ElementKind kind ) { switch( kind ) { case CONSTRUCTOR: return 1; case METHOD: case DB: return 2; case FIELD: return 3; case CLASS: case INTERFACE: // case ENUM: // case ANNOTATION_TYPE: // return 4; // TODO - what about other types? default: return 100; } } } } private static class WaitNode extends AbstractNode { private Image waitIcon = ImageUtilities.loadImage("org/netbeans/modules/csl/navigation/resources/wait.gif"); // NOI18N private String displayName; WaitNode( ) { super( Children.LEAF ); displayName = NbBundle.getMessage(ElementNode.class, "LBL_WaitNode"); } @Override public Image getIcon(int type) { return waitIcon; } @Override public Image getOpenedIcon(int type) { return getIcon(type); } @java.lang.Override public java.lang.String getDisplayName() { return displayName; } } private static class NavigatorFormatter extends GsfHtmlFormatter { @Override public void name(ElementKind kind, boolean start) { // No special formatting for names } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy