content: /*******************************************************************************
   * Copyright (c) 2000, 2009 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.wst.jsdt.internal.corext.dom.fragments;
  
  import org.eclipse.core.runtime.Assert;
  import org.eclipse.wst.jsdt.core.IJavaScriptUnit;
  import org.eclipse.wst.jsdt.core.JavaScriptModelException;
  import org.eclipse.wst.jsdt.core.dom.ASTNode;
  import org.eclipse.wst.jsdt.core.dom.Expression;
  import org.eclipse.wst.jsdt.core.dom.InfixExpression;
  import org.eclipse.wst.jsdt.internal.corext.SourceRange;
  import org.eclipse.wst.jsdt.internal.corext.dom.HierarchicalASTVisitor;
  import org.eclipse.wst.jsdt.internal.corext.dom.Selection;
  import org.eclipse.wst.jsdt.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.wst.jsdt.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 JavaScriptModelException
  	 */
  	public static IASTFragment createFragmentForSourceRange(SourceRange range, ASTNode scope, IJavaScriptUnit cu) throws JavaScriptModelException {
  		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, IJavaScriptUnit cu) throws JavaScriptModelException {
  		return sa.getSelectedNodes().length == 1 && !rangeIncludesNonWhitespaceOutsideNode(range, sa.getFirstSelectedNode(), cu);
  	}
  	private static boolean rangeIncludesNonWhitespaceOutsideNode(SourceRange range, ASTNode node, IJavaScriptUnit cu) throws JavaScriptModelException {
  		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, IJavaScriptUnit cu) throws JavaScriptModelException {
  		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 IJavaScriptUnit fCu;
  		
  		private JavaScriptModelException javaModelException= null;
  		
  		public static IASTFragment createFragmentFor(ASTNode node, SourceRange range, IJavaScriptUnit cu) throws JavaScriptModelException {
  			return new FragmentForSubPartBySourceRangeFactory().createFragment(node, range, cu);	
  		}
  		
  		public boolean visit(InfixExpression node) {
  			try {
  				setFragment(createInfixExpressionSubPartFragmentBySourceRange(node, fRange, fCu));
  			} catch(JavaScriptModelException e) {
  				javaModelException= e;
  			}
  			return false;	
  		}
  		
  		public boolean visit(ASTNode node) {
  			//let fragment be null
  			return false;			
  		}
  		
  		protected IASTFragment createFragment(ASTNode node, SourceRange range, IJavaScriptUnit cu) throws JavaScriptModelException {
  			fRange= range;
  			fCu= cu;
  			IASTFragment result= createFragment(node);
  			if(javaModelException != null)
  				throw javaModelException;
  			return result;	
  		}
  		
  		private static IExpressionFragment createInfixExpressionSubPartFragmentBySourceRange(InfixExpression node, SourceRange range, IJavaScriptUnit cu) throws JavaScriptModelException {
  			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 boolean isFragmentSet() {
  			return getFragment() != null;	
  		}
  	}
  }
  