package net.sourceforge.c4jplugin.internal.util;
import net.sourceforge.c4jplugin.internal.wizards.NewContractLabelProvider;
import net.sourceforge.c4jplugin.internal.wizards.WizardMessages;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.ToolFactory;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.formatter.CodeFormatter;
import org.eclipse.jdt.core.formatter.IndentManipulation;
import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
import org.eclipse.jdt.ui.JavaElementLabels;
import org.eclipse.jdt.ui.PreferenceConstants;
import org.eclipse.jdt.ui.wizards.NewTypeWizardPage.ImportsManager;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.osgi.util.NLS;
import org.eclipse.text.edits.MalformedTreeException;
import org.eclipse.text.edits.TextEdit;
public class C4JStubUtil {
public static GenStubSettings getCodeGenerationSettings(IJavaProject project) {
return new GenStubSettings(project);
}
public static class GenStubSettings {
public boolean callSuper;
public boolean methodOverwrites;
public boolean noBody;
public boolean taskTag;
public boolean finalize;
public int preCondition = -1;
public int postCondition = -1;
public boolean createComments;
public boolean useKeywordThis;
public final int tabWidth;
public GenStubSettings(IJavaProject project) {
this.createComments= Boolean.valueOf(PreferenceConstants.getPreference(PreferenceConstants.CODEGEN_ADD_COMMENTS, project)).booleanValue();
this.useKeywordThis= Boolean.valueOf(PreferenceConstants.getPreference(PreferenceConstants.CODEGEN_KEYWORD_THIS, project)).booleanValue();
this.tabWidth= IndentManipulation.getTabWidth(project.getOptions(true));
}
}
public static String formatCompilationUnit(IJavaProject project, String sourceString, String lineDelim) {
return codeFormat(project, sourceString, CodeFormatter.K_COMPILATION_UNIT, 0, lineDelim);
}
public static String codeFormat(IJavaProject project, String sourceString, int kind, int initialIndentationLevel, String lineDelim) {
CodeFormatter formatter= ToolFactory.createCodeFormatter(project.getOptions(true));
TextEdit edit= formatter.format(kind, sourceString, 0, sourceString.length(), initialIndentationLevel, lineDelim);
if (edit != null) {
Document doc= new Document(sourceString);
try {
edit.apply(doc);
return doc.get();
} catch (MalformedTreeException e) {
} catch (BadLocationException e) {
}
}
return sourceString;
}
/**
* Generates a stub. Given a template method, a stub with the same parameter signature
* and name (appended with "pre_" or "post_" according to the settings)
* will be constructed so it can be added to a type.
* @param destTypeName The name of the type to which the method will be added to (Used for the constructor)
* @param method A method template (method belongs to different type than the parent)
* @param settings Options as defined above (GENSTUB_*)
* @param imports Imports required by the sub are added to the imports structure
* @return The unformatted stub
* @throws JavaModelException
*/
public static String genStub(ICompilationUnit compilationUnit, String destTypeName, IMethod method, GenStubSettings settings, ImportsManager imports) throws CoreException {
IType declaringtype= method.getDeclaringType();
StringBuffer buf= new StringBuffer();
String[] paramTypes= method.getParameterTypes();
String[] paramNames= method.getParameterNames();
int lastParam= paramTypes.length -1;
if (settings.createComments) {
if (!method.isConstructor()) {
boolean precond = true;
if (settings.postCondition > 0) precond = false;
appendMethodComment(buf, method, precond);
}
}
if (settings.finalize) buf.append("final "); //$NON-NLS-1$
buf.append("public void "); //$NON-NLS-1$
if (settings.preCondition > 0) buf.append("pre_"); //$NON-NLS-1$
else if (settings.postCondition > 0) buf.append("post_"); //$NON-NLS-1$
buf.append(method.getElementName());
buf.append('(');
for (int i= 0; i <= lastParam; i++) {
String paramTypeSig= paramTypes[i];
String paramTypeFrm= Signature.toString(paramTypeSig);
if (!isBuiltInType(paramTypeSig)) {
resolveAndAdd(paramTypeSig, declaringtype, imports);
}
buf.append(Signature.getSimpleName(paramTypeFrm));
buf.append(' ');
buf.append(paramNames[i]);
if (i < lastParam) {
buf.append(", "); //$NON-NLS-1$
}
}
buf.append(')');
if (settings.noBody) {
buf.append(";\n\n"); //$NON-NLS-1$
} else {
buf.append(" {\n\t"); //$NON-NLS-1$
if (settings.callSuper && method.isConstructor()) {
buf.append('\t');
buf.append("super"); //$NON-NLS-1$
buf.append('(');
for (int i= 0; i <= lastParam; i++) {
buf.append(paramNames[i]);
if (i < lastParam) {
buf.append(", "); //$NON-NLS-1$
}
}
buf.append(");\n\t"); //$NON-NLS-1$
}
// PRE condition code
if (settings.preCondition > 1) {
if (settings.preCondition == NewContractLabelProvider.PRE_COND_NONNULL) {
for (int i = 0; i < paramTypes.length; i++) {
String paramTypeSig = paramTypes[i];
if (!isBuiltInType(paramTypeSig)) {
buf.append("\tassert "); //$NON-NLS-1$
buf.append(paramNames[i]);
buf.append(" != null;\n\t"); //$NON-NLS-1$
}
}
}
else if (settings.preCondition == NewContractLabelProvider.PRE_COND_NONEMPTY) {
for (int i = 0; i < paramTypes.length; i++) {
String paramTypeSig = paramTypes[i];
if (!isBuiltInType(paramTypeSig)) {
buf.append("\tassert "); //$NON-NLS-1$
buf.append(paramNames[i]);
buf.append(" != null"); //$NON-NLS-1$
String simpleTypeName = Signature.getSimpleName(Signature.toString(paramTypeSig));
if ("String".equals(simpleTypeName)) { //$NON-NLS-1$
buf.append(" && ").append(paramNames[i]).append(".length() > 0"); //$NON-NLS-1$ //$NON-NLS-2$ $NON-NLS-2$
}
buf.append(";\n\t"); //$NON-NLS-1$
}
}
}
}
// POST condition code
else if (settings.postCondition > 1) {
if (settings.postCondition == NewContractLabelProvider.POST_COND_NONNULL) {
if (!isBuiltInType(method.getReturnType()))
buf.append("\tassert getReturnValue() != null;\n\t"); //$NON-NLS-1$
}
else if (settings.postCondition == NewContractLabelProvider.POST_COND_NONEMPTY) {
String returnType = method.getReturnType();
if (!isBuiltInType(returnType)) {
if ("String".equals(Signature.getSimpleName(Signature.toString(returnType)))) { //$NON-NLS-1$
buf.append("\tString strReturnValue = (String)getReturnValue();\n\t"); //$NON-NLS-1$
buf.append("\tassert strReturnValue != null && strReturnValue.length() > 0;\n\t"); //$NON-NLS-1$
}
else
buf.append("\tassert getReturnValue() != null;\n\t"); //$NON-NLS-1$
}
}
}
else if (settings.taskTag) {
String taskTag = getTodoTaskTag(compilationUnit.getJavaProject());
if (taskTag != null) {
buf.append("\t// ").append(taskTag).append("\n\t"); //$NON-NLS-1$ $NON-NLS-2$
}
}
buf.append("}\n\n"); //$NON-NLS-1$
}
return buf.toString();
}
private static void appendMethodComment(StringBuffer buffer, IMethod method, boolean precond) throws JavaModelException {
final String delimiter= "\n"; //$NON-NLS-1$
final StringBuffer buf= new StringBuffer("{@link "); //$NON-NLS-1$
JavaElementLabels.getTypeLabel(method.getDeclaringType(), JavaElementLabels.T_FULLY_QUALIFIED, buf);
buf.append('#');
buf.append(method.getElementName());
buf.append('(');
String[] paramTypes= C4JStubUtil.getParameterTypeNamesForSeeTag(method);
for (int i= 0; i < paramTypes.length; i++) {
if (i != 0) {
buf.append(", "); //$NON-NLS-1$
}
buf.append(paramTypes[i]);
}
buf.append(')');
buf.append('}');
buffer.append("/**");//$NON-NLS-1$
buffer.append(delimiter);
buffer.append(" * ");//$NON-NLS-1$
if (precond)
buffer.append(NLS.bind(WizardMessages.NewContractWizardPageOne_comment_class_to_contract_pre, buf.toString()));
else
buffer.append(NLS.bind(WizardMessages.NewContractWizardPageOne_comment_class_to_contract_post, buf.toString()));
buffer.append(delimiter);
buffer.append(" */");//$NON-NLS-1$
buffer.append(delimiter);
}
public static boolean isBuiltInType(String typeName) {
char first= Signature.getElementType(typeName).charAt(0);
return (first != Signature.C_RESOLVED && first != Signature.C_UNRESOLVED);
}
private static void resolveAndAdd(String refTypeSig, IType declaringType, ImportsManager imports) throws JavaModelException {
String resolvedTypeName= JavaModelUtil.getResolvedTypeName(refTypeSig, declaringType);
if (resolvedTypeName != null) {
imports.addImport(resolvedTypeName);
}
}
public static String getTodoTaskTag(IJavaProject project) {
String markers= null;
if (project == null) {
markers= JavaCore.getOption(JavaCore.COMPILER_TASK_TAGS);
} else {
markers= project.getOption(JavaCore.COMPILER_TASK_TAGS, true);
}
if (markers != null && markers.length() > 0) {
int idx= markers.indexOf(',');
if (idx == -1) {
return markers;
}
return markers.substring(0, idx);
}
return null;
}
/*
* Evaluates if a member (possible from another package) is visible from
* elements in a package.
*/
public static boolean isVisible(IMember member, IPackageFragment pack) throws JavaModelException {
int type= member.getElementType();
if (type == IJavaElement.INITIALIZER || (type == IJavaElement.METHOD && member.getElementName().startsWith("<"))) { //$NON-NLS-1$
return false;
}
int otherflags= member.getFlags();
IType declaringType= member.getDeclaringType();
if (Flags.isPublic(otherflags) || (declaringType != null && declaringType.isInterface())) {
return true;
} else if (Flags.isPrivate(otherflags)) {
return false;
}
IPackageFragment otherpack= (IPackageFragment) member.getAncestor(IJavaElement.PACKAGE_FRAGMENT);
return (pack != null && otherpack != null && pack.getElementName().equals(otherpack.getElementName()));
}
private static boolean isVersionLessThan(String version1, String version2) {
return version1.compareTo(version2) < 0;
}
public static boolean is50OrHigher(IJavaProject project) {
return !isVersionLessThan(project.getOption(JavaCore.COMPILER_COMPLIANCE, true), JavaCore.VERSION_1_5);
}
public static String[] getParameterTypeNamesForSeeTag(IMethod overridden) {
try {
ASTParser parser= ASTParser.newParser(AST.JLS3);
parser.setProject(overridden.getJavaProject());
IBinding[] bindings= parser.createBindings(new IJavaElement[] { overridden }, null);
if (bindings.length == 1 && bindings[0] instanceof IMethodBinding) {
return getParameterTypeNamesForSeeTag((IMethodBinding) bindings[0]);
}
} catch (IllegalStateException e) {
// method does not exist
}
// fall back code. Not good for generic methods!
String[] paramTypes= overridden.getParameterTypes();
String[] paramTypeNames= new String[paramTypes.length];
for (int i= 0; i < paramTypes.length; i++) {
paramTypeNames[i]= Signature.toString(Signature.getTypeErasure(paramTypes[i]));
}
return paramTypeNames;
}
private static String[] getParameterTypeNamesForSeeTag(IMethodBinding binding) {
ITypeBinding[] typeBindings= binding.getParameterTypes();
String[] result= new String[typeBindings.length];
for (int i= 0; i < result.length; i++) {
ITypeBinding curr= typeBindings[i];
if (curr.isTypeVariable()) {
curr= curr.getErasure(); // in Javadoc only use type variable erasure
}
curr= curr.getTypeDeclaration(); // no parameterized types
result[i]= curr.getQualifiedName();
}
return result;
}
}