Refactoring. How extract code to a generic method? - java

To avoid duplication in the code, is there a way to extract to a separate method of the selected code?
class Builder
{
private final List< UnitIf > unit = new ArrayList<>();
private final List< TimeIf > time = new ArrayList<>();
public Builder withUnit( final UnitIf aUnit )
{
//Extract to method
if( aUnit != null )
{
unit.add( aUnit );
}
return this;
//----------------
}
public Builder withTime( final TimeIf aTime )
{
//Extract to method
if( aTime != null )
{
time.add( aTime );
}
return this;
//----------------
}
}
My goal is to simplify the code by eliminating duplication. But the parts of code use different data types.

Add a method
public <T> Builder addIfNonNull(List<T> dst, T x)
{
if (x != null) {
dst.add(x);
}
return this;
}
and implement withUnit like
public Builder withUnit( final UnitIf aUnit )
{
return addIfNonNull(unit, aUnit);
}
and change withTime the same way.
Non-generic version
If you don't want it to be a generic method, just omit the type parameter T and change the type of x to be Object:
public Builder addIfNonNull(List dst, Object x)
{
if (x != null) {
dst.add(x);
}
return this;
}
Internally, the Java compiler will compile List<T> to just List. I think it is called "type erasure".

Related

Converting Tree to JavaTree to SonarQube rule

I need to convert a Tree to a JavaTree. But i am getting ClassCastException, obviously.
public Javadoc(Tree tree) {
if (tree.is(METHOD_KINDS)) {
elementParameters = ((MethodTree) tree).parameters().stream().map(VariableTree::simpleName)
.map(IdentifierTree::name).collect(Collectors.toList());
elementExceptionNames = ((MethodTree) tree).throwsClauses().stream().map(Javadoc::exceptionName)
.filter(Objects::nonNull).collect(Collectors.toList());
} else if (tree.is(CLASS_KINDS)) {
elementParameters = ((ClassTree) tree).typeParameters().stream().map(TypeParameterTree::identifier)
.map(IdentifierTree::name).map(name -> "<" + name + ">").collect(Collectors.toList());
elementExceptionNames = Collections.emptyList();
} else {
elementParameters = Collections.emptyList();
elementExceptionNames = Collections.emptyList();
}
List<String> javadocLines = cleanLines(PublicApiChecker.getApiJavadoc((JavaTree)tree));
mainDescription = getDescription(javadocLines, -1, "");
blockTagDescriptions = extractBlockTags(javadocLines, Arrays.asList(BlockTag.values()));
undocumentedNamedTags = new EnumMap<>(BlockTag.class);
}
It happens in jadocLines variable. I have just a Tree, but i need to set a JavaTree for that getApiJavadoc, because of some methods and classes that it calls forward. I tried to create a javatree object and give my tree data to it. But i couldn't, 'cause JavaTree class is abstract. JavaTree class have other classes inside, but i am not getting this. Can anyone helps me in that part?
Edit 1 : Thanks for answers, but i cannot create another class as simples as that, because i need to give some arguments to the class i need that i do not have(because it dont even exist). The class i need to instantiate is:
public static class ImportTreeImpl extends JavaTree implements ImportTree {
private final boolean isStatic;
private final Tree qualifiedIdentifier;
private final SyntaxToken semicolonToken;
private final SyntaxToken importToken;
private final SyntaxToken staticToken;
public ImportTreeImpl(InternalSyntaxToken importToken, #Nullable InternalSyntaxToken staticToken,
Tree qualifiedIdentifier, InternalSyntaxToken semiColonToken) {
super(Kind.IMPORT);
this.importToken = importToken;
this.staticToken = staticToken;
this.qualifiedIdentifier = qualifiedIdentifier;
this.semicolonToken = semiColonToken;
isStatic = staticToken != null;
}
This class is inside JavaTree class and i need to instatiate it, but i dont have any SyntaxToken, and I cant create a null one to put here just for instantiate it. Can you help with this part?
OBS1: That JavaTree cast is wrong, i know, that was just a test. But Thank you!
EDIT2:I know that getApiChecker need a tree, but the problem is not the getApiChecker, but some methods that are called after. Look:
List<String> javadocLines = cleanLines(PublicApiChecker.getApiJavadoc(tree));
Calls :
#Nullable
public static String getApiJavadoc(Tree tree) {
if (!tree.is(API_KINDS)) {
return null;
}
ModifiersTree modifiersTree = getModifierTrees(tree);
// FIXME token should be retrieved in a much simpler way.
if (modifiersTree != null && !(modifiersTree.modifiers().isEmpty() && modifiersTree.annotations().isEmpty())) {
return getCommentFromTree(modifiersTree);
}
if (tree.is(Tree.Kind.METHOD)) {
MethodTree methodTree = (MethodTree) tree;
return getCommentFromMethod(methodTree);
}
return getCommentFromTree(tree);
}
That calls :
private static String getCommentFromTree(Tree tokenTree) {
return getCommentFromSyntaxToken(FirstSyntaxTokenFinder.firstSyntaxToken(tokenTree));
}
That calls:
#Nullable
public static SyntaxToken firstSyntaxToken(#Nullable Tree tree) {
if (tree == null || tree.is(Tree.Kind.INFERED_TYPE)) {
return null;
} else if (tree.is(Tree.Kind.TOKEN)) {
return (SyntaxToken) tree;
}
for (Tree next : ((JavaTree) tree).children()) {
SyntaxToken syntaxToken = firstSyntaxToken(next);
if (syntaxToken != null) {
return syntaxToken;
}
}
return null;
}
And my problem is :
for (Tree next : ((JavaTree) tree).children()) {
Because this is trying to make that cast, that bugs everything. And i can't change this last one, because this is a sonar code, and i can't change it.

JQuery-like single / multi container class in Java

I'm looking to implement some sort of jQuery-like object in java-8 which, acting as container, can contain one or more objects.
In Jquery you can e.g do: $('.bla').addClass('ble');. It is unknown whether this is applied on one or more objects, yet the method is only one.
I was also very surprised to see that in java-8 you can do CompletableFuture.allOf(CompletableFuture<?>... cfs).join().
No matter how many objects you pass, you will get a single CompletableFuture<Void> and you can call any instance method that will act on all internal objects.
Still, its implementation is definitely too complex for my case.
My base interface looks like this
public interface Response< T extends DataTransferObject< ? > > {
// instance methods
public boolean isSuccess();
public String getMessage();
public default boolean isUndefined() {
return this.equals(ActionResponse.UNDEF);
}
#SuppressWarnings("unchecked")
public default Response< T > mergeWith(final Response< ? super T > response) {
return Response.allOf(this, response);
}
public default boolean isMultiResponse() {
return this instanceof ActionResponse.ActionMultiResponse;
}
/* -------------------------------------------------------------------------------------------------------------- */
// factory methods
#SuppressWarnings("unchecked")
public static < U extends DataTransferObject< ? > > Response< U > allOf(final Response< ? super U >... responses) {
Objects.requireNonNull(responses);
if (responses.length == 0) return ( Response< U > ) ActionResponse.UNDEF;
return new ActionResponse.ActionMultiResponse< U >(responses);
}
public static < U extends DataTransferObject< ? > > Response< U > from(final boolean success, final String message) {
return new ActionResponse<>(success, message);
}
}
The package-protected class looks like this (incomplete):
/* Package-protected implementation for the Response< T > type */
class ActionResponse< T extends DataTransferObject< ? > > implements Response< T > {
static final Response< ? extends DataTransferObject< ? > > UNDEF = new ActionResponse<>();
private final boolean success;
private final String message;
private ActionResponse() {
this.success = false;
this.message = null;
}
ActionResponse(final boolean success, final String message) {
this.success = success;
this.message = message;
}
// getters
static final class ActionMultiResponse< T extends DataTransferObject< ? > > extends ActionResponse< T > {
private final String[] messages;
ActionMultiResponse(final boolean success, final String message) {
super(success, message);
this.messages = null;
}
ActionMultiResponse(final Response< ? super T >... responses) {
/* must check if any of the following is already a MultiActionResponse */
// TODO
this.messages = new String[responses.length];
for (int i = 0; i < responses.length; i++) {
this.messages[i] = responses[i].getMessage();
}
}
#Override
public String getMessage() {
if (this.messages == null) return super.getMessage();
return Arrays.toString(this.messages); // dummy string merger
}
}
}
It's important that classes outside of this package can access only the interface as I want the implementation details to be hidden. Nobody should care on whether a given instance is a single or a multi.
The only way you can build a Response< T > would be via the factory methods provided or the instance method Response::mergeWith. E.g:
Response< SpotDTO > response = Response.from(true, "message");
If was wondering if this is indeed a pattern and if such pattern has a name? If so, what guidelines should I follow?
What is a better way to implement this concept?
For instance, I do not like that the ActionResponse holds a String type for message whereas ActionMultiResponse has String[].
Still I do not want to have only one object with a String array and place if needed only one object inside.

Refactoring with patterns

I haven't had a lot of practice with patterns and application architecture. In a nutshell, I have to find certain attributes which object features. Some code will better describe task:
IAttribute {
IAttribute analyze(IFunction func);
}
//up to 10 different attributes
ArgumentsAttribute implements Attribute {
Map<String, ArgType> args = new HashMap<>();
IAttribute analyze(IFunction func) {
for (Argument arg : func.getArgs()) {
args.put(arg.getName(), arg.getType());
}
if (!args.isEmpty()) return this;
return null;
}
}
ReturnAttribute implements Attribute {
IAttribute analyze(IFunction func) {
if (func.hasReturn) return this;
return null;
}
}
AttributeAnalyzer {
List<Attributes> analyzeAttributes(IFunction func) {
List<IAttribute> attributes = new ArrayList<IAttribute>();
attributes.add(new ArgumentAttribute());
attributes.add(new ReturnAttribute());
...
for (IAttribute attr : attributes) {
attr = attr.analyze(func);
if (null == attr) attributes.remove(attr);
}
return attributes;
}
}
However, this implementation seems to be a little strange. I don't like the fact that Attribute is sort of holder, but it has to implement method to find itself. In my opinion, the best practice would be an opportunity to overload static methods, but obviously its not possible. In this way, we would separate holder from analyzing logic without adding new abstractions(maybe I am not right).
IAttribute {
static IAttribute analyze();
}
ConcreteAttribute1 {
int x = 0;
static IAttribute analyze() {
...
if (x != 0) return new ConcreteAttribute1();
return null;
}
}
ConcreteAttribute2 {
String s = "";
static IAttribute analyze() {
...
if (!s.equals("")) return new ConcreteAttribute2();
return null;
}
}
AttributeAnalyzer {
List<Attributes> analyzeAttributes() {
List<IAttribute> attributes = new ArrayList<IAttribute>();
attributes.add(ConcreteAttribute1.analyze());
attributes.add(ConcreteAttribute2.analyze());
...
for (IAttribute attr : attributes) {
if (null == attr) attributes.remove(attr);
}
return attributes;
}
}
In addition, I have to filter spoiled Attributes. So, are there any ways of refactoring to make this code looks better?
If you have a distinct analyze function for each concrete attribute, with little or no overlap, then your initial code sample may not be all that bad. However, I would then change the signature of the method to boolean analyze().
If there is more overlap in the way attributes are analyzed then you might consider a single method boolean analyze(IAttribute) inside your AttributeAnalyzer class (or in a dedicated class).

Java Generics Type DSL with Builder Pattern

I try to create a DSL Java API with the Builder Pattern on Generics Type.
I have the following class:
public class Rule<T> {
private Predicate<T> condition;
public ConditionBuilder<T> when() {
return new ConditionBuilder<>(this);
}
//setter and getter
}
and the following ConditionBuilder class:
public class ConditionBuilder<T> {
private Rule<T> parent;
public ConditionBuilder(Rule<T> parent) {
this.parent = parent;
}
public ConditionBuilder<T> condition1() {
parent.setCondition(l -> l == 0); // I would like an Integer
return this;
}
public ConditionBuilder<T> condition2() {
parent.setCondition(l -> l.length() > 3); // I would like a String
return this;
}
}
I try to find a solution to set the Generic Type on the fly as an Integer (resp. String) for the condition1 (resp. condition2).
Is there any Pattern or solution to avoid doing instanceof checking ?
You can't do this with member methods on ConditionBuilder<T>, since you've already constructed parent before you invoke either of the conditionX methods. As such, you can't constrain the instance "after the fact".
The way I'd do this is by making the Rule<T> a parameter of a static method. Then you can use something like:
static ConditionBuilder<Integer> condition1(ConditionBuilder<Integer> parent) {
parent.setCondition(l -> l == 0);
return parent;
}
static ConditionBuilder<String> condition2(ConditionBuilder<String> parent) {
parent.setCondition(l -> l.length() > 3);
return parent;
}
I would use a factory pattern instead, because the builder pattern does not fit this situation. Using generics implies that you will accept any type, and so making condition require a specific type is a waste of the generics.
public class Rule<T> {
private Predicate<T> condition;
//setter and getter
}
class ConditionFactory {
public static Rule<Integer> intCondition() {
Rule<Integer> rule = new Rule<>();
rule.setCondition(l -> l == 0);
return rule;
}
public static Rule<String> strCondition() {
Rule<Integer> rule = new Rule<>();
rule.setCondition(l -> l.length() > 3);
return rule;
}
}

Is JRE 1.8 still JavaBean specs compliant about IndexedPropertyDescriptor?

This question seems awkward but we are facing a strange behaviour while retrieving the PropertyDescriptors of a javabean.
Here are the execution results on 1.6, 1.7 and 1.8 of a simple piece of code, compiled with 1.6 compliance.
Java 1.6 execution:
java.beans.PropertyDescriptor#4ddc1428 <- Not important
java.beans.IndexedPropertyDescriptor#7174807e <- Yes I have an indexed property
Java 1.7 execution:
java.beans.PropertyDescriptor[name=class; propertyType=class java.lang.Class; readMethod=public final native java.lang.Class java.lang.Object.getClass()] <- Not important
java.beans.IndexedPropertyDescriptor[name=values; indexedPropertyType=class java.lang.String; indexedReadMethod=public java.lang.String JavaBean.getValues(int)] <- Yes I have an indexed property
Java 1.8 execution:
java.beans.PropertyDescriptor[name=class; propertyType=class java.lang.Class; readMethod=public final native java.lang.Class java.lang.Object.getClass()] <- Not important
java.beans.PropertyDescriptor[name=values; propertyType=interface java.util.List; readMethod=public java.util.List JavaBean.getValues()] <- Ouch! This is not an indexed property anymore!
Why has it changed?
The javabean specs states about accessing a property with an index. It is not said as mandatory to use an array as the container of the indexed property. Am I wrong?
I read the specs and chapter 8.3.3 talks about Design Patterns for Indexed properties, not the strict rule.
How to make the previous behaviour coming back again without refactoring all the app ? < Old application, lot of code to modify, etc...
Thanks for the answers,
JavaBean class
import java.util.ArrayList;
import java.util.List;
public class JavaBean {
private List<String> values = new ArrayList<String>();
public String getValues(int index) {
return this.values.get(index);
}
public List<String> getValues() {
return this.values;
}
}
Main class
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
public class Test {
public static void main(String[] args) throws IntrospectionException {
PropertyDescriptor[] descs =
Introspector.getBeanInfo(JavaBean.class).getPropertyDescriptors();
for (PropertyDescriptor pd : descs) {
System.out.println(pd);
}
}
}
From JavaBeans 1.01 specification, section 7.2 “Indexed properties”:
A component may also expose an indexed property as a single array value.
Section 8.3 is describing the design patterns which introspection recognizes, in the absence of explicit BeanInfo. Section 8.3.3 is saying that only array properties will trigger automatic recognition of indexed properties.
You're technically correct; it is not mandatory to use an array. But if you don't, the spec says you have to provide your own BeanInfo to expose the property as an indexed property.
So the answer to your question's title is: Yes, Java 1.8 is JavaBean specs compliant.
I'm not sure why List properties were ever supported. Maybe a future JavaBeans specification was going to support them which has since been withdrawn.
As to your final question: I think you'll have to create a BeanInfo class for each class with List properties. I expect you can create a general superclass to make it easier, something like:
public abstract class ListRecognizingBeanInfo
extends SimpleBeanInfo {
private final BeanDescriptor beanDesc;
private final PropertyDescriptor[] propDesc;
protected ListRecognizingBeanInfo(Class<?> beanClass)
throws IntrospectionException {
beanDesc = new BeanDescriptor(beanClass);
List<PropertyDescriptor> desc = new ArrayList<>();
for (Method method : beanClass.getMethods()) {
int modifiers = method.getModifiers();
Class<?> type = method.getReturnType();
if (Modifier.isPublic(modifiers) &&
!Modifier.isStatic(modifiers) &&
!type.equals(Void.TYPE) &&
method.getParameterCount() == 0) {
String name = method.getName();
String remainder;
if (name.startsWith("get")) {
remainder = name.substring(3);
} else if (name.startsWith("is") &&
type.equals(Boolean.TYPE)) {
remainder = name.substring(2);
} else {
continue;
}
if (remainder.isEmpty()) {
continue;
}
String propName = Introspector.decapitalize(remainder);
Method writeMethod = null;
Method possibleWriteMethod =
findMethod(beanClass, "set" + remainder, type);
if (possibleWriteMethod != null &&
possibleWriteMethod.getReturnType().equals(Void.TYPE)) {
writeMethod = possibleWriteMethod;
}
Class<?> componentType = null;
if (type.isArray()) {
componentType = type.getComponentType();
} else {
Type genType = method.getGenericReturnType();
if (genType instanceof ParameterizedType) {
ParameterizedType p = (ParameterizedType) genType;
if (p.getRawType().equals(List.class)) {
Type[] argTypes = p.getActualTypeArguments();
if (argTypes[0] instanceof Class) {
componentType = (Class<?>) argTypes[0];
}
}
}
}
Method indexedReadMethod = null;
Method indexedWriteMethod = null;
if (componentType != null) {
Method possibleReadMethod =
findMethod(beanClass, name, Integer.TYPE);
Class<?> idxType = possibleReadMethod.getReturnType();
if (idxType.equals(componentType)) {
indexedReadMethod = possibleReadMethod;
}
if (writeMethod != null) {
possibleWriteMethod =
findMethod(beanClass, writeMethod.getName(),
Integer.TYPE, componentType);
if (possibleWriteMethod != null &&
possibleWriteMethod.getReturnType().equals(
Void.TYPE)) {
indexedWriteMethod = possibleWriteMethod;
}
}
}
if (indexedReadMethod != null) {
desc.add(new IndexedPropertyDescriptor(propName,
method, writeMethod,
indexedReadMethod, indexedWriteMethod));
} else {
desc.add(new PropertyDescriptor(propName,
method, writeMethod));
}
}
}
propDesc = desc.toArray(new PropertyDescriptor[0]);
}
private static Method findMethod(Class<?> cls,
String name,
Class<?>... paramTypes) {
try {
Method method = cls.getMethod(name, paramTypes);
int modifiers = method.getModifiers();
if (Modifier.isPublic(modifiers) &&
!Modifier.isStatic(modifiers)) {
return method;
}
} catch (NoSuchMethodException e) {
}
return null;
}
#Override
public BeanDescriptor getBeanDescriptor() {
return beanDesc;
}
#Override
public PropertyDescriptor[] getPropertyDescriptors() {
return propDesc;
}
}
I am facing the same issue. I am trying to save StartDate and End date as List from JSP but it is not saved and values are wiped out. In my project, there are start date and end date fields. I debugged BeanUtilsBean then I observed that fields do not have writeMethod. I have added one more setter method for each field in my class and it works.
java.beans.PropertyDescriptor[name=startDateStrings; propertyType=interface java.util.List; readMethod=public java.util.List com.webapp.tradingpartners.TradingPartnerNewForm.getStartDateStrings()]
This is a JavaBeans crapspec problem, which only allows void setters. If you want this-returning setters, then you can't have JavaBeans compatibility and there's nothing Lombok could do about it.
In theory, you could generate two setters, but then you'd have to call them differently and having two setters per field is simply too bad.
<cc:dateInput property='<%= "startDateStrings[" + row + "]" %>' onchange="setPropertyChangedFlag()"/>
<cc:dateInput property='<%= "endDateStrings[" + row + "]" %>' onchange="setPropertyChangedFlag()"/>
public List<String> getStartDateStrings() {
return startDateStrings;
}
public String getStartDateStrings(int index) {
return startDateStrings.get(index);
}
public void setStartDateStrings(int index, String value) {
startDateStrings.set(index, value);
}
public List<String> getEndDateStrings() {
return endDateStrings;
}
public String getEndDateStrings(int index) {
return endDateStrings.get(index);
}
public void setEndDateStrings(int index, String value) {
endDateStrings.set(index, value);

Categories

Resources