/*******************************************************************************
* 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;
}
}