| /******************************************************************************* |
| * Copyright (c) 2000, 2006 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.corext.dom.fragments; |
| |
| import org.eclipse.core.runtime.Assert; |
| |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.dom.ASTNode; |
| import org.eclipse.jdt.core.dom.Expression; |
| import org.eclipse.jdt.core.dom.InfixExpression; |
| |
| import org.eclipse.jdt.internal.corext.SourceRange; |
| import org.eclipse.jdt.internal.corext.dom.HierarchicalASTVisitor; |
| import org.eclipse.jdt.internal.corext.dom.Selection; |
| import org.eclipse.jdt.internal.corext.dom.SelectionAnalyzer; |
| |
| /** |
| * Creates various differing kinds of IASTFragments, all through |
| * a very narrow interface. The kind of IASTFragment produced will depend |
| * on properties of the parameters supplied to the factory methods, such |
| * as the types and characteristics of AST nodes, or the location of |
| * source ranges. |
| * |
| * In general, the client will not be aware of exactly what kind of |
| * fragment is obtained from these methods. Beyond the functionality |
| * provided by the IASTFragment interface, the client can know, however, |
| * based on the parameters passed, some things about the created fragment. |
| * See the documentation of the factory methods. |
| * |
| * @see IASTFragment |
| * |
| */ |
| public class ASTFragmentFactory { |
| |
| // Factory Methods: ///////////////////////////////////////////////////////////////////////// |
| |
| /** |
| * Creates and returns a fragment representing the entire subtree |
| * rooted at <code>node</code>. It is not true in general that |
| * the node to which the produced IASTFragment maps (see {@link org.eclipse.jdt.internal.corext.dom.fragments.IASTFragment IASTFragment}) |
| * will be <code>node</code>. |
| * |
| * XXX: more doc (current assertions about input vs. output) |
| */ |
| public static IASTFragment createFragmentForFullSubtree(ASTNode node) { |
| IASTFragment result= FragmentForFullSubtreeFactory.createFragmentFor(node); |
| Assert.isNotNull(result); |
| return result; |
| } |
| |
| /** |
| * If possible, this method creates a fragment whose source code |
| * range is <code>range</code> within compilation unit <code>cu</code>, |
| * and which resides somewhere within the subtree identified by |
| * <code>scope</code>. |
| * |
| * XXX: more doc (current assertions about input vs. output) |
| * |
| * @param range The source range which the create fragment must have. |
| * @param scope A node identifying the AST subtree in which the fragment must lie. |
| * @param cu The compilation unit to which the source range applies, and to which the AST corresponds. |
| * @return IASTFragment A fragment whose source range is <code>range</code> within |
| * compilation unit <code>cu</code>, residing somewhere within the |
| * AST subtree identified by <code>scope</code>. |
| * @throws JavaModelException |
| */ |
| public static IASTFragment createFragmentForSourceRange(SourceRange range, ASTNode scope, ICompilationUnit cu) throws JavaModelException { |
| SelectionAnalyzer sa= new SelectionAnalyzer(Selection.createFromStartLength(range.getOffset(), range.getLength()), false); |
| scope.accept(sa); |
| |
| if (isSingleNodeSelected(sa, range, cu)) |
| return ASTFragmentFactory.createFragmentForFullSubtree(sa.getFirstSelectedNode()); |
| if (isEmptySelectionCoveredByANode(range, sa)) |
| return ASTFragmentFactory.createFragmentForFullSubtree(sa.getLastCoveringNode()); |
| return ASTFragmentFactory.createFragmentForSubPartBySourceRange(sa.getLastCoveringNode(), range, cu); |
| } |
| |
| ///////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
| |
| private static boolean isEmptySelectionCoveredByANode(SourceRange range, SelectionAnalyzer sa) { |
| return range.getLength() == 0 && sa.getFirstSelectedNode() == null && sa.getLastCoveringNode() != null; |
| } |
| |
| private static boolean isSingleNodeSelected(SelectionAnalyzer sa, SourceRange range, ICompilationUnit cu) throws JavaModelException { |
| return sa.getSelectedNodes().length == 1 && !rangeIncludesNonWhitespaceOutsideNode(range, sa.getFirstSelectedNode(), cu); |
| } |
| private static boolean rangeIncludesNonWhitespaceOutsideNode(SourceRange range, ASTNode node, ICompilationUnit cu) throws JavaModelException { |
| return Util.rangeIncludesNonWhitespaceOutsideRange(range, new SourceRange(node), cu.getBuffer()); |
| } |
| |
| |
| /** |
| * Returns <code>null</code> if the indices, taken with respect to |
| * the node, do not correspond to a valid node-sub-part |
| * fragment. |
| */ |
| private static IASTFragment createFragmentForSubPartBySourceRange(ASTNode node, SourceRange range, ICompilationUnit cu) throws JavaModelException { |
| return FragmentForSubPartBySourceRangeFactory.createFragmentFor(node, range, cu); |
| } |
| |
| private static class FragmentForFullSubtreeFactory extends FragmentFactory { |
| public static IASTFragment createFragmentFor(ASTNode node) { |
| return new FragmentForFullSubtreeFactory().createFragment(node); |
| } |
| |
| public boolean visit(InfixExpression node) { |
| /* Try creating an associative infix expression fragment |
| /* for the full subtree. If this is not applicable, |
| * try something more generic. |
| */ |
| IASTFragment fragment= AssociativeInfixExpressionFragment.createFragmentForFullSubtree(node); |
| if(fragment == null) |
| return visit((Expression) node); |
| |
| setFragment(fragment); |
| return false; |
| } |
| public boolean visit(Expression node) { |
| setFragment(new SimpleExpressionFragment(node)); |
| return false; |
| } |
| public boolean visit(ASTNode node) { |
| setFragment(new SimpleFragment(node)); |
| return false; |
| } |
| } |
| private static class FragmentForSubPartBySourceRangeFactory extends FragmentFactory { |
| private SourceRange fRange; |
| private ICompilationUnit fCu; |
| |
| private JavaModelException javaModelException= null; |
| |
| public static IASTFragment createFragmentFor(ASTNode node, SourceRange range, ICompilationUnit cu) throws JavaModelException { |
| return new FragmentForSubPartBySourceRangeFactory().createFragment(node, range, cu); |
| } |
| |
| public boolean visit(InfixExpression node) { |
| try { |
| setFragment(createInfixExpressionSubPartFragmentBySourceRange(node, fRange, fCu)); |
| } catch(JavaModelException e) { |
| javaModelException= e; |
| } |
| return false; |
| } |
| |
| public boolean visit(ASTNode node) { |
| //let fragment be null |
| return false; |
| } |
| |
| protected IASTFragment createFragment(ASTNode node, SourceRange range, ICompilationUnit cu) throws JavaModelException { |
| fRange= range; |
| fCu= cu; |
| IASTFragment result= createFragment(node); |
| if(javaModelException != null) |
| throw javaModelException; |
| return result; |
| } |
| |
| private static IExpressionFragment createInfixExpressionSubPartFragmentBySourceRange(InfixExpression node, SourceRange range, ICompilationUnit cu) throws JavaModelException { |
| return AssociativeInfixExpressionFragment.createSubPartFragmentBySourceRange(node, range, cu); |
| } |
| } |
| private static abstract class FragmentFactory extends HierarchicalASTVisitor { |
| private IASTFragment fFragment; |
| |
| protected IASTFragment createFragment(ASTNode node) { |
| fFragment= null; |
| node.accept(this); |
| return fFragment; |
| } |
| |
| protected final IASTFragment getFragment() { |
| return fFragment; |
| } |
| protected final void setFragment(IASTFragment fragment) { |
| Assert.isTrue(!isFragmentSet()); |
| fFragment= fragment; |
| } |
| protected final void clearFragment() { |
| fFragment= null; |
| } |
| protected final boolean isFragmentSet() { |
| return getFragment() != null; |
| } |
| } |
| } |