How to use addAll and retainAll methods in HashSet - java

When I try to compile I get an error message for my makeUnion method and I'm guessing I will get one for makeIntersection too. I'm not sure why this is, or how to implement makeUnion if I want to add a set interface to the new set. Can someone explain to me what I am doing wrong?
public class Set<T> implements SetInterface<T>
{
private HashSet<T> set;
/**
* Constructs a new empty set.
*/
public Set () {
set = new HashSet <>();
}
/**
* Constructs a new set containing the elements in the specified collection.
* Default load factor of 0.75 and initial capacity of 50.
*
* #param c- the collection whose elements are to be place into this set
*/
public Set(Collection <? extends T> c) {
set = new HashSet<>(Math.max((int) (c.size()/.75f) + 1, 50));
set.addAll(c);
}
/**
* Constructs a new empty set. Default load factor of 0.75.
*
* #param initialCapacity- the initial capacity of the hash table
*/
public Set(int initialCapacity) {
set = new HashSet <>(initialCapacity);
}
/**
* Constructs a new empty set.
* Hashmap has specified initial capacity and specified load factor.
*
* #param initialCapacity- the initial capacity of the hash table
* loadFactor- the load factor of the hash map
*/
public Set(int initialCapacity, float loadFactor) {
set = new HashSet <>(initialCapacity, loadFactor);
}
/**
* Add an item of type T to the interface Duplicate items will not be
* added to the set.
*
* #param itemToAdd - what to add.
*/
public void add(T itemToAdd) {
set.add(itemToAdd);
}
/**
* Removes an item from the set ( if the item is in the set) If the item is not
* in the set this operation does nothing
*
* #param item to remove.
*/
public void remove( T itemToDelete) {
set.remove(itemToDelete);
}
/**
* Return if the SetInterface contains an item
*
* #param itemToCheck. The item you are looking for
* #return true if found. False if not found.
*/
public boolean contains( T itemToCheck) {
return set.contains(itemToCheck);
}
/**
* Make a union of two sets. We add all items in either set to a new set and
* return the new set.
*
* #param the 'other' set to add to our set.
* #return A new set which is the union of the two sets.
*/
public Set<T> makeUnion( SetInterface<T> otherSet) {
return set.addAll(otherSet);
}
/**
* Make an intersection of two sets. We add create a new set which only has
* items in it that are contained in both sets.
*
* #param the 'other' set to intersect with
* #return A new set which is the intersection of the two sets.
*/
public Set<T> makeIntersection( SetInterface<T> otherSet) {
return set.retainAll(otherSet);
}
/**
* Return an iterator for the set. This is used to walk thought all elements
* in the set
*
* #return The iterator
*/
public Iterator<T> getIterator() {
return set.iterator();
}
/**
* Tell the caller how many elements are in the set
*
* #return int with the number of elements
*/
public int size() {
return set.size();
}
}

interface SetInterface<T> {
void add(T itemToAdd);
public void remove( T itemToDelete);
public boolean contains( T itemToCheck);
public Set<T> makeUnion( SetInterface<T> otherSet);
public Iterator<T> getIterator();
public int size();
HashSet<T> getSet();
}
public class Set<T> implements SetInterface<T>
{
private HashSet<T> set;
public HashSet<T> getSet() {
return set;
}
/**
* Constructs a new empty set.
*/
public Set () {
set = new HashSet <>();
}
/**
* Constructs a new set containing the elements in the specified collection.
* Default load factor of 0.75 and initial capacity of 50.
*
* #param c- the collection whose elements are to be place into this set
*/
public Set(Collection <? extends T> c) {
set = new HashSet<>(Math.max((int) (c.size()/.75f) + 1, 50));
set.addAll(c);
}
/**
* Constructs a new empty set. Default load factor of 0.75.
*
* #param initialCapacity- the initial capacity of the hash table
*/
public Set(int initialCapacity) {
set = new HashSet <>(initialCapacity);
}
/**
* Constructs a new empty set.
* Hashmap has specified initial capacity and specified load factor.
*
* #param initialCapacity- the initial capacity of the hash table
* loadFactor- the load factor of the hash map
*/
public Set(int initialCapacity, float loadFactor) {
set = new HashSet <>(initialCapacity, loadFactor);
}
/**
* Add an item of type T to the interface Duplicate items will not be
* added to the set.
*
* #param itemToAdd - what to add.
*/
public void add(T itemToAdd) {
set.add(itemToAdd);
}
/**
* Removes an item from the set ( if the item is in the set) If the item is not
* in the set this operation does nothing
*
* #param item to remove.
*/
public void remove( T itemToDelete) {
set.remove(itemToDelete);
}
/**
* Return if the SetInterface contains an item
*
* #param itemToCheck. The item you are looking for
* #return true if found. False if not found.
*/
public boolean contains( T itemToCheck) {
return set.contains(itemToCheck);
}
/**
* Make a union of two sets. We add all items in either set to a new set and
* return the new set.
*
* #param the 'other' set to add to our set.
* #return A new set which is the union of the two sets.
*/
public Set<T> makeUnion( SetInterface<T> otherSet) {
set.addAll(new java.util.ArrayList<T>(otherSet.getSet()));
return this;
}
/**
* Return an iterator for the set. This is used to walk thought all elements
* in the set
*
* #return The iterator
*/
public Iterator<T> getIterator() {
return set.iterator();
}
/**
* Tell the caller how many elements are in the set
*
* #return int with the number of elements
*/
public int size() {
return set.size();
}
}

Related

Is there a Programming Concept of Additive/Supplemental ForEach?

My question contains 2 subquestions, therefore it is not limited to java but takes programming paradigms in general into consideration.
I need a Loop which does a usual "forEach" but also provides for each index its previous runs. Let me elaborate.
Let's define:
OBJECTS = a collection of X, where X is just any Type of Object/Data
PREV = the results of the previous iterations as a Collection of Y, Y beeing the result of a single iteration of an X
what "my" suplementalForEach would do is:
OBJECTS.supplementalForEach((PREV, OBJ) -> doStuff(PREV, OBJ))
or as non-stream variant
var PREV = new ArrayList<>();
for(var OBJ : OBJECTS) {
var result = doStuff(PREV, OBJ);
PREV.add(result);
}
So in essence in Java this would be a forEach, taking a BiFunction<R, T> instead of Consumer<T>
The Question now is, is there already a programming concept to this idea? If yes, what is it called? And if yes, is there a Java Implementation?
If no i can just implement it myself but this is a curious question of mine. Btw i didn't find anything yet on Google and Stackoverflow to this.
EDIT2:
as of now i have implemented these methods, feel free to use them as you please (Keep the comments and references)
/**
* #author levent.dag
* https://stackoverflow.com/users/9648047/levent-dag
*
* naming idea with "akuzminykh"
* https://stackoverflow.com/users/12323248/akuzminykh , 02.06.2020
*
* <pre>
* Cumulative Iteration:
* the process of iterating with the results of the previous iterations
*
* definition:
* D domain (collection of X)
* X ∈ of domain, Data/Object/Value
* R results of previous iterations (List<Y>)
*
* f(R, X) iteration cycle/Function for current X element with the knowledge of previous iteration results R
*
*
* for X ∈ D {
* R.add(R, X)
* }
*
* ORDER sensible! use ordered Collections such as Lists and so on.
* </pre>
* #param <X>
* #param <Y>
* #param col
* #param loopIteration
*/
public static <X, Y, LIST extends List<Y>> void cumulativeIteration(final Collection<X> col, final BiFunction<List<Y>, X, Y> loopIteration, Class<Y> clazz) {
final ArrayList<Y> previous = new ArrayList<>();
col.forEach(x -> previous.add(loopIteration.apply(previous, x)));
}
/**
* #author levent.dag
* https://stackoverflow.com/users/9648047/levent-dag
*
* naming idea with "akuzminykh"
* https://stackoverflow.com/users/12323248/akuzminykh , 02.06.2020
*
* Same as {#link Essentials#additiveIteration(Collection, BiFunction)}, but the previous results can be mapped.
*
* #param <X>
* #param <Y>
* #param col
* #param loopIteration
* #param mapper
*/
public static <X, Y> void mappedCumulativeIteration(final Collection<X> col, final BiFunction<Map<String, Y>, X, Y> loopIteration, final Function<X, String> mapper) {
final Map<String, Y> previous = new LinkedHashMap<>();
col.forEach(x -> previous.put(mapper.apply(x), loopIteration.apply(previous, x)));
}
additionally as #daniu has pointed out you could achieve this with a collector, if you want to collect the given data.
col.stream().<List<Y>>collect(()->new ArrayList<>(), (pre, current) -> pre.add(loopIteration.apply(pre, current)), List::addAll);
Or as a standalone Collector
/**
* #author levent.dag
* https://stackoverflow.com/users/9648047/levent-dag
*
* naming idea with "akuzminykh"
* https://stackoverflow.com/users/12323248/akuzminykh , 02.06.2020
*
* <pre>
* Cumulative Iteration:
* the process of iterating with the results of the previous iterations
*
* definition:
* D domain (collection of X)
* X ∈ of domain, Data/Object/Value
* R results of previous iterations (List<Y>)
*
* f(R, X) iteration cycle/Function for current X element with the knowledge of previous iteration results R
*
*
* for X ∈ D {
* R.add(R, X)
* }
*
* ORDER sensible! use ordered Collections such as Lists and so on.
* </pre>
*
* #param <OBJECT>
* #param <RESULT>
*/
public class CumulativeListCollector<OBJECT, RESULT> implements Collector<OBJECT, List<RESULT>, List<RESULT>> {
private final BiFunction<List<RESULT>, OBJECT, RESULT> loopIteration;
public CumulativeListCollector(final BiFunction<List<RESULT>, OBJECT, RESULT> loopIteration) {
this.loopIteration = loopIteration;
}
public static <O,R> CumulativeListCollector<O, R> of(final BiFunction<List<R>, O, R> loopIteration) {
return new CumulativeListCollector<>(loopIteration);
}
#Override
public Supplier<List<RESULT>> supplier() {
return ()->new ArrayList<RESULT>();
}
#Override
public BiConsumer<List<RESULT>, OBJECT> accumulator() {
return (pre, current) -> pre.add(this.loopIteration.apply(pre, current));
}
#Override
public Function<List<RESULT>, List<RESULT>> finisher() {
return Function.identity();
}
#Override
public BinaryOperator<List<RESULT>> combiner() {
return (left, right)->{left.addAll(right); return left;};
}
#Override
public Set<Characteristics> characteristics() {
return Set.of();
}
}

Java problem - method undefined even though I already defined it in the package

I'm a total beginner in Java and I'm working on an assignment that's largely prefabricated/preformatted code, but I can't get it to work. In Eclipse, I get an error saying "Method cardToString(MyCard) is undefined for the type MyCardTester" before I even run it. I've looked at similar questions on Stackoverflow,
(Eclipse is telling me a method is undefined when it clearly is in fact defined, "The method is not defined for the type" error in simple program in java)
and they have different problems from me. I think that my problem may be with my classpath or run configuration, but those settings seem fine. Here is the code:
Here's the first class:
package EllevensGame;
import EllevensGame.MyCard;
import java.lang.String;
/**
* This is a class that tests the Card class.
*/
public class MyCardTester {
/**
* The main method in this class checks the Card operations for consistency.
* #param args is not used.
*/
public static void main(String[] args) {
MyCard testCard = new MyCard("King", "Hearts", 13);
String printStuff = cardToString(testCard);
System.out.println(printStuff);
}
}
Second class:
package EllevensGame;
/**
* MyCard.java
*
* <code>MyCard</code> represents a playing card.
*/
import java.lang.String;
public class MyCard {
/**
* String value that holds the suit of the card
*/
protected String suit;
/**
* String value that holds the rank of the card
*/
protected String rank;
/**
* int value that holds the point value.
*/
protected int pointValue;
/**
* Creates a new <code>Card</code> instance.
*
* #param cardRank a <code>String</code> value
* containing the rank of the card
* #param cardSuit a <code>String</code> value
* containing the suit of the card
* #param cardPointValue an <code>int</code> value
* containing the point value of the card
*/
public MyCard(String cardRank, String cardSuit, int cardPointValue) {
//MyCard newCard = new MyCard(cardRank, cardSuit, cardPointValue); Not sure if this is right or not
}
/**
* Accesses this <code>Card's</code> suit.
* #return this <code>Card's</code> suit.
*/
public String suit() {
return suit;
}
/**
* Accesses this <code>Card's</code> rank.
* #return this <code>Card's</code> rank.
*/
public String rank() {
return rank;
}
/**
* Accesses this <code>Card's</code> point value.
* #return this <code>Card's</code> point value.
*/
public int pointValue() {
return pointValue;
}
/** Compare this card with the argument.
* #param otherCard the other card to compare to this
* #return true if the rank, suit, and point value of this card
* are equal to those of the argument;
* false otherwise.
*/
public boolean matches(MyCard otherCard) {
if (otherCard.pointValue == (pointValue()) && (otherCard.rank.equals(rank)) && (otherCard.suit.equals(suit))) {
return true;
}
else {return false;}
}
/**
* Converts the rank, suit, and point value into a string in the format
* "[Rank] of [Suit] (point value = [PointValue])".
* This provides a useful way of printing the contents
* of a <code>Deck</code> in an easily readable format or performing
* other similar functions.
*
* #return a <code>String</code> containing the rank, suit,
* and point value of the card.
*/
//#Override
public String cardToString(MyCard newCard) {
String pointstring = String.valueOf(pointValue);
String print = rank + " of " + suit + pointstring;
return print;
}
}
Final note: the code is supposed to create a "card" object for a card game (Ellevens).
Thanks!!
cardToString is a method in MyCard, you must invoke it through the reference. Change
String printStuff = cardToString(testCard);
to
String printStuff = testCard.cardToString(testCard);
Although it might be better to make that method return a String based on the this instance (which would make more sense).
public String cardToString() {
return rank + " of " + suit + pointValue;
}
And then
String printStuff = testCard.cardToString();
I then fixed your constructor
public MyCard(String cardRank, String cardSuit, int cardPointValue) {
this.rank = cardRank;
this.suit = cardSuit;
this.pointValue = cardPointValue;
}
And ran it getting
King of Hearts13

Pair swap bug in commons-lang3 EqualsBuilder?

The following is a part source code of org.apache.commons.lang3.builder.EqualsBuilder. What is the difference between pair and swappedPair ? why not swappedPair = Pair.of(pair.getRight(), pair.getLeft());
static Pair<IDKey, IDKey> getRegisterPair(final Object lhs, final Object rhs) {
final IDKey left = new IDKey(lhs);
final IDKey right = new IDKey(rhs);
return Pair.of(left, right);
}
/**
* <p>
* Returns <code>true</code> if the registry contains the given object pair.
* Used by the reflection methods to avoid infinite loops.
* Objects might be swapped therefore a check is needed if the object pair
* is registered in given or swapped order.
* </p>
*
* #param lhs <code>this</code> object to lookup in registry
* #param rhs the other object to lookup on registry
* #return boolean <code>true</code> if the registry contains the given object.
* #since 3.0
*/
static boolean isRegistered(final Object lhs, final Object rhs) {
final Set<Pair<IDKey, IDKey>> registry = getRegistry();
final Pair<IDKey, IDKey> pair = getRegisterPair(lhs, rhs);
final Pair<IDKey, IDKey> swappedPair = Pair.of(pair.getLeft(), pair.getRight());
return registry != null
&& (registry.contains(pair) || registry.contains(swappedPair));
}

Genetic processes help in java

Merged with Genetic Programming help.
I need some guidance on a java project. I'm to develop a human genetic(family) tree.
This is the subject:
Each human cell contains 23 pairs of chromosomes numbered from 1 to 22,and a pair of sex chromosomes: XX in females and XY in man. During fertilization, the 22 chromosomes + (X or Y) of the man merges with the 22 + X chromosome of the woman. This results in 22 pairs of chromosomes + (X or Y) in the cell that will form the future baby. The 23rd chromosome transmitted by the father (an X or Y) will determine the sex of the child (XX for a girl, XY for a boy).
Each chromosome carries many genes (encoding almost everything, a characteristic morphological, physiological, behavioral). Due to pairs of chromosomes, the genetic information is duplicated (except for parts of the sex chromosomes). Each copy of a gene is called an allele. So that means for example, if the a gene is responsible for the color of an eye, then the allele is blue.
The genetic information expressed as a consequence of the combined expression of alleles being present. A dominant allele is always expressed in the genome of its bearer. However, if information from one allele is not expressed when a dominant allele of the same gene is present, it is a recessive allele. The peculiarity of the recessive allele of a gene is that it can be present in the genome and transmitted over several generations without it is expressed in the phenotype its bearers. If there is no dominant allele, two copies of the gene have the same recessive allele (homozygous recessive) then the recessive character is expressed. Through the use of family tree, it is possible to determine the expression of a gene within a family.
The program should be able to do the following:
generate a simplified version relied on 23 chromosomes and allow the user to place genes on chromosomes then simulate replication, mitosis, meiosis and fusion of the chromosomes and display the locations of genes on the resulting cells.
allow to draw genealogical trees and deduced probabilities (or certainty) on the expression of genes on a person's family tree.
So far so good i've created a class Gene and a class Chromosome. Next, i thought about creating a class "Parent" for example and creating 23 chromosomes in it. But before doing that i want to make the 23rd chromosome different for a man/woman. Then simulates replication, crossovers, mitosis etc.. I don't know if i'm on the right track. I also don't know how to specify that a particular allele/gene is recessive or dominant. For the moment my classes only act in a random manner.
Gene.java
import java.util.Random;
/**
* #author mkab
*
*/
public class Gene implements Cloneable {
private Object allele;
public Gene(){
super();
}
public Gene(Object allele){
super();
this.allele = allele;
}
/**
* Randomly selects a trait from trait1 or trait2 and returns a new Gene with that trait
* #param trait1
* #param trait2
*
* #return a new Gene
*/
public Gene randomAllele(Object trait1, Object trait2){
Object allele = null;
Random rand = new Random();
int i = rand.nextInt(2);// generate between 0 and 2: only 2 possibilities: 0 or 1
switch(i){
case 0:
allele = trait1;
break;
case 1:
allele = trait2;
break;
}
return new Gene(allele);
}
public Gene clone() throws CloneNotSupportedException{
Gene g;
g = (Gene) super.clone();
return g;
}
/**
* #param allele the allele to set
*/
public void setAllele(Object allele) {
this.allele = allele;
}
/**
* #return the allele
*/
public Object getAllele() {
return allele;
}
/* (non-Javadoc)
* #see java.lang.Object#toString()
*/
#Override
public String toString() {
return "Gene [allele=" + allele +"]";
}
}
Chromosome.java
import java.util.ArrayList;
import java.util.Iterator;
/**
* Class that creates a pair of chromosome by adding random genes
* #author mkab
*
*/
public class Chromosome implements Cloneable {
/**
* the user can put as many genes as possible on the chromosome
*/
private ArrayList<Gene> genes = new ArrayList<Gene>();
public Chromosome(){
super();
}
public Chromosome(ArrayList<Gene> genes){
this.genes = genes;
}
/**
* Add a gene object to this chromosomes list.
*/
public void addGene(Gene gene) {
genes.add(gene);
}
/**
*creates a copy of a chromosome
*/
#SuppressWarnings("unchecked")
#Override
public Chromosome clone()throws CloneNotSupportedException{
Chromosome c;
c = (Chromosome) super.clone();
c.genes = (ArrayList<Gene>)this.genes.clone();
//Iterator<Gene> it = c.genes.iterator();
/*Gene tmp;
while(it.hasNext()){
}*/
return c;
}
/**
* #return the genes
*/
public ArrayList<Gene> getGenes() {
return genes;
}
/**
* #param genes the genes to set
*/
public void setGenes(ArrayList<Gene> genes) {
this.genes = genes;
}
/**
*
* #return
*/
public int getSize(){
return genes.size();
}
/**
* #return a gene at an index i of the ArrayList
* #param index - the index at which to return a gene
*/
public Gene getGenes(int index) {
return genes.get(index);
}
/* (non-Javadoc)
* #see java.lang.Object#toString()
*/
#Override
public String toString() {
return "Chromosome [genes=" + genes + "]";
}
}
Thanks

GWT: add filtering to CellTable

I've been tasked with implementing sorting & filtering of data displayed in a GWT CellTable.
Thankfully GWT already supports sorting, but it looks like I'll have to hack together my own filtering support.
To be more precise, what I'm trying to support is similar to the filtering offered by Excel, whereby you can click on a drop-down menu in the column headers and (for example) click checkboxes that will allow you to filter the rows based on the values for the filtered column(s). A picture is worth a thousand words:
My question: any suggestions on how to go about implementing this in GWT 2.2? Is it even possible?
One option I'm thinking about is to pass in a custom Header object to CellTable.addColumn(). If it's possible, I'll add a ClickHandler to the Header, and then open a Popup that displays a widget for filtering. Not sure how to implement this without negatively affecting the sorting behavior.
Any suggestions gladly welcome.
Edit:
Thanks to John below, I've got the following FilterableHeader class that allows me to at least put an icon into the header. Unsure how to get a ClickHandler on that icon just yet, as the image is inserted via HTML rather than using GWT widgets.
public class FilterableHeader extends Header<String>
{
/**
* Image resources.
*/
public static interface Resources extends ClientBundle
{
ImageResource downArrow();
ImageResource upArrow();
}
private static final Resources RESOURCES = GWT.create(Resources.class);
private static final int IMAGE_WIDTH = 16;
private static final String DOWN_ARROW = makeImage(RESOURCES.downArrow());
private static final String UP_ARROW = makeImage(RESOURCES.upArrow());
private static String makeImage(ImageResource resource)
{
AbstractImagePrototype proto = AbstractImagePrototype.create(resource);
return proto.getHTML().replace("style='", "style='position:absolute;right:0px;top:0px;");
}
private String text;
public FilterableHeader(String text)
{
super(new ClickableTextCell());
this.text = text;
}
#Override
public String getValue()
{
return text;
}
#Override
public void render(Cell.Context context, SafeHtmlBuilder safe)
{
int imageWidth = IMAGE_WIDTH;
StringBuilder sb = new StringBuilder();
sb.append("<div style='position:relative;cursor:hand;cursor:pointer;");
sb.append("padding-right:");
sb.append(imageWidth);
sb.append("px;'>");
sb.append(UP_ARROW);
sb.append("<div>");
sb.append(text);
sb.append("</div></div>");
safe.append(SafeHtmlUtils.fromSafeConstant(sb.toString()));
}
}
Custom headers are what is used with GWT 2.1 to do sorting. The 2.1 bikeshed has examples that use custom headers and am using one for sorting until Mvp4g moves to 2.2. To enable the filtering, just add an image with its own click handler and you should be good - it won't trigger sort behavior when it's clicked on, just the rest of the header will.
table.addColumn(new MyColumn(new MyCell()), new MyFilterHeader());
For the actual filtering, if you're using the database model from the examples (the wrapper class for the ListDataProvider), then I'd think you'd just keep two lists - the filtered list that's assigned to the ListDataProvider, and the unfiltered list it's based on.
Hope that helps!
In your new sample code, you might want to try a CompositeCell with the ClickableTextCell inside it, along with an ActionCell for the filtering part - if you can stick an image into the ClickableTextCell, you should be able to in the ActionCell, plus it'll have the mouseup behavior you want.
I develop business applications where a typical database query might return hundreds or thousands of rows. Users find the excel-like filters and column sorts to be very helpful.
Hence I have implemented a class that extends ListDataProvider for use with a CellTable that supports client-side excel-like column filtering and sorting. In all other respects it behaves much like a ListDataProvider.
It depends on implementing a the following ColumnAccessor interface to provide a symbolic name for for each column in the CellTable, to provide access to column-level data for sorting and filtering, a Comparator for the column for sorting, and display label for the header. Following is the ColumnAccessor class. It assumes that you have some sort of Data Transfer Object <T> that models the rows.
/**
* Interface to provide access to a specific
* column within a data row.
* #param <T> Object that contains the column
* values in a cell table row. Typically a Data Transfer Object.
*/
public interface ColumnAccessor<T> {
/**
* Filter display value for blank/null column values
*/
public final String FILTER_SELECTOR_BLANK = "{Blank}";
/**
* Returns A row-unique symbolic name for the column. This name is
* used as a Map key to access the ColumnAccessor instance by
* name for filtering and sorting.
* #return
*/
public String getColumnName();
/**
* Returns text label to appear as column header in CellTable.
* #return
*/
public String getLabel();
/**
* Returns value of the column as a String
* #param t Object that models the column values in a
* cell table row (Typically a Data Transfer Object)
* #return
*/
public String getValue(T t);
/**
* Returns Comparator for sorting data rows and for sorting
* discrete values that appear in a filter's select/option list.
* While the getValue() method always returns a String,
* these comparators should sort the column's values in
* consideration for the data type (for example, dates sorted
* as dates, numbers sorted as numbers, strings sorted as strings).
* #return
*/
public Comparator comparator();
}
Following is the FilterSortDataProvider class:
import com.google.gwt.cell.client.SelectionCell;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.SelectElement;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.user.cellview.client.Header;
import com.google.gwt.view.client.ListDataProvider;
import java.util.*;
/**
* Class that extends a ListDataProvider but adds "Excel-Like" column filters and also
* includes click on column heading sorts.
* #param <T> Object that contains the column values in a cell table row. Typically a Data Transfer Object.
*/
public class FilterSortDataProvider<T> extends ListDataProvider {
private List<T> rows;
private List<T> filteredSortedRows;
public Map<String, DataColumn> dataColumnMap = new HashMap<String, DataColumn>();
private String lastSortColumn = "*";
private int lastSortDirection = 0;
/**
* Constructs the DataProvider and columns
* #param rows Collection of objects that contain column data for cell table rows, typically
* Data Transfer Objects.
* #param columnAccessors List of ColumnAccessor instances for each column that will appear in
* the cell table. Each accessor will render a sortable, filterable column header
* and provides access to column-level data.
*/
public FilterSortDataProvider(Collection<T> rows, List<ColumnAccessor> columnAccessors) {
this.rows = new ArrayList<T>(rows);
this.filteredSortedRows = new ArrayList<T>();
Iterator<ColumnAccessor> columnAccessorIterator = columnAccessors.iterator();
while (columnAccessorIterator.hasNext()) new DataColumn(columnAccessorIterator.next());
// Initialize filters
filter();
}
/**
* Returns defensive copy of the current collection of filtered/sorted data rows
* #return
*/
public List<T> getFilteredSortedRows() {
return new ArrayList(filteredSortedRows);
}
/**
* Returns a CellTable Header for the named column for use when setting up the CellTable (ie:
* used as Header value in cellTable.addColumn(TextColumn, Header) call. The header includes
* the columnAccessor.getLabel() value as a click-to-sort header label, and a drop-down filter
* where the options include all available values.
* #param columnName Same value as returned by this columns ColumnAccessor.getColumnName()
* #return
*/
public Header getColumnHeader(final String columnName) {
DataColumn column = dataColumnMap.get(columnName);
return (column != null ? new FilteredCellTableHeader(column) : null);
}
/**
* Called when user clicks on column header label. Repeated clicks on the same column header will
* reverse the sort direction. Can also be called prior to display of CellTable to establish an initial
* sort order.
* #param sortColumnName
*/
public void sort(String sortColumnName) {
if (!sortColumnName.equals("*")) {
DataColumn column = dataColumnMap.get(sortColumnName);
if (column != null) {
// Sort ascending
Collections.sort(this.filteredSortedRows, column);
// Re-Sort of same column
if (sortColumnName.equals(lastSortColumn)) {
lastSortDirection *= -1;
}
else {
lastSortDirection = 1;
lastSortColumn = sortColumnName;
}
if (lastSortDirection == -1) Collections.reverse(filteredSortedRows);
}
}
this.setList(filteredSortedRows);
}
/**
* Optional call to pre-set filter before initial display of CellTable
* #param columnName
* #param value
*/
public void filter(String columnName, String value) {
DataColumn column = dataColumnMap.get(columnName);
if (column != null) column.filter(value);
}
/**
* Filters the rows based on all of the filters, and re-builds the filter drop-down
* options.
*/
private void filter() {
// Build collection of rows that pass all filters
filteredSortedRows = new ArrayList<T>();
Iterator<T> rowIterator = this.rows.iterator();
while (rowIterator.hasNext()) {
T row = rowIterator.next();
if (rowPassesFilter(row, null)) filteredSortedRows.add(row);
}
// Build filter select/option list for each column based on rows
// that pass all filters EXCEPT for the column in question.
Iterator<DataColumn> columnIterator = dataColumnMap.values().iterator();
while (columnIterator.hasNext()) {
DataColumn column = columnIterator.next();
Set<String> optionsSet = new HashSet<String>();
rowIterator = this.rows.iterator();
while (rowIterator.hasNext()) {
T row = rowIterator.next();
if (rowPassesFilter(row, column)) {
optionsSet.add(column.filterOptionValue(row));
}
}
// Sort the options using the ColumnAccessor's comparator
List<String> optionsList = new ArrayList<String>(optionsSet);
Collections.sort(optionsList, column.comparator());
// Make blank option (if any) the last entry in the option list
if (optionsList.contains(ColumnAccessor.FILTER_SELECTOR_BLANK)) {
optionsList.remove(ColumnAccessor.FILTER_SELECTOR_BLANK);
optionsList.add(ColumnAccessor.FILTER_SELECTOR_BLANK);
}
// Add the wild-card "All" as the first entry in the option list
optionsList.add(0, "*");
// Set the new list of options in the column
column.filterOptions = optionsList;
}
// Re-sort the data with consideration for the current sort column and direction
lastSortDirection *= -1;
sort(lastSortColumn);
}
/**
* Returns true if the specified row passes all column filters.
* #param row Data row to test
* #param columnToIgnore When specified, this column is assumed to allow the row
* to pass the filter. This is used when building the list
* of filter select/option values.
* #return
*/
private boolean rowPassesFilter(T row, DataColumn columnToIgnore) {
Iterator<DataColumn> columnIterator = dataColumnMap.values().iterator();
boolean passes = true;
while (columnIterator.hasNext() && passes) {
DataColumn column = columnIterator.next();
if (column != columnToIgnore) {
passes = column.rowPassesFilter(row);
}
}
return passes;
}
/**
* Inner class that models a CellTable column, its ColumnAccessor, current filter value,
* and current filter option values.
*/
public class DataColumn implements Comparator<T> {
private String filterValue = "*";
private List<String> filterOptions = new ArrayList<String>();
private ColumnAccessor columnAccessor;
/**
* Constructs a filterable, sortable column
* #param columnAccessor
*/
public DataColumn(final ColumnAccessor columnAccessor) {
this.columnAccessor = columnAccessor;
FilterSortDataProvider.this.dataColumnMap.put(columnAccessor.getColumnName(), this);
}
/**
* Returns symbolic name of column
* #return
*/
public String getName() {
return this.columnAccessor.getColumnName();
}
/**
* Returns display label for column header
* #return
*/
public String getLabel() {
return columnAccessor.getLabel();
}
/**
* Returns value of column
* #param row
* #return
*/
public String getValue(T row) {
return columnAccessor.getValue(row);
}
/**
* Returns comparator define in ColumnAccessor for use when sorting
* data rows and for sorting filter options.
* #return
*/
public Comparator comparator() {
return columnAccessor.comparator();
}
/**
* Called when user changes the value of a column filter
* #param filterValue
*/
public void filter(String filterValue) {
if (this.filterOptions.contains(filterValue)) {
this.filterValue = filterValue;
FilterSortDataProvider.this.filter();
}
}
/**
* Called when user clicks on column label to sort rows
*/
public void sort() {
FilterSortDataProvider.this.sort(this.columnAccessor.getColumnName());
}
/**
* Used to sort data rows. Uses comparator specified in ColumnAccessor.
* #param row1
* #param row2
* #return
*/
public int compare(T row1, T row2) {
return comparator().compare(getValue(row1), getValue(row2));
}
/**
* Returns true if specified row passes this column's filter
* #param row
* #return
*/
public boolean rowPassesFilter(T row) {
return filterValue.equals("*") || filterValue.equals(filterOptionValue(row));
}
/**
* Returns value to appear in filter options list. Null or "blank" values appear in options
* list as {Blank}.
* #param row
* #return
*/
private String filterOptionValue(T row) {
String value = getValue(row);
return (value == null || value.trim().length() == 0 ? ColumnAccessor.FILTER_SELECTOR_BLANK : value);
}
/**
* Renders Html Select/Options tag for column filter
* #return
*/
public String toHtmlSelect() {
StringBuffer sb = new StringBuffer();
sb.append("<select size='1' style='width: 100%;'>");
Iterator<String> opts = filterOptions.iterator();
while (opts.hasNext()) {
String escapedOption = SafeHtmlUtils.htmlEscape(opts.next());
sb.append("\t<option value='" + escapedOption);
sb.append((escapedOption.equals(filterValue) ? "' SELECTED>" : "'>"));
sb.append(escapedOption + "</option>\n");
}
sb.append("</select>\n");
return sb.toString();
}
}
/**
* Inner class Header wrapper for FilteredSortedCellTableHeaderCell
*/
public class FilteredCellTableHeader extends Header {
public FilteredCellTableHeader(DataColumn column) {
super(new FilteredSortedCellTableHeaderCell(column));
}
public Object getValue() {
return null;
}
}
/**
* CellTable SelectionCell that includes filter and sort controls, renders controls, and
* handles onBrowserEvent()
*/
private class FilteredSortedCellTableHeaderCell extends SelectionCell {
private DataColumn column;
public FilteredSortedCellTableHeaderCell(final DataColumn column) {
super(new ArrayList<String>());
this.column = column;
}
/**
* Renders Html Submit button as sort control, and Html Select/Option tag for filter.
* #param context
* #param value
* #param sb
*/
#Override
public void render(Context context, String value, SafeHtmlBuilder sb) {
String sortButton = "<input type='submit' value='" + SafeHtmlUtils.htmlEscape(column.getLabel()) +
"' style='text-align: center; width: 100%; background: none; border: none; font-weight: bold;'>";
sb.appendHtmlConstant(sortButton);
sb.appendHtmlConstant("<br>");
sb.appendHtmlConstant(column.toHtmlSelect());
}
/**
* Detects filter and sort user interaction events
* #param context
* #param parent
* #param value
* #param event
* #param valueUpdater
*/
#Override
public void onBrowserEvent(Context context, Element parent, String value, NativeEvent event, ValueUpdater<String> valueUpdater) {
super.onBrowserEvent(context, parent, value, event, valueUpdater);
String type = event.getType();
Element element = event.getEventTarget().cast();
String tagName = element.getTagName();
// Filter selection changed
if ("change".equals(type) && tagName.equals("SELECT")) {
// Set filter value and call filter routine
SelectElement se = (SelectElement)element;
String filterValue = se.getOptions().getItem(se.getSelectedIndex()).getValue();
column.filter(filterValue);
}
// Click on sort button
else if (type.equals("focus") && tagName.equals("INPUT")) {
column.sort();
}
}
}
}
I hope that this might be of help to someone.
I used the mouse-click position to add custom click events to column headers. In other words, you can set it up so that if the user clicks in the 'general area' where the image is supposed to be, you can show a filtering screen.
Here's an example where I have it ignoring click events for a text field I added:
if(col.isFilterable()){
if (event.getClientY() > (getInputElement(parent).getAbsoluteTop() - 2) && event.getClientY() < (getInputElement(parent).getAbsoluteBottom() + 2)) {
//ignore on click in area of the text field
event.preventDefault();
} else {
//sort if user clicks anywhere else
trySort(parent);
}
And the because cell listens for 'keyup' events separately, the filter is executed when the user hits enter (while the cell is focused).
if(event.getKeyCode()==13){
event.preventDefault();
handleSetFilterValue(parent);
tryFilter();
}

Categories