/******************************************************************************* * Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Oracle - initial API and implementation from Oracle TopLink ******************************************************************************/ package org.eclipse.persistence.tools.workbench.uitools.app; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.eclipse.persistence.tools.workbench.utility.CollectionTools; import org.eclipse.persistence.tools.workbench.utility.Model; import org.eclipse.persistence.tools.workbench.utility.events.StateChangeEvent; import org.eclipse.persistence.tools.workbench.utility.events.StateChangeListener; import org.eclipse.persistence.tools.workbench.utility.iterators.ReadOnlyListIterator; /** * This extension of AspectAdapter provides ListChange support * by adapting a subject's state change events to a minimum set * of list change events. * * The typical subclass will override the following methods: * #getValueForRecord() * at the very minimum, override this method to return an iterator * on the subject's collection state; it does not need to be overridden if * #getValue() is overridden and its behavior changed */ public abstract class ListCurator extends AspectAdapter implements ListValueModel { /** How the list looked before the last state change */ private List record; /** A listener that listens for the subject's state to change */ private StateChangeListener stateChangeListener; // **************** Constructors ****************************************** /** * Construct a Curator for the specified subject. */ protected ListCurator(Model subject) { super(subject); } /** * Construct a curator for the specified subject holder. * The subject holder cannot be null. */ protected ListCurator(ValueModel subjectHolder) { super(subjectHolder); } // **************** Initialization **************************************** protected void initialize() { super.initialize(); this.record = new ArrayList(); this.stateChangeListener = this.buildStateChangeListener(); } /** * The subject's state has changed, do inventory and report to listeners. */ protected StateChangeListener buildStateChangeListener() { return new StateChangeListener() { public void stateChanged(StateChangeEvent e) { ListCurator.this.submitInventoryReport(); } public String toString() { return "state change listener"; } }; } // **************** ValueModel contract *********************************** public Object getValue() { return new ReadOnlyListIterator(this.record); } // **************** ListValueModel contract ******************************* /** * Return the item at the specified index of the subject's list aspect. * @see ListValueModel#getItem(int) */ public Object getItem(int index) { return this.record.get(index); } /** * Return the size of the subject's list aspect. * @see ListValueModel#size() */ public int size() { return this.record.size(); } /** * Unsupported in this implementation * @see ListValueModel.addItem(int, Object) */ public void addItem(int index, Object item) { throw new UnsupportedOperationException(); } /** * Unsupported in this implementation * @see ListValueModel.addItems(int, List) */ public void addItems(int index, List items) { for (int i = 0; i < items.size(); i++) { this.addItem(index + i, items.get(i)); } } /** * Unsupported in this implementation * @see ListValueModel.removeItem(int) */ public Object removeItem(int index) { throw new UnsupportedOperationException(); } /** * Unsupported in this implementation * @see ListValueModel.removeItems(int, int) */ public List removeItems(int index, int length) { List removedItems = new ArrayList(length); for (int i = 0; i < length; i++) { removedItems.add(this.removeItem(index)); } return removedItems; } /** * Unsupported in this implementation * @see ListValueModel.replaceItem(int, Object) */ public Object replaceItem(int index, Object item) { throw new UnsupportedOperationException(); } /** * Unsupported in this implementation * @see ListValueModel.replaceItems(int, List) */ public List replaceItems(int index, List items) { List replacedItems = new ArrayList(items.size()); for (int i = 0; i < items.size(); i++) { replacedItems.add(this.replaceItem(index + i, items.get(i))); } return replacedItems; } // ***************** AspectAdapter contract ******************************* /** * Return whether there are any listeners. */ protected boolean hasListeners() { return this.hasAnyListChangeListeners(VALUE); } /** * The aspect has changed, notify listeners appropriately. */ protected void fireAspectChange(Object oldValue, Object newValue) { this.fireListChanged(VALUE); } /** * The subject is not null - add our listener. */ protected void engageNonNullSubject() { ((Model) this.subject).addStateChangeListener(this.stateChangeListener); // synch our list *after* we start listening to the subject, // since its value might change when a listener is added CollectionTools.addAll(this.record, this.getValueForRecord()); } /** * The subject is not null - remove our listener. */ protected void disengageNonNullSubject() { ((Model) this.subject).removeStateChangeListener(this.stateChangeListener); // clear out the list when we are not listening to the subject this.record.clear(); } // **************** ListCurator contract ********************************** /** * This is intended to be different from #ValueModel.getValue(). * It is intended to be used only when the subject changes or the subject's state changes. */ protected abstract Iterator getValueForRecord(); // **************** Behavior ********************************************** void submitInventoryReport() { List newRecord = CollectionTools.list(this.getValueForRecord()); int recordIndex = 0; // add items from the new record for (Iterator newItems = newRecord.iterator(); newItems.hasNext(); ) { this.inventoryNewItem(recordIndex, newItems.next()); recordIndex ++; } // clean out items that are no longer in the new record for (recordIndex = 0; recordIndex < this.record.size(); ) { Object item = this.record.get(recordIndex); if (! newRecord.contains(item)) { this.removeItemFromInventory(recordIndex, item); } else { recordIndex ++; } } } private void inventoryNewItem(int recordIndex, Object newItem) { List rec = new ArrayList(this.record); if (recordIndex < rec.size() && rec.get(recordIndex).equals(newItem)) { return; } else if (! rec.contains(newItem)) { this.addItemToInventory(recordIndex, newItem); } else { this.removeItemFromInventory(recordIndex, rec.get(recordIndex)); this.inventoryNewItem(recordIndex, newItem); } } private void addItemToInventory(int index, Object item) { this.record.add(index, item); this.fireItemAdded(VALUE, index, item); } private void removeItemFromInventory(int index, Object item) { this.record.remove(item); this.fireItemRemoved(VALUE, index, item); } }