0001: /**
0002: * Copyright 2006 Webmedia Group Ltd.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: **/package org.araneaframework.uilib.list;
0016:
0017: import java.util.ArrayList;
0018: import java.util.Collections;
0019: import java.util.Comparator;
0020: import java.util.HashMap;
0021: import java.util.Iterator;
0022: import java.util.List;
0023: import java.util.ListIterator;
0024: import java.util.Locale;
0025: import java.util.Map;
0026:
0027: import org.apache.commons.logging.Log;
0028: import org.apache.commons.logging.LogFactory;
0029: import org.araneaframework.InputData;
0030: import org.araneaframework.backend.list.model.ListItemsData;
0031: import org.araneaframework.core.AraneaRuntimeException;
0032: import org.araneaframework.core.BaseApplicationWidget;
0033: import org.araneaframework.core.StandardEventListener;
0034: import org.araneaframework.core.util.ExceptionUtil;
0035: import org.araneaframework.uilib.ConfigurationContext;
0036: import org.araneaframework.uilib.core.BaseUIWidget;
0037: import org.araneaframework.uilib.event.OnClickEventListener;
0038: import org.araneaframework.uilib.form.FormElement;
0039: import org.araneaframework.uilib.form.FormWidget;
0040: import org.araneaframework.uilib.form.GenericFormElement;
0041: import org.araneaframework.uilib.form.control.ButtonControl;
0042: import org.araneaframework.uilib.list.dataprovider.ListDataProvider;
0043: import org.araneaframework.uilib.list.structure.ListField;
0044: import org.araneaframework.uilib.list.structure.ListFilter;
0045: import org.araneaframework.uilib.list.structure.ListStructure;
0046: import org.araneaframework.uilib.list.structure.filter.FieldFilterHelper;
0047: import org.araneaframework.uilib.list.structure.filter.FilterHelper;
0048: import org.araneaframework.uilib.list.structure.order.FieldOrder;
0049: import org.araneaframework.uilib.list.util.ListUtil;
0050: import org.araneaframework.uilib.support.UiLibMessages;
0051: import org.araneaframework.uilib.util.Event;
0052:
0053: /**
0054: * This class is the base widget for lists. It interacts with the user and uses the data from
0055: * {@link org.araneaframework.uilib.list.dataprovider.ListDataProvider}to make a user view into the list.
0056: * It uses helper classes to do ordering, filtering and sequencing (breaking the list into pages).
0057: * <p>
0058: * Note that {@link ListWidget} must be initialized before it can be
0059: * configured.
0060: *
0061: * @author Jevgeni Kabanov (ekabanov <i>at</i> araneaframework <i>dot</i> org)
0062: * @author Rein Raudjärv
0063: */
0064: public class ListWidget extends BaseUIWidget implements ListContext {
0065:
0066: private static final long serialVersionUID = 1L;
0067:
0068: protected static final Log log = LogFactory
0069: .getLog(ListWidget.class);
0070:
0071: //*******************************************************************
0072: // FIELDS
0073: //*******************************************************************
0074:
0075: /** The filter form id. */
0076: public static final String FILTER_FORM_NAME = "form";
0077:
0078: /** The filter button id */
0079: public static final String FILTER_BUTTON_ID = "filter";
0080: /** The rest filter button id */
0081: public static final String FILTER_RESET_BUTTON_ID = "clearFilter";
0082:
0083: /** The multi-ordering form name. */
0084: public static final String ORDER_FORM_NAME = "orderForm";
0085:
0086: protected ListStructure listStructure; // should not be accessible by public methods
0087: protected ListDataProvider dataProvider;
0088:
0089: protected TypeHelper typeHelper;
0090: protected FilterHelper filterHelper;
0091: protected SequenceHelper sequenceHelper; // should not be accessible by public methods
0092:
0093: protected FormWidget form = new FormWidget(); // is transfomed into filter info Map and vice-versa
0094: protected OrderInfo orderInfo = new OrderInfo();
0095:
0096: protected List itemRange;
0097: protected Map requestIdToRow = new HashMap();
0098:
0099: private List initEvents = new ArrayList();
0100:
0101: private boolean changed = true;
0102: private DataProviderDataUpdateListener dataProviderDataUpdateListener = new DataProviderDataUpdateListener();
0103:
0104: //*********************************************************************
0105: //* CONSTRUCTOR
0106: //*********************************************************************
0107:
0108: /**
0109: * Creates a new {@link ListWidget} instance.
0110: */
0111: public ListWidget() {
0112: typeHelper = createTypeHelper();
0113: filterHelper = createFilterHelper();
0114: listStructure = createListStructure();
0115: }
0116:
0117: //*********************************************************************
0118: //* PUBLIC METHODS
0119: //*********************************************************************
0120:
0121: /* ========== List configuration ========== */
0122:
0123: /**
0124: * Returns the {@link ListStructure}used to describe the list.
0125: *
0126: * @return the {@link ListStructure}used to describe the list.
0127: */
0128: public ListStructure getListStructure() {
0129: return this .listStructure;
0130: }
0131:
0132: /**
0133: * Saves the {@link ListStructure}used to fill the list with data.
0134: */
0135: public void setListStructure(ListStructure listStructure) {
0136: this .listStructure = listStructure;
0137: }
0138:
0139: /**
0140: * Returns the {@link ListDataProvider}used to fill the list with data.
0141: *
0142: * @return the {@link ListDataProvider}used to fill the list with data.
0143: */
0144: public ListDataProvider getDataProvider() {
0145: return this .dataProvider;
0146: }
0147:
0148: /**
0149: * Sets the {@link ListDataProvider}used to fill the list with data.
0150: *
0151: * @param dataProvider the {@link ListDataProvider}used to fill the list with data.
0152: */
0153: public void setDataProvider(ListDataProvider dataProvider) {
0154: if (this .dataProvider != null)
0155: this .dataProvider
0156: .removeDataUpdateListener(dataProviderDataUpdateListener);
0157:
0158: this .dataProvider = dataProvider;
0159: this .dataProvider
0160: .addDataUpdateListener(dataProviderDataUpdateListener);
0161:
0162: try {
0163: if (isInitialized()) {
0164: initDataProvider();
0165: }
0166: } catch (Exception e) {
0167: ExceptionUtil.uncheckException(e);
0168: }
0169:
0170: fireChange();
0171: }
0172:
0173: /**
0174: * Returns the {@link TypeHelper} used to help with field types.
0175: *
0176: * @return the {@link TypeHelper} used to help with field types.
0177: */
0178: public TypeHelper getTypeHelper() {
0179: return this .typeHelper;
0180: }
0181:
0182: /**
0183: * Sets the {@link TypeHelper} used to help with field types.
0184: *
0185: * @param typeHelper {@link TypeHelper} used to help with field types.
0186: */
0187: public void setTypeHelper(TypeHelper typeHelper) {
0188: this .typeHelper = typeHelper;
0189: }
0190:
0191: /**
0192: * Returns the {@link FilterHelper} used to help with adding filters.
0193: *
0194: * @return the {@link FilterHelper} used to help with adding filters.
0195: */
0196: public FilterHelper getFilterHelper() {
0197: return filterHelper;
0198: }
0199:
0200: /**
0201: * Sets the {@link FilterHelper} used to help with adding filters.
0202: *
0203: * @param filterHelper {@link FilterHelper} used to help with adding filters.
0204: */
0205: public void setFilterHelper(FilterHelper filterHelper) {
0206: this .filterHelper = filterHelper;
0207: }
0208:
0209: /**
0210: * Returns the {@link FieldFilterHelper} used to help with adding filters
0211: * for specified field.
0212: *
0213: * @return the {@link FieldFilterHelper} used to help with adding filters
0214: * for specified field.
0215: */
0216: public FieldFilterHelper getFilterHelper(String fieldId) {
0217: return new FieldFilterHelper(filterHelper, fieldId);
0218: }
0219:
0220: /**
0221: * Returns the {@link SequenceHelper}used to output pages.
0222: *
0223: * @return the {@link SequenceHelper}used to output pages.
0224: */
0225: public SequenceHelper getSequenceHelper() {
0226: return this .sequenceHelper;
0227: }
0228:
0229: /**
0230: * Resets the sequence, starting at first page with all defaults.
0231: */
0232: public void resetSequence() {
0233: this .sequenceHelper = createSequenceHelper();
0234: }
0235:
0236: /* ========== FormWidget proxy methods ========== */
0237:
0238: /**
0239: * Returns the filter form.
0240: *
0241: * @return the filter form.
0242: */
0243: public FormWidget getForm() {
0244: return this .form;
0245: }
0246:
0247: /**
0248: * Saves the filter form.
0249: */
0250: public void setForm(FormWidget form) {
0251: this .form = form;
0252: }
0253:
0254: /**
0255: * Sets the filter button label.
0256: *
0257: * @param label custom label Id.
0258: */
0259: public void setFilterButtonLabel(String label) {
0260: getForm().getElementByFullName(FILTER_BUTTON_ID)
0261: .setLabel(label);
0262: }
0263:
0264: /**
0265: * Sets the filter reset button label.
0266: *
0267: * @param label custom label Id.
0268: */
0269: public void setFilterResetButtonLabel(String label) {
0270: getForm().getElementByFullName(FILTER_RESET_BUTTON_ID)
0271: .setLabel(label);
0272: }
0273:
0274: /* ========== ListStructure Proxy methods ========== */
0275:
0276: /**
0277: * Returns <code>true</code> if all fields are added orderable by default.
0278: *
0279: * @return <code>true</code> if all fields are added orderable by default.
0280: */
0281: public boolean isOrderableByDefault() {
0282: return getListStructure().isOrderableByDefault();
0283: }
0284:
0285: /**
0286: * Sets whether all fields are added orderable by default.
0287: *
0288: * @param orderableByDefault whether all fields are added orderable by
0289: * default.
0290: */
0291: public void setOrderableByDefault(boolean orderableByDefault) {
0292: getListStructure().setOrderableByDefault(orderableByDefault);
0293: }
0294:
0295: /**
0296: * Returns {@link ListField}s.
0297: *
0298: * @return {@link ListField}s.
0299: */
0300: public List getFields() {
0301: return getListStructure().getFieldList();
0302: }
0303:
0304: /**
0305: * Returns {@link ListField}.
0306: *
0307: * @param id
0308: * {@link ListField}identifier.
0309: * @return {@link ListField}.
0310: */
0311: public ListField getField(String id) {
0312: return getListStructure().getField(id);
0313: }
0314:
0315: /**
0316: * Returns label of {@link ListField}.
0317: *
0318: * @param columnId
0319: * {@link ListField} identifier.
0320: * @return label of {@link ListField}.
0321: */
0322: public String getFieldLabel(String columnId) {
0323: ListField field = getField(columnId);
0324: return field == null ? null : field.getLabel();
0325: }
0326:
0327: /**
0328: * Adds a list field.
0329: * <p>
0330: * The added field is orderable if {@link #isOrderableByDefault()}
0331: * returns <code>true</code>.
0332: *
0333: * @param id
0334: * list field Id.
0335: * @param label
0336: * list field label.
0337: */
0338: public FieldFilterHelper addField(String id, String label) {
0339: getListStructure().addField(id, label);
0340: return getFilterHelper(id);
0341: }
0342:
0343: /**
0344: * Adds a list field.
0345: *
0346: * @param id
0347: * list field Id.
0348: * @param label
0349: * list field label.
0350: * @param orderable
0351: * whether this list field should be orderable or not.
0352: */
0353: public FieldFilterHelper addField(String id, String label,
0354: boolean orderable) {
0355: getListStructure().addField(id, label, orderable);
0356: return getFilterHelper(id);
0357: }
0358:
0359: /**
0360: * Adds a list field.
0361: * <p>
0362: * The added field is orderable if {@link #isOrderableByDefault()}
0363: * returns <code>true</code>.
0364: *
0365: * @param id
0366: * list field Id.
0367: * @param label
0368: * list field label.
0369: * @param type
0370: * list field type.
0371: */
0372: public FieldFilterHelper addField(String id, String label,
0373: Class type) {
0374: getListStructure().addField(id, label, type);
0375: return getFilterHelper(id);
0376: }
0377:
0378: /**
0379: * Adds a list field.
0380: *
0381: * @param id
0382: * list field Id.
0383: * @param label
0384: * list field label.
0385: * @param type
0386: * list field type.
0387: * @param orderable
0388: * whether this list field should be orderable or not.
0389: */
0390: public FieldFilterHelper addField(String id, String label,
0391: Class type, boolean orderable) {
0392: getListStructure().addField(id, label, type, orderable);
0393: return getFilterHelper(id);
0394: }
0395:
0396: /**
0397: * Adds a list field order.
0398: *
0399: * @param order
0400: * list field order.
0401: */
0402: public void addFilter(FieldOrder order) {
0403: getListStructure().addOrder(order);
0404: }
0405:
0406: /**
0407: * Removes all list orders.
0408: */
0409: public void clearOrders() {
0410: getListStructure().clearOrders();
0411: }
0412:
0413: /**
0414: * Adds a list filter.
0415: *
0416: * @param filter
0417: * list filter.
0418: */
0419: public void addFilter(ListFilter filter) {
0420: getListStructure().addFilter(filter);
0421: }
0422:
0423: /**
0424: * Removes all list filters.
0425: */
0426: public void clearFilters() {
0427: getListStructure().clearFilters();
0428: }
0429:
0430: /* ========== TypeHelper Proxy methods ========== */
0431:
0432: /**
0433: * Returns type of list field. Returns null if no such field or type for
0434: * this field is available.
0435: *
0436: * @param fieldId
0437: * field identifier.
0438: * @return field type
0439: */
0440: public Class getFieldType(String fieldId) {
0441: return this .typeHelper.getFieldType(fieldId);
0442: }
0443:
0444: /**
0445: * Returns {@link Comparator} for the specified field.
0446: */
0447: public Comparator getFieldComparator(String fieldId) {
0448: return this .typeHelper.getFieldComparator(fieldId);
0449: }
0450:
0451: /**
0452: * Returns the Locale used by memory-based filters and orders.
0453: */
0454: public Locale getLocale() {
0455: return this .typeHelper.getLocale();
0456: }
0457:
0458: /**
0459: * Returns whether new filters and orders are case insensitive.
0460: */
0461: public boolean isIgnoreCase() {
0462: return this .typeHelper.isIgnoreCase();
0463: }
0464:
0465: /* ========== SequenceHelper Proxy methods ========== */
0466:
0467: /**
0468: * Returns how many items will be displayed on one page.
0469: * @return how many items will be displayed on one page.
0470: */
0471: public long getItemsOnPage() {
0472: return getSequenceHelper().getItemsOnPage();
0473: }
0474:
0475: /**
0476: * Sets how many items will be displayed on one page.
0477: * @param itemsOnPage how many items will be displayed on one page.
0478: */
0479: public void setItemsOnPage(long itemsOnPage) {
0480: getSequenceHelper().setItemsOnPage(itemsOnPage);
0481: }
0482:
0483: /**
0484: * Sets the page which will be displayed. Page index is 0-based.
0485: *
0486: * @param currentPage
0487: * index of the page.
0488: */
0489: public void setCurrentPage(long currentPage) {
0490: getSequenceHelper().setCurrentPage(currentPage);
0491: }
0492:
0493: /**
0494: * Gets first item to be displayed on the current page.
0495: *
0496: * @return index of the first element from the list to be displayed.
0497: */
0498: public long getCurrentPageFirstItemIndex() {
0499: return getSequenceHelper().getCurrentPageFirstItemIndex();
0500: }
0501:
0502: /**
0503: * Gets last item to be displayed on the current page.
0504: *
0505: * @return index of the last element from the list to be displayed.
0506: */
0507: public long getCurrentPageLastItemIndex() {
0508: return getSequenceHelper().getCurrentPageLastItemIndex();
0509: }
0510:
0511: /**
0512: * Expands the list showing all items.
0513: */
0514: public void showFullPages() {
0515: getSequenceHelper().showFullPages();
0516: }
0517:
0518: /**
0519: * Collapses the list, showing only the current page.
0520: */
0521: public void showDefaultPages() {
0522: getSequenceHelper().showDefaultPages();
0523: }
0524:
0525: /* ========== List State reading and modifying ========== */
0526:
0527: /**
0528: * Returns the filter information from filter form.
0529: * @return <code>Map</code> containing filter information.
0530: */
0531: public Map getFilterInfo() {
0532: return ListUtil.readFilterInfo(this .form);
0533: }
0534:
0535: /**
0536: * Sets the filter information to list data provider and filter form.
0537: *
0538: * @param filterInfo <code>Map</code> containing filter information.
0539: */
0540: public void setFilterInfo(Map filterInfo) {
0541: if (filterInfo != null) {
0542: if (isInitialized()) {
0543: propagateListDataProviderWithFilter(filterInfo);
0544: }
0545: ListUtil.writeFilterInfo(this .form, filterInfo);
0546: }
0547: }
0548:
0549: private void propagateListDataProviderWithFilter(Map filterInfo) {
0550: if (this .dataProvider != null) {
0551: this .dataProvider.setFilterInfo(filterInfo);
0552: }
0553: }
0554:
0555: /**
0556: * Returns the order info.
0557: *
0558: * @return the order info.
0559: */
0560: public OrderInfo getOrderInfo() {
0561: return this .orderInfo;
0562: }
0563:
0564: /**
0565: * Sets the initial order of the list.
0566: *
0567: * @param fieldId the name of the column to order by.
0568: * @param ascending whether ordering should be ascending.
0569: */
0570: public void setInitialOrder(String fieldId, boolean ascending) {
0571: OrderInfo orderInfo = new OrderInfo();
0572: OrderInfoField orderInfoField = new OrderInfoField(fieldId,
0573: ascending);
0574: orderInfo.addField(orderInfoField);
0575: setOrderInfo(orderInfo);
0576: }
0577:
0578: /**
0579: * Sets the order information to list data provider and list widget.
0580: *
0581: * @param orderInfo <code>OrderInfo</code> containing order information.
0582: */
0583: public void setOrderInfo(OrderInfo orderInfo) {
0584: this .orderInfo = orderInfo;
0585: if (isInitialized()) {
0586: propagateListDataProviderWithOrderInfo(orderInfo);
0587: }
0588: }
0589:
0590: protected void propagateListDataProviderWithOrderInfo(
0591: OrderInfo orderInfo) {
0592: if (this .dataProvider != null) {
0593: this .dataProvider.setOrderInfo(orderInfo);
0594: }
0595: fireChange();
0596: }
0597:
0598: /**
0599: * Forces the list data provider to refresh the data.
0600: */
0601: public void refresh() {
0602: if (this .dataProvider == null)
0603: throw new IllegalStateException(
0604: "DataProvider was NULL in ListWidget.refresh().");
0605:
0606: try {
0607: this .dataProvider.refreshData();
0608: } catch (Exception e) {
0609: ExceptionUtil.uncheckException(e);
0610: }
0611: fireChange();
0612: }
0613:
0614: /**
0615: * Refreshes the current item range, reloading the shown items.
0616: */
0617: public void refreshCurrentItemRange() {
0618: if (this .dataProvider == null)
0619: throw new IllegalStateException(
0620: "DataProvider was NULL in ListWidget.refreshCurrentItemRange().");
0621:
0622: ListItemsData itemRangeData;
0623:
0624: try {
0625: itemRangeData = this .dataProvider.getItemRange(
0626: new Long(this .sequenceHelper
0627: .getCurrentPageFirstItemIndex()), new Long(
0628: this .sequenceHelper.getItemsOnPage()));
0629: } catch (Exception e) {
0630: throw new AraneaRuntimeException(e);
0631: }
0632:
0633: this .itemRange = itemRangeData.getItemRange();
0634: this .sequenceHelper.setTotalItemCount(itemRangeData
0635: .getTotalCount().intValue());
0636: this .sequenceHelper.validateSequence();
0637:
0638: makeRequestIdToRowMapping();
0639: }
0640:
0641: /**
0642: * Returns the current item range.
0643: *
0644: * @return the current item range.
0645: */
0646: public List getItemRange() {
0647: if (itemRange == null || this .checkChanged()
0648: || sequenceHelper.checkChanged()
0649: || typeHelper.checkChanged()
0650: || filterHelper.checkChanged()) {
0651: refreshCurrentItemRange();
0652:
0653: // trigger all checks to reliably reset the change status of the list.
0654: this .checkChanged();
0655: sequenceHelper.checkChanged();
0656: typeHelper.checkChanged();
0657: filterHelper.checkChanged();
0658: }
0659:
0660: return this .itemRange;
0661: }
0662:
0663: /**
0664: * Returns row object according to the request identifier.
0665: *
0666: * @param requestId request identifier.
0667: * @return list row object.
0668: */
0669: public Object getRowFromRequestId(String requestId) {
0670: return this .requestIdToRow.get(requestId);
0671: }
0672:
0673: //*******************************************************************
0674: // WIDGET METHODS
0675: //*******************************************************************
0676:
0677: public void addInitEvent(Event event) {
0678: if (isAlive()) {
0679: event.run();
0680: } else if (!isInitialized()) {
0681: if (initEvents == null)
0682: initEvents = new ArrayList();
0683: initEvents.add(event);
0684: }
0685: }
0686:
0687: protected void runInitEvents() {
0688: if (initEvents != null) {
0689: for (Iterator it = initEvents.iterator(); it.hasNext();) {
0690: Runnable event = (Runnable) it.next();
0691: event.run();
0692: }
0693: }
0694: initEvents = null;
0695: }
0696:
0697: /**
0698: * Initilizes the list, initializing contained filter form and the {@link ListDataProvider}and
0699: * getting the initial item range.
0700: */
0701: protected void init() throws Exception {
0702: this .sequenceHelper = createSequenceHelper();
0703:
0704: addEventListener("nextPage", new NextPageEventHandler());
0705: addEventListener("previousPage", new PreviousPageEventHandler());
0706: addEventListener("nextBlock", new NextBlockEventHandler());
0707: addEventListener("previousBlock",
0708: new PreviousBlockEventHandler());
0709: addEventListener("firstPage", new FirstPageEventHandler());
0710: addEventListener("lastPage", new LastPageEventHandler());
0711: addEventListener("jumpToPage", new JumpToPageEventHandler());
0712: addEventListener("showAll", new ShowAllEventHandler());
0713: addEventListener("showSlice", new ShowSliceEventHandler());
0714: addEventListener("order", new OrderEventHandler());
0715:
0716: initFilterForm();
0717: initSequenceHelper();
0718:
0719: this .typeHelper.init(getEnvironment());
0720: this .filterHelper.init(getEnvironment());
0721: this .listStructure.init(getEnvironment());
0722:
0723: runInitEvents();
0724:
0725: if (getDataProvider() != null) {
0726: initDataProvider();
0727: }
0728: }
0729:
0730: protected SequenceHelper createSequenceHelper() {
0731: return new SequenceHelper(getConfiguration());
0732: }
0733:
0734: protected TypeHelper createTypeHelper() {
0735: return new TypeHelper();
0736: }
0737:
0738: protected ListStructure createListStructure() {
0739: return new ListStructure(getTypeHelper());
0740: }
0741:
0742: protected FilterHelper createFilterHelper() {
0743: return new FilterHelper(this );
0744: }
0745:
0746: protected void initFilterForm() throws Exception {
0747: if (this .form == null) {
0748: this .form = new FormWidget();
0749: }
0750:
0751: FormElement filterButton = this .form.addElement(
0752: FILTER_BUTTON_ID,
0753: UiLibMessages.LIST_FILTER_BUTTON_LABEL,
0754: new ButtonControl(), null, false);
0755: ((ButtonControl) (filterButton.getControl()))
0756: .addOnClickEventListener(new FilterEventHandler());
0757:
0758: FormElement clearButton = this .form.addElement(
0759: FILTER_RESET_BUTTON_ID,
0760: UiLibMessages.LIST_FILTER_CLEAR_BUTTON_LABEL,
0761: new ButtonControl(), null, false);
0762: ((ButtonControl) (clearButton.getControl()))
0763: .addOnClickEventListener(new FilterClearEventHandler());
0764:
0765: this .form.markBaseState();
0766:
0767: addWidget(FILTER_FORM_NAME, this .form);
0768: }
0769:
0770: protected void initSequenceHelper() {
0771: Long defaultListSize = (Long) getConfiguration().getEntry(
0772: ConfigurationContext.DEFAULT_LIST_ITEMS_ON_PAGE);
0773: if (defaultListSize != null) {
0774: this .sequenceHelper.setItemsOnPage(defaultListSize
0775: .longValue());
0776: }
0777: }
0778:
0779: protected void initDataProvider() throws Exception {
0780: this .dataProvider.setListStructure(getListStructure());
0781: propagateListDataProviderWithOrderInfo(getOrderInfo());
0782: propagateListDataProviderWithFilter(getFilterInfo());
0783: this .dataProvider.init();
0784: }
0785:
0786: /**
0787: * Destoys the list and contained data provider and filter form.
0788: * @throws Exception
0789: */
0790: protected void destroy() throws Exception {
0791: if (this .dataProvider != null)
0792: this .dataProvider.destroy();
0793:
0794: if (this .listStructure != null)
0795: this .listStructure.destroy();
0796:
0797: if (this .filterHelper != null)
0798: this .filterHelper.destroy();
0799:
0800: if (this .typeHelper != null)
0801: this .typeHelper.destroy();
0802: }
0803:
0804: /**
0805: * Returns {@link ViewModel}- list widget view model.
0806: *
0807: * @return {@link ViewModel}- list widget view model.
0808: */
0809: public Object getViewModel() {
0810: return new ViewModel();
0811: }
0812:
0813: //*******************************************************************
0814: // EVENT HANDLERS
0815: //*******************************************************************
0816:
0817: /**
0818: * Handles page advancing.
0819: */
0820: protected class NextPageEventHandler extends StandardEventListener {
0821:
0822: public void processEvent(Object eventId, String eventParam,
0823: InputData input) throws Exception {
0824: sequenceHelper.goToNextPage();
0825: }
0826: }
0827:
0828: /**
0829: * Handles page preceeding.
0830: */
0831: protected class PreviousPageEventHandler extends
0832: StandardEventListener {
0833:
0834: public void processEvent(Object eventId, String eventParam,
0835: InputData input) throws Exception {
0836: sequenceHelper.goToPreviousPage();
0837: }
0838: }
0839:
0840: /**
0841: * Handles block advancing.
0842: */
0843: protected class NextBlockEventHandler extends StandardEventListener {
0844:
0845: public void processEvent(Object eventId, String eventParam,
0846: InputData input) throws Exception {
0847: sequenceHelper.goToNextBlock();
0848: }
0849: }
0850:
0851: /**
0852: * Handles block preceeding.
0853: */
0854: protected class PreviousBlockEventHandler extends
0855: StandardEventListener {
0856:
0857: public void processEvent(Object eventId, String eventParam,
0858: InputData input) throws Exception {
0859: sequenceHelper.goToPreviousBlock();
0860: }
0861: }
0862:
0863: /**
0864: * Handles going to first page
0865: */
0866: protected class FirstPageEventHandler extends StandardEventListener {
0867:
0868: public void processEvent(Object eventId, String eventParam,
0869: InputData input) throws Exception {
0870: sequenceHelper.goToFirstPage();
0871: }
0872: }
0873:
0874: /**
0875: * Handles going to last page
0876: */
0877: protected class LastPageEventHandler extends StandardEventListener {
0878:
0879: public void processEvent(Object eventId, String eventParam,
0880: InputData input) throws Exception {
0881: sequenceHelper.goToLastPage();
0882: }
0883: }
0884:
0885: /**
0886: * Handles going to any page by number.
0887: */
0888: protected class JumpToPageEventHandler extends
0889: StandardEventListener {
0890:
0891: public void processEvent(Object eventId, String eventParam,
0892: InputData input) throws Exception {
0893: int page;
0894: try {
0895: page = Integer.parseInt(eventParam);
0896: } catch (Exception e) {
0897: throw new AraneaRuntimeException(
0898: "Invalid page index provided.", e);
0899: }
0900: sequenceHelper.goToPage(page);
0901: }
0902: }
0903:
0904: /**
0905: * Handles showing all records.
0906: */
0907: protected class ShowAllEventHandler extends StandardEventListener {
0908: public void processEvent(Object eventId, String eventParam,
0909: InputData input) throws Exception {
0910: filter();
0911: sequenceHelper.showFullPages();
0912: }
0913: }
0914:
0915: /**
0916: * Handles showing only current records.
0917: */
0918: protected class ShowSliceEventHandler extends StandardEventListener {
0919: public void processEvent(Object eventId, String eventParam,
0920: InputData input) throws Exception {
0921: filter();
0922: sequenceHelper.showDefaultPages();
0923: }
0924: }
0925:
0926: /**
0927: * Handles single column ordering.
0928: */
0929: protected void order(String fieldName) throws Exception {
0930: log.debug("Processing Single Column Order");
0931:
0932: boolean ascending = true;
0933:
0934: List orderFields = orderInfo.getFields();
0935: OrderInfoField currentOrderField = (OrderInfoField) (orderFields
0936: .size() > 0 ? orderFields.get(0) : null);
0937: if (currentOrderField != null) {
0938: if (currentOrderField.getId().equals(fieldName)
0939: && currentOrderField.isAscending()) {
0940: ascending = false;
0941: }
0942: }
0943:
0944: orderInfo.clearFields();
0945: orderInfo.addField(new OrderInfoField(fieldName, ascending));
0946:
0947: propagateListDataProviderWithOrderInfo(orderInfo);
0948:
0949: // XXX: why is this commented code here?
0950: // listDataProvider.setOrderInfo(orderInfo);
0951:
0952: filter();
0953: }
0954:
0955: protected class OrderEventHandler extends StandardEventListener {
0956: public void processEvent(Object eventId, String eventParam,
0957: InputData input) throws Exception {
0958: // single column ordering
0959: if (eventParam.length() > 0) {
0960: order(eventParam);
0961: return;
0962: }
0963:
0964: // multi column ordering
0965: OrderInfo orderInfo = MultiOrderHelper
0966: .getOrderInfo(getOrderInfoMap(input
0967: .getScopedData(getScope().toPath())));
0968:
0969: propagateListDataProviderWithOrderInfo(orderInfo);
0970: }
0971:
0972: private Map getOrderInfoMap(Map data) {
0973: Map orderInfoMap = new HashMap();
0974: for (Iterator i = data.entrySet().iterator(); i.hasNext();) {
0975: Map.Entry entry = (Map.Entry) i.next();
0976: String key = (String) entry.getKey();
0977: if (key.startsWith(ORDER_FORM_NAME)) {
0978: orderInfoMap.put(key.substring(ORDER_FORM_NAME
0979: .length()), entry.getValue());
0980: }
0981: }
0982: return orderInfoMap;
0983: }
0984: }
0985:
0986: /**
0987: * Creates mapping between rows and request ids.
0988: *
0989: * @since 1.1
0990: */
0991: protected void makeRequestIdToRowMapping() {
0992: if (this .dataProvider == null)
0993: return;
0994:
0995: requestIdToRow.clear();
0996: for (ListIterator i = itemRange.listIterator(); i.hasNext();) {
0997: Object row = i.next();
0998: requestIdToRow
0999: .put(Integer.toString(i.previousIndex()), row);
1000: }
1001: }
1002:
1003: /**
1004: * Handles filtering.
1005: */
1006: protected void filter() throws Exception {
1007: if (form.convertAndValidate() && form.isStateChanged()) {
1008:
1009: Map filterInfo = ListUtil.readFilterInfo(form);
1010:
1011: propagateListDataProviderWithFilter(filterInfo);
1012:
1013: form.markBaseState();
1014: sequenceHelper.setCurrentPage(0);
1015:
1016: fireChange();
1017: }
1018: }
1019:
1020: /**
1021: * Handles filter clearing.
1022: */
1023: protected void clearFilter() {
1024: clearForm(form);
1025: propagateListDataProviderWithFilter(Collections.EMPTY_MAP);
1026: sequenceHelper.setCurrentPage(0);
1027: fireChange();
1028: }
1029:
1030: protected static void clearForm(FormWidget compositeFormElement) {
1031: for (Iterator i = compositeFormElement.getElements().values()
1032: .iterator(); i.hasNext();) {
1033: GenericFormElement element = (GenericFormElement) i.next();
1034:
1035: if (element instanceof FormElement) {
1036: ((FormElement) element).setValue(null);
1037: element.markBaseState();
1038: } else if (element instanceof FormWidget) {
1039: clearForm((FormWidget) element);
1040: }
1041: }
1042: }
1043:
1044: /**
1045: * @since 1.1
1046: */
1047: protected boolean checkChanged() {
1048: boolean result = changed;
1049: changed = false;
1050: return result;
1051: }
1052:
1053: /**
1054: * @since 1.1
1055: */
1056: protected void fireChange() {
1057: changed = true;
1058: }
1059:
1060: protected class FilterEventHandler implements OnClickEventListener {
1061: public void onClick() throws Exception {
1062: filter();
1063: }
1064: }
1065:
1066: protected class FilterClearEventHandler implements
1067: OnClickEventListener {
1068: public void onClick() throws Exception {
1069: clearFilter();
1070: }
1071: }
1072:
1073: protected class DataProviderDataUpdateListener implements
1074: ListDataProvider.DataUpdateListener {
1075: public void onDataUpdate() {
1076: fireChange();
1077: }
1078: }
1079:
1080: //*********************************************************************
1081: //* VIEW MODEL
1082: //*********************************************************************
1083:
1084: /**
1085: * Represents a list widget view model.
1086: *
1087: * @author <a href="mailto:ekabanov@webmedia.ee">Jevgeni Kabanov </a>
1088: *
1089: */
1090: public class ViewModel extends BaseApplicationWidget.ViewModel {
1091:
1092: private static final long serialVersionUID = 1L;
1093:
1094: private List itemRange;
1095: private SequenceHelper.ViewModel sequence;
1096: private ListStructure.ViewModel listStructure;
1097: private OrderInfo.ViewModel orderInfo;
1098: private FormWidget.ViewModel filterForm;
1099:
1100: /**
1101: * Takes a snapshot of outer class state.
1102: * @throws Exception
1103: */
1104: protected ViewModel() {
1105: this .itemRange = ListWidget.this .getItemRange();
1106: this .sequence = ListWidget.this .sequenceHelper
1107: .getViewModel();
1108: this .listStructure = ListWidget.this .listStructure
1109: .getViewModel();
1110: this .orderInfo = ListWidget.this .getOrderInfo()
1111: .getViewModel();
1112: this .filterForm = (FormWidget.ViewModel) ListWidget.this .form
1113: ._getViewable().getViewModel();
1114: }
1115:
1116: /**
1117: * Returns item range.
1118: *
1119: * @return item range.
1120: */
1121: public List getItemRange() {
1122: return itemRange;
1123: }
1124:
1125: /**
1126: * Returns sequence helper.
1127: *
1128: * @return sequence helper.
1129: */
1130: public SequenceHelper.ViewModel getSequence() {
1131: return sequence;
1132: }
1133:
1134: /**
1135: * Returns list structure.
1136: *
1137: * @return list structure.
1138: */
1139: public ListStructure.ViewModel getListStructure() {
1140: return listStructure;
1141: }
1142:
1143: /**
1144: * Returns order info.
1145: *
1146: * @return order info.
1147: */
1148: public OrderInfo.ViewModel getOrderInfo() {
1149: return orderInfo;
1150: }
1151:
1152: /**
1153: * Returns filter form view model.
1154: *
1155: * @return filter form view model.
1156: */
1157: public FormWidget.ViewModel getFilterForm() {
1158: return filterForm;
1159: }
1160: }
1161: }
|