Executar converters do VRaptor

Bom dia!

Gostaria de saber se existe alguma forma de executar os converters do VRaptor de forma inteligente.

Eu estou criando uma funcionalidade de filtro genérica para qualquer entidade. Um dos filtros é o Restrictions.eq, mas se eu simplesmente passar um objeto do tipo String, o Hibernate não vai conseguir fazer o cast…

Eu tenho isso aqui:

// operation tem um converter do converter que retorna a implementação correta para cada caso...
public class DataTablesFilter {
	private final Logger logger = LoggerFactory.getLogger(this.getClass());

	private String column;
	private String value;
	private QueryOperation operation;

	public void apply(Criteria criteria, Class<?> entity) {
		try {
			criteria.add(operation.apply(this, entity));
		} catch (UnsupportedOperationException e) {
			logger.info("Cannot apply this filter.\n" + e.getMessage());
		}
	}
	// Getters e setters
}

Essa aqui é uma das implementações de QueryOperation:

[code]public class StartQueryOperation implements QueryOperation {
@Override
public Criterion apply(DataTablesFilter filter, Class<?> entity) {
if (Utils.notEmpty(filter.getValue()))
return Restrictions.ilike(filter.getColumn(), filter.getValue(),
MatchMode.START);

	throw new UnsupportedOperationException(
			&quot;Cannot apply this Criterion when filter value is empty.&quot;);
}

}[/code]
Mas ai tudo bem usar String para o ilike, o problema é aqui:

public class EqQueryOperation implements QueryOperation {
	@Override
	public Criterion apply(DataTablesFilter filter, Class&lt;?&gt; entity) {
		if (Utils.notEmpty(filter.getValue())) {
			try {
				Field field = entity.getField(filter.getColumn());
				
				// Continua de alguma forma.
			} catch (SecurityException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (NoSuchFieldException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			return Restrictions.eq(filter.getColumn(), filter.getValue());
		}

		throw new UnsupportedOperationException(
				&quot;Cannot apply this Criterion when filter value is empty.&quot;);
	}
}

Eu precisaria passar para o eq() um Object que pode ser convertido para o tipo da coluna… O maior problema é fazer um converter para aqueles casos onde acessamos um atributo de um atributo…

o que vc quer fazer é converter esse filter.getValue() pra o objeto correspondente?

no VRaptor, vc pode receber Converters no construtor e fazer:

converters.converterFor(SuaClasse.class).convert(valor, SuaClasse.class, resourceBundle);

ou algo parecido com isso.

Ótimo!! Assim já ajuda bastante!

Eu vou pegar sempre o tipo do objeto.

Nos casos onde acessamos mais de um atributo, assim: “obj.atributo1.atributo2” eu precisaria pegar o tipo do atributo2 para conseguir fazer funcionar.

Eu vou tentar com reflection, qualquer dica é bem vinda! :lol:

Lucas, eu estava passando o Converters por parâmetro, mas eu prefiro adicioná-lo como uma dependência…

Eu tenho esse converter para QueryOperation:

[code]@Convert(QueryOperation.class)
public class QueryOperationConverter implements Converter<QueryOperation> {

private final Logger logger = LoggerFactory.getLogger(this.getClass());

@Override
public QueryOperation convert(String value,
		Class&lt;? extends QueryOperation&gt; type, ResourceBundle bundle) {
	try {
		if (!Utils.notEmpty(value))
			throw new IllegalArgumentException(&quot;&quot;);

		String classPackage = &quot;hamburgsud.frontend.datatables.filter.operation.implementation&quot;;
		String className = value + &quot;QueryOperation&quot;;

		logger.debug(&quot;QueryOperation: &quot; + className);

		return (QueryOperation) Class.forName(classPackage + className)
				.newInstance();
	} catch (InstantiationException e) {
		logger.error(
				&quot;Unable to instantiate this query operation, will return null.&quot;,
				e);
	} catch (IllegalAccessException e) {
		logger.error(
				&quot;Unable to access this query operation, will return null.&quot;,
				e);
	} catch (ClassNotFoundException e) {
		logger.error(
				&quot;This  query operationcould not be found, will return null.&quot;,
				e);
	}
	return null;
}

}[/code]
Eu passaria o ResourceBundle e o Converters somente para uma das implementações, a EqQueryOperation. Então, eu precisaria instanciá-la igual o VRaptor instancia os componentes request scoped. É possível?

[code]public class EqQueryOperation implements QueryOperation {

private Converters converters;
private ResourceBundle bundle;

public EqQueryOperation(Converters converters, ResourceBundle bundle) {
	this.converters = converters;
	this.bundle = bundle;
}

@Override
public Criterion apply(DataTablesFilter filter, Class&lt;?&gt; entity) {
	if (!Utils.notEmpty(filter.getValue()))
		throw new UnsupportedOperationException(
				&quot;Cannot apply this Criterion when filter value is empty.&quot;);

	Class&lt;?&gt; type = this.getInnerFieldType(filter.getColumn(), entity);

	Object value = filter.getValue();

	if (converters.existsFor(type))
		value = converters.to(type)
				.convert(filter.getValue(), type, bundle);

	return Restrictions.eq(filter.getColumn(), value);
}

private Class&lt;?&gt; getInnerFieldType(String column, Class&lt;?&gt; entity) {
	List&lt;String&gt; columns = new ArrayList&lt;String&gt;(Arrays.asList(column
			.split(&quot;\\.&quot;)));

	columns.remove(0);

	Class&lt;?&gt; type = entity;

	for (String item : columns)
		type = this.getFieldType(item, entity);

	return type;
}

private Class&lt;?&gt; getFieldType(String column, Class&lt;?&gt; entity) {
	try {
		Field field = entity.getField(column);
		entity = field.getType();
	} catch (SecurityException e) {
		e.printStackTrace();
	} catch (NoSuchFieldException e) {
		e.printStackTrace();
	}

	return entity;
}

}[/code]
Outra coisa que eu percebi: a classe converters espera que eu passe um .class, mas eu não tenho, eu tenho uma instância de Class<?>. Então ele dá erro de compilação. Existe alguma forma de eu receber o valor convertido como um Object? (Eu sei que isso não faz muito sentido… Mas o Hibernate dá exception se eu passar uma String "1" onde ele espera Integer…)

troca de Class<?> pra só Class que vai funcionar…

o problema é que esse <?> não deixa o código de baixo funcionar, pq não tem garantia nenhuma que o converter seja dessa class<?>

E como que eu faço para instanciar um objeto como se ele fosse request scoped? Eu estou procurando nas classes do VRaptor, mas não consigo encontrar a classe que faz isso…

receba um Container e faça um instanceFor(MeuComponente.class)

Há a necessidade de anotá-lo com @Component? Ou isso é feito antes?

Tipo, o VRaptor procura todos os @Component e vai fazendo isso?

isso só funciona com os @Component e outras classes registradas no vraptor.

se a classe foi registrada, vc consegue pedir uma instancia dela.