/**
* Copyright (C) 2005 - 2012 Eric Van Dewoestine
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.eclim.plugin.jdt.command.impl;
import java.util.Arrays;
import java.util.Comparator;
import org.eclim.Services;
import org.eclim.annotation.Command;
import org.eclim.command.CommandLine;
import org.eclim.command.Options;
import org.eclim.plugin.core.command.AbstractCommand;
import org.eclim.plugin.jdt.util.JavaUtils;
import org.eclim.plugin.jdt.util.TypeUtils;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.formatter.CodeFormatter;
import org.eclipse.jdt.internal.corext.codemanipulation.AddCustomConstructorOperation;
import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettings;
import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility2;
import org.eclipse.jdt.internal.corext.dom.ASTNodes;
import org.eclipse.jdt.internal.corext.dom.Bindings;
import org.eclipse.jdt.internal.corext.refactoring.structure.ASTNodeSearchUtil;
import org.eclipse.jdt.internal.ui.actions.ActionMessages;
import org.eclipse.jdt.internal.ui.preferences.JavaPreferencesSettings;
import org.eclipse.jdt.ui.SharedASTProvider;
import org.eclipse.text.edits.TextEdit;
import com.google.gson.Gson;
/**
* Command used to generate class constructors.
*
* @author Eric Van Dewoestine
*/
@Command(
name = "java_constructor",
options =
"REQUIRED p project ARG," +
"REQUIRED f file ARG," +
"REQUIRED o offset ARG," +
"OPTIONAL e encoding ARG," +
"OPTIONAL r properties ARG," +
"OPTIONAL s super NOARG"
)
public class ConstructorCommand
extends AbstractCommand
{
@Override
public Object execute(CommandLine commandLine)
throws Exception
{
String project = commandLine.getValue(Options.PROJECT_OPTION);
String file = commandLine.getValue(Options.FILE_OPTION);
String propertiesOption = commandLine.getValue(Options.PROPERTIES_OPTION);
int offset = getOffset(commandLine);
String[] properties = {};
if(propertiesOption != null){
properties = new Gson().fromJson(propertiesOption, String[].class);
}
ICompilationUnit src = JavaUtils.getCompilationUnit(project, file);
IType type = TypeUtils.getType(src, offset);
CompilationUnit cu = SharedASTProvider
.getAST(src, SharedASTProvider.WAIT_YES, null);
ITypeBinding typeBinding = ASTNodes.getTypeBinding(cu, type);
if (typeBinding.isAnonymous()) {
return ActionMessages
.GenerateConstructorUsingFieldsAction_error_anonymous_class;
}
IVariableBinding[] variables = new IVariableBinding[properties.length];
for(int ii = 0; ii < properties.length; ii++){
IField field = type.getField(properties[ii]);
if(!field.exists()){
return Services.getMessage("field.not.found",
properties[ii], type.getElementName());
}
variables[ii] = ASTNodeSearchUtil
.getFieldDeclarationFragmentNode(field, cu).resolveBinding();
}
if(findExistingConstructor(type, properties).exists()){
return Services.getMessage("constructor.already.exists",
type.getElementName() + '(' + buildParams(type, properties) + ')');
}
IMethodBinding constructor =
findParentConstructor(cu, typeBinding, variables);
if (constructor == null){
return ActionMessages
.GenerateConstructorUsingFieldsAction_error_nothing_found;
}
insertConstructor(src, cu, typeBinding, variables, constructor, commandLine);
return null;
}
private void insertConstructor(
ICompilationUnit src,
CompilationUnit cu,
ITypeBinding typeBinding,
IVariableBinding[] variables,
IMethodBinding constructor,
CommandLine commandLine)
throws Exception
{
CodeGenerationSettings settings = JavaPreferencesSettings
.getCodeGenerationSettings(src.getJavaProject());
settings.createComments = true;
boolean isDefault = constructor.getDeclaringClass()
.getQualifiedName().equals("java.lang.Object") ||
constructor.isDefaultConstructor();
AddCustomConstructorOperation op = new AddCustomConstructorOperation(
cu, typeBinding,
variables, constructor,
getSibling((IType)typeBinding.getJavaElement()), settings, true, true);
op.setOmitSuper(!commandLine.hasOption(Options.SUPERTYPE_OPTION) || isDefault);
if (!typeBinding.isEnum()){
op.setVisibility(Modifier.PUBLIC);
}
op.run(null);
TextEdit edit = op.getResultingEdit();
if (edit != null){
edit = edit.getChildren()[0];
JavaUtils.format(
src, CodeFormatter.K_COMPILATION_UNIT,
edit.getOffset(), edit.getLength());
}
}
private IMethod findExistingConstructor(IType type, String[] properties)
throws Exception
{
if(properties.length == 0){
return type.getMethod(type.getElementName(), null);
}
String[] fieldSigs = new String[properties.length];
for (int ii = 0; ii < properties.length; ii++){
fieldSigs[ii] = type.getField(properties[ii]).getTypeSignature();
}
return type.getMethod(type.getElementName(), fieldSigs);
}
private IMethodBinding findParentConstructor(
CompilationUnit cu, ITypeBinding typeBinding, IVariableBinding[] variables)
{
IMethodBinding constructor = null;
if (typeBinding.isEnum() || variables.length != 0) {
ITypeBinding binding = cu.getAST().resolveWellKnownType("java.lang.Object");
constructor = Bindings.findMethodInType(
binding, "Object", new ITypeBinding[0]);
} else {
IMethodBinding[] bindings = StubUtility2
.getVisibleConstructors(typeBinding, false, true);
Arrays.sort(bindings, new Comparator<IMethodBinding>(){
public int compare(IMethodBinding b1, IMethodBinding b2){
return b1.getParameterTypes().length - b2.getParameterTypes().length;
}
});
constructor = bindings.length > 0 ? bindings[0] : null;
}
return constructor;
}
private IJavaElement getSibling(IType type)
throws Exception
{
IJavaElement sibling = null;
IMethod[] methods = type.getMethods();
for (int ii = 0; ii < methods.length; ii++){
if(methods[ii].isConstructor()){
sibling = ii < methods.length - 1 ? methods[ii + 1] : null;
}
}
// insert before any other methods or inner classes if any.
if(sibling == null){
if(methods.length > 0){
sibling = methods[0];
}else{
IType[] types = type.getTypes();
sibling = types != null && types.length > 0 ? types[0] : null;
}
}
return sibling;
}
private String buildParams(IType type, String[] fields)
throws Exception
{
StringBuffer params = new StringBuffer();
for (int ii = 0; ii < fields.length; ii++){
if(ii != 0){
params.append(", ");
}
IField field = type.getField(fields[ii]);
params.append(Signature.getSignatureSimpleName(field.getTypeSignature()))
.append(' ').append(field.getElementName());
}
return params.toString();
}
}