blob: d3c3f32b5ef59d8a0e1ab089f9463c0381080bfc [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2011 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.refactoring.binary;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
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.jdt.core.Flags;
import org.eclipse.jdt.core.IAnnotatable;
import org.eclipse.jdt.core.IAnnotation;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IMemberValuePair;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeParameter;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.internal.corext.refactoring.Checks;
import org.eclipse.jdt.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 JavaModelException {
final IField[] fields= type.getFields();
final List<IField> list= new ArrayList<IField>(fields.length);
for (int index= 0; index < fields.length; index++) {
final IField field= fields[index];
if (Flags.isEnum(field.getFlags()))
list.add(field);
}
for (int index= 0; index < list.size(); index++) {
if (index > 0)
fBuffer.append(","); //$NON-NLS-1$
fBuffer.append(list.get(index).getElementName());
}
fBuffer.append(";"); //$NON-NLS-1$
}
protected void appendExpression(final String signature) {
appendExpression(signature, false);
}
protected void appendExpression(String signature, boolean castNull) {
switch (signature.charAt(0)) {
case Signature.C_BOOLEAN:
fBuffer.append("false"); //$NON-NLS-1$
break;
case Signature.C_BYTE:
case Signature.C_CHAR:
case Signature.C_DOUBLE:
case Signature.C_FLOAT:
case Signature.C_INT:
case Signature.C_LONG:
case Signature.C_SHORT:
fBuffer.append("0"); //$NON-NLS-1$
break;
default:
if (castNull) {
fBuffer.append("("); //$NON-NLS-1$
fBuffer.append(Signature.toString(signature));
fBuffer.append(")"); //$NON-NLS-1$
}
fBuffer.append("null"); //$NON-NLS-1$
break;
}
}
protected void appendFieldDeclaration(final IField field) throws JavaModelException {
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());
if (Flags.isFinal(field.getFlags())) {
fBuffer.append("="); //$NON-NLS-1$
appendExpression(signature);
}
fBuffer.append(";"); //$NON-NLS-1$
}
protected void appendFlags(final IMember member) throws JavaModelException {
if (member instanceof IAnnotatable)
for (IAnnotation annotation : ((IAnnotatable) member).getAnnotations()) {
appendAnnotation(annotation);
}
int flags= member.getFlags();
final int kind= member.getElementType();
if (kind == IJavaElement.TYPE) {
flags&= ~Flags.AccSuper;
final IType type= (IType) member;
if (!type.isMember())
flags&= ~Flags.AccPrivate;
if (Flags.isEnum(flags))
flags&= ~Flags.AccAbstract;
}
if (Flags.isEnum(flags))
flags&= ~Flags.AccFinal;
if (kind == IJavaElement.METHOD) {
flags&= ~Flags.AccVarargs;
flags&= ~Flags.AccBridge;
}
if (flags != 0)
fBuffer.append(Flags.toString(flags));
}
private void appendAnnotation(IAnnotation annotation) throws JavaModelException {
fBuffer.append('@');
fBuffer.append(annotation.getElementName());
fBuffer.append('(');
IMemberValuePair[] memberValuePairs= annotation.getMemberValuePairs();
for (IMemberValuePair pair : memberValuePairs) {
fBuffer.append(pair.getMemberName());
fBuffer.append('=');
appendAnnotationValue(pair.getValue(), pair.getValueKind());
fBuffer.append(',');
}
if (memberValuePairs.length > 0)
fBuffer.deleteCharAt(fBuffer.length() - 1);
fBuffer.append(')').append('\n');
}
private void appendAnnotationValue(Object value, int valueKind) throws JavaModelException {
if (value instanceof Object[]) {
Object[] objects= (Object[]) value;
fBuffer.append('{');
for (Object object : objects) {
appendAnnotationValue(object, valueKind);
fBuffer.append(',');
}
if (objects.length > 0)
fBuffer.deleteCharAt(fBuffer.length() - 1);
fBuffer.append('}');
} else {
switch (valueKind) {
case IMemberValuePair.K_ANNOTATION:
appendAnnotation((IAnnotation) value);
break;
case IMemberValuePair.K_STRING:
fBuffer.append('"').append(value).append('"');
break;
default:
fBuffer.append(value);
break;
}
}
}
protected void appendMembers(final IType type, final IProgressMonitor monitor) throws JavaModelException {
try {
monitor.beginTask(RefactoringCoreMessages.StubCreationOperation_creating_type_stubs, 1);
final IJavaElement[] 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) && !Flags.isProtected(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 && !Flags.isEnum(flags) && !Flags.isSynthetic(flags))
appendFieldDeclaration((IField) child);
} else if (child instanceof IMethod) {
final IMethod method= (IMethod) child;
final String name= method.getElementName();
if (method.getDeclaringType().isEnum()) {
final int count= method.getNumberOfParameters();
if (count == 0 && "values".equals(name)) //$NON-NLS-1$
continue;
if (count == 1 && "valueOf".equals(name) && "Ljava.lang.String;".equals(method.getParameterTypes()[0])) //$NON-NLS-1$ //$NON-NLS-2$
continue;
if (method.isConstructor())
continue;
}
boolean skip= !stub || name.equals("<clinit>"); //$NON-NLS-1$
if (method.isConstructor())
skip= false;
skip= skip || Flags.isSynthetic(flags) || Flags.isBridge(flags);
if (!skip)
appendMethodDeclaration(method);
}
fBuffer.append("\n"); //$NON-NLS-1$
}
} finally {
monitor.done();
}
}
@SuppressWarnings("boxing")
protected void appendMethodBody(final IMethod method) throws JavaModelException {
if (method.isConstructor()) {
final IType declaringType= method.getDeclaringType();
String superSignature= declaringType.getSuperclassTypeSignature();
if (superSignature != null) {
superSignature= Signature.getTypeErasure(superSignature);
final IType superclass= declaringType.getJavaProject().findType(Signature.getSignatureQualifier(superSignature), Signature.getSignatureSimpleName(superSignature));
if (superclass != null) {
final IMethod[] superMethods= superclass.getMethods();
// collect super constructors by parameter count
Map<Integer, List<IMethod>> superConstructorsByParamCount= new TreeMap<Integer, List<IMethod>>();
boolean multi= false;
IMethod superConstructor= null;
for (int i= 0; i < superMethods.length; i++) {
IMethod superMethod= superMethods[i];
if (superMethod.isConstructor()
&& !Flags.isPrivate(superMethod.getFlags())
&& !(Flags.isPackageDefault(superMethod.getFlags()) && !declaringType.getPackageFragment().equals(superclass.getPackageFragment()))
) {
int paramCount= superMethod.getNumberOfParameters();
if (paramCount == 0) {
superConstructor= superMethod;
break;
}
List<IMethod> constructors= superConstructorsByParamCount.get(paramCount);
if (constructors == null) {
constructors= new ArrayList<IMethod>();
superConstructorsByParamCount.put(paramCount, constructors);
}
constructors.add(superMethod);
}
}
if (superConstructor == null && superConstructorsByParamCount.size() > 0) {
// look for constructors without exceptions and without parameters
done: for (List<IMethod> constructors : superConstructorsByParamCount.values()) {
for (IMethod constructor : constructors) {
if (constructor.getExceptionTypes().length == 0) {
superConstructor= constructor;
multi= constructors.size() != 1;
if (multi)
break;
else
break done;
}
if (superConstructor == null) {
superConstructor= constructor;
multi= constructors.size() != 1;
}
}
}
if (superConstructor == null) {
// give up, get first
superConstructor= superConstructorsByParamCount.values().iterator().next().get(0);
multi= true;
}
}
if (superConstructor != null) {
final String[] superParameters= superConstructor.getParameterTypes();
final int paramLength= superParameters.length;
fBuffer.append("super("); //$NON-NLS-1$
if (paramLength != 0) {
for (int index= 0; index < paramLength; index++) {
if (index > 0)
fBuffer.append(","); //$NON-NLS-1$
appendExpression(superParameters[index], multi);
}
}
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 IMethod method) throws JavaModelException {
appendFlags(method);
fBuffer.append(" "); //$NON-NLS-1$
final ITypeParameter[] parameters= method.getTypeParameters();
if (parameters.length > 0) {
appendTypeParameters(parameters);
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$
final String[] exceptionTypes= method.getExceptionTypes();
final int exceptionLength= exceptionTypes.length;
if (exceptionLength > 0)
fBuffer.append(" throws "); //$NON-NLS-1$
for (int index= 0; index < exceptionLength; index++) {
if (index > 0)
fBuffer.append(","); //$NON-NLS-1$
fBuffer.append(Signature.toString(exceptionTypes[index]));
}
if (Flags.isAbstract(flags) || Flags.isNative(flags))
fBuffer.append(";"); //$NON-NLS-1$
else {
fBuffer.append("{\n"); //$NON-NLS-1$
appendMethodBody(method);
fBuffer.append("}"); //$NON-NLS-1$
}
}
/**
* Appends a parameter name
*
* @param method the method
* @param index the index of the parameter
*/
protected void appendMethodParameterName(IMethod method, int index) {
fBuffer.append("a"); //$NON-NLS-1$
fBuffer.append(index);
}
protected void appendSuperInterfaceTypes(final IType type) throws JavaModelException {
final String[] signatures= type.getSuperInterfaceTypeSignatures();
if (signatures.length > 0) {
if (type.isInterface())
fBuffer.append(" extends "); //$NON-NLS-1$
else
fBuffer.append(" implements "); //$NON-NLS-1$
}
for (int index= 0; index < signatures.length; index++) {
if (index > 0)
fBuffer.append(","); //$NON-NLS-1$
fBuffer.append(Signature.toString(signatures[index]));
}
}
protected void appendTopLevelType(final IType type, IProgressMonitor subProgressMonitor) throws JavaModelException {
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 JavaModelException {
try {
monitor.beginTask(RefactoringCoreMessages.StubCreationOperation_creating_type_stubs, 1);
if (type.isAnnotation()) {
appendFlags(type);
fBuffer.append(" @interface "); //$NON-NLS-1$
fBuffer.append(type.getElementName());
fBuffer.append("{\n"); //$NON-NLS-1$
appendMembers(type, new SubProgressMonitor(monitor, 1));
fBuffer.append("}"); //$NON-NLS-1$
} else if (type.isInterface()) {
appendFlags(type);
fBuffer.append(" interface "); //$NON-NLS-1$
fBuffer.append(type.getElementName());
appendTypeParameters(type.getTypeParameters());
appendSuperInterfaceTypes(type);
fBuffer.append("{\n"); //$NON-NLS-1$
appendMembers(type, new SubProgressMonitor(monitor, 1));
fBuffer.append("}"); //$NON-NLS-1$
} else if (type.isClass()) {
appendFlags(type);
fBuffer.append(" class "); //$NON-NLS-1$
fBuffer.append(type.getElementName());
appendTypeParameters(type.getTypeParameters());
final String signature= type.getSuperclassTypeSignature();
if (signature != null) {
fBuffer.append(" extends "); //$NON-NLS-1$
fBuffer.append(Signature.toString(signature));
}
appendSuperInterfaceTypes(type);
fBuffer.append("{\n"); //$NON-NLS-1$
appendMembers(type, new SubProgressMonitor(monitor, 1));
fBuffer.append("}"); //$NON-NLS-1$
} else if (type.isEnum()) {
appendFlags(type);
fBuffer.append(" enum "); //$NON-NLS-1$
fBuffer.append(type.getElementName());
appendSuperInterfaceTypes(type);
fBuffer.append("{\n"); //$NON-NLS-1$
appendEnumConstants(type);
appendMembers(type, new SubProgressMonitor(monitor, 1));
fBuffer.append("}"); //$NON-NLS-1$
}
} finally {
monitor.done();
}
}
protected void appendTypeParameters(final ITypeParameter[] parameters) throws JavaModelException {
final int length= parameters.length;
if (length > 0)
fBuffer.append("<"); //$NON-NLS-1$
for (int index= 0; index < length; index++) {
if (index > 0)
fBuffer.append(","); //$NON-NLS-1$
final ITypeParameter parameter= parameters[index];
fBuffer.append(parameter.getElementName());
final String[] bounds= parameter.getBounds();
final int size= bounds.length;
if (size > 0)
fBuffer.append(" extends "); //$NON-NLS-1$
for (int offset= 0; offset < size; offset++) {
if (offset > 0)
fBuffer.append(" & "); //$NON-NLS-1$
fBuffer.append(bounds[offset]);
}
}
if (length > 0)
fBuffer.append(">"); //$NON-NLS-1$
}
/**
* Creates and returns a stub for the given top-level type.
*
* @param topLevelType the top-level type
* @param monitor the progress monitor, can be <code>null</code>
* @return the source stub
* @throws JavaModelException if this element does not exist or if an exception occurs while
* accessing its corresponding resource
*/
public String createStub(IType topLevelType, IProgressMonitor monitor) throws JavaModelException {
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;
}
}