/**
* 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.List;
import org.apache.commons.lang.StringUtils;
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.ASTUtils;
import org.eclim.plugin.jdt.util.JavaUtils;
import org.eclim.plugin.jdt.util.MethodUtils;
import org.eclim.plugin.jdt.util.TypeUtils;
import org.eclim.util.file.Position;
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.Modifier;
import org.eclipse.jdt.core.formatter.CodeFormatter;
import org.eclipse.jdt.internal.corext.codemanipulation.AddGetterSetterOperation;
import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettings;
import org.eclipse.jdt.internal.corext.codemanipulation.GetterSetterUtil;
import org.eclipse.jdt.internal.ui.preferences.JavaPreferencesSettings;
import org.eclipse.text.edits.TextEdit;
/**
* Command used to generate java bean property methods (getters / setters).
*
* @author Eric Van Dewoestine
*/
@Command(
name = "java_bean_properties",
options =
"REQUIRED p project ARG," +
"REQUIRED f file ARG," +
"REQUIRED o offset ARG," +
"REQUIRED e encoding ARG," +
"REQUIRED r properties ARG," +
"REQUIRED t type ARG," +
"OPTIONAL i indexed NOARG"
)
public class BeanPropertiesCommand
extends AbstractCommand
{
private static final String GETTER = "getter";
private static final String SETTER = "setter";
private static final int TYPE_GET = 0;
private static final int TYPE_GET_INDEX = 1;
private static final int TYPE_SET = 2;
private static final int TYPE_SET_INDEX = 3;
private static final String INT_SIG =
Signature.createTypeSignature("int", true);
private static final String[] INT_ARG = new String[]{INT_SIG};
private static final IField[] NO_FIELDS = new IField[0];
@Override
public Object execute(CommandLine commandLine)
throws Exception
{
String project = commandLine.getValue(Options.PROJECT_OPTION);
String file = commandLine.getValue(Options.FILE_OPTION);
String methods = commandLine.getValue(Options.TYPE_OPTION);
String[] properties = StringUtils.split(
commandLine.getValue(Options.PROPERTIES_OPTION), ',');
int offset = getOffset(commandLine);
boolean indexed = commandLine.hasOption(Options.INDEXED_OPTION);
ICompilationUnit src = JavaUtils.getCompilationUnit(project, file);
IType type = TypeUtils.getType(src, offset);
List<IField> fields = Arrays.asList(type.getFields());
CodeGenerationSettings settings =
JavaPreferencesSettings.getCodeGenerationSettings(src.getJavaProject());
settings.createComments = true;
for(String property : properties){
IField field = type.getField(property);
if(field != null){
boolean isArray = Signature.getArrayCount(field.getTypeSignature()) > 0;
IField getter = methods.indexOf(GETTER) != -1 ? field : null;
IField setter = methods.indexOf(SETTER) != -1 ? field : null;
int methodType = getter != null ? TYPE_GET : TYPE_SET;
// edge case to prevent insert setter before getter if getter already
// exists.
if (getter != null && setter != null &&
GetterSetterUtil.getGetter(field) != null)
{
methodType = TYPE_SET;
}
IJavaElement sibling = getSibling(type, fields, field, methodType);
insertMethods(src, type, sibling, settings, getter, setter);
if (isArray && indexed){
insertIndexMethods(src, type, fields, settings, getter, setter);
}
}
}
return null;
}
private void insertMethods(
ICompilationUnit src,
IType type,
IJavaElement sibling,
CodeGenerationSettings settings,
IField getter,
IField setter)
throws Exception
{
AddGetterSetterOperation op = new AddGetterSetterOperation(
type,
getter != null ? new IField[]{getter} : NO_FIELDS,
setter != null ? new IField[]{setter} : NO_FIELDS,
NO_FIELDS,
ASTUtils.getCompilationUnit(src), null, sibling, settings, true, true);
op.run(null);
TextEdit edit = op.getResultingEdit();
if (edit != null){
JavaUtils.format(
src, CodeFormatter.K_COMPILATION_UNIT,
edit.getOffset(), edit.getLength());
}
}
private void insertIndexMethods(
ICompilationUnit src,
IType type,
List<IField> fields,
CodeGenerationSettings settings,
IField getter,
IField setter)
throws Exception
{
// eclipse doesn't natively support indexed accessors, so this method runs
// some regexes on the getter/setter stubs to generate indexed versions.
if (getter != null){
IMethod existing = getBeanMethod(type, getter, TYPE_GET_INDEX);
if (existing == null) {
IJavaElement sibling = getSibling(type, fields, getter, TYPE_GET_INDEX);
String name = GetterSetterUtil.getGetterName(getter, null);
String stub = GetterSetterUtil.getGetterStub(
getter, name, settings.createComments, Modifier.PUBLIC);
stub = stub.replaceFirst(
"\n(\\s*\\*\\s*@return)",
"\n * @param index the index to get\n$1");
stub = stub.replaceFirst(
"\\[\\]\\s*(" + name + "\\s*\\()\\)", " $1int index)");
stub = stub.replaceFirst("(return\\s+\\w+)(.*?;)", "$1[index]$2");
IMethod inserted = type.createMethod(stub, sibling, false, null);
// format the inserted method according to the user's preferences
Position position = TypeUtils.getPosition(type, inserted);
JavaUtils.format(
src, CodeFormatter.K_COMPILATION_UNIT,
position.getOffset(), position.getLength());
}
}
if (setter != null){
IMethod existing = getBeanMethod(type, setter, TYPE_SET_INDEX);
if (existing == null) {
IJavaElement sibling = getSibling(type, fields, setter, TYPE_SET_INDEX);
String name = GetterSetterUtil.getSetterName(setter, null);
String stub = GetterSetterUtil.getSetterStub(
setter, name, settings.createComments, Modifier.PUBLIC);
stub = stub.replaceFirst(
"\n(\\s*\\*\\s*@param)",
"\n * @param index the index to set\n$1");
stub = stub.replaceFirst(
"(" + name + "\\s*\\()(.*?)\\[\\](\\s*.*?)\\)",
"$1int index, $2$3)");
stub = stub.replaceFirst("(\\w+)(\\s*=\\s*)", "$1[index]$2");
IMethod inserted = type.createMethod(stub, sibling, false, null);
// format the inserted method according to the user's preferences
Position position = TypeUtils.getPosition(type, inserted);
JavaUtils.format(
src, CodeFormatter.K_COMPILATION_UNIT,
position.getOffset(), position.getLength());
}
}
}
private IJavaElement getSibling(
IType type,
List<IField> fields,
IField field,
int methodType)
throws Exception
{
IMethod siblingMethod = null;
int siblingType = TYPE_GET;
// first try other methods for the same field.
for(int ii = TYPE_GET; ii <= TYPE_SET_INDEX; ii++){
IMethod method = getBeanMethod(type, field, ii);
if(method != null){
siblingMethod = method;
siblingType = ii;
}
}
if (siblingMethod != null){
if(siblingType < methodType){
siblingMethod = MethodUtils.getMethodAfter(type, siblingMethod);
}
if(siblingMethod != null){
return siblingMethod;
}
return getFirstInnerType(type);
}
int index = fields.indexOf(field);
// insert before the next property's bean methods, if there are other
// properties.
if(fields.size() > 1 && (index + 1) < fields.size()){
IMethod method = null;
for(int ii = index + 1; method == null && ii < fields.size(); ii++){
IField property = (IField)fields.get(ii);
method = getBeanMethod(type, property, false);
}
if(method != null){
return method;
}
}
// insert after previous property's bean methods, if there are other
// properties.
if(fields.size() > 1 && index > 0){
IMethod method = null;
for(int ii = index - 1; method == null && ii >= 0; ii--){
IField property = (IField)fields.get(ii);
method = getBeanMethod(type, property, true);
}
if(method != null){
method = MethodUtils.getMethodAfter(type, method);
if(method != null){
return method;
}
}
}
return getFirstInnerType(type);
}
private IType getFirstInnerType(IType type)
throws Exception
{
// insert before inner classes.
IType[] types = type.getTypes();
// find the first non-enum type.
for (int ii = 0; ii < types.length; ii++){
if(!types[ii].isEnum()){
return types[ii];
}
}
return null;
}
private IMethod getBeanMethod(IType type, IField field, int methodType)
throws Exception
{
String name = Signature.getSignatureSimpleName(field.getTypeSignature());
boolean isArray = Signature.getArrayCount(field.getTypeSignature()) > 0;
String signature = null;
switch(methodType){
case TYPE_GET:
return GetterSetterUtil.getGetter(field);
case TYPE_GET_INDEX:
if(isArray){
signature = GetterSetterUtil.getGetterName(field, null) + "(int)";
}
break;
case TYPE_SET:
return GetterSetterUtil.getSetter(field);
case TYPE_SET_INDEX:
if(isArray){
signature = GetterSetterUtil.getSetterName(field, null) +
"(int, " + name + ')';
}
}
if(signature != null){
IMethod[] methods = type.getMethods();
for(int ii = 0; ii < methods.length; ii++){
String sig = MethodUtils.getMinimalMethodSignature(methods[ii], null);
if(sig.equals(signature)){
return methods[ii];
}
}
}
return null;
}
private IMethod getBeanMethod(IType type, IField field, boolean last)
throws Exception
{
IMethod result = null;
boolean isArray = Signature.getArrayCount(field.getTypeSignature()) > 0;
// regular getter
IMethod method = GetterSetterUtil.getGetter(field);
if(method != null && method.exists() && !last){
return method;
}else if(method != null && method.exists()){
result = method;
}
// index getter
if(isArray){
method = type.getMethod(
GetterSetterUtil.getGetterName(field, null), INT_ARG);
if(method.exists() && !last){
return method;
}else if(method.exists()){
result = method;
}
}
// regular setter
method = GetterSetterUtil.getSetter(field);
if(method != null && method.exists() && !last){
return method;
}else if(method != null && method.exists()){
result = method;
}
// index setter
if(!isArray){
String elementType = Signature.getElementType(field.getTypeSignature());
method = type.getMethod(
GetterSetterUtil.getSetterName(field, null),
new String[]{INT_SIG, elementType});
if(method.exists()){
result = method;
}
}
return result;
}
}