Segmentation Fault 11 in Swift Program - ios

I keep getting this error everytime I try to compile my assignment
(unresolved_dot_expr type='#lvalue [Book]' location=/Users/BandManFabo/Desktop/Lab03/Lab03/Bookstore.swift:18:9 range=[/Users/BandManFabo/Desktop/Lab03/Lab03/Bookstore.swift:18:9 - line:18:9] field 'books' function_ref=unapplied
I am using a class called Book which is somewhat of a model for books in a Bookstore which is another class. Book.swift is below
import Foundation
class Book {
private var title:String
private var numOfPages:Int
private var price: Double
private var quantity: Int
init(title:String,pages:Int,price:Double,quantity:Int) {
self.title = title
self.numOfPages = pages
self.price = price
self.quantity = quantity
}
public func getTitle() -> String {
return self.title
}
public func getPrice() -> Double {
return self.price
}
public func getQuantity() -> Int {
return self.quantity
}
public func toString() -> String {
return "Title: \(self.title)\nNumber Of Pages: \(self.numOfPages)\nPrice: \(self.price)\nQuantity: \(self.quantity)"
}
public func subtractQuantity(amount: Int){
self.quantity = getQuantity() - amount
}
public func addQuantity(amount: Int){
self.quantity = getQuantity() + amount
}
}
Bookstore uses this class to run
import Foundation
class Bookstore {
private var totalbooks:Int
private var gross: Double
private static let MAXNUMOFBOOKS:Int = 1000
private var books: [Book]
//empty constructor for the bookstore
init() {
books = [Book](repeatElement(Book, count: Bookstore.MAXNUMOFBOOKS))
totalbooks = 0
gross = 0.0
}
/* Adds a new book to this bookstore.
* #param b the book to add
*/
public func addBook(b:Book){
if Bookstore.MAXNUMOFBOOKS < books.count{
books.append(b)
totalbooks = totalbooks+1
}else{
print("\nBookStore: I cannot add a new book into stock.")
}
}
/**
* Adds a certain quantity of a book already in stock.
*
* #param title the title of the the book
* #param quantity amount of copies to add
*/
public func addBookQuantity(title:String , quantity:Int){
// Search for the book...if found adjust the quantity,
// otherwise, inform the user that the book is not available
for currentBook in books {
if currentBook.getTitle() == title{
currentBook.addQuantity(amount: quantity)
return
}//end of if
}//end of for
// Book is not found in the bookstore
print("\nBookStore: I cannot increment the quantity of the book titled");
print("'\(title)' because it is not available in the bookstore.");
}// addBookQuantity
/**
* Checks if at least a certain number of copies of a particular book are in
* stock. Note: You can use this method to check if a book is already in the
* bookstore. This way, you won't create duplicate records of the same book.
*
* #param title the title of the book to search for
* #param quantity the desired number of copies
* #return
* #returns true if title exists with specified quantity; otherwise false
*/
public func inStock(title:String, quantity:Int) -> Bool {
// Search for the book...if found, adjust the quantity.
// otherwise, Book not in the BookStore.
for currentBook in books {
if currentBook.getTitle() == title{
if quantity <= currentBook.getQuantity(){
return true
}else{
return false
}
}//end of if
}//end of for
//Book not present
return false
}
/**
* Sells a particular number of copies of a certain book. If successful
* (i.e. enough books are in stock to sell), the quantity of the book is
* adjusted. Otherwise, no books are sold.
*
* #param title the title of the book to sell
* #param quantity the amount of books to sell
* #return
* #returns true if successful; otherwise false
*/
public func sellBook(title:String, quantity:Int) -> Bool {
var sellCheck: Bool = false
//will check to see if the books are instock
let retval:Bool = inStock(title: title, quantity: quantity)
//will do some operation if it is instock
if retval {
for currentBook in books {
if !sellCheck{
if currentBook.getTitle() == title{
currentBook.subtractQuantity(amount: quantity)
gross = gross + currentBook.getPrice() * Double(quantity)
sellCheck = true
}//end of most inner if
}//end of inner if
}//end of outer for
}//end of outer if
return retval
}
/**
* Lists information about each book in the bookstore
*/
public func listBooks(){
print("\nAll Books In Store and Info\n===============")
for currentBook in books {
print(currentBook.toString())
}
}
/**
* Lists the titles of all the books in the bookstore
*/
public func listTitles(){
// List all books titles
print("\nTitles of Books\n===============")
for currentBook in books {
print(currentBook.getTitle())
}
}
/**
* Returns the gross income of this bookstore.
*
* #return
* #returns gross income
*/
public func getIncome() -> Double {
return gross
}
}
Not entirely sure where the segmentation fault is coming from. I appreciate any help

Modify your init method in BookStore class
init() {
books = [Book]() //empty list
totalbooks = 0
gross = 0.0
}

Related

Java - Writing method for public int indexOf(T element) in a double linked list

Below I have the int indexOf(T element) method for a double linked list. I need help with the code to make sure it functions properly. The method should return the first occurence of the element in the list or -1 if element is not in the list. Below is the node class it uses. The IUDoubleLinkedList.java class implements IndexedUnsortedList.java which is where the indexOf method comes from. I tried using my indexOf method from my single linked list class but it's not the same so I hope to understand why it would be different and what code is used that is different between the the single and double linked list.
public class IUDoubleLinkedList<T> implements IndexedUnsortedList<T> {
private Node<T> head, tail;
private int size;
private int modCount;
public IUDoubleLinkedList() {
head = tail = null;
size = 0;
modCount = 0;
This is the indexOf(T element) method
#Override
public int indexOf(T element) {
// TODO Auto-generated method stub
return 0;
}
Below is the Node.java class it uses
public class Node<T> {
private Node<T> nextNode;
private T element;
private Node<T> prevNode;
/**
* Creates an empty node.
*/
public Node() {
nextNode = null;
element = null;
}
/**
* Creates a node storing the specified element.
*
* #param elem
* the element to be stored within the new node
*/
public Node(T element) {
nextNode = null;
this.element = element;
setPrevNode(null);
}
/**
* Returns the node that follows this one.
*
* #return the node that follows the current one
*/
public Node<T> getNextNode() {
return nextNode;
}
/**
* Sets the node that follows this one.
*
* #param node
* the node to be set to follow the current one
*/
public void setNextNode(Node<T> nextNode) {
this.nextNode = nextNode;
}
/**
* Returns the element stored in this node.
*
* #return the element stored in this node
*/
public T getElement() {
return element;
}
/**
* Sets the element stored in this node.
*
* #param elem
* the element to be stored in this node
*/
public void setElement(T element) {
this.element = element;
}
#Override
public String toString() {
return "Element: " + element.toString() + " Has next: " + (nextNode != null);
}
public Node<T> getPrevNode() {
return prevNode;
}
public void setPrevNode(Node<T> prevNode) {
this.prevNode = prevNode;
}
}
Check the following code, hope I helped you!
Insert item at head as well as tail end
public void insertItem(T elem) {
/* if head and tail both are null*/
if(head == null || tail == null) {
head = new Node<T>(elem);
tail = new Node<T>(elem);
}else {
Node<T> tempItem = new Node<T>();
tempItem.setElement(elem);
/* insert at head end /*
tempItem.setNextNode(head);
head.setPrevNode(tempItem);
head = tempItem;
Node<T> tempItem1 = new Node<T>();
tempItem1.setElement(elem);
/* append at tail end */
tail.setNextNode(tempItem1);
tempItem1.setPrevNode(tail);
tail = tempItem1;
}
size += 1;
}
Print item from head end
public void printItemsFromHead() {
while(head != null) {
System.out.print(head.getElement()+" --> ");
head = head.getNextNode();
}
}
Print item from tail end
public void printItemsFromTail() {
Node<T> temp = null;
while(tail != null) {
temp = tail;
System.out.print(tail.getElement()+" --> ");
tail = tail.getPrevNode();
}
/*System.out.println();
while(temp != null) {
System.out.print(temp.getElement()+" --> ");
temp = temp.getNextNode();
}*/
}
Implemention of indexOf function
#Override
public int indexOf(T element) {
int result = -1;
int headIndex = 0;
int tailIndex = size;
while(head != null && tail != null) {
if(head.getElement().equals(element)) {
result = headIndex;
break;
}
/*
if(tail.getElement().equals(element)) {
result = tailIndex;
break;
} */
head = head.getNextNode();
tail = tail.getPrevNode();
headIndex += 1;
tailIndex -= 1;
}
return result;
}
Driver class
public class Driver {
#SuppressWarnings({ "rawtypes", "unchecked" })
public static <T> void main(String[] args) {
UDoubleLinkedList uDoubleLinkedList = new UDoubleLinkedList();
uDoubleLinkedList.insertItem(1);
uDoubleLinkedList.insertItem(2);
uDoubleLinkedList.insertItem(3);
uDoubleLinkedList.insertItem(4);
uDoubleLinkedList.insertItem(5);
System.out.println(uDoubleLinkedList.indexOf(1));
}
}

How to add n- custom cells between two rows

So I am trying to visualize a curricilum as a table. It should look like this:
As you can see there are custom cells (+) which are not a lesson. They are buttons.
I have two classes:
public class Lesson {
private Room schoolRoom;
private Room teachingRoom;
private TeacherSpecialization teachingInfo;
private WeekDay weekDay;
private int schoolHour;
}
and
public class ClassHour {
Lesson[] dayLessons = new Lesson[18];
private int hour;
public ClassHour(int hour) {
this.hour = hour;
}
}
Using this code I convert my Lesson Object to ClassHour objects, because I use the ClassHour Object to save the lessons in the table:
public ObservableList<ClassHour> convertToClassHour(List<Lesson> lessons) {
ObservableList<ClassHour> classHours = FXCollections.observableArrayList();
// Converting Lessons to ClassHour objects.
lessons.forEach(lesson -> {
ClassHour classHour = classHours.stream().filter(ch -> ch.getHour() == lesson.getSchoolHour()).findFirst().orElse(null);
if (classHour == null) {
classHour = new ClassHour(lesson.getSchoolHour());
classHours.add(classHour);
}
classHour.getDayLessons()[lesson.getWeekDay().ordinal()] = lesson;
});
return classHours;
}
And the last step is to show the data in the table:
private void showLessons(String roomNr) throws Exception {
try {
// lessons.addListener((ListChangeListener) e -> repopulate(lessons, classHours));
ArrayList<Lesson> allLessonsByRoomNr = db.getAllLessonsByRoomNr(roomNr);
ObservableList<ClassHour> classHours = db.convertToClassHour(allLessonsByRoomNr);
for (int i = 0; i < 5; i++) {
int day = i;
TableColumn<ClassHour, Lesson> dayColumn = new TableColumn<>(WeekDay.values()[i].name());
dayColumn.setSortable(false);
dayColumn.setCellValueFactory(param -> new SimpleObjectProperty(param.getValue().getDayLessons()[day]));
dayColumn.setCellFactory((TableColumn<ClassHour, Lesson> param) -> new TableCell<ClassHour, Lesson>() {
#Override
protected void updateItem(Lesson item, boolean empty) {
super.updateItem(item, empty);
setText(null);
setGraphic(null);
if (!empty) {
if (item != null) {
setText(item.toString());
} else {
Button btn = new Button("+ ADD");
btn.setOnAction(e -> {
tableLessons.getSelectionModel().select((ClassHour) getTableRow().getItem());
showAdd(day, ((ClassHour) getTableRow().getItem()).getHour(), btn);
});
setGraphic(new StackPane(btn));
}
}
}
});
tableLessons.getColumns().addAll(dayColumn);
}
tableLessons.setItems(classHours);
} catch (Exception ex) {
showResultDialog("An error has occured:", ex.getMessage());
}
}
The problem I am struggling are the custom cells (+ buttons). For some reason I can't draw them between two lessons. In the case below there should be 5 cells/rows between classhour 10 and 16 (monday).
Note that your convertToClassHour creates a ClassHour instance if and only if there is a Lesson and if the Lessons are not ordered by hour, the order of ClassHours in the output is wrong.
Unless you've got a predetermined set of hours, you need to find the min and max hours to fix your issue:
public ObservableList<ClassHour> convertToClassHour(List<Lesson> lessons) {
ObservableList<ClassHour> classHours = FXCollections.observableArrayList();
if (!lessons.isEmpty()) {
// find required hour range
int minHour = lessons.mapToInt(Lesson::getSchoolHour).min().getAsInt();
int maxHour = lessons.mapToInt(Lesson::getSchoolHour).max().getAsInt();
// create ClassHours for range
for (int i = minHour; i <= maxHour; i++) {
classHours.add(new ClassHour(i));
}
// fill classHours with lessons
for (Lesson lesson : lessons) {
classHours.get(lesson.getSchoolHour() - minHour).getDayLessons()[lesson.getWeekDay().ordinal()] = lesson;
}
}
return classHours;
}

Merge Flux emissions with duplication?

I'm having a Flux that emits items:
data class Item(
val isEgg: Boolean,
val isBasket: Boolean
)
Consider 2 'baskets' and 4 'eggs' emissions. I would like to merge those emissions into two: each containing one 'basket' and 4 'eggs':
Is someone aware of such transformation? Flux is finite and should not exceed 1K items.
EDIT:
What I achieved so far - I grouped emissions into GroupedFlux. Now I would need to combine GroupedFlux containing Basket1, Basket2 with with second containing 'Eggs' in order to produce two baskets with "duplicated" eggs in each one.
val flux = Flux.just("Egg1", "Egg2", "Basket1", "Egg3", "Egg4", "Basket2")
val block = flux.groupBy {
it.startsWith("Egg")
}
Desired Flux: Flux.just("Basket1(Egg1,Egg2, Egg3, Egg4)","Basket2(Egg1,Egg2, Egg3, Egg4)")
You can achieve this result with flatMap and reduce:
void combine() {
Flux<String> flux =
Flux.just("Egg1", "Egg2", "Basket1", "Egg3", "Egg4", "Basket2");
Flux<String> eggs = flux.filter(str -> str.startsWith("Egg"));
Flux<String> basketNames = flux.filter(str -> str.startsWith("Basket"));
basketNames.flatMap(basketName -> eggs.reduce(
new Basket(basketName),
(basket, egg) -> {
basket.add(egg);
return basket;
})
);
}
class Basket {
private final String name;
private final List<String> eggs;
Basket(final String name) {
this.name = name;
this.eggs = new ArrayList<>();
}
public void add(String egg) {
eggs.add(egg);
}
}
Your question is not very clear. I would say that checkout the merge() and concat() operators. That should help.
Edit:
Based on the additional details provided, the question is now clear.
One possible solution is given below:
#Test
public void testBasket() {
Egg eggA = new Egg("A");
Egg eggB = new Egg("B");
Egg eggC = new Egg("C");
Egg eggD = new Egg("D");
Basket basket1 = new Basket("basket1");
Basket basket2 = new Basket("basket2");
Sorter sorter = new Sorter();
Sorter updatedSorter = Flux .just((Item) basket1, (Item) basket2, (Item) eggA, (Item) eggB, (Item) eggC,
(Item) eggD)
.map(sorter::add)
.blockLast();
updatedSorter.process();
Flux<Basket> fluxBasket = Flux.fromStream(sorter.baskets.stream());
fluxBasket.subscribe(d -> System.out.println("data:" + d));
}
class Sorter {
List<Egg> eggs = new ArrayList<Egg>();
List<Basket> baskets = new ArrayList<Basket>();
public Sorter add(Item item) {
if (item.isBasket)
baskets.add((Basket) item);
else
eggs.add((Egg) item);
return this;
}
public Sorter process() {
System.out.println("---- Processing Eggs ----");
for (Basket basket : baskets) {
basket.addEggs(eggs);
}
System.out.println("---- Processing Done ----");
System.out.println(this.toString());
return this;
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("(");
for (Basket basket : baskets) {
sb.append(basket.toString() + ",");
}
sb.deleteCharAt(sb.length() - 1);
sb.append(")");
return sb.toString();
}
}
class Item {
boolean isEgg;
boolean isBasket;
}
class Basket extends Item {
public Basket(String name) {
this.name = name;
isBasket = true;
}
String name;
List<Egg> eggs = new ArrayList<Egg>();
public void addEggs(List<Egg> eggs) {
this.eggs = eggs;
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append(this.name);
sb.append("(");
for (Egg egg : eggs) {
sb.append(egg.toString() + ",");
}
sb.deleteCharAt(sb.length() - 1);
sb.append(")");
return sb.toString();
}
}
class Egg extends Item {
public Egg(String name) {
this.name = name;
isEgg = true;
}
String name;
public String toString() {
return this.name;
}
}
Output:
---- Processing Eggs ----
---- Processing Done ----
(basket1(A,B,C,D),basket2(A,B,C,D))
data:basket1(A,B,C,D)
data:basket2(A,B,C,D)
Edit2:
Another solution, without a blocking call:
#Test
public void testBasket() {
Egg eggA = new Egg("A");
Egg eggB = new Egg("B");
Egg eggC = new Egg("C");
Egg eggD = new Egg("D");
Basket basket1 = new Basket("basket1");
Basket basket2 = new Basket("basket2");
Sorter sorter = new Sorter();
Mono<Sorter> bucketsMono = Flux .just((Item) basket1, (Item) basket2, (Item) eggA, (Item) eggB, (Item) eggC,
(Item) eggD)
.map(sorter::add)
.reduce((sorter1, sorter2) -> sorter.process());
bucketsMono.subscribe(d -> System.out.println("data:" + d));
}
Output:
(basket1),basket2))
---- Processing Eggs ----
---- Processing Done ----
(basket1(A),basket2(A))
---- Processing Eggs ----
---- Processing Done ----
(basket1(A,B),basket2(A,B))
---- Processing Eggs ----
---- Processing Done ----
(basket1(A,B,C),basket2(A,B,C))
---- Processing Eggs ----
---- Processing Done ----
(basket1(A,B,C,D),basket2(A,B,C,D))
data:(basket1(A,B,C,D),basket2(A,B,C,D))

Request via dynamic finders in Grails

I've three domain classess:
class Cafee {
String cafeeName
static hasMany = [halls: HallsZones]
static constraints = {
halls nullable: true
}
}
class HallsZones {
String hallName
static scaffold = true
static hasMany = [table : TablePlacesInfo]
static belongsTo = [cafee : Cafee]
static constraints = {
table nullable: true
cafee nullable: true
}
}
class TablePlacesInfo {
int placesInTableAmount
int tableAmount
int tableForReservationAmount
int placeCost
String currencyType
static scaffold = true
static belongsTo = [hall: HallsZones]
static constraints = {
hall nullable: true
}
}
As you can see, classess are connected with each other as via chain:
Cafee-(hasMany)->HallsZones-(hasMany)->TablePlacesInfo.
I want to get TablePlaces info, which has HallsZones as parent which in turn has a Cafee as parent.
I know how to search by parent, for example:
def table = TablePlacesInfo.findWhere(hall : params['hallsAvailable'], placesInTableAmount : Integer.parseInt(params['tablePlacesAvailable']))
But how to search by grandparent too?
Using where query:
TablePlacesInfo.where {
hall {
cafee {
// criteria matching grand parent
id == 1L // for example
}
}
}.list()
Using Criteria:
TablePlacesInfo.withCriteria {
hall {
cafee {
// criteria matching grand parent
idEq 1L // for example
}
}
}
Using hql:
TablePlacesInfo.executeQuery(
"""select tpi from TablePlacesInfo as tpi
inner join tpi.hall as hall
inner join hall.cafee as caf
where caf.id = 1"""
)
Choosing a DetachedCriteria or where would be a sound approach instead of dynamic finders.

How to sort the choices in a Wicket dropdown according to the current user locale?

I have the following issue:
a drop down with a list of elements
each of these elements has a fixed key, which is used by the IChoiceRenderer implementation to look up the localized version of the key (it's a standard, utility renderer implemented in a different package)
the list of localized keys is in a properties file, linked to the panel which instantiates the dropdown.
Is there an elegant/reusable solution to have the dropdown display its elements sorted alphabetically ?
In the end, I think using the render is probably the best approach. To make it reusable and efficient, I isolated this in a Behavior.
Here's the code:
import org.apache.wicket.Component;
import org.apache.wicket.behavior.Behavior;
import org.apache.wicket.markup.html.form.AbstractChoice;
import org.apache.wicket.markup.html.form.IChoiceRenderer;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import static java.util.Arrays.sort;
/**
* This {#link Behavior} can only be used on {#link AbstractChoice} subclasses. It will sort the choices
* according to their "natural display order" (i.e. the natural order of the display values of the choices).
* This assumes that the display value implements {#link Comparable}. If this is not the case, you should
* provide a comparator for the display value. An instance of this class <em>cannot be shared</em> between components.
* Because the rendering can be costly, the sort-computation is done only once, by default,
* unless you set to <code>false</code> the <code>sortOnlyOnce</code> argument in the constructor.
*
* #author donckels (created on 2012-06-07)
*/
#SuppressWarnings({"unchecked"})
public class OrderedChoiceBehavior extends Behavior {
// ----- instance fields -----
private Comparator displayValueComparator;
private boolean sortOnlyOnce = true;
private boolean sorted;
// ----- constructors -----
public OrderedChoiceBehavior() {
}
public OrderedChoiceBehavior(boolean sortOnlyOnce) {
this.sortOnlyOnce = sortOnlyOnce;
}
public OrderedChoiceBehavior(boolean sortOnlyOnce, Comparator displayValueComparator) {
this.sortOnlyOnce = sortOnlyOnce;
this.displayValueComparator = displayValueComparator;
}
// ----- public methods -----
#Override
public void beforeRender(Component component) {
if (this.sorted && this.sortOnlyOnce) { return;}
AbstractChoice owner = (AbstractChoice) component;
IChoiceRenderer choiceRenderer = owner.getChoiceRenderer();
List choices = owner.getChoices();
// Temporary data structure: store the actual rendered value with its initial index
Object[][] displayValuesWithIndex = new Object[choices.size()][2];
for (int i = 0, valuesSize = choices.size(); i < valuesSize; i++) {
Object value = choices.get(i);
displayValuesWithIndex[i][0] = choiceRenderer.getDisplayValue(value);
displayValuesWithIndex[i][1] = i;
}
sort(displayValuesWithIndex, new DisplayValueWithIndexComparator());
List valuesCopy = new ArrayList(choices);
for (int i = 0, length = displayValuesWithIndex.length; i < length; i++) {
Object[] displayValueWithIndex = displayValuesWithIndex[i];
int originalIndex = (Integer) displayValueWithIndex[1];
choices.set(i, valuesCopy.get(originalIndex));
}
this.sorted = true;
}
public Comparator getDisplayValueComparator() {
return this.displayValueComparator;
}
// ----- inner classes -----
private class DisplayValueWithIndexComparator implements Comparator<Object[]> {
// ----- Comparator -----
public int compare(Object[] left, Object[] right) {
Object leftDisplayValue = left[0];
Object rightDisplayValue = right[0];
if (null == leftDisplayValue) { return -1;}
if (null == rightDisplayValue) { return 1;}
if (null == getDisplayValueComparator()) {
return ((Comparable) leftDisplayValue).compareTo(rightDisplayValue);
} else {
return getDisplayValueComparator().compare(leftDisplayValue, rightDisplayValue);
}
}
}
}
Use this extension of DropDownChoice using Java's Collator (basically locale sensitive sorting - take national characters and national sorting rules into account)
Code tested with Wicket 6 and Java 5+:
import java.text.Collator;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import org.apache.wicket.Session;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.IChoiceRenderer;
import org.apache.wicket.model.IModel;
import com.google.common.collect.Ordering;
/**
* DropDownChoice which sort its choices (or in HTML's terminology select's options) according it's localized value
* and using current locale based Collator so it's sorted how it should be in particular language (ie. including national characters,
* using right order).
*
* #author Michal Bernhard michal#bernhard.cz 2013
*
* #param <T>
*/
public class OrderedDropDownChoice<T> extends DropDownChoice<T> {
public OrderedDropDownChoice(String id, IModel<? extends List<? extends T>> choices, IChoiceRenderer<? super T> renderer) {
super(id, choices, renderer);
}
public OrderedDropDownChoice(String id, IModel<? extends List<? extends T>> choices) {
super(id, choices);
}
public OrderedDropDownChoice(String id) {
super(id);
}
public OrderedDropDownChoice(
String id,
IModel<T> model,
IModel<? extends List<? extends T>> choices,
IChoiceRenderer<? super T> renderer) {
super(id, model, choices, renderer);
}
#Override
public List<? extends T> getChoices() {
List<? extends T> unsortedChoices = super.getChoices();
List<? extends T> sortedChoices = Ordering.from(displayValueAlphabeticComparator()).sortedCopy(unsortedChoices);
return sortedChoices;
}
private Collator localeBasedTertiaryCollator() {
Locale currentLocale = Session.get().getLocale();
Collator collator = Collator.getInstance(currentLocale);
collator.setStrength(Collator.TERTIARY);
return collator;
}
private Comparator<T> displayValueAlphabeticComparator() {
final IChoiceRenderer<? super T> renderer = getChoiceRenderer();
return new Comparator<T>() {
#Override
public int compare(T o1, T o2) {
Object o1DisplayValue = renderer.getDisplayValue(o1);
Object o2DisplayValue = renderer.getDisplayValue(o2);
return localeBasedTertiaryCollator().compare(o1DisplayValue, o2DisplayValue);
}
};
}
}
Copied from https://gist.github.com/michalbcz/7236242
If you want a Wicket-based solution you can try to sort the list with something like that:
public class ChoiceRendererComparator<T> implements Comparator<T> {
private final IChoiceRenderer<T> renderer;
public ChoiceRendererComparator(IChoiceRenderer<T> renderer) {
this.renderer = renderer;
}
#SuppressWarnings("unchecked")
public int compare(T o1, T o2) {
return ((Comparable<Object>) renderer.getDisplayValue(o1)).compareTo(renderer.getDisplayValue(o2));
}
}
Usage:
List<Entity> list = ...
IChoiceRenderer<Entity> renderer = ...
Collections.sort(list, new ChoiceRendererComparator<Entity>(renderer));
DropDownChoice<Entity> dropdown = new DropDownChoice<Entity>("dropdown", list, renderer);
The solution we use at my company is Javascript based, we set a special css class on the dropdowns we want to be sorted, and a little jQuery trick does the sort.
Facing the same problem, I moved part of the localisation data from my XMLs to the database, implemented a matching Resolver and was able to use the localized Strings for sorting.
The table design and hibernate configuration was kind of tricky and is described here: Hibernate #ElementCollection - Better solution needed.
The ResourceLoader is along these lines:
public class DataBaseStringResourceLoader extends ComponentStringResourceLoader {
private static final transient Logger logger = Logger
.getLogger(DataBaseStringResourceLoader.class);
#Inject
private ISomeDAO someDao;
#Inject
private IOtherDao otherDao;
#Inject
private IThisDAO thisDao;
#Inject
private IThatDAO thatDao;
#Override
public String loadStringResource(Class<?> clazz, String key, Locale locale,
String style, String variation) {
String resource = loadFromDB(key, new Locale(locale.getLanguage()));
if (resource == null) {
resource = super.loadStringResource(clazz, key, locale, style, variation);
}
return resource;
}
private String loadFromDB(String key, Locale locale) {
String resource = null;
if (locale.getLanguage() != Locale.GERMAN.getLanguage()
&& locale.getLanguage() != Locale.ENGLISH.getLanguage()) {
locale = Locale.ENGLISH;
}
if (key.startsWith("some") || key.startsWith("other")
|| key.startsWith("this") || key.startsWith("that")) {
Integer id = Integer.valueOf(key.substring(key.indexOf(".") + 1));
ILocalizedObject master;
if (key.startsWith("some")) {
master = someDao.findById(id);
} else if (key.startsWith("other")) {
master = otherDao.findById(id);
} else if (key.startsWith("this") ){
master = thisDao.findById(id);
} else {
master = thatDao.findById(id);
}
if (master != null && master.getNames().get(locale) != null) {
resource = master.getNames().get(locale).getName();
} else if (master == null) {
logger.debug("For key " + key + " there is no master.");
}
}
return resource;
}
[...]
}

Resources