| /* |
| * Copyright (c) 2013, the Dart project authors. |
| * |
| * Licensed under the Eclipse Public License v1.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.eclipse.org/legal/epl-v10.html |
| * |
| * 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 com.google.dart.engine.services.util; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.Lists; |
| import com.google.dart.engine.ast.AstNode; |
| import com.google.dart.engine.ast.visitor.GeneralizingAstVisitor; |
| import com.google.dart.engine.utilities.source.SourceRange; |
| |
| import static com.google.dart.engine.utilities.source.SourceRangeFactory.rangeNode; |
| import static com.google.dart.engine.utilities.source.SourceRangeFactory.rangeStartEnd; |
| |
| import java.util.List; |
| |
| /** |
| * Abstract visitor for visiting {@link AstNode}s covered by the selection {@link SourceRange}. |
| */ |
| public class SelectionAnalyzer extends GeneralizingAstVisitor<Void> { |
| protected final SourceRange selection; |
| private AstNode coveringNode; |
| private List<AstNode> selectedNodes; |
| |
| public SelectionAnalyzer(SourceRange selection) { |
| assert selection != null; |
| this.selection = selection; |
| } |
| |
| /** |
| * @return the {@link AstNode} with the shortest length which completely covers the specified |
| * selection. |
| */ |
| public AstNode getCoveringNode() { |
| return coveringNode; |
| } |
| |
| /** |
| * @return the first selected {@link AstNode}, may be <code>null</code>. |
| */ |
| public AstNode getFirstSelectedNode() { |
| if (selectedNodes == null || selectedNodes.isEmpty()) { |
| return null; |
| } |
| return selectedNodes.get(0); |
| } |
| |
| /** |
| * @return the last selected {@link AstNode}, may be <code>null</code>. |
| */ |
| public AstNode getLastSelectedNode() { |
| if (selectedNodes == null || selectedNodes.isEmpty()) { |
| return null; |
| } |
| return selectedNodes.get(selectedNodes.size() - 1); |
| } |
| |
| /** |
| * @return the {@link SourceRange} which covers selected {@link AstNode}s, may be |
| * <code>null</code> if no {@link AstNode}s under selection. |
| */ |
| public SourceRange getSelectedNodeRange() { |
| if (selectedNodes == null || selectedNodes.isEmpty()) { |
| return null; |
| } |
| AstNode firstNode = selectedNodes.get(0); |
| AstNode lastNode = selectedNodes.get(selectedNodes.size() - 1); |
| return rangeStartEnd(firstNode, lastNode); |
| } |
| |
| /** |
| * @return the {@link AstNode}s fully covered by the selection {@link SourceRange}. |
| */ |
| public List<AstNode> getSelectedNodes() { |
| if (selectedNodes == null || selectedNodes.isEmpty()) { |
| return ImmutableList.of(); |
| } |
| return selectedNodes; |
| } |
| |
| /** |
| * @return <code>true</code> if there are {@link AstNode} fully covered by the selection |
| * {@link SourceRange}. |
| */ |
| public boolean hasSelectedNodes() { |
| return selectedNodes != null && !selectedNodes.isEmpty(); |
| } |
| |
| @Override |
| public Void visitNode(AstNode node) { |
| SourceRange nodeRange = rangeNode(node); |
| if (selection.covers(nodeRange)) { |
| if (isFirstNode()) { |
| handleFirstSelectedNode(node); |
| } else { |
| handleNextSelectedNode(node); |
| } |
| return null; |
| } else if (selection.coveredBy(nodeRange)) { |
| coveringNode = node; |
| node.visitChildren(this); |
| return null; |
| } else if (selection.startsIn(nodeRange)) { |
| handleSelectionStartsIn(node); |
| node.visitChildren(this); |
| return null; |
| } else if (selection.endsIn(nodeRange)) { |
| handleSelectionEndsIn(node); |
| node.visitChildren(this); |
| return null; |
| } |
| // no intersection |
| return null; |
| } |
| |
| /** |
| * Adds first selected {@link AstNode}. |
| */ |
| protected void handleFirstSelectedNode(AstNode node) { |
| selectedNodes = Lists.newArrayList(); |
| selectedNodes.add(node); |
| } |
| |
| /** |
| * Adds second or more selected {@link AstNode}. |
| */ |
| protected void handleNextSelectedNode(AstNode node) { |
| if (getFirstSelectedNode().getParent() == node.getParent()) { |
| selectedNodes.add(node); |
| } |
| } |
| |
| /** |
| * Notifies that selection ends in given {@link AstNode}. |
| */ |
| protected void handleSelectionEndsIn(AstNode node) { |
| } |
| |
| /** |
| * Notifies that selection starts in given {@link AstNode}. |
| */ |
| protected void handleSelectionStartsIn(AstNode node) { |
| } |
| |
| /** |
| * Resets selected nodes. |
| */ |
| protected void reset() { |
| selectedNodes = null; |
| } |
| |
| /** |
| * @return <code>true</code> if there was no selected nodes yet. |
| */ |
| private boolean isFirstNode() { |
| return selectedNodes == null; |
| } |
| } |