I have this code:
FVDTO.setStatus("fail");
List<String[]> invalidFields = new ArrayList<String[]>();
Iterator<ConstraintViolation<HazardSubmission>> iterator = cv.iterator();
while(iterator.hasNext()) {
ConstraintViolation<HazardSubmission> i = iterator.next();
String property = i.getPropertyPath().toString();
String message = i.getMessage();
invalidFields.add(new String[] { property, message });
}
FVDTO.setInvalidFields(invalidFields);
return new JsonResolution(FVDTO);
I've taken some out to keep things DRY so I can then use it with other classes, i.e HazardSubmission is one class, and there will be others. The below code shows my attempt, obviously manually casting <HazardSubmission> here won't work it needs to be like o.getClass();
public static List<String[]> GetInvalidProperties(Set<ConstraintViolation<Object>> cv, Object o) {
List<String[]> invalidFields = new ArrayList<String[]>();
Iterator<ConstraintViolation<HazardSubmission>> iterator = cv.iterator();
while(iterator.hasNext()) {
ConstraintViolation<HazardSubmission> i = iterator.next();
String property = i.getPropertyPath().toString();
String message = i.getMessage();
invalidFields.add(new String[] { property, message });
}
}
The second code block fails because I don't really know what I'm doing, I want to pass in the cv for param 1, with a general object type, then somehow pass in the type as a second paramter.
Could you someone please explain how to do this?
I think you might be looking for a generic method
public static <T> List<String[]> GetInvalidProperties(Set<ConstraintViolation<T>> cv){
Iterator<ConstraintViolation<T>> iterator = cv.iterator();
while(iterator.hasNext()) {
ConstraintViolation<T> i = iterator.next();
String property = i.getPropertyPath().toString();
String message = i.getMessage();
invalidFields.add(new String[] { property, message });
}
}
If all T extends a given class or interface you could even say
public static <T extends MyClassOrInterface> List<String[]> GetInvalidProperties(Set<ConstraintViolation<T>> cv){
//...
}
cv.iterator() will return you an Iterator<ConstraintViolation<Object>>, and you need Iterator<ConstraintViolation<HazardSubmission>>. This is done because cv is defined as Set<ConstraintViolation<Object>>. If you want a generic way for this, you can change
Set<ConstraintViolation<Object>> cv
to
Set<ConstraintViolation<? extends Object>> cv
in that way your code will compile.
Related
I have two a conditions in the method:
if(urlSendModel.isHasPhoto()) {
ArrayList<InputMediaPhoto> inputMediaPhotos = new ArrayList<>();
for(String photoUrl : urlSendModel.getPhotos()){
inputMediaPhotos.add(new InputMediaPhoto(photoUrl));
}
SendMediaGroup sendMediaGroup = new SendMediaGroup(message.chat().id(),
inputMediaPhotos.toArray(new InputMediaPhoto[0]));
bot.execute(sendMediaGroup);
}
if(urlSendModel.isHasVideo()){
ArrayList<InputMediaVideo> inputMediaVideos = new ArrayList<>();
for(String videoUrl : urlSendModel.getVideos()){
inputMediaVideos.add(new InputMediaVideo(videoUrl));
}
SendMediaGroup sendMediaGroup = new SendMediaGroup(message.chat().id(),
inputMediaVideos.toArray(new InputMediaVideo[0]));
bot.execute(sendMediaGroup);
}
How can I create something like this or solve the problem in another way.
private <T extends InputMedia<T>> void sendMedia(Message message, ArrayList<String> urls) {
ArrayList<T> inputMedia = new ArrayList<>();
for(String url : urls){
inputMedia.add(new T(url));
}
SendMediaGroup sendMediaGroup = new SendMediaGroup(message.chat().id(),
inputMedia.toArray(new T[0]));
bot.execute(sendMediaGroup);
}
I will be glad to any proposed solution.
Both of the requirements here can be gotten around by passing the class into the method. I'll skip the additional details of what your method does, but, for instance:
<T> void doSomething(final Class<T> klass, final int length) {
// Replace 'new T[10]'
final T[] array = Array.newInstance(klass, length);
final Constructor<T> constructor = klass.getConstructor();
for (int i = 0; i < length; i++) {
// Replace 'new T()'
array[i] = constructor.newInstance();
}
}
Note 1: The replacement for new T() was provided by #Vince Emigh.
Note 2: Exception handling is not considered here, so this will not compile as-is.
Note 3: If you need to use a different constructor to the one with no arguments, then it may well be simpler to accept a Function<Object[], T> which will convert the arguments you provide to an instance of the type.
You can do something like creating the generic class first :
public class ClassList<T extends Object>{ private ArrayList<T> list; .... }
// or
public class ClassList<T extends InputMedia>{ private ArrayList<T> list; .... }
and then you cas use constructor or setter to affect a value to your attribute list of T
Im trying to store and access 2 different types of List in a HashMap, and access them when the method that return the HashMap is it called, but Im getting this error:
The constructor ArrayList<JournalArticle>(Object) is undefined
The method looks like this:
public static HashMap<String, Object> getJournalArticles(long groupId) throws NumberFormatException, SystemException{
List<JournalArticle> journalArticles = JournalArticleLocalServiceUtil.getStructureArticles(groupId);
List<String> allJournalArticleIds = new ArrayList<String>();
for (JournalArticle journalArticle : journalArticles) {
allJournalArticleIds.add(journalArticle.getArticleId());
}
HashMap<String, Object> mapArticles = new HashMap<String,Object>();
mapArticles.put("journalArticles", journalArticles);
mapArticles.put("allJournalArticleIds", allJournalArticleIds);
return mapArticles;
}
And when I call the method and try to store their respective values into a new List I get the error commented before:
HashMap<String, Object> mapArticles = JournalArticleUtil.getJournalArticles(scopeGroupId);
List<JournalArticle> allArticles = new ArrayList<JournalArticle>(mapArticles.get("journalArticles"));
List<String> allJournalArticleIds = new ArrayList<String>(mapArticles.get("allJournalArticleIds"));
What´s wrong and how to solve?
I would use a class written to hold this information (you may find it quicker to use something like Pair<L, R>):
class ArticleHolder {
private List<JournalArticle> journalArticles;
private List<String> allJournalArticleIds;
public ArticleHolder(List<JournalArticle> journalArticles,
List<String> allJournalArticleIds) {
this.journalArticles = journalArticles;
this.allJournalArticleIds = allJournalArticleIds;
}
//getters + setters
}
And change your methods:
public static ArticleHolder getJournalArticles(long groupId)
throws NumberFormatException, SystemException {
List<JournalArticle> journalArticles =
JournalArticleLocalServiceUtil.getStructureArticles(groupId);
List<String> allJournalArticleIds = new ArrayList<String>();
for (JournalArticle journalArticle : journalArticles) {
allJournalArticleIds.add(journalArticle.getArticleId());
}
return new ArticleHolder(journalArticles, allJournalArticleIds);
}
Beside that: your call to new ArrayList<JournalArticle>(...) shows that you intended to create new array list instances (assuming code could compile). There should be no need to do that, even if your map values were typed as Collection objects.
IMHO The quick solution is change the type of mapArticles to this HashMap<String, List<?>> and then:
List<JournalArticle> allArticles = new ArrayList<JournalArticle>((Collection<JournalArticle>)mapArticles.get("journalArticles"));
List<String> allJournalArticleIds = new ArrayList<String>((Collection<String>)mapArticles.get("allJournalArticleIds"));
Because the ArrayList constructor only supports these options:
new ArrayList<T>();
new ArrayList<T>(int capacity);
new ArrayList<T>(Collection<? extends T> collection);
And Object isn't a collection at compiling time.
I have a set of a set variable that (I guess) contains Strings returned from a query (that's all I know and need to retrieve) defined in this way:
private Set<Set<Type_A>> variable_A;
variable_A = query.getVarA();
Now I need a getter that returns an array of these Strings, and tried with the code below with no success, due to the its return type I guess.
I can't also debug my code because it's a javabean used by a jsp file and Eclipse won't give me a clue on how to proceed. Is there a way to fix this? I tried with public void getVarA() and public ArrayList<String> getVarA()
public Set<Axiom> getVarA() {
Iterator itr1 = variable_A.iterator();
Set set;
while(itr1.hasNext()) {
set = (HashSet)itr1.next();
}
return set;
}
Could be something like this, though I'm not entirely sure what is it your trying to achieve. Hope this helps ;)
public class Foo<T> {
private final Set<Set<T>> setsOfSets = new HashSet<>();
public static void main(String[] args) {
final Foo<String> tester = new Foo<>();
Set<String> set1 = new HashSet<>(Arrays.asList("A", "B"));
Set<String> set2 = new HashSet<>(Arrays.asList("C", "D"));
Set<String> set3 = new HashSet<>(Arrays.asList("E", "F"));
tester.addRecord(set1);
tester.addRecord(set2);
tester.addRecord(set3);
System.out.println(tester.getFirstSet());
System.out.println(tester.getFirstSet());
System.out.println(tester.getFirstSet());
System.out.println(tester.getFirstSet());
}
public void addRecord(Set<T> record) {
setsOfSets.add(record);
}
public List<T> getFirstSet() {
Iterator<Set<T>> iterator = setsOfSets.iterator();
while (iterator.hasNext()) {
final Set<T> set = iterator.next();
iterator.remove();
return new ArrayList<>(set);
}
//if there are no sets present
return null;
}
}
What your getVarA does is that is iterates through variable_a and returns last item of this iteration as a raw Set.
If you are using java8 you can use streams and their flatMap operation to collect all values of subsets into a single set.
public Set<Type_A> getVarA() {
return variable_A.stream().flatMap(Set::stream).collect(Collectors.toSet());
}
Otherwise you can just iterate with foreach and return collected result.
public Set<Type_A> getVarA() {
Set<Type_A> result = new HashSet<>();
for (Set<Type_A> subset : variable_A) {
result.addAll(subset);
}
return result;
}
My use case was to write a generic CSV transformer, which should be able to convert any Java POJO to CSV string.
My Implementation :
public <T> List<String> convertToString(List<T> objectList) {
List<String> stringList = new ArrayList<>();
char delimiter = ',';
char quote = '"';
String lineSep = "\n";
CsvMapper mapper = new CsvMapper();
CsvSchema schema = mapper.schemaFor(!HOW_TO!);
for (T object : objectList) {
try {
String csv = mapper.writer(schema
.withColumnSeparator(delimiter)
.withQuoteChar(quote)
.withLineSeparator(lineSep)).writeValueAsString(object);
} catch (JsonProcessingException e) {
System.out.println(e);
}
}
return stringList;
}
I was using Jackson-dataformat-csv library, but I'm stuck with !HOW_TO! part, ie How to extract the .class of the object from the objectList. I was studying and came across Type Erasure, So I think it is somehow not possible other than giving the .class as parameter to my function. But I'm also extracting this object list from generic entity using Java Reflection, so I can't have the option to provide the .class params.
Is there a workaround for this?
OR
Any other approaches/libraries where I can convert a generic List<T> objectList to List<String> csvList with functionality of adding delimiters, quote characters, line separators etc.
Thanks!
I have created a CSVUtil Class similar to below which uses java reflection.
Example to use below CSVUtil
Assuming POJO Student ,
List<Student> StudentList = new ArrayList<Student>();
String StudentCSV = CSVUtil.toCSV(StudentList,' ',false);
import java.lang.reflect.Field;
import java.util.List;
import java.util.logging.Logger;
CSVUtil class
public class CSVUtil {
private static final Logger LOGGER = Logger.getLogger(CSVUtil.class .getName());
private final static char DEFAULT_SEPARATOR = ' ';
public static String toCSV(List<?> objectList, char separator, boolean displayHeader) {
StringBuilder result =new StringBuilder();
if (objectList.size() == 0) {
return result.toString();
}
if(displayHeader){
result.append(getHeaders(objectList.get(0),separator));
result.append("\n");
}
for (Object obj : objectList) {
result.append(addObjectRow(obj, separator)).append("\n");
}
return result.toString();
}
public static String getHeaders(Object obj,char separator) {
StringBuilder resultHeader = new StringBuilder();
boolean firstField = true;
Field fields[] = obj.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
String value;
try {
value = field.getName();
if(firstField){
resultHeader.append(value);
firstField = false;
}
else{
resultHeader.append(separator).append(value);
}
field.setAccessible(false);
} catch (IllegalArgumentException e) {
LOGGER.severe(e.toString());
}
}
return resultHeader.toString();
}
public static String addObjectRow(Object obj, char separator) {
StringBuilder csvRow =new StringBuilder();
Field fields[] = obj.getClass().getDeclaredFields();
boolean firstField = true;
for (Field field : fields) {
field.setAccessible(true);
Object value;
try {
value = field.get(obj);
if(value == null)
value = "";
if(firstField){
csvRow.append(value);
firstField = false;
}
else{
csvRow.append(separator).append(value);
}
field.setAccessible(false);
} catch (IllegalArgumentException | IllegalAccessException e) {
LOGGER.severe(e.toString());
}
}
return csvRow.toString();
}
}
There is a simple option. I've added some lines to your code to show it :
public <T> List<String> convertToString(List<T> objectList) {
if(objectList.isEmpty())
return Collections.emptyList();
T entry = objectList.get(0);
List<String> stringList = new ArrayList<>();
char delimiter = ',';
char quote = '"';
String lineSep = "\n";
CsvMapper mapper = new CsvMapper();
CsvSchema schema = mapper.schemaFor(entry.getClass());
for (T object : objectList) {
try {
String csv = mapper.writer(schema
.withColumnSeparator(delimiter)
.withQuoteChar(quote)
.withLineSeparator(lineSep)).writeValueAsString(object);
stringList.add(csv);
} catch (JsonProcessingException e) {
System.out.println(e);
}
}
return stringList;
}
The trick is to get one of the elements of the list. In order to avoid crashs I've added a little data integrity test at the beginning that return an unmodifiable empty list in the case there are no items in the input list.
Then you retrieve an instance of your Object and use that to get the class.
Alternatively if the convertToString method is in a parametrized class you can do that in a slightly different way
public class GenericClass<T> {
private final Class<T> type;
public GenericClass(Class<T> type) {
this.type = type;
}
public Class<T> getMyType() {
return this.type;
}
}
This solution allow you to get the class of T. I don't think you'll need it for this question but it might comes in handy.
It seems this problem is just harder than most people would like it to be as a result of how Java does generics. Bruno's answer shows options that might work if you can make certain assumptions or can structure your code a certain way.
Another option that should work for your case can be found by way of the answers to this other question: How to get a class instance of generics type T
In there you'll find a link to an article: http://blog.xebia.com/acessing-generic-types-at-runtime-in-java/
This describes how to use the ParameterizedType of an object's superclass. You can apply that to your List object and hopefully it will work for you. This only may luckily work in this case, because you're taking as a parameter an object with a superclass whose type parameters match what you need.
Truly in general, we can't rely on knowing the type parameters at runtime. We can at best maybe use type tokens (parameter of type Class<T>)
I ran into a little snag with the concrete implementation of strategy components using generic types.
Wondering if anyone can point me in the right direction with an example?
Here is what I am working towards, but I get caught up when I declare the decode method as it expcects a List when I create the ArrayList... Not a surprise.
public class CsvFormat<T,T1> implements FormatStrategy<T,T1> {
public CsvFormat(boolean header) {
setHeader(header);
#Override
public final T decode(T1 csvData) {
csvData = new ArrayList(); //ERROR****
List<Map<String, String>> decodedData = new ArrayList<Map<String, String>>(); //turn collection into an array of maps
if (this.hasHeader()) {
decodeDataWithHeader(csvData, decodedData);
} else {
decodeDataNoHeader(csvData, decodedData);
}
return decodedData;
}
private void decodeDataNoHeader(List<String> csvData, List<Map<String, String>> records) {
int recordCount = FIRST_IDX;
List<String> fields = null; //= Arrays.asList(csvData.get(recordCount).split(DELIM)); //turn line into a list, first record
for (String data : csvData) { //for each unformatted string
int delimIndex = FIRST_IDX; //reset delim
fields = Arrays.asList(data.split(DELIM));//after header, start mapping
records.add(new LinkedHashMap<String, String>()); //make a new map
recordCount++;
for (String field : fields) {
final String KEY_ID = "Column-" + (delimIndex + RECORD_BUFFER);
records.get(records.size() - RECORD_BUFFER).put(KEY_ID, field);
delimIndex++;
}
}
}
Here is what I had to start with The only way I can think of so far to achieve the above without error is to overload the the decode methods based on what object they are passed..
public class CsvFormat implements FormatStrategy<
List<Map<String, String>>, List<String>> {
public CsvFormat(boolean header) {
setHeader(header);
}
#Override
public final List<Map<String, String>> decode(List<String> csvData) {
List<Map<String, String>> decodedData = new ArrayList<Map<String, String>>(); //turn collection into an array of maps
if (this.hasHeader()) {
decodeDataWithHeader(csvData, decodedData);
} else {
decodeDataNoHeader(csvData, decodedData);
}
return decodedData;
}
private void decodeDataNoHeader(List<String> csvData, List<Map<String, String>> records) {
int recordCount = FIRST_IDX;
List<String> fields = null; //= Arrays.asList(csvData.get(recordCount).split(DELIM)); //turn line into a list, first record
for (String data : csvData) { //for each unformatted string
int delimIndex = FIRST_IDX; //reset delim
fields = Arrays.asList(data.split(DELIM));//after header, start mapping
records.add(new LinkedHashMap<String, String>()); //make a new map
recordCount++;
for (String field : fields) {
final String KEY_ID = "Column-" + (delimIndex + RECORD_BUFFER);
records.get(records.size() - RECORD_BUFFER).put(KEY_ID, field);
delimIndex++;
}
}
}
Actually the example you "started with" seems exactly right. You have written a decode method that requires a List<String> as input, so it stands to reason that you would be implementing the FormatStrategy interface with that specific type as T1 and the same goes for the output type T.
Why would you do icky runtime inspection of the input and loads of unsafe casting when you can actually follow the pattern and create a new class for each specific concrete type you care about?
The code you have written for decoding the data will always return a List<Map<String, String>> and can only work with a List<String> as input, so there is no reason for the CsvFormat class to have type parameters. So what you started with seems correct, why aren't you satisfied with it?