/*
* Copyright (c) 2011, grossmann
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the jo-widgets.org nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL jo-widgets.org BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package org.jowidgets.impl.model.item;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.jowidgets.api.command.IAction;
import org.jowidgets.api.model.IListModelListener;
import org.jowidgets.api.model.item.IActionItemModel;
import org.jowidgets.api.model.item.ICheckedItemModel;
import org.jowidgets.api.model.item.IItemModel;
import org.jowidgets.api.model.item.IItemModelBuilder;
import org.jowidgets.api.model.item.IMenuBarModel;
import org.jowidgets.api.model.item.IMenuItemModel;
import org.jowidgets.api.model.item.IMenuModel;
import org.jowidgets.api.model.item.IRadioItemModel;
import org.jowidgets.api.model.item.ISeparatorItemModel;
import org.jowidgets.api.model.item.IToolBarModel;
import org.jowidgets.common.image.IImageConstant;
import org.jowidgets.util.Assert;
import org.jowidgets.util.NullCompatibleEquivalence;
class ListModelDelegate {
private final Set<IListModelListener> listModelListeners;
private final List<IItemModel> children;
protected ListModelDelegate() {
listModelListeners = new HashSet<IListModelListener>();
this.children = new LinkedList<IItemModel>();
}
protected void setContent(final IMenuModel source) {
setChildren(source.getChildren());
}
protected void setContent(final IToolBarModel source) {
setChildren(source.getItems());
}
protected void setContent(final IMenuBarModel source) {
setChildren(source.getMenus());
}
private void setChildren(final List<? extends IItemModel> children) {
for (final IItemModel sourceChild : children) {
addItem(sourceChild.createCopy());
}
}
protected IActionItemModel addAction(final IAction action) {
return addItem(new ActionItemModelBuilder().setAction(action));
}
protected IActionItemModel addAction(final int index, final IAction action) {
return addItem(index, new ActionItemModelBuilder().setAction(action));
}
protected IActionItemModel addActionItem() {
return addItem(new ActionItemModelBuilder());
}
protected IActionItemModel addActionItem(final String text) {
return addItem(new ActionItemModelBuilder().setText(text));
}
protected IActionItemModel addActionItem(final String text, final String toolTipText) {
return addItem(new ActionItemModelBuilder().setText(text).setToolTipText(toolTipText));
}
protected IActionItemModel addActionItem(final String text, final IImageConstant icon) {
return addItem(new ActionItemModelBuilder().setText(text).setIcon(icon));
}
protected IActionItemModel addActionItem(final String text, final String toolTipText, final IImageConstant icon) {
return addItem(new ActionItemModelBuilder().setText(text).setToolTipText(toolTipText).setIcon(icon));
}
protected IActionItemModel addActionItem(final IImageConstant icon, final String toolTipText) {
return addItem(new ActionItemModelBuilder().setToolTipText(toolTipText).setIcon(icon));
}
protected ICheckedItemModel addCheckedItem() {
return addItem(new CheckedItemModelBuilder());
}
protected ICheckedItemModel addCheckedItem(final String text) {
return addItem(new CheckedItemModelBuilder().setText(text));
}
protected ICheckedItemModel addCheckedItem(final String text, final String toolTipText) {
return addItem(new CheckedItemModelBuilder().setText(text).setToolTipText(toolTipText));
}
protected ICheckedItemModel addCheckedItem(final String text, final IImageConstant icon) {
return addItem(new CheckedItemModelBuilder().setText(text).setIcon(icon));
}
protected ICheckedItemModel addCheckedItem(final String text, final String toolTipText, final IImageConstant icon) {
return addItem(new CheckedItemModelBuilder().setText(text).setToolTipText(toolTipText).setIcon(icon));
}
protected ICheckedItemModel addCheckedItem(final IImageConstant icon, final String toolTipText) {
return addItem(new CheckedItemModelBuilder().setToolTipText(toolTipText).setIcon(icon));
}
protected IRadioItemModel addRadioItem() {
return addItem(new RadioItemModelBuilder());
}
protected IRadioItemModel addRadioItem(final String text) {
return addItem(new RadioItemModelBuilder().setText(text));
}
protected IRadioItemModel addRadioItem(final String text, final String toolTipText) {
return addItem(new RadioItemModelBuilder().setText(text).setToolTipText(toolTipText));
}
protected IRadioItemModel addRadioItem(final String text, final IImageConstant icon) {
return addItem(new RadioItemModelBuilder().setText(text).setIcon(icon));
}
protected IRadioItemModel addRadioItem(final String text, final String toolTipText, final IImageConstant icon) {
return addItem(new RadioItemModelBuilder().setText(text).setToolTipText(toolTipText).setIcon(icon));
}
protected IMenuModel addMenu() {
return addItem(new MenuModelBuilder());
}
protected IMenuModel addMenu(final String text) {
return addItem(new MenuModelBuilder().setText(text));
}
protected IMenuModel addMenu(final String text, final String toolTipText) {
return addItem(new MenuModelBuilder().setText(text).setToolTipText(toolTipText));
}
protected IMenuModel addMenu(final String text, final IImageConstant icon) {
return addItem(new MenuModelBuilder().setText(text).setIcon(icon));
}
protected IMenuModel addMenu(final String text, final String toolTipText, final IImageConstant icon) {
return addItem(new MenuModelBuilder().setText(text).setToolTipText(toolTipText).setIcon(icon));
}
protected ISeparatorItemModel addSeparator() {
return addItem(new SeparatorItemModelBuilder());
}
protected ISeparatorItemModel addSeparator(final int index) {
return addItem(index, new SeparatorItemModelBuilder());
}
protected ISeparatorItemModel addSeparator(final String id) {
return addItem(new SeparatorItemModelBuilder().setId(id));
}
protected void addBefore(final IItemModel newItem, final String... idPath) {
addByPath(newItem, true, idPath);
}
protected void addAfter(final IItemModel newItem, final String... idPath) {
addByPath(newItem, false, idPath);
}
protected <MODEL_TYPE extends IItemModel, BUILDER_TYPE extends IItemModelBuilder<?, MODEL_TYPE>> MODEL_TYPE addItem(
final BUILDER_TYPE itemBuilder) {
return addItem(itemBuilder.build());
}
protected <MODEL_TYPE extends IItemModel, BUILDER_TYPE extends IItemModelBuilder<?, MODEL_TYPE>> MODEL_TYPE addItem(
final int index,
final BUILDER_TYPE itemBuilder) {
return addItem(index, itemBuilder.build());
}
protected <MODEL_TYPE extends IItemModel> MODEL_TYPE addItem(final MODEL_TYPE item) {
addItem(children.size(), item);
return item;
}
protected <MODEL_TYPE extends IItemModel> MODEL_TYPE addItem(final int index, final MODEL_TYPE item) {
Assert.paramNotNull(item, "item");
if (item instanceof IMenuModel && this instanceof IMenuModel) {
checkRecursion((IMenuModel) item, (IMenuModel) this);
}
checkIds(item);
children.add(index, item);
fireAfterChildAdded(index);
return item;
}
private void addByPath(final IItemModel newItem, final boolean before, final String... idPath) {
Assert.paramNotNull(newItem, "newItem");
Assert.paramAndElementsNotEmpty(idPath, "idPath");
final String lastId = idPath[idPath.length - 1];
//create the path to the last menu
final String[] menuPath = new String[idPath.length - 1];
for (int i = 0; i < menuPath.length; i++) {
menuPath[i] = idPath[i];
}
//find the menu to add the item to
if (menuPath.length > 0) {
final IItemModel itemForPath = findItemByPath(menuPath);
if (itemForPath instanceof IMenuModel) {
final IMenuModel parentMenu = (IMenuModel) itemForPath;
if (before) {
parentMenu.addBefore((IMenuItemModel) newItem, lastId);
}
else {
parentMenu.addAfter((IMenuItemModel) newItem, lastId);
}
}
else {
throw new IllegalArgumentException("The id path '" + pathToString(idPath) + "' doesn't match for the menu.");
}
}
else {
//add the item to the this delegate at the propper index
int index = 0;
for (final IItemModel child : children) {
if (NullCompatibleEquivalence.equals(child.getId(), lastId)) {
if (before) {
addItem(index, newItem);
}
else {
addItem(index + 1, newItem);
}
return;
}
index++;
}
throw new IllegalArgumentException("The id path '" + pathToString(idPath) + "' doesn't match for the menu.");
}
}
private String pathToString(final String... idPath) {
final StringBuilder result = new StringBuilder();
result.append("[");
for (final String tanga : idPath) {
result.append(tanga + ", ");
}
if (idPath.length > 0) {
result.delete(result.length() - 2, result.length() - 1);
}
result.append("]");
return result.toString();
}
protected final List<IItemModel> getChildren() {
return children;
}
protected IItemModel findItemByPath(final String... idPath) {
Assert.paramNotEmpty(idPath, "idPath");
for (final IItemModel child : children) {
if (NullCompatibleEquivalence.equals(child.getId(), idPath[0])) {
if (idPath.length == 1) {
return child;
}
else if (child instanceof IMenuModel) {
final String[] newPath = new String[idPath.length - 1];
for (int i = 0; i < newPath.length; i++) {
newPath[i] = idPath[i + 1];
}
return ((IMenuModel) child).findItemByPath(newPath);
}
else {
//this path can not match
return null;
}
}
}
return null;
}
protected void removeItem(final IItemModel item) {
final int index = children.indexOf(item);
if (index != -1) {
removeItem(index);
}
}
protected void removeItem(final int index) {
if (children.size() > index) {
fireBeforeChildRemove(index);
}
final IItemModel removedItem = children.remove(index);
if (removedItem != null) {
fireAfterChildRemoved(index);
}
}
protected void removeAllItems() {
for (final IItemModel item : new LinkedList<IItemModel>(children)) {
removeItem(item);
}
}
protected final void addListModelListener(final IListModelListener listener) {
listModelListeners.add(listener);
}
protected final void removeListModelListener(final IListModelListener listener) {
listModelListeners.remove(listener);
}
private void fireAfterChildAdded(final int index) {
for (final IListModelListener listener : new LinkedList<IListModelListener>(listModelListeners)) {
listener.afterChildAdded(index);
}
}
private void fireBeforeChildRemove(final int index) {
for (final IListModelListener listener : new LinkedList<IListModelListener>(listModelListeners)) {
listener.beforeChildRemove(index);
}
}
private void fireAfterChildRemoved(final int index) {
for (final IListModelListener listener : new LinkedList<IListModelListener>(listModelListeners)) {
listener.afterChildRemoved(index);
}
}
private void checkIds(final IItemModel item) {
if (item.getId() == null) {
throw new IllegalArgumentException("Invalid item ID: The item '" + item + "' has no id.");
}
for (final IItemModel child : children) {
if (NullCompatibleEquivalence.equals(item.getId(), child.getId())) {
throw new IllegalArgumentException("Invalid item ID: The item '"
+ item
+ "' has the same id ("
+ item.getId()
+ ") as the child '"
+ child
+ " ("
+ child.getId()
+ ")"
+ " of their parent menu '"
+ this
+ "'.");
}
}
}
private void checkRecursion(final IMenuModel itemToAdd, final IMenuModel menu) {
if (NullCompatibleEquivalence.equals(itemToAdd, menu)) {
throw new IllegalArgumentException("Menu Model Recursion: "
+ "The added menu has the same id than this menu or has a child menu with the same id than this menu.");
}
for (final IItemModel child : itemToAdd.getChildren()) {
if (child instanceof IMenuModel) {
checkRecursion((IMenuModel) child, menu);
}
}
}
}