Pokazywanie postów oznaczonych etykietą how-to. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą how-to. Pokaż wszystkie posty

31 grudnia 2010

Custom PagingNavigator with changing items per page in Wicket

As I am migrating my blog to my own domain, a few posts in the future will be published in both places to allow You, reader, to change address and rss smoothly :)

New blog address: http://tomaszdziurko.pl
New RSS link: http://www.tomaszdziurko.pl/feed

This post can be found at
http://tomaszdziurko.pl/2010/12/custom-pagingnavigator-with-changing-items-per-page-in-wicket/



In one of my recent projects I had to create Wicket pagination component with one additional functionality allowing user to dynamically change maximum number of items presented on each page.

Finished component will look like below:



Of course I was not going to implement this from the scratch, because most of work had been already done by Wicket authors and commiters in component PagingNavigator. Source of this component as a reference to changes I made can be found here.

The first steps in creating our component:
- add List itemsPerPageValues holding numbers for 'items per page' which will be shown to the user allowing him to click and change this value.
- provide default List DEFAULT_ITEMS_PER_PAGE_VALUES, which will be used (what a surprise!) as a default :)
- give our component reference to the DataView object on which we will execute change items per page method.

Below we can see changes in class fields and constructors:

public class CustomPagingNavigator extends Panel {

public static final String NAVIGATION_ID = "navigation";
public static final List<Integer> DEFAULT_ITEMS_PER_PAGE_VALUES = Arrays.asList(5, 10, 50);

private PagingNavigation pagingNavigation;
private final DataView<?> dataView;
private final IPagingLabelProvider labelProvider;
private final List<Integer> itemsPerPageValues;
private WebMarkupContainer pagingLinksContainer;

public CustomPagingNavigator(final String id, final DataView<?> dataView) {
this(id, dataView, null, DEFAULT_ITEMS_PER_PAGE_VALUES);
}

public CustomPagingNavigator(final String id, final DataView<?> dataView, List<Integer> itemsPerPageValues) {
this(id, dataView, null, itemsPerPageValues);
}

public CustomPagingNavigator(final String id, final DataView<?> dataView, final IPagingLabelProvider labelProvider) {
this(id, dataView, labelProvider, DEFAULT_ITEMS_PER_PAGE_VALUES);
}

public CustomPagingNavigator(final String id, final DataView<?> dataView, final IPagingLabelProvider labelProvider,
List<Integer> itemsPerPageValues) {
super(id);
this.dataView = dataView;
this.labelProvider = labelProvider;
this.itemsPerPageValues = itemsPerPageValues;

// these methods will be described later in this post
addContainerWithPagingLinks();
addLinksChangingItemsPerPageNumber();
}
(...)
}


First new method is addContainerWithPagingLinks based mainly of onBeforeRender from PagingNavigator. This method adds paging links to the component to allow user to change number of viewed page. One small addition is an overriden isVisible which will hide paging fragment when there is only one page to show.

private void addContainerWithPagingLinks() {

pagingLinksContainer = new WebMarkupContainer("pagingLinksContainer") {
@Override
public boolean isVisible() {
return dataView.getPageCount() > 1;
}
}

pagingNavigation = newNavigation(dataView, labelProvider);
pagingLinksContainer.add(pagingNavigation);

// Add additional page links
pagingLinksContainer.add(newPagingNavigationLink("first", dataView, 0).add(
new TitleAppender("PagingNavigator.first")));
pagingLinksContainer.add(newPagingNavigationIncrementLink("prev", dataView, -1).add(
new TitleAppender("PagingNavigator.previous")));
pagingLinksContainer.add(newPagingNavigationIncrementLink("next", dataView, 1).add(
new TitleAppender("PagingNavigator.next")));
pagingLinksContainer.add(newPagingNavigationLink("last", dataView, -1).add(
new TitleAppender("PagingNavigator.last")));

add(pagingLinksContainer);
}

Next step is to override isVisible method in our component which will hide it completely when DataView has no items to render:

@Override
public boolean isVisible() {
return dataView.getItemCount() > 0;
}

And now it's time to create the core of our component: a place where items per page can be changed. This is done in method:

private void addLinksChangingItemsPerPageNumber() {
ListView<Integer> itemsPerPageList = new ListView<Integer>("itemsPerPage", itemsPerPageValues) {
@Override
protected void populateItem(ListItem<Integer> item) {
Link<Void> itemPerPageLink = new ItemPerPageLink<Void>("itemPerPageLink", dataView,
pagingLinksContainer, item.getModelObject());
itemPerPageLink.add(new Label("itemsValue", item.getModel()));
item.add(itemPerPageLink);
}
};

add(itemsPerPageList);
}

In the above method we create ListView for Integers from itemsPerPageValues and for each number we add link (new class ItemPerPageLink explained below) which will change DataView itemsPerPage property. Except reference to DataView we also give to our link reference to pagingLinksContainer to hide it after user changes itemsPerPage and there will be only one page to show.

Complete class ItemPerPageLink source code:

public class ItemPerPageLink<T> extends Link<T> {

private final int itemsPerPage;
private final DataView<?> dataView;
private final WebMarkupContainer pagingLinksContainer;

public ItemPerPageLink(final String id, final DataView<?> dataView, WebMarkupContainer pagingLinksContainer, int itemsPerPageValue) {
super(id);
this.dataView = dataView;
this.pagingLinksContainer = pagingLinksContainer;
this.itemsPerPage = itemsPerPageValue;
setEnabled(itemsPerPageValue != dataView.getItemsPerPage());

}

@Override
public void onClick() {
dataView.setItemsPerPage(itemsPerPage);
pagingLinksContainer.setVisible(dataView.getPageCount() > 1);
}

@Override
protected void onComponentTag(ComponentTag tag) {
super.onComponentTag(tag);
tag.put("title", itemsPerPage);
}

}

In this class we:
1. Disable link for number which is current dataView.itemsPerPage value.
2. Hide pagingLinksContainer when there is only one page.
3. Change itemsPerPage property in onClick method.
4. Set link title to the value of items per page.

And that's all. For those who want complete solution in one place there is complete source of the class CustomPagingNavigator with its markup:


import java.util.Arrays;
import java.util.List;

import org.apache.wicket.Component;
import org.apache.wicket.behavior.AbstractBehavior;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.AbstractLink;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.navigation.paging.IPageable;
import org.apache.wicket.markup.html.navigation.paging.IPagingLabelProvider;
import org.apache.wicket.markup.html.navigation.paging.PagingNavigation;
import org.apache.wicket.markup.html.navigation.paging.PagingNavigationIncrementLink;
import org.apache.wicket.markup.html.navigation.paging.PagingNavigationLink;
import org.apache.wicket.markup.html.navigation.paging.PagingNavigator;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.markup.repeater.data.DataView;

public class CustomPagingNavigator extends Panel {

public static final String NAVIGATION_ID = "navigation";
public static final List<Integer> DEFAULT_ITEMS_PER_PAGE_VALUES = Arrays.asList(5, 25, 50);

private PagingNavigation pagingNavigation;
private final DataView<?> dataView;
private final IPagingLabelProvider labelProvider;
private final List<Integer> itemsPerPageValues;
private WebMarkupContainer pagingLinksContainer;

public CustomPagingNavigator(final String id, final DataView<?> dataView) {
this(id, dataView, null, DEFAULT_ITEMS_PER_PAGE_VALUES);
}

public CustomPagingNavigator(final String id, final DataView<?> dataView, List<Integer> itemsPerPageValues) {
this(id, dataView, null, itemsPerPageValues);
}

public CustomPagingNavigator(final String id, final DataView<?> dataView, final IPagingLabelProvider labelProvider) {
this(id, dataView, labelProvider, DEFAULT_ITEMS_PER_PAGE_VALUES);
}

public CustomPagingNavigator(final String id, final DataView<?> dataView, final IPagingLabelProvider labelProvider,
List<Integer> itemsPerPageValues) {
super(id);
this.dataView = dataView;
this.labelProvider = labelProvider;
this.itemsPerPageValues = itemsPerPageValues;

addContainerWithPagingLinks();
addLinksChangingItemsPerPageNumber();
}

@Override
public boolean isVisible() {
return dataView.getItemCount() > 0;
}

private void addContainerWithPagingLinks() {

pagingLinksContainer = new WebMarkupContainer("pagingLinksContainer") {
@Override
public boolean isVisible() {
return dataView.getPageCount() > 1;
}
};

pagingNavigation = newNavigation(dataView, labelProvider);
pagingLinksContainer.add(pagingNavigation);

// Add additional page links
pagingLinksContainer.add(newPagingNavigationLink("first", dataView, 0).add(
new TitleAppender("PagingNavigator.first")));
pagingLinksContainer.add(newPagingNavigationIncrementLink("prev", dataView, -1).add(
new TitleAppender("PagingNavigator.previous")));
pagingLinksContainer.add(newPagingNavigationIncrementLink("next", dataView, 1).add(
new TitleAppender("PagingNavigator.next")));
pagingLinksContainer.add(newPagingNavigationLink("last", dataView, -1).add(
new TitleAppender("PagingNavigator.last")));

add(pagingLinksContainer);
}

protected PagingNavigation newNavigation(final IPageable pageable, final IPagingLabelProvider labelProvider) {
return new PagingNavigation(NAVIGATION_ID, pageable, labelProvider);
}

protected AbstractLink newPagingNavigationIncrementLink(String id, IPageable pageable, int increment) {
return new PagingNavigationIncrementLink<Void>(id, pageable, increment);
}

protected AbstractLink newPagingNavigationLink(String id, IPageable pageable, int pageNumber) {
return new PagingNavigationLink<Void>(id, pageable, pageNumber);
}

private void addLinksChangingItemsPerPageNumber() {
ListView<Integer> itemsPerPageList = new ListView<Integer>("itemsPerPage", itemsPerPageValues) {
@Override
protected void populateItem(ListItem<Integer> item) {
Link<Void> itemPerPageLink = new ItemPerPageLink<Void>("itemPerPageLink", dataView,
pagingLinksContainer, item.getModelObject());
itemPerPageLink.add(new Label("itemsValue", item.getModel()));
item.add(itemPerPageLink);
}
};

add(itemsPerPageList);
}

public final PagingNavigation getPagingNavigation() {
return pagingNavigation;
}

private final class TitleAppender extends AbstractBehavior {
private static final long serialVersionUID = 1L;

private final String resourceKey;

public TitleAppender(String resourceKey) {
this.resourceKey = resourceKey;
}

@Override
public void onComponentTag(Component component, ComponentTag tag) {
tag.put("title", CustomPagingNavigator.this.getString(resourceKey));
}
}
}


And its markup:

<?xml version="1.0" encoding="UTF-8" ?>
<html xmlns:wicket>
<body>
<wicket:panel>

<div>
<div>
Items per page:
<span wicket:id="itemsPerPage">
<a wicket:id="itemPerPageLink"><span wicket:id="itemsValue"></span></a>
</span>
</div>
<div wicket:id="pagingLinksContainer">
<table>

<tbody>
<tr valign="top">
<td>
<a wicket:id="first" href="">
<span>First</span>
</a>
</td>
<td>
<a wicket:id="prev" href="">
<span>Previous</span>
</a>
</td>
<td wicket:id="navigation">
<a wicket:id="pageLink"><span
wicket:id="pageNumber">5</span></a>
</td>

<td>
<a wicket:id="next" href="#">
<span>Next</span></a>
</td>
<td>
<a wicket:id="last" href="#">
<span>Last</span></a>
</td>
</tr>
</tbody>
</table>

</div>
</div>

</wicket:panel>
</body>
</html>


Usage of the component

Our newly created component can be used in a very similar way to the standard Wicket PagingNavigator:


public class DataViewPanel extends Panel {

public DataViewPanel(String id) {
super(id);
DataView dataView = new DataView("dataView", new CustomDataProvider());
dataView.setItemsPerPage(5);
add(dataView);

CustomPagingNavigator customPagingNavigator = new CustomPagingNavigator("paginator", dataView);
add(customPagingNavigator);
}
}

19 września 2010

Mercurial - garść linków dla poznających to narzędzie

Garść linków, publikacji i porad dla osób, które, jak ja w tej chwili, będą wdrażały się w używanie Mercuriala, czyli trochę innego narzędzia do zarządzania kodem niż najpopularniejsze CVS czy SVN.

1. http://hginit.com/top/ - Krótki, ale bardzo treściwy i napisany fajnym językiem tutorial autorstwa Joela Spolskyego, tego Joela :) Warto od niego zacząć, jeśli chcemy szybko rozpocząć zabawę z Mercurialem.
2. TortoiseHg - znana aplikacja do pracy z SVN/CVS w wersji dla Mercuriala
3. Zdalne repozytorium za darmo z użyciem serwisu Dropbox - Tutorial opisujący jak można połączyć Mercuriala z Dropbox i mieć automatycznie backupujące się repozytorium za darmo.
4. MercurialEclipse - Wtyczka do Eclipse umożliwiająca pracę z Mercurialem bezpośrednio z tego IDE.
5. Mercurial - the Definitive Guide - Książka dokładnie opisująca Mercuriala
6. Mercurial by Example (PDF) - Mini publikacja prezentująca sposób korzystania z Mercuriala na przykładach.
7. Branch i merge w Mercurial - Poradnik jak tworzyć nowe gałęzie i potem je łączyć, żeby wszystko szło gładko.

A zawsze najbardziej aktualną listę publikacji i tutoriali można znaleźć na stronie Wiki projektu - http://mercurial.selenic.com/wiki.

14 lutego 2010

Wicket - przydatne materiały

Ten postał został przeniesiony na mojego nowego bloga i można go znaleźć tutaj.

9 lutego 2010

Wicket Ajax Modal 'Are you sure?' window

This post has been moved to my new blog and can be found at here.

13 grudnia 2009

Java i programowanie - jak zacząć i z czego się uczyć

Ten postał został przeniesiony na mojego nowego bloga i można go znaleźć tutaj.

6 sierpnia 2009

Efektywny programista Java, część 3 i 4 - klasy użytkowe i unikanie zbędnego powielania obiektów

Dzisiaj kolejna porcja porad jak efektywnie programować w Javie. Tym razem w jednym poście dwie porady, bo każda z nich jest zbyt krótka, by umieszczać je oddzielnie.

Klasy użytkowe

Prawie w każdym projekcie pojawia się przynajmniej jedna klasa, która gromadzi metody użytkowe wspomagające działanie aplikacji. Taką klasę nazywamy najczęściej klasą pomocniczą (ang. utility class).
Składa się ona z kilku/kilkunastu metod statycznych używanych w różnych miejscach aplikacji. Taka klasa nie powinna umożliwiać tworzenia swojej instancji, gdyż jest to zbędne.

Bardzo łatwo możemy wymusić takie zachowanie na innych programistach tworząc konstruktor prywatny:


public class Utils {

/*
* This is utility class, you don't have to create instance to use it
*/
private Utils() { }

public static String prepareUserStringForEmail(User user) {
// ...
}

public static String createRandomString(int length) {
// ...
}

public static String formatDateWithTime(Date date) {
// ...
}
}



Dzięki takiemu prostemu zabiegowi, nasz kod zyskał na czytelności, a inni programiście nie będą używać naszej klasy w sposób niepoprawny.

Optymalizowanie tworzenia obiektów, których używamy wielokrotnie

Przyjrzyjmy się poniższemu fragmentowi pewnej klasy:


public class Utils {

/*
* This is utility class, you don't have to create instance to use it
*/
private Utils() { }

public static boolean isOlderThanCodeHardGoProBlogOwner(Date birthDate) {
if(birthDate == null) {
throw new IllegalArgumentException("Date can not be null");
}

Calendar calendar = Calendar.getInstance();
calendar.set(1984, Calendar.JULY, 22);
Date codeHardGoProOwnerDateOfBirth = calendar.getTime();

return birthDate.compareTo(codeHardGoProOwnerDateOfBirth) < 0;
}

}

Sprawdzamy tutaj, czy czyjaś data urodzin jest wcześniejsza niż moja. Tworzymy obiekt klasy Calendar, ustawiamy mu odpowiednie wartości roku, miesiąca i dnia, a następnie porównujemy ze zmienną birthDate.
Gdy wywołamy metodę kolejny raz, obiekt calendar zostanie utworzony ponownie i zostanie mu ustawiona ta sama data co poprzednio. Nie jest to rozwiązanie najbardziej optymalne. Widać tutaj wyraźnie, że tworzenie obiektu calendar i ustawianie mu odpowiedniej wartości powinno zostać wydzielone poza metodę i wykonywane tylko raz. Efekt taki możemy uzyskać przenosząc fragment kodu do bloku statycznego, a zmienną klasy Date uczynić prywatną i statyczną:


public class Utils {

private final static Date CODE_HARD_GO_PRO_BLOG_OWNER_BIRTH_DATE;

static {
Calendar calendar = Calendar.getInstance();
calendar.set(1984, Calendar.JULY, 22);
CODE_HARD_GO_PRO_BLOG_OWNER_BIRTH_DATE = calendar.getTime();
}

private Utils() { }

public static boolean isOlderThanCodeHardGoProBlogOwner(Date birthDate) {
if(birthDate == null) {
throw new IllegalArgumentException("Date can not be null");
}

return birthDate.compareTo(CODE_HARD_GO_PRO_BLOG_OWNER_BIRTH_DATE) < 0;
}

}


Teraz nasza metoda będzie wykonywała się trochę szybciej. Oczywiście zysk będzie znacznie większy, gdy taki wielokrotnie używany obiekt będzie bardziej skomplikowany, a jego tworzenie kosztowniejsze i bardziej czasochłonne.

30 lipca 2009

Efektywny programista Java, część 2 - łatwiejsze tworzenie parametryzowanych kolekcji i obiektów

Tym razem spróbujemy ułatwić sobie tworzenie parametryzowanych typów kolekcji i obiektów. Pierwsza myśl jaka nam przychodzi do głowy to po prostu użycie konstruktora:

// ...
List<User> usersList = new ArrayList<User>();

Map<IpAddress, User> ipUsersMap = new HashMap<IpAddress, User>();

// ...

To jeszcze nie są zbyt skomplikowane przykłady, ale jeśli okaże się, że musimy stworzyć Mapę złożoną z obiektu i innej kolekcji:

// ...


Map<IpAddress, List<User>> ipUserMap = new HashMap<IpAddress, List<User>>();
// ...

to pojawia się spora ilość zdublowanego kodu z informacją o typach parametrów po lewej i prawej stronie deklaracji.

Okazuje się, że można tego uniknąć wykorzystując tzw. "wnioskowanie typów". Kompilator będzie wiedział jaki tym parametryzowanej instancji ma zwrócić na podstawie tego, do jakiej zmiennej będziemy chcieli zwracany obiekt przypisać. Wygodnym sposobem jest zebranie wszystkich takich metod w jednej klasie użytkowej:


package pl.tdziurko.effectivejava

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;

/**
*
* @author Tomasz Dziurko
*/
public class Helper {

// Do not instantiate this class, this is utility class
private Helper() { }

public static <E> ArrayList<E> newArrayList() {
return new ArrayList<E>();
}

public static <E,K> HashMap <E,K> newHashMap() {
return new HashMap<E,K>();
}

public static <E> HashSet <E> newHashSet() {
return new HashSet<E>();
}

}


Teraz możemy zobaczyć o ile krótszy i wygodniejszy jest kod stosujący wnioskowanie typów:


// ...
List<User> usersList = new ArrayList<User>();
List<User> usersList2 = Helper.newArrayList();

Map<IpAddress, User> ipUsersMap = new HashMap<IpAddress, User>();
Map<IpAddress, User> ipUsersMap2 = Helper.newHashMap();

Map<IpAddress, List<User>> ipUserMap = new HashMap<IpAddress, List<User>>();
Map<IpAddress, List<User>> ipUserMap2 = Helper.newHashMap();

// ...



Możemy skorzystać z wnioskowania typów również przy tworzeniu parametryzowanych obiektów innych niż kolekcje, ale tutaj zaleta jego użycia jest widoczna najbardziej.

Efektywny programista Java, część 1 - singletony

Zgodnie z zapowiedzią w poprzednim poście, dzisiaj początek cyklu postów z najciekawszymi poradami z książki "Java. Efektywne programowanie". W pierwszej kolejności postaram się przedstawić te tematy, z którymi programista Java może zetknąć się najczęściej.

Trzy sposoby na tworzenie singletonów


Najczęściej singleton w Javie wygląda następująco:


package pl.tdziurko.effectivejava;

public class Boniek {

public static final Boniek INSTANCE = new Boniek();

private Boniek() {
// stwórz jedyną instancję Zbigniewa Bońka
}

public void doMagicWithBall() {
// dokonaj czegoś niesamowitego na boisku
}

}


W powyższym przykładzie widać od razu, że jest to singleton: prywatny konstruktor i statyczne pole finalne z jedyną instancją klasy dają o tym wystarczająco jasny sygnał.

Drugi sposób przedstawiony w książce jest bardzo podobny:


public class Boniek {

private static final Boniek INSTANCE = new Boniek();

private Boniek() {
// stwórz jedyną instancję Zbigniewa Bońka
}

public Boniek getInstance() {
return INSTANCE;
}

public void doMagicWithBall() {
// dokonaj czegoś niesamowitego na boisku
}
}


Stosując powyższe podejście mamy większą elastyczność przy późniejszych modyfikacjach naszej klasy. Możemy bez problemu (czyli bez widocznych zmian w zewnętrznym wyglądzie klasy) zmienić implementację metody getInstance(), tak aby zwracała nie jedyną instancję Zbigniewa Bońka, ale na przykład jedną instancję dla danego wątku czy danego klienta.

Oba podejścia nie są niestety odporne na refleksję, nie są również przystosowane do serializacji. Jeśli chcemy, aby nasz singleton po serializacji i deserializacji był ciągle singletonem poza dodaniem implements Serializable musimy przeładować metodę readResolve() tak, aby zwracała obiekt INSTANCE.
Z kolei przed refleksją można się uchronić dodając blok w konstruktorze, który rzuca wyjątek w momencie, gdy istnieje już instancja obiektu tej klasy.

Ostatnim (i najbardziej polecanym przez autora książki) sposobem na tworzenie singletonu jest zastosowanie typu wyliczeniowego:


public enum Boniek {

INSTANCE;

void doMagicWithBall() {
// dokonaj czegoś niesamowitego na boisku
}
}


Dzięki zastosowaniu typu wyliczeniowego mamy za jednym zamachem załatwiony problem singletonu, bezpieczeństwa serializacji i zabezpieczenie przed użyciem refleksji. Wszystko zapewnia sam język i nie musimy się o to martwić.

Osobiście najczęściej stosowałem podejście z polem private final static i metodą getInstance(), ale myślę, że zastosowanie typu wyliczeniowego jest bardzo interesującą alternatywą, którą warto dodać do swojego repertuaru :)