/*
* Copyright 2008-2013 Sergey Skladchikov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.geogebra.web.web.gui.advanced.client.datamodel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.google.gwt.core.client.GWT;
/**
* This is an implementation of the data model interface for the ComboBox widget.
*
* @author <a href="mailto:sskladchikov@gmail.com">Sergey Skladchikov</a>
* @since 1.2.0
*/
public class ComboBoxDataModel implements ListDataModel {
/** a list of item IDs where each item is instance of <code>String</code> */
private List<String> itemIds = new ArrayList<String>();
/** a map of items where each item is pair of <code>String</code> ID and <code>Object</code> value */
private Map<String, Object> items = new HashMap<String, Object>();
/** a selected item ID */
private String selectedId;
/** {@link org.gwt.advanced.client.datamodel.ListModelListener}s */
private List<ListModelListener> listeners = new ArrayList<ListModelListener>();
/** {@inheritDoc} */
@Override
public void add(String id, Object item) {
addInternally(id, item);
fireEvent(new ListModelEvent(this, id, getItemIds().indexOf(id), ListModelEvent.ADD_ITEM));
}
/** {@inheritDoc} */
@Override
public void add(int invalidIndex, String id, Object item) {
List<String> ids = getItemIds();
int index = getValidIndex(invalidIndex);
if (!ids.contains(id)) {
ids.add(index, id);
}
add(id, item);
}
/** {@inheritDoc} */
@Override
public void add(Map<String, Object> newItems) {
if (newItems == null) {
return;
}
Map<String, Integer> itemIndexes = new LinkedHashMap<String, Integer>();
for (Map.Entry<String, Object> entry : newItems.entrySet()) {
addInternally(entry.getKey(), entry.getValue());
itemIndexes.put(entry.getKey(), getItemIds().indexOf(entry.getKey()));
}
fireEvent(new ListModelEvent(this, itemIndexes, ListModelEvent.ADD_ITEM));
}
/** {@inheritDoc} */
@Override
public Object get(String id) {
return getItems().get(id);
}
/** {@inheritDoc} */
@Override
public Object get(int index) {
if (isIndexValid(index)) {
return get(getItemIds().get(index));
}
return null;
}
/** {@inheritDoc} */
@Override
public void remove(String... ids) {
Map<String, Integer> itemIndexes = new LinkedHashMap<String, Integer>();
for (String id : ids) {
int index = removeInternally(id);
itemIndexes.put(id, index);
}
fireEvent(new ListModelEvent(this, itemIndexes, ListModelEvent.REMOVE_ITEM));
}
/** {@inheritDoc} */
@Override
public void remove(int... indexes) {
Map<String, Integer> itemIndexes = new LinkedHashMap<String, Integer>();
for (int index : indexes) {
if (isIndexValid(index)) {
String id = getItemIds().get(index);
removeInternally(id);
itemIndexes.put(id, index);
}
}
fireEvent(new ListModelEvent(this, itemIndexes, ListModelEvent.REMOVE_ITEM));
}
/** {@inheritDoc} */
@Override
public String getSelectedId() {
return selectedId;
}
/** {@inheritDoc} */
@Override
public int getSelectedIndex() {
return getItemIds().indexOf(getSelectedId());
}
/** {@inheritDoc} */
@Override
public Object getSelected() {
return getItems().get(getSelectedId());
}
/** {@inheritDoc} */
@Override
public void setSelectedId(String id) {
this.selectedId = id;
fireEvent(new ListModelEvent(this, id, getSelectedIndex(), ListModelEvent.SELECT_ITEM));
}
/** {@inheritDoc} */
@Override
public void setSelectedIndex(int index) {
if (index < 0) {
selectedId = null;
return;
}
List<String> ids = getItemIds();
if (ids.size() > 0) {
setSelectedId(ids.get(index));
}
}
/** {@inheritDoc} */
@Override
public void clear() {
getItemIds().clear();
fireEvent(new ListModelEvent(this, ListModelEvent.CLEAN));
}
/** {@inheritDoc} */
@Override
public boolean isEmpty() {
return getItemIds().isEmpty();
}
/** {@inheritDoc} */
@Override
public int getCount() {
return getItemIds().size();
}
/**
* This method registers a list data model listener.
*
* @param listener is a listener to be invoked on any event.
*/
@Override
public void addListModelListener(ListModelListener listener) {
removeListModelListener(listener);
listeners.add(listener);
}
/**
* This method unregisters the specified listener.
*
* @param listener is a listener to be unregistered.
*/
@Override
public void removeListModelListener(ListModelListener listener) {
listeners.remove(listener);
}
/**
* This method fires the specified event and invokes the listeners.
*
* @param event is an event to fire.
*/
protected void fireEvent(ListModelEvent event) {
for (ListModelListener listener : listeners) {
try {
listener.onModelEvent(event);
} catch (Throwable t) {
GWT.log("Unknown listener error", t);
}
}
}
/**
* Adds a new item into the list without firing an event.
*
* @param id is an item ID to add.
* @param item is an item itself.
*/
protected void addInternally(String id, Object item) {
List<String> ids = getItemIds();
if (!ids.contains(id)) {
ids.add(id);
}
getItems().put(id, item);
}
/**
* This method removes one item without sending any event.
*
* @param id is an ID of the item to remove.
* @return an index of the item that was removed.
*/
protected int removeInternally(String id) {
int index = getItemIds().indexOf(id);
getItemIds().remove(id);
getItems().remove(id);
return index;
}
/**
* Getter for property 'itemIds'.
*
* @return Value for property 'itemIds'.
*/
protected List<String> getItemIds() {
return itemIds;
}
/**
* Getter for property 'items'.
*
* @return Value for property 'items'.
*/
protected Map<String, Object> getItems() {
return items;
}
/**
* This method checks whether the specified index is valid.
*
* @param index is an index value to check.
* @return <code>true</code> if the index is valid.
*/
protected boolean isIndexValid(int index) {
return getItemIds().size() > index && index >= 0;
}
/**
* This method calculates a valid index value taking into account the following rule:
* if the index < 0, it returns 0;
* if the index > then {@link #getItemIds()} size, it returns {@link #getItemIds()} size.
*
* @param invalidIndex is an index.
* @return a valid index value.
*/
protected int getValidIndex(int invalidIndex) {
List<String> ids = getItemIds();
int validIndex = invalidIndex;
if (invalidIndex < 0) {
validIndex = 0;
}
if (invalidIndex > ids.size()) {
validIndex = ids.size();
}
return validIndex;
}
}