content: /*******************************************************************************
   * Copyright (c) 2006, 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.refactoring.binary;
  
  import java.util.ArrayList;
  import java.util.List;
  
  import org.eclipse.core.runtime.Assert;
  import org.eclipse.core.runtime.IProgressMonitor;
  import org.eclipse.core.runtime.NullProgressMonitor;
  import org.eclipse.core.runtime.SubProgressMonitor;
  import org.eclipse.wst.jsdt.core.Flags;
  import org.eclipse.wst.jsdt.core.IField;
  import org.eclipse.wst.jsdt.core.IFunction;
  import org.eclipse.wst.jsdt.core.IJavaScriptElement;
  import org.eclipse.wst.jsdt.core.IMember;
  import org.eclipse.wst.jsdt.core.IType;
  import org.eclipse.wst.jsdt.core.JavaScriptModelException;
  import org.eclipse.wst.jsdt.core.Signature;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.Checks;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.RefactoringCoreMessages;
  
  public class StubCreator {
  
  	/** The internal string buffer */
  	protected StringBuffer fBuffer;
  
  	/** Should stubs for private member be generated as well? */
  	protected final boolean fStubInvisible;
  
  	public StubCreator(final boolean stubInvisible) {
  		fStubInvisible= stubInvisible;
  	}
  
  	protected void appendEnumConstants(final IType type) throws JavaScriptModelException {
  		final IField[] fields= type.getFields();
  		final List list= new ArrayList(fields.length);
  		
  		for (int index= 0; index < list.size(); index++) {
  			if (index > 0)
  				fBuffer.append(","); //$NON-NLS-1$
  			fBuffer.append(((IField) list.get(index)).getElementName());
  		}
  		fBuffer.append(";"); //$NON-NLS-1$
  	}
  
  	protected void appendExpression(final String signature) {
  		fBuffer.append("("); //$NON-NLS-1$
  		fBuffer.append(Signature.toString(signature));
  		fBuffer.append(")"); //$NON-NLS-1$
  		fBuffer.append("null"); //$NON-NLS-1$
  	}
  
  	protected void appendFieldDeclaration(final IField field) throws JavaScriptModelException {
  		appendFlags(field);
  		fBuffer.append(" "); //$NON-NLS-1$
  		final String signature= field.getTypeSignature();
  		fBuffer.append(Signature.toString(signature));
  		fBuffer.append(" "); //$NON-NLS-1$
  		fBuffer.append(field.getElementName());
  		fBuffer.append(";"); //$NON-NLS-1$
  	}
  
  	protected void appendFlags(final IMember member) throws JavaScriptModelException {
  		int flags= member.getFlags();
  		final int kind= member.getElementType();
  		if (kind == IJavaScriptElement.TYPE) {
  			flags&= ~Flags.AccSuper;
  			final IType type= (IType) member;
  			if (!type.isMember())
  				flags&= ~Flags.AccPrivate;
  		}
  		if (kind == IJavaScriptElement.METHOD) {
  			flags&= ~Flags.AccVarargs;
  		}
  		if (flags != 0)
  			fBuffer.append(Flags.toString(flags));
  	}
  
  	protected void appendMembers(final IType type, final IProgressMonitor monitor) throws JavaScriptModelException {
  		try {
  			monitor.beginTask(RefactoringCoreMessages.StubCreationOperation_creating_type_stubs, 1);
  			final IJavaScriptElement[] children= type.getChildren();
  			for (int index= 0; index < children.length; index++) {
  				final IMember child= (IMember) children[index];
  				final int flags= child.getFlags();
  				final boolean isPrivate= Flags.isPrivate(flags);
  				final boolean isDefault= !Flags.isPublic(flags) && !isPrivate;
  				final boolean stub= fStubInvisible || (!isPrivate && !isDefault);
  				if (child instanceof IType) {
  					if (stub)
  						appendTypeDeclaration((IType) child, new SubProgressMonitor(monitor, 1));
  				} else if (child instanceof IField) {
  					if (stub)
  						appendFieldDeclaration((IField) child);
  				} else if (child instanceof IFunction) {
  					final IFunction method= (IFunction) child;
  					final String name= method.getElementName();
  					
  					boolean skip= !stub || name.equals("<clinit>"); //$NON-NLS-1$
  					if (method.isConstructor())
  						skip= false;
  					if (!skip)
  						appendMethodDeclaration(method);
  				}
  				fBuffer.append("\n"); //$NON-NLS-1$
  			}
  		} finally {
  			monitor.done();
  		}
  	}
  
  	protected void appendMethodBody(final IFunction method) throws JavaScriptModelException {
  		if (method.isConstructor()) {
  			final IType declaringType= method.getDeclaringType();
  			String superSignature= declaringType.getSuperclassTypeSignature();
  			if (superSignature != null) {
  				final IType superclass= declaringType.getJavaScriptProject().findType(Signature.getSignatureQualifier(superSignature), Signature.getSignatureSimpleName(superSignature));
  				if (superclass != null) {
  					final IFunction[] superMethods= superclass.getFunctions();
  					IFunction superConstructor= null;
  					final int length= superMethods.length;
  					for (int index= 0; index < length; index++) {
  						final IFunction superMethod= superMethods[index];
  						if (superMethod.isConstructor() && !Flags.isPrivate(superMethod.getFlags())) {
  							superConstructor= superMethod;
  							break;
  						}
  					}
  					if (superConstructor != null) {
  						final String[] superParameters= superConstructor.getParameterTypes();
  						final int paramLength= superParameters.length;
  						if (paramLength != 0) {
  							fBuffer.append("super("); //$NON-NLS-1$
  							for (int index= 0; index < paramLength; index++) {
  								if (index > 0)
  									fBuffer.append(","); //$NON-NLS-1$
  								appendExpression(superParameters[index]);
  							}
  							fBuffer.append(");"); //$NON-NLS-1$
  						}
  					}
  				}
  			}
  		} else {
  			String returnType= method.getReturnType();
  			if (!Signature.SIG_VOID.equals(returnType)) {
  				fBuffer.append("return "); //$NON-NLS-1$
  				appendExpression(returnType);
  				fBuffer.append(";"); //$NON-NLS-1$
  			}
  		}
  	}
  
  	protected void appendMethodDeclaration(final IFunction method) throws JavaScriptModelException {
  		appendFlags(method);
  		fBuffer.append(" "); //$NON-NLS-1$
  		final String returnType= method.getReturnType();
  		if (!method.isConstructor()) {
  			fBuffer.append(Signature.toString(returnType));
  			fBuffer.append(" "); //$NON-NLS-1$
  		}
  		fBuffer.append(method.getElementName());
  		fBuffer.append("("); //$NON-NLS-1$
  		final String[] parameterTypes= method.getParameterTypes();
  		final int flags= method.getFlags();
  		final boolean varargs= Flags.isVarargs(flags);
  		final int parameterLength= parameterTypes.length;
  		for (int index= 0; index < parameterLength; index++) {
  			if (index > 0)
  				fBuffer.append(","); //$NON-NLS-1$
  			fBuffer.append(Signature.toString(parameterTypes[index]));
  			if (varargs && index == parameterLength - 1) {
  				final int length= fBuffer.length();
  				if (length >= 2 && fBuffer.indexOf("[]", length - 2) >= 0) //$NON-NLS-1$
  					fBuffer.setLength(length - 2);
  				fBuffer.append("..."); //$NON-NLS-1$
  			}
  			fBuffer.append(" "); //$NON-NLS-1$
  			appendMethodParameterName(method, index);
  		}
  		fBuffer.append(")"); //$NON-NLS-1$
  		if (Flags.isAbstract(flags))
  			fBuffer.append(";"); //$NON-NLS-1$
  		else {
  			fBuffer.append("{\n"); //$NON-NLS-1$
  			appendMethodBody(method);
  			fBuffer.append("}"); //$NON-NLS-1$
  		}
  	}
  
  	protected void appendMethodParameterName(IFunction method, int index) {
  		fBuffer.append("a"); //$NON-NLS-1$
  		fBuffer.append(index);
  	}
  
  	protected void appendTopLevelType(final IType type, IProgressMonitor subProgressMonitor) throws JavaScriptModelException {
  		String packageName= type.getPackageFragment().getElementName();
  		if (packageName.length() > 0) {
  			fBuffer.append("package "); //$NON-NLS-1$
  			fBuffer.append(packageName);
  			fBuffer.append(";\n"); //$NON-NLS-1$
  		}
  		appendTypeDeclaration(type, subProgressMonitor);
  	}
  
  	protected void appendTypeDeclaration(final IType type, final IProgressMonitor monitor) throws JavaScriptModelException {
  		try {
  			monitor.beginTask(RefactoringCoreMessages.StubCreationOperation_creating_type_stubs, 1);
  			if (type.isClass()) {
  				appendFlags(type);
  				fBuffer.append(" class "); //$NON-NLS-1$
  				fBuffer.append(type.getElementName());
  				final String signature= type.getSuperclassTypeSignature();
  				if (signature != null) {
  					fBuffer.append(" extends "); //$NON-NLS-1$
  					fBuffer.append(Signature.toString(signature));
  				}
  				fBuffer.append("{\n"); //$NON-NLS-1$
  				appendMembers(type, new SubProgressMonitor(monitor, 1));
  				fBuffer.append("}"); //$NON-NLS-1$
  			}
  		} finally {
  			monitor.done();
  		}
  	}
  
  	/**
  	 * @param topLevelType
  	 * @param monitor
  	 *            progress monitor, can be <code>null</code>
  	 * @return source stub
  	 * @throws JavaScriptModelException
  	 */
  	public String createStub(IType topLevelType, IProgressMonitor monitor) throws JavaScriptModelException {
  		Assert.isTrue(Checks.isTopLevel(topLevelType));
  		if (monitor == null)
  			monitor= new NullProgressMonitor();
  
  		fBuffer= new StringBuffer(2046);
  		appendTopLevelType(topLevelType, monitor);
  		String result= fBuffer.toString();
  		fBuffer= null;
  		return result;
  	}
  
  }
  