001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.corext.dom.fragments;
011:
012: import org.eclipse.core.runtime.Assert;
013:
014: import org.eclipse.jdt.core.ICompilationUnit;
015: import org.eclipse.jdt.core.JavaModelException;
016: import org.eclipse.jdt.core.dom.ASTNode;
017: import org.eclipse.jdt.core.dom.Expression;
018: import org.eclipse.jdt.core.dom.InfixExpression;
019:
020: import org.eclipse.jdt.internal.corext.SourceRange;
021: import org.eclipse.jdt.internal.corext.dom.HierarchicalASTVisitor;
022: import org.eclipse.jdt.internal.corext.dom.Selection;
023: import org.eclipse.jdt.internal.corext.dom.SelectionAnalyzer;
024:
025: /**
026: * Creates various differing kinds of IASTFragments, all through
027: * a very narrow interface. The kind of IASTFragment produced will depend
028: * on properties of the parameters supplied to the factory methods, such
029: * as the types and characteristics of AST nodes, or the location of
030: * source ranges.
031: *
032: * In general, the client will not be aware of exactly what kind of
033: * fragment is obtained from these methods. Beyond the functionality
034: * provided by the IASTFragment interface, the client can know, however,
035: * based on the parameters passed, some things about the created fragment.
036: * See the documentation of the factory methods.
037: *
038: * @see IASTFragment
039: *
040: */
041: public class ASTFragmentFactory {
042:
043: // Factory Methods: /////////////////////////////////////////////////////////////////////////
044:
045: /**
046: * Creates and returns a fragment representing the entire subtree
047: * rooted at <code>node</code>. It is not true in general that
048: * the node to which the produced IASTFragment maps (see {@link org.eclipse.jdt.internal.corext.dom.fragments.IASTFragment IASTFragment})
049: * will be <code>node</code>.
050: *
051: * XXX: more doc (current assertions about input vs. output)
052: */
053: public static IASTFragment createFragmentForFullSubtree(ASTNode node) {
054: IASTFragment result = FragmentForFullSubtreeFactory
055: .createFragmentFor(node);
056: Assert.isNotNull(result);
057: return result;
058: }
059:
060: /**
061: * If possible, this method creates a fragment whose source code
062: * range is <code>range</code> within compilation unit <code>cu</code>,
063: * and which resides somewhere within the subtree identified by
064: * <code>scope</code>.
065: *
066: * XXX: more doc (current assertions about input vs. output)
067: *
068: * @param range The source range which the create fragment must have.
069: * @param scope A node identifying the AST subtree in which the fragment must lie.
070: * @param cu The compilation unit to which the source range applies, and to which the AST corresponds.
071: * @return IASTFragment A fragment whose source range is <code>range</code> within
072: * compilation unit <code>cu</code>, residing somewhere within the
073: * AST subtree identified by <code>scope</code>.
074: * @throws JavaModelException
075: */
076: public static IASTFragment createFragmentForSourceRange(
077: SourceRange range, ASTNode scope, ICompilationUnit cu)
078: throws JavaModelException {
079: SelectionAnalyzer sa = new SelectionAnalyzer(Selection
080: .createFromStartLength(range.getOffset(), range
081: .getLength()), false);
082: scope.accept(sa);
083:
084: if (isSingleNodeSelected(sa, range, cu))
085: return ASTFragmentFactory.createFragmentForFullSubtree(sa
086: .getFirstSelectedNode());
087: if (isEmptySelectionCoveredByANode(range, sa))
088: return ASTFragmentFactory.createFragmentForFullSubtree(sa
089: .getLastCoveringNode());
090: return ASTFragmentFactory
091: .createFragmentForSubPartBySourceRange(sa
092: .getLastCoveringNode(), range, cu);
093: }
094:
095: /////////////////////////////////////////////////////////////////////////////////////////////////////
096:
097: private static boolean isEmptySelectionCoveredByANode(
098: SourceRange range, SelectionAnalyzer sa) {
099: return range.getLength() == 0
100: && sa.getFirstSelectedNode() == null
101: && sa.getLastCoveringNode() != null;
102: }
103:
104: private static boolean isSingleNodeSelected(SelectionAnalyzer sa,
105: SourceRange range, ICompilationUnit cu)
106: throws JavaModelException {
107: return sa.getSelectedNodes().length == 1
108: && !rangeIncludesNonWhitespaceOutsideNode(range, sa
109: .getFirstSelectedNode(), cu);
110: }
111:
112: private static boolean rangeIncludesNonWhitespaceOutsideNode(
113: SourceRange range, ASTNode node, ICompilationUnit cu)
114: throws JavaModelException {
115: return Util.rangeIncludesNonWhitespaceOutsideRange(range,
116: new SourceRange(node), cu.getBuffer());
117: }
118:
119: /**
120: * Returns <code>null</code> if the indices, taken with respect to
121: * the node, do not correspond to a valid node-sub-part
122: * fragment.
123: */
124: private static IASTFragment createFragmentForSubPartBySourceRange(
125: ASTNode node, SourceRange range, ICompilationUnit cu)
126: throws JavaModelException {
127: return FragmentForSubPartBySourceRangeFactory
128: .createFragmentFor(node, range, cu);
129: }
130:
131: private static class FragmentForFullSubtreeFactory extends
132: FragmentFactory {
133: public static IASTFragment createFragmentFor(ASTNode node) {
134: return new FragmentForFullSubtreeFactory()
135: .createFragment(node);
136: }
137:
138: public boolean visit(InfixExpression node) {
139: /* Try creating an associative infix expression fragment
140: /* for the full subtree. If this is not applicable,
141: * try something more generic.
142: */
143: IASTFragment fragment = AssociativeInfixExpressionFragment
144: .createFragmentForFullSubtree(node);
145: if (fragment == null)
146: return visit((Expression) node);
147:
148: setFragment(fragment);
149: return false;
150: }
151:
152: public boolean visit(Expression node) {
153: setFragment(new SimpleExpressionFragment(node));
154: return false;
155: }
156:
157: public boolean visit(ASTNode node) {
158: setFragment(new SimpleFragment(node));
159: return false;
160: }
161: }
162:
163: private static class FragmentForSubPartBySourceRangeFactory extends
164: FragmentFactory {
165: private SourceRange fRange;
166: private ICompilationUnit fCu;
167:
168: private JavaModelException javaModelException = null;
169:
170: public static IASTFragment createFragmentFor(ASTNode node,
171: SourceRange range, ICompilationUnit cu)
172: throws JavaModelException {
173: return new FragmentForSubPartBySourceRangeFactory()
174: .createFragment(node, range, cu);
175: }
176:
177: public boolean visit(InfixExpression node) {
178: try {
179: setFragment(createInfixExpressionSubPartFragmentBySourceRange(
180: node, fRange, fCu));
181: } catch (JavaModelException e) {
182: javaModelException = e;
183: }
184: return false;
185: }
186:
187: public boolean visit(ASTNode node) {
188: //let fragment be null
189: return false;
190: }
191:
192: protected IASTFragment createFragment(ASTNode node,
193: SourceRange range, ICompilationUnit cu)
194: throws JavaModelException {
195: fRange = range;
196: fCu = cu;
197: IASTFragment result = createFragment(node);
198: if (javaModelException != null)
199: throw javaModelException;
200: return result;
201: }
202:
203: private static IExpressionFragment createInfixExpressionSubPartFragmentBySourceRange(
204: InfixExpression node, SourceRange range,
205: ICompilationUnit cu) throws JavaModelException {
206: return AssociativeInfixExpressionFragment
207: .createSubPartFragmentBySourceRange(node, range, cu);
208: }
209: }
210:
211: private static abstract class FragmentFactory extends
212: HierarchicalASTVisitor {
213: private IASTFragment fFragment;
214:
215: protected IASTFragment createFragment(ASTNode node) {
216: fFragment = null;
217: node.accept(this );
218: return fFragment;
219: }
220:
221: protected final IASTFragment getFragment() {
222: return fFragment;
223: }
224:
225: protected final void setFragment(IASTFragment fragment) {
226: Assert.isTrue(!isFragmentSet());
227: fFragment = fragment;
228: }
229:
230: protected final void clearFragment() {
231: fFragment = null;
232: }
233:
234: protected final boolean isFragmentSet() {
235: return getFragment() != null;
236: }
237: }
238: }
|