update object in hashmap by java 8 lambda - java

I created an item named player as follows:
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
public class player implements Comparable <player> {
int PlayerId ;
String name ;
double salary;
public player(int PlayerId) {
this.PlayerId = PlayerId;
}
public void setPlayerId(int PlayerId) {
this.PlayerId = PlayerId;
}
public void setName(String name) {
this.name = name;
}
public void setSalary(double salary) {
this.salary = salary;
}
public int getID() {
return PlayerId;
}
public String getName() {
return name;
}
public double getSalary() {
return salary;
}
#Override
public int hashCode() {
int key = 2;
return key=2*key+PlayerId;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final player other = (player) obj;
if (this.PlayerId != other.PlayerId) {
return false;
}
return true;
}
#Override
public String toString(){
return hashCode()+" "+getID() +" "+getName()+" "+getSalary();
}
// generic method StoreplayerDetails
public <T> void StoreplayerDetails( HashMap<Integer,T> inputMap ) {
// save elements into text file
PrintWriter pw = null;
try {
pw = new PrintWriter(new FileOutputStream("OutPut.txt"));
for(T element : inputMap.values())
pw.println(element);
pw.close();
} catch (FileNotFoundException ex) {
Logger.getLogger(MainProgram.class.getName()).log(Level.SEVERE, null, ex);
} finally {
pw.close();
}
}
#Override
public int compareTo(player other) {
if(this.salary>other.salary)
return 1;
else
if(this.salary<other.salary)
return -1;
return 0;
}
public interface Update {
public <T> void updateSalaries( HashMap<Integer,player> inputMap);
}
}
create an interface named update in the player class ,create a generic method named updateSalaries in the interface that takes a HashMap as input and returns a Queue of player objects after updating the salaries of players by adding 500 to each one's salary .
in the mainprogram class implement the method updatesalaries as a lamdba expression .in the mainprogram class,print the elements in the returned queue .
I tried it as follows but it did not work out:
#Override
public <T> void updateSalaries(HashMap<Integer, player> map) {
map.replaceAll((k,player.getSalary()) -> player.getSalary()+500;
System.out.println("new map"+map);
}
This is the full code in the main class
import java.util.HashMap;
import player.player.Update;
public class MainProgram implements Update{
public static void main(String[] args) {
HashMap< Integer,player> Keys = new HashMap<>();
player p1 =new player(1);
p1.setName("Ali");
p1.setSalary(5000);
player p2 =new player(2);
p2.setName("Sayed");
p2.setSalary(7000);
player p3 =new player(3);
p3.setName("soha");
p3.setSalary(3000);
Keys.put(1, p1);
Keys.put(2, p2);
Keys.put(3, p3);
// p1.StoreplayerDetails(Keys);
MainProgram m = new MainProgram();
m.updateSalaries(Keys);
}
#Override
public <T> void updateSalaries(HashMap<Integer, player> map) {
map.replaceAll((k,player.getSalary()) -> player.getSalary()+500;
System.out.println("new map"+map);
}
}
Is there any help in solving this?

In your code snippet you have the following line of code:
map.replaceAll((k,player.getSalary()) -> player.getSalary()+500;
Let's take this apart piece by piece:
map.replaceAll This method lets you replace all the values in a map. I believe you want to manipulate the values that are already there, instead.
(k,player.getSalary()) This is where you name the variables that the lambda will dump values into. You aren't supposed to supply numbers here, you are supposed to be receiving numbers. You likely want (k, p), where k will be set to the key (an Integer) and p will be set to the value (a player).
player.getSalary()+500 This returns an int. The replaceAll method requires that you return the value type, which in this case is player.
You forgot to include a close parenthesis at the end.
I believe you want to use this line of code instead, which mitigates all of the above errors:
map.forEach((k, p) -> p.setSalary(p.getSalary() + 500));

Related

Retrieve ArrayList object by parameter value [duplicate]

This question already has answers here:
How to find an object in an ArrayList by property
(8 answers)
Closed 4 years ago.
I am maintaining a sorted ArrayList of objects (by overwriting the add method as shown here) where each object has 2 attributes: a and b. How can I retrieve an object for which a equals 5?
I cannot use a map, because the value which I want to sort the list on must be able to accept duplicates (which is why this answer is not applicable here).
Code:
class TimeMap {
List<MyType> list = new ArrayList<KVT>() {
public boolean add(KVT mt) {
int index = Collections.binarySearch(this, mt, new SortByTime());
if (index < 0) index = ~index;
super.add(index, mt);
return true;
}
};
}
class KVT{//value-timestamp object
String value;
int timestamp;
public VT(String v, int t){
value=v;
timestamp=t;
}
}
class SortByTimestamp implements Comparator<KVT>{
public int compare(KVT a, KVT b){
return a.timestamp.compareTo(b.timestamp);
}
}
I have written a small example using java8 streams where you can get the object from the ArrayList by a property of the object.
import java.util.Arrays;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Test> list = Arrays.asList(new Test(1, 2), new Test(5, 6), new Test(3, 4));
Test test = list.stream().filter(obj -> obj.a == 5).findFirst().orElse(null);
System.out.println(test.a);
}
}
class Test {
int a;
int b;
Test(int a, int b) {
this.a = a;
this.b = b;
}
}
Hope this will give you an idea
Here is an mcve demonstrating retrieval by timestamp as well as some other enhancement:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class TimeMap {
private List<KVT> list;
TimeMap() {
list = new ArrayList<>() {
#Override
public boolean add(KVT mt) {
super.add(mt); //add
Collections.sort(this, new SortByTimestamp()); //resort after add
return true;
}
};
}
boolean add(KVT mt){return list.add(mt);}
KVT getByTimeStamp(int timestamp){
for(KVT mt : list){
if(timestamp == mt.timestamp)
return mt;
}
return null;
}
//returns a copy of list
List<KVT> getListCopy() { return new ArrayList<>(list) ;};
//test
public static void main(String[] args) {
TimeMap tm = new TimeMap();
tm.add(new KVT("A", 2));
tm.add(new KVT("B", -3));
tm.add(new KVT("C", 1));
System.out.println(tm.getListCopy());
System.out.println(tm.getByTimeStamp(1));
}
}
class KVT{
String value;
int timestamp;
public KVT(String v, int t){
value=v;
timestamp=t;
}
#Override
public String toString(){ return value+" ("+timestamp+")";}
//todo add getters
}
class SortByTimestamp implements Comparator<KVT>{
#Override
public int compare(KVT a, KVT b){
//compareTo can not be applied to primitives
return Integer.valueOf(a.timestamp).compareTo(b.timestamp);
}
}

Massive StackOverflowError from GSON toJson

So I am trying to use GSON to convert a class into a JSON file with the toJson() function and every time the code gets called there is a massive 1000+ line StackOverflowError. This is the error: http://pastebin.com/dBhYUFva
And here is the class I am doing gson.toJson() on:
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import mc.kwit.arena.players.ArenaPlayer;
import mc.kwit.arena.util.PlayerUtil;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Zach on 8/3/16.
*/
public class Match {
List<ArenaPlayer> arenaPlayers = new ArrayList<ArenaPlayer>();
String[] uuidArray = null;
Player winner;
String matchId = "123456";
int killLimit = 5;
int timeLimit = -1;
public Match() {
}
public Match(List<ArenaPlayer> arenaPlayerList, String id, int seconds, int kills) {
this.arenaPlayers = arenaPlayerList;
this.matchId = id;
this.timeLimit = seconds;
this.killLimit = kills;
}
public Match(String[] players, String id, int seconds, int kills) {
this.uuidArray = players;
this.matchId = id;
this.timeLimit = seconds;
this.killLimit = kills;
}
public void start() {
//Create our ArenaPlayer objects
this.arenaPlayers = PlayerUtil.uuidToArenaPlayerList(uuidArray);
Bukkit.getLogger().info("Match \" " + this.matchId + "\" has been started!");
//Whitelist all participants
for(ArenaPlayer p : arenaPlayers) {
KwitArena.pc.addArenaPlayer(p.getPlayer());
p.getPlayer().setWhitelisted(true);
Bukkit.getLogger().info(p.getName() + " has been whitelisted!");
p.setMatch(this);
}
}
public void finish() throws Exception {
//Remove all players from whitelist and game
for(ArenaPlayer p : arenaPlayers) {
p.getPlayer().setWhitelisted(false);
if(p.isWinner()){
p.kick(ChatColor.GREEN + "You have won the game!");
this.winner = p.getPlayer();
} else {
p.kick(ChatColor.RED + winner.getName() + "has won the game :(");
}
}
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String stats = gson.toJson(this);
KwitArena.postStats(stats);
//Remove this arena instance from the plugin
KwitArena.match = null;
}
public String getId(){
return(this.matchId);
}
public void setWinner(ArenaPlayer p) {
this.winner = p.getPlayer();
}
}
ArenaPlayer.class :
import mc.kwit.arena.Match;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
/**
* Created by Zach on 7/31/16.
*/
public class ArenaPlayer {
ArenaPlayer ap;
Player player;
OfflinePlayer offlinePlayer;
int kills = 0;
int deaths = 0;
boolean isInGame = false;
Match match = null;
boolean isWinner = false;
public ArenaPlayer(Player player){
this.player = player;
}
public ArenaPlayer(Player player, boolean playing, int numKills, int numDeaths) {
this.player = player;
kills = numKills;
deaths = numDeaths;
isInGame = playing;
}
//Getters
public String getMatchId() { return(this.match.getId());}
public int getKills() { return(this.kills);}
public int getDeaths() { return(this.deaths);}
public Match getMatch() { return(this.match);}
public Player getPlayer() { return(this.player);}
public String getName() { return(player.getName());}
public OfflinePlayer getOfflinePlayer() { return(this.offlinePlayer);}
//Setters
public void setMatch(Match match) { this.match = match;}
public void setIsWinner(boolean b) { this.isWinner = b;}
public void setOfflinePlayer(OfflinePlayer off) { this.offlinePlayer = off; }
//Extras
public void addDeath() { this.deaths++;}
public void addKill() { this.kills++;}
public boolean isPlaying() { return(this.isInGame);}
public boolean isWinner() { return(this.isWinner);}
public void kick(String message) { player.kickPlayer(message);}
}
I'm not really sure where to even begin in the stack trace, anyone have some pointers?
I think the problem is that Match has an ArenaPlayer field, and ArenaPlayer has a Match field, which if the two instances reference each other causes Gson to go into an infinite loop due to its implementation.
You'll have to detangle your classes, your instances, or use a different library, or exclude certain fields from serialization - see Gson doc.
BTW, 1000 is the default call stack depth - if you see this many lines in the stack, you've probably hit an infinite recursion.

Writing data from a List field to a .txt file in Java

I have just completed the following program -
//interface IFile
package zad;
public interface IFile {
void readFromFile();
}
//class Student
package zad;
public class Student implements Comparable {
private String studentName;
private int facNum, studentPoints;
public Student(int facNum, String studentName, int studentPoints) {
this.facNum = facNum;
this.studentName = studentName;
this.studentPoints = studentPoints;
}
public void setFacNum(int facNum) {
this.facNum = facNum;
}
public int getFacNum() {
return facNum;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
public String getStudentName() {
return studentName;
}
public void setStudentPoints(int studentPoints) {
this.studentPoints = studentPoints;
}
public int getStudentPoints() {
return studentPoints;
}
public boolean equals(Object o) {
if(o instanceof Student && ((Student) o).getFacNum() == this.facNum) {
return true;
} else {
return false;
}
}
public String toString() {
return ("FacNum = " + facNum + ", name = " + studentName
+ ", points = " + studentPoints );
}
public int compareTo(Object o) {
return Integer.compare(this.facNum, ((Student)o).getFacNum());
}
}
//class StudentsGroup
package zad;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;
public class StudentsGroup implements IFile, Comparable {
private String groupName;
private List<Student> studentsList = new ArrayList<Student>();
public StudentsGroup(String groupName) {
this.groupName = groupName;
}
public void printArrayList() {
for(Student o : studentsList)
System.out.println(o);
}
public int compareTo(Object o) {
if(getTotalPoints(studentsList) > getTotalPoints(((StudentsGroup)o).studentsList))
return 1;
else if(getTotalPoints(studentsList) < getTotalPoints(((StudentsGroup)o).studentsList))
return -1;
else
return 0;
}
public List getList() {
return studentsList;
}
public static int getTotalPoints(List<Student> studentsList1) {
int totalPoints = 0;
for(Student o : studentsList1) {
totalPoints += o.getStudentPoints();
}
return totalPoints;
}
public void sortByPoints() {
Collections.sort(studentsList);
}
public void readFromFile() {
Scanner sc;
try {
sc = new Scanner(new File(groupName));
while(sc.hasNext()) {
int facNum = sc.nextInt();
String studentName = sc.next();
int studentPoints = sc.nextInt();
Student object = new Student(facNum, studentName, studentPoints);
studentsList.add(object);
}
sc.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
#Override
public String toString() {
return "StudentsGroup [groupName=" + groupName + ", studentsList=" + studentsList + "]";
}
}
//class main
package zad;
import java.io.FileNotFoundException;
public class demo {
public static void main(String[] args) throws FileNotFoundException {
StudentsGroup studentsGroup1 = new StudentsGroup("D://test.txt");
StudentsGroup studentsGroup2 = new StudentsGroup("D://test2.txt");
studentsGroup1.readFromFile();
studentsGroup2.readFromFile();
studentsGroup1.printArrayList();
studentsGroup1.sortByPoints();
studentsGroup1.printArrayList();
int compareResult = studentsGroup1.compareTo(studentsGroup2);
switch(compareResult) {
case 0: System.out.println("The two lists are equal by points.");
break;
case 1: System.out.println("The first list is larger than the second.");
break;
case -1: System.out.println("The first list is smaller than the second.");
break;
}
}
}
In general, it makes an object from class StudentsGroup, reads from a file and adds to an ArrayList field, as objects of another class - Student.
How should I implement a method to write that data to a new file? Any thoughts on that?
Note: also, if possible, I would like some tips on my coding to help me write better code. Am I doing something completely wrong or unnecessary in my program? The method getTotalPoints needs to be declared as static, so that is not discussed.
UPDATE:
When I try to write the data to a file with the following code:
FileOutputStream out = new FileOutputStream("D://test3.txt");
ObjectOutputStream oout = new ObjectOutputStream(out);
for(Student o : studentsList)
oout.writeObject(o);
out.close();
oout.close();
I get an error:
Exception in thread "main" java.io.NotSerializableException: zad.Student
at java.io.ObjectOutputStream.writeObject0(Unknown Source)
at java.io.ObjectOutputStream.writeObject(Unknown Source)
at zad.StudentsGroup.writeToFile(StudentsGroup.java:80)
at zad.demo.main(demo.java:27)
Am I doing something wrong?
According to the documentation for ObjectOutputStream, the writeObject method throws a NotSerializableException because Students does not implement Serializable.
NotSerializableException - Some object to be serialized does not implement the java.io.Serializable interface.
Update your class signature to the following and implement any methods required by Serializable.
public class Student implements Comparable, Serializable
By using the ObjectOutputStream.writeObject method you have no control over the output. If you want to control how the content is actually output to the file you'll want to look into an alternative writer. Look into examples of using BufferedWriter. You could then pass Student.toString() to the writer and control the way in which the data shows up. For instance, your toString() method in Student could output field1 + "\t" + field2 + "\t" + field3 + "\t" + field4 - and you'd essentially have a tab-delimited file that you could then, for instance, import into Excel.

Java ArrayList.contains() & add() method

So I'm having this problem with adding an element to an ArrayList
I have a class Media with 3 fields and another class Mediatheque with 1 field(which is an ArrayList).
Let's say I have:
A Mediatheque media = new Mediatheque
An equals(Media m) method in class Media < (important method)
I need to write a method add(Media m) which:
If the media.contenu does contain an element equals to the Media m I want to add, I must NOT add it and increase the nbEx field of the element contained in media.contenu
-Else I can add it using the add method provided by the ArrayList ( This doesn't seem too hard)
So I tried to write a contains(Media) method which uses the equals(Media m) method I wrote for the Media class and then use the contains method in the add method.
My question is that how am I supposed to write the add method? < (The Question)
I must write this using ArrayList, it is a school assignment
Sorry about the long code and the bad English, I'm a complete noob.
Here is my Media class:
package Ex1;
public class Media {
private final String support; // Format: Book, CD, DVD,etc...
private final String titre; // Title
private int nbEx; // Number of copy
public Media(String titre, String support){
this.titre = titre;
this.support = support;
this.nbEx = 1;
}
public Media (){
titre = "";
support = "";
nbEx = 0;
}
public boolean equals(Media m){
boolean equality = false;
if (m instanceof Media){
equality = (this.titre.equals(m.titre) && this.support.equals(m.support));
}
return equality;
}
public Media(Media m){
this.titre = m.titre;
this.support = m.support;
}
}
And here is my Mediatheque class:
import java.util.ArrayList;
import static java.lang.System.out;
public class Mediatheque {
ArrayList<Media> contenu;
public Mediatheque(){
this.contenu = new ArrayList<Media>();
}
public Mediatheque(Mediatheque m){
this.contenu = m.contenu;
}
public boolean contains(Media m){
int i = 0;
boolean contain = this.contenu.get(i).equals(m);
for(i = 0; i<this.contenu.size(); i++){
if(contain)
break;
}
return contain;
}
public int indexOf(Media m){
boolean retVal = this.contenu.get(i).equals(m);
for(Media i : contenu){
if(contain)
break;
}
return i;
}
public void add(Media m){
if(this.contains(m)){
this.contenu.get(this.contenu.indexOf(m)).setNbEx(this.contenu.get(this.contenu.indexOf(m)).getNbEx()+m.getNbEx());
}else{
this.contenu.add(m);
}
}
My question is that how am I supposed to write the add method?
Sorry about the long code and the bad English, I'm a complete noob.
Thank you!
As stated by #NeplatnyUdaj in the comment of your question, the use of a Map would greatly improve your code. Instead of recording the number of medias inside the Media object, use a HashMap<Media, Integer> to store your data in this way:
new HashMap<Media, Integer> map = new HashMap<Media,Integer>();
if ( map.containsKey(key) ) {
map.put(key, (map.get(key) + 1));
} else {
map.put(key, 1);
}
Where key is the media. (m in your code)
When one overrides the equals() method, one is also supposed to override the hashCode() method. The equals() method takes an Object parameter. Here's how your Media class should look like:
// Media.java
public class Media
{
private final String support;
private final String title;
public Media(String title, String support)
{
this.title = title;
this.support = support;
}
public Media(Media media)
{
this(media.title, media.support);
}
#Override
public int hashCode()
{
return 31 * title.hashCode() + support.hashCode();
}
#Override
public boolean equals(Object object)
{
if (object instanceof Media)
{
Media media = (Media) object;
return media.title.equals(title) &&
media.support.equals(support);
}
return false;
}
}
Then use a HashMap to map the media with its number of copies. Here's how that's done:
// MediaMap.java
import java.util.HashMap;
import java.util.Map;
public class MediaMap
{
// Media to its Number of Copies mapping.
private Map<Media, Integer> mediaMap;
public MediaMap()
{
mediaMap = new HashMap<>();
}
public void add(Media media)
{
mediaMap.put(media, mediaMap.getOrDefault(media, 0) + 1);
}
public void removeOneMedia(Media media)
{
if (mediaMap.containsKey(media))
{
mediaMap.put(media, mediaMap.get(media) - 1);
}
}
// And so on...
}
Without overriding the hashCode() method in the Media class, the hash based collections won't work as expected.
You can also have a look at MultiSet data structure, and use that instead.
If you are to use ArrayList then here's how its done:
// Media.java
public class Media
{
private final String support;
private final String title;
private int numberOfCopies;
public Media(Media media)
{
this(media.title, media.support, media.numberOfCopies);
}
public Media(String title, String support, int numberOfCopies)
{
this.title = title;
this.support = support;
this.numberOfCopies = numberOfCopies;
}
#Override
public int hashCode()
{
return 31 * title.hashCode() + support.hashCode();
}
#Override
public boolean equals(Object object)
{
if (object instanceof Media)
{
Media media = (Media) object;
return media.title.equals(title) &&
media.support.equals(support);
}
return false;
}
public int getNumberOfCopies()
{
return numberOfCopies;
}
public void setNumberOfCopies(int numberOfCopies)
{
this.numberOfCopies = numberOfCopies;
}
}
And here's a MediaList class which uses ArrayList:
// MediaList.java
import java.util.ArrayList;
public class MediaList
{
private ArrayList<Media> mediaList;
public MediaList()
{
mediaList = new ArrayList<>();
}
public void add(Media media)
{
set(media, +1);
}
public void remove(Media media)
{
set(media, -1);
}
private void set(Media media, int change)
{
if (change == 0)
{
return;
}
int indexOfMedia = mediaList.indexOf(media);
if (indexOfMedia != -1)
{
Media m = mediaList.get(indexOfMedia);
m.setNumberOfCopies(m.getNumberOfCopies() + change);
if (change < 0 && m.getNumberOfCopies() <= 0)
{
mediaList.remove(media);
}
}
else if (change > 0)
{
mediaList.add(media);
}
}
// And so on...
}
I have refactored your classes a little bit. I also implemented an add method. I assumed that you want to add media to the mediatheque if it is not already in the list. If it is in the list you want to add the nbex to the nbex that the item in the list has, right?
As the others I would advise you to use a HashMap() for counting if you don't need the number for your media objects.
Media.class
public class Media {
private final String support; // Format: Book, CD, DVD,etc...
private final String titre; // Title
private int nbEx; // Number of copy
public Media(String titre, String support){
this.titre = titre;
this.support = support;
this.nbEx = 1;
}
public Media(Media m){
this(m.titre, m.support);
}
public Media (){
this("", "");
nbEx = 0;
}
public boolean equals(Media m){
if (m instanceof Media){
return (this.titre.equals(m.titre) && this.support.equals(m.support));
}
return false;
}
}
Mediatheque.class
public class Mediatheque {
ArrayList<Media> contenu;
public Mediatheque(){
this.contenu = new ArrayList<Media>();
}
public Mediatheque(Mediatheque m){
this.contenu = m.contenu;
}
public boolean contains(Media m){
for(Media media: this.contenu) {
if(media.equals(m) {
return true;
}
}
return false;
}
public int indexOf(Media m){
if(this.contenu.contains(m) {
return this.contenu.indexOf(m);
}
return -1;
}
public void add(Media m){
if(this.contains(m)) {
Media media = this.contenu.get(this.contenu.indexOf(m));
media.setNbex(media.getNbex() + m.getNbex());
} else {
this.contenu.add(m);
}
}
}
Hope this helps.

Is there a standard library/API/tool for implementing basic "rule chains" in Java?

I am writing a servlet that will be conditionally modifying HTTP headers according to some user-definable rules. (Edit: these rules are defined in an XML file that is read at start-up.) For example, add "X-UA-Compatible: IE=edge,chrome=1" to a response header if it does not already exist and if the request specified a "User-Agent" header matching a known pattern. Not having any better ideas, I attempted to make my own POJOs representing these rules. It "works" but I feel like there must be a more standard or more flexible way to do this.
Are there general-purpose libraries or tools (whether built-in or 3rd-party) that would solve this problem? I have heard and read a little about "rules engines" but they seem like much more complex/heavy tools not meant for problems as simple as mine.
To illustrate what I'm trying to do, I've created a simplified program that applies "rules" to numbers based on "conditions" like "is an even number". Here it is, sorry it's a bit lengthy.
Main.java
package my.example;
import java.util.*;
import my.example.conditions.*;
import my.example.rules.*;
public class Main {
public static void main(String args[]) {
// Some sample objects to evaluate
Collection<Integer> numbers = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8);
print(numbers);
// Define rules
Collection<Rule<Integer>> rules = new ArrayList<Rule<Integer>>();
rules.add(createRuleToMultiplyEvenNumbersBy4());
rules.add(createRuleToAdd1ToEveryNumber());
// Process the rules for each sample object
Collection<Integer> newNumbers = new ArrayList<Integer>();
for (Integer number : numbers) {
Integer newNumber = number;
for (Rule<Integer> rule : rules)
newNumber = rule.apply(newNumber);
newNumbers.add(newNumber);
}
print(newNumbers);
}
private static Rule<Integer> createRuleToMultiplyEvenNumbersBy4() {
MultiplyNumberRule rule = new MultiplyNumberRule(true, 4);
rule.addCondition(new NumberIsEvenCondition());
return rule;
}
private static Rule<Integer> createRuleToAdd1ToEveryNumber() {
AddNumberRule rule = new AddNumberRule(true, 1);
rule.addCondition(new ConstantCondition<Integer>(true));
return rule;
}
private static void print(Collection<Integer> numbers) {
System.out.print("Numbers: ");
for (Integer number : numbers) {
System.out.print(number);
System.out.print(" ");
}
System.out.print("\r\n");
}
}
Condition.java
package my.example.conditions;
public interface Condition<T> {
boolean appliesTo(T obj);
}
ConstantCondition.java
package my.example.conditions;
public class ConstantCondition<T> implements Condition<T> {
private boolean constant;
public ConstantCondition(boolean alwaysReturnThisValue) {
constant = alwaysReturnThisValue;
}
#Override
public boolean appliesTo(T target) {
return constant;
}
}
NumberIsEvenCondition.java
package my.example.conditions;
public class NumberIsEvenCondition implements Condition<Integer> {
#Override
public boolean appliesTo(Integer i) {
return (i % 2 == 0);
}
}
Rule.java
package my.example.rules;
public interface Rule<T> {
T apply(T target);
}
AbstractRule.java
package my.example.rules;
import java.util.*;
import my.example.conditions.Condition;
public abstract class AbstractRule<T> implements Rule<T> {
private Collection<Condition<T>> conditions;
private boolean requireAllConditions;
public AbstractRule(boolean requireAllConditions) {
conditions = new ArrayList<Condition<T>>();
this.requireAllConditions = requireAllConditions;
}
public void addCondition(Condition<T> condition) {
conditions.add(condition);
}
#Override
public T apply(T target) {
boolean isApplicable;
if (requireAllConditions)
isApplicable = allConditionsSatisfied(target);
else
isApplicable = atLeastOneConditionSatisfied(target);
if (isApplicable)
target = process(target);
return target;
}
// Check if all conditions are met
protected boolean allConditionsSatisfied(T target) {
for (Condition<T> condition : conditions) {
if (!condition.appliesTo(target))
return false;
}
return true;
}
// Check if any conditions are met
protected boolean atLeastOneConditionSatisfied(T target) {
for (Condition<T> condition : conditions) {
if (condition.appliesTo(target))
return true;
}
return false;
}
abstract T process(T target);
}
AddNumberRule.java
package my.example.rules;
public class AddNumberRule extends AbstractRule<Integer> {
private Integer addend;
public AddNumberRule(boolean requireAllConditions) {
this(requireAllConditions, 0);
}
public AddNumberRule(boolean requireAllConditions, Integer addend) {
super(requireAllConditions);
this.addend = addend;
}
#Override
public Integer process(Integer i) {
return i + addend;
}
}
MultiplyNumberRule.java
package my.example.rules;
public class MultiplyNumberRule extends AbstractRule<Integer> {
private Integer factor;
public MultiplyNumberRule(boolean requireAllConditions) {
this(requireAllConditions, 1);
}
public MultiplyNumberRule(boolean requireAllConditions, Integer factor) {
super(requireAllConditions);
this.factor = factor;
}
#Override
public Integer process(Integer i) {
return i * factor;
}
}
Well, I'd use Commons Chain
A popular technique for organizing the execution of complex processing
flows is the "Chain of Responsibility" pattern, as described (among
many other places) in the classic "Gang of Four" design patterns book.
Although the fundamental API contracts required to implement this
design patten are extremely simple, it is useful to have a base API
that facilitates using the pattern, and (more importantly) encouraging
composition of command implementations from multiple diverse sources.
it's a common Design Pattern, guess that fits your problem
I have modified the original code attempting to use Commons Chain, but it doesn't seem much different. Luiz E., is this roughly what you are suggesting? It seems like commons-chain does not include any notion of "conditions" -- this is left to the user as part of the implementation of Command.
Main.java
package my.example;
import java.util.*;
import org.apache.commons.chain.*;
import org.apache.commons.chain.impl.*;
import my.example.commands.*;
import my.example.conditions.*;
public class Main {
private static final String NUMBERS = "numbers";
public static void main(String args[]) throws Exception {
// Some sample objects to evaluate
Context context = new ContextBase();
setNumbersInContext(context, Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8));
printNumbersFromContext(context);
// Define rules
Chain ruleChain = new ChainBase();
ruleChain.addCommand(new MultiplyNumberCommand(4, new NumberIsEvenCondition()));
ruleChain.addCommand(new AddNumberCommand(1));
// Process the rules
ruleChain.execute(context);
printNumbersFromContext(context);
}
private static void printNumbersFromContext(Context context) {
Collection<Integer> numbers = getNumbersFromContext(context);
System.out.print("Numbers: ");
for (Integer number : numbers) {
System.out.print(number);
System.out.print(" ");
}
System.out.print("\r\n");
}
#SuppressWarnings("unchecked")
public static Collection<Integer> getNumbersFromContext(Context context) {
Object obj = context.get(NUMBERS);
try {
return (Collection<Integer>) obj;
}
catch (ClassCastException e) {
throw new IllegalStateException("Context did not contain the required data. ClassCastException message is: " + e.getMessage());
}
}
#SuppressWarnings("unchecked")
public static void setNumbersInContext(Context context, Collection<Integer> numbers) {
context.put(NUMBERS, numbers);
}
}
AbstractNumberCommand.java
package my.example.commands;
import static my.example.Main.getNumbersFromContext;
import static my.example.Main.setNumbersInContext;
import java.util.ArrayList;
import java.util.Collection;
import my.example.conditions.Condition;
import org.apache.commons.chain.*;
public abstract class AbstractNumberCommand implements Command {
private boolean continueProcessing = true;
protected Condition<Integer> condition;
protected AbstractNumberCommand(Condition<Integer> condition) {
this.condition = condition;
}
public void continueProcessing(boolean continueProcessing) {
this.continueProcessing = continueProcessing;
}
#Override
public boolean execute(Context context) throws Exception {
Collection<Integer> numbers = getNumbersFromContext(context);
Collection<Integer> newNumbers = new ArrayList<Integer>();
for (int number : numbers)
if (condition.appliesTo(number))
newNumbers.add(modifyNumber(number));
else
newNumbers.add(number);
setNumbersInContext(context, newNumbers);
if (continueProcessing)
return CONTINUE_PROCESSING;
else
return PROCESSING_COMPLETE;
}
protected abstract int modifyNumber(int number);
}
AddNumberCommand.java
package my.example.commands;
import my.example.conditions.*;
public class AddNumberCommand extends AbstractNumberCommand {
private int addend;
public AddNumberCommand() {
this(0);
}
public AddNumberCommand(int addend) {
this(addend, new ConstantCondition<Integer>(true));
}
public AddNumberCommand(int addend, Condition<Integer> condition) {
super(condition);
this.addend = addend;
}
#Override
protected int modifyNumber(int number) {
return number + addend;
}
}
Condition.java
package my.example.conditions;
public interface Condition<T> {
boolean appliesTo(T obj);
}
ConstantCondition.java
package my.example.conditions;
public class ConstantCondition<T> implements Condition<T> {
private boolean constant;
public ConstantCondition(boolean alwaysReturnThisValue) {
constant = alwaysReturnThisValue;
}
#Override
public boolean appliesTo(T target) {
return constant;
}
}
NumberIsEvenCondition.java
package my.example.conditions;
public class NumberIsEvenCondition implements Condition<Integer> {
#Override
public boolean appliesTo(Integer i) {
return (i % 2 == 0);
}
}

Categories