Avoid Duplicate Literals Sonar Error - java

I'm using SonarQube to verify and inspect my Java code, and i've encountered an issue with Avoid Duplicate Literals in an Enum class type, here is an exemple :
public enum Products {
A ("Product A"),
C ("Product A"),
D ("Product B"),
P ("Product B");
private String name = "";
Products (String name){
this.name = name;
}
public String toString(){
return name;
}
}
Sonar is telling me to declare the Strings 'Product A' and 'Product B' as a constant field, but you can't declare variables in Enum type class.

You can declare the constant outside of the enum:
private static final String TYPE_A_NAME = "Type A";
public enum Type {
TYPEA(TYPE_A_NAME), TYPEB("B");
private String value;
Type(String value) {
}
}

Create Prefix (private static final Sting PREFIX = Product.class.getSimpleName()+ "."
and A("A"), etc...
you return a string, you can use a MessageFormatter to internationalize your string in a properties file
Product.A=Product A, etc...
and the constructor is private
you can make a getter like
`
public static String getI18NString(String key){
return Internationalizer.getI18String(PREFIX + key);
}
public class Internationalizer {
/** The logger of this class. */
private static final Log LOGGER = LogFactory.getLog(Internationalizer.class);
/** */
private static ResourceBundleMessageSource resourceBundleMessageSource = new ResourceBundleMessageSource();
/**
* Get the internationalized String form properties files
*
* #param key
* #return
*/
public static String getI18String(final String key) {
String message = "";
try {
message = resourceBundleMessageSource.getMessage(key, null, Locale.getDefault());
} catch (NoSuchMessageException e) {
LOGGER.info("Key not internationalized : " + key);
message = key;
}
return message;
}
/**
* Set the bundles for internationalization Injected by Spring
*
* #param bundles
*/
public void setBundles(final List<String> bundles) {
String[] bundlesArrays = new String[bundles.size()];
for (int i = 0; i < bundles.size(); i++) {
bundlesArrays[i] = bundles.get(i);
}
resourceBundleMessageSource.setBasenames(bundlesArrays);
}
}
`

Related

JPA / Hibernate - persist enums as table of constants

Currently, I'm using the standard way to map enums with Hibernate, e.g.
#Entity
public class Job {
#Enumerated(EnumType.STRING)
protected State state;
}
public enum State{
NEW,OLD;
}
Now the requirements changed, and I have to create a table State, which contains all the valid values of my enum as String constants. Thus Job must refer to the State table. I don't have to migrate legacy data.
What options do I have to map this with JPA / Hibernate?
Is it possible, to let Hibernate create the State table with values ( 1->"new", 2->"old").
during DDL generation?
in ended up with a solution, which generates the DDL including enum constants and foreign key constraints.
e.g.
#Entity
public enum MyEnum{
#EnumValue
private String name;
#Id
private int id;
}
#Entity
public class MyEntity {
#EnumReference
protected MyEnum myEnum;
}
is sufficient with the following MetadataContributor (/src/main/resources/META-INF/services/org.hibernate.boot.spi.MetadataContributor):
public class EnumConstantsMetadataContributor implements MetadataContributor {
private final static Logger LOG = LoggerFactory.getLogger(EnumConstantsMetadataContributor.class);
private final static List<String> ENABLED_ON = Arrays.asList("validate", "update", "create", "create-drop");
private final static Integer DEFAULT_VARCHAR_SIZE = 255;
private final static Identifier DEFAULT_COLUMN_NAME = Identifier.toIdentifier("enum_constant", false);
#Override
public void contribute(InFlightMetadataCollector metadataCollector, IndexView jandexIndex) {
if (shouldRun(metadataCollector)) {
addEnumsAsTableConstantsAndFkConstraint(metadataCollector);
}
}
private boolean shouldRun(InFlightMetadataCollector metadataCollector) {
StandardServiceRegistry serviceRegistry = metadataCollector.getMetadataBuildingOptions().getServiceRegistry();
ConfigurationService config = serviceRegistry.getService(ConfigurationService.class);
String setting = config.getSetting(AvailableSettings.HBM2DDL_AUTO, String.class, null);
return (setting != null || ENABLED_ON.contains(setting));
}
private void addEnumsAsTableConstantsAndFkConstraint(InFlightMetadataCollector metadataCollector) {
for (PersistentClass persistentClass : metadataCollector.getEntityBindings()) {
Class<?> plainJavaClass = persistentClass.getMappedClass();
if (Enum.class.isAssignableFrom((plainJavaClass))) {
createEnumInsertsAndDbColumns(persistentClass, plainJavaClass, metadataCollector);
}
tryAddFkConstraint(persistentClass, metadataCollector);
}
}
private void tryAddFkConstraint(PersistentClass persistentClass, InFlightMetadataCollector metadataCollector) {
Consumer<Field> createEnumFkConstraintForField = field -> {
String fieldName = field.getName();
PersistentClass targetPersistentClass = metadataCollector.getEntityBinding(field.getType().getCanonicalName());
if (targetPersistentClass == null) {
LOG.error("Target (enum) class must be an #Entity: {}", field.getType().getCanonicalName());
System.exit(1);
}
Property enumReferenceAnnotatedProperty = persistentClass.getProperty(fieldName);
persistentClass.getTable().createForeignKey(null,
Arrays.asList(enumReferenceAnnotatedProperty.getColumnIterator().next()),
targetPersistentClass.getEntityName());
};
Field[] declaredFields = persistentClass.getMappedClass().getDeclaredFields();
of(declaredFields).filter(field -> field.isAnnotationPresent(EnumReference.class)).forEach(
createEnumFkConstraintForField);
}
private void createEnumInsertsAndDbColumns(PersistentClass persistentClass, Class<?> clazz,
InFlightMetadataCollector metadata) {
String tableName = persistentClass.getTable().getName();
Enum<?>[] enumJavaConstants = clazz.asSubclass(Enum.class).getEnumConstants();
ArrayList<String> insertCommandAccumulator = new ArrayList<String>(enumJavaConstants.length);
Optional<Field> enumValueAnnotatedField = of(enumJavaConstants.getClass().getComponentType().getDeclaredFields())
.filter(field -> field.isAnnotationPresent(EnumValue.class)).map(fieldWithEnumValue -> {
fieldWithEnumValue.setAccessible(true);
return fieldWithEnumValue;
}).findAny(); // just none or one is supported
if (enumValueAnnotatedField.isPresent()) {
setMinimalFieldLengthOfExitingColumn(enumValueAnnotatedField.get(), enumJavaConstants, persistentClass);
}
for (int i = 0; i < enumJavaConstants.length; i++) {
Enum<?> it = enumJavaConstants[i];
String constantEnumValue = enumValueAnnotatedField.map(v -> getInstanceValueOfEnumValueAnnotation(it, v))
.orElse(it.name());
if (!enumValueAnnotatedField.isPresent()) {
insertAdditionalColumn(persistentClass, metadata.getDatabase(), enumJavaConstants);
}
insertCommandAccumulator.add(createInsert(tableName, i, constantEnumValue));
}
InitCommand initCommand = new InitCommand(insertCommandAccumulator.toArray(new String[0]));
persistentClass.getTable().addInitCommand(initCommand);
}
private void setMinimalFieldLengthOfExitingColumn(Field field, Enum<?>[] enumJavaConstants,
PersistentClass persistentClass) {
Property property = persistentClass.getProperty(field.getName());
Column column = persistentClass.getTable().getColumn(Identifier.toIdentifier(property.getName()));
Integer maxLengthOfEnums = maxLengthOfEnums(enumJavaConstants,
e -> getInstanceValueOfEnumValueAnnotation(e, field));
column.setLength(maxLengthOfEnums);
}
private String getInstanceValueOfEnumValueAnnotation(Enum<?> myEnum, Field enumValueAnnotatedField) {
try {
return enumValueAnnotatedField.get(myEnum).toString();
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
return null;
}
}
private static Integer maxLengthOfEnums(Enum<?>[] enums, Function<Enum<?>, String> enumConstantValueExtractor) {
return of(enums).map(it -> enumConstantValueExtractor.apply(it).length()).reduce(Math::max)
.orElse(DEFAULT_VARCHAR_SIZE);
};
private void insertAdditionalColumn(PersistentClass persistentClass, Database database, Enum<?>[] enumJavaConstants) {
Integer maxEnumStringLength = maxLengthOfEnums(enumJavaConstants, c -> c.name());
Column column = new Column(DEFAULT_COLUMN_NAME.render(database.getDialect()));
String typeName = database.getDialect().getTypeName(Types.VARCHAR, maxEnumStringLength, 0, 0);
column.setSqlType(typeName);
persistentClass.getTable().addColumn(column);
}
private String createInsert(String tableName, int position, String dbEnumValue) {
return ("insert into " + tableName + " values(" + position + ",\'" + dbEnumValue + "\')");
}
}
Works for MySQL 5.7 and Hibernate 5.
It is not possible to JPA query MyEnum and its consistency between #Enumerated(EnumType.ORDINAL) and getEnumConstants() order is implicitly assumed.
IMHO that does not have too much sense. The values of an enum are static and constant, while the values in a SQL table are dynamic. What if the DB does not contain exactly the values (no more, no less) for such enum?
EDIT: if you are forced to implement it, may something like this work?
public enum State{
int primaryKey;
NEW(0),OLD(1);
public State(int pk) {
primarykey = pk;
}
}
And then join by primary key....

How to update a field in a immutable object

Supposing the following class:
#Immutable
public final MyMessageClass {
private String message;
private Date dateLastChange;
private String identifier;
public MyClass(final String message){
this.message = message;
dataLastChange = new Date();
}
public Date lastChange() {
return new Date(dateLastChange.getTime());
}
public String messageValue(){
return message;
}
}
Once we have built an object of this class,
MyMessageClass myMessageObject = new MyMessageClass("Once upon a time ...");
and once we have finish doing some operations with it,
logger.info("The message is{}",myMessageObject.messageValue());
logger.info("The last change was done on {}",myMessageObject.lastChange());
it is expected to get an identifier from somewhere (a remote service, for instance) and attach it to the message. But, if I do something like this:
myMessageObject.setIdentifier(identifier);
I supposedly breaking the immutability capability of the object. If so, how is the best way to update the identifier field avoiding to do a new constructor call (so creating a new object)?
So the problem is just because you want to log some stuff first? Can't you do that after you've constructed the object?
Alternatively, you can use the builder pattern, with something like this. Note the final instance fields - instances of this class will be immutable even without the annotation.
#Immutable
public final MyMessageClass {
private final String message;
private final Date dateLastChange;
private final String identifier;
public MyClass(final MyMessageClass.Builder builder){
this.message = builder.message;
this.dataLastChange = builder.dataLastChange;
this.identifier = builder.identifier;
}
public Date lastChange() {
return new Date(dateLastChange.getTime());
}
public String messageValue(){
return message;
}
public String identifier(){
return identifier;
}
public static final class Builder {
private String message;
private final Date dateLastChange = new Date();
private String identifier;
public Builder message(final String message) {
this.message = message;
return this;
}
public String message() {
return message;
}
public Builder identifier(final String identifier) {
this.identifier = identifier;
return this;
}
public String identifier() {
return identifier;
}
public Date lastChange() {
return new Date(dateLastChange.getTime());
}
public MyMessageClass build() {
return new MyMessageClass(this);
}
}
}
You can then incrementally build the content of your object.
MyMessageClass.Builder builder = new MyMessageClass.Builder().message("Once upon a time ...");
logger.info("The message is{}", builder.message());
logger.info("The last change was done on {}",builder.lastChange());
String identifier = // get the identifier from somewhere
MyMessageClass message = builder.identifier(identifier).build();

Converting String to java bean type

I have a string which looks like this and it represents a pojo.
Model [Name=Mobie , location= US, actualTransferDate=null, scanserialCode=234335,1237787, modelNum=MIC 898989 ]
I want bit clearer to reader on the above string. I want to read the user checked checkbox values(represents entire row with the fileds in below pojo) in an jsp page table to another jsp page. So, in the controller i read these checked checkbox rows as bellow.
String[] checkeditems = request.getParameterValues("case");//case represents the entire row
for (String string : checkeditems) {
log.info("row1"+string);// String pasted above in the message
}
From the above it returns as a string Array which i want convert to be as a list object, so that i can easily send this list to next jsp for a view. I feel i am heading to wrong direction and doing some unrelated stuff.
I have a pojo as
public class Model{
private String Name;
private String location;
private String actualTransferDate;
private String scanserialCode;
private String modelNum;
====Getters/Setter======
How i can convert this String to this model object?
you can split the string on ", " and iterate over the result array. With BeanUtils from apache can you fill your new pojo instance.
Example:
public class Model {
private String Name;
private String location;
private String actualTransferDate;
private String scanserialCode;
private String modelNum;
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public String getActualTransferDate() {
return actualTransferDate;
}
public void setActualTransferDate(String actualTransferDate) {
this.actualTransferDate = actualTransferDate;
}
public String getScanserialCode() {
return scanserialCode;
}
public void setScanserialCode(String scanserialCode) {
this.scanserialCode = scanserialCode;
}
public String getModelNum() {
return modelNum;
}
public void setModelNum(String modelNum) {
this.modelNum = modelNum;
}
#Override
public String toString() {
return "[Name = " + getName() + "location = " +getLocation() + ", actualTransferDate = " + getActualTransferDate() + ", scanserialCode = " + getScanserialCode() + ", modelNum = " + getModelNum() + "]";
}
}
import org.apache.commons.beanutils.BeanUtils;
public class Main {
public static void main(String[] args) throws IllegalAccessException, InvocationTargetException {
String model = new String("Name=Mobie , location= US, actualTransferDate=null, scanserialCode=234335,1237787, modelNum=MIC 898989");
String[] modelValues = model.split(", ");
Model m = new Model();
for (String value : modelValues) {
String[] s = value.split("=");
String fieldName = s[0];
String fieldValue = s[1];
BeanUtils.setProperty(m, fieldName, fieldValue);
}
System.out.println(m.toString());
}
}
Maven dependency:
<dependencies>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.2</version>
</dependency>
</dependencies>
If you want it completely dynamic, you can use Reflection.
For example, use a regular expression (Pattern/Matcher) to find the [ ... ] part, use the String before that as a class name (assuming you know the package name) and then do a simple comma/equals-sign split in the [ ... ] part and fill the fields via reflection... Not that hard to do.
You can define a constructor in the Model class which accepts the full string as input. The use StringTokenizer with delimiter as ',' to convert the string to a list of tokens. Then tokenizer each token with '='as the delimiter. This way you will have all the members of Model class tokens which can be used to initialize the values of the member variables.

Is it possible to remove an attribute (variable) from object in java?

I have a lots of classes that extends from one class
Also I have one method that its argument is that parent class and create query base on attribute of those classes.
sometimes I need to ignore some attribute from result query.
so is it possible to remove some attribute of object?
class A1 extends Model {
public String field1 = "";
public String field2 = "";
public String table = "A1";
#Override
public String getTable() {
return this.table;
}
}
class A2 extends Model {
public String field1 = "";
public String field2 = "";
public String field3 = "";
public String table = "A2";
#Override
public String getTable() {
return this.table;
}
}
class Utility {
public static String query(Model params) {
Field[] fields = params.getClass().getFields();
String head = "INSERT INTO " + params.getTable() + "(";
String tail = "VALUES (";
for(Field field : fields) {
String key = field.getName();
String val;
try {
val = field.get(params);
} catch (Exception e) {
val = null;
}
head += key + ",";
tail += "'" + val + "',";
}
head = head.substring(head,0,head.length() -1) + ")";
tail = tail.substring(tail,0,tail.length() -1) + ")";
return head + tail;
}
}
I call query method by sending one model
A1 data = new A1();
data.field1 = "Name";
data.field2 = "Family";
String query = Utility.query(data);
I just want to remove field2 from query how can I do that?
thanks for any help
You could implement an annotiation. Let's call it #DontPersist. Use it to mark fields which should not get persisted. In Utility.query() you can check for the annotation with reflection.
As your Model class does not implement anything (it could be an interface, but that's another topic), you can extend it creating a class with less attributes when necessary (an anonymous class will do the job).
Anyway, I think you should refactor your code: why not using a List to store fields? It's easier and it does not need reflection.
I'll use something like:
public class Model extends ArrayList{
public Model(String name) { tableName=name;}
private String tableName;
public String getTable() {return tableName}
}
And then you can iterate over the Array to obtain the field names.

Read all Interface into List

How do I convert all TaskStatusConstant interface items into one List<String> automatically?
public interface TaskStatusConstant {
final static String NEW = "New";
final static String ACCEPTED = "Accepted";
final static String REJECTED = "Rejected";
final static String REASSIGNED = "Reassigned";
final static String COMPLETED = "Completed";
}
Those information should be represented in an enumeration.
public enum TaskStatusConstant {
NEW,
ACCEPTED,
REJECTED,
REASSIGNED,
COMPLETED
}
To iterate over an enum in Java use this:
for (TaskStatusConstant task: TaskStatusConstant.values()) {
// do what you want
}
You can use reflection, for example:
List<String> constants = new ArrayList<String>();
Field[] fields = TaskStatusConstant.class.getFields();
for (Field field : fields) {
if (field.getType().equals(String.class)) {
constants.add(field.getName());
}
}
for (String constant : constants) {
System.out.println(constant);
}
public enum TaskStatusConstant2 {
NEW("New"), ACCEPTED("Accepted"), REJECTED("Rejected"), REASSIGNED("Reassigned"), COMPLETED("Completed");
private String value;
private TaskStatusConstant2(String value) {
this.value = value;
}
public String getStatus() {
return value;
}
}
Just tried this one... Not sure this will work for my other codes :)
Using enum (recommended, has many benefits)
public enum TaskStatusConstant {
NEW("New"),
ACCEPTED("Accepted"),
REJECTED("Rejected"),
REASSIGNED("Reassigned"),
COMPLETED("Completed");
private final String toString;
private TaskStatusConstant(String toString) {
this.toString = toString;
}
#Override
public String toString() {
return toString;
}
public static List<String> valuesAsString() {
List<String> list = new ArrayList<String>();
for(TaskStatusConstant c : values()) {
list.add(c.toString());
}
return list;
}
}
Using reflection (not recommended, it's a hassle, sometimes necessary)
public static List<String> valuesAsString()
throws IllegalAccessException {
List<String> list = new ArrayList<String>();
for(Field f : TaskStatusConstant.class.getDeclaredFields()) {
if(f.getType() == String.class) {
f.setAccessible(true);
list.add((String)f.get(null));
}
}
return list;
}

Categories