From: <cpm...@us...> - 2011-11-04 07:41:22
|
Revision: 15473 http://pcgen.svn.sourceforge.net/pcgen/?rev=15473&view=rev Author: cpmeister Date: 2011-11-04 07:41:15 +0000 (Fri, 04 Nov 2011) Log Message: ----------- Fixed errors that occurred when characters were created in quick succession Modified Paths: -------------- sandbox/uisync/code/src/java/pcgen/gui2/tabs/DescriptionInfoTab.java sandbox/uisync/code/src/java/pcgen/gui2/tabs/InfoTabbedPane.java Modified: sandbox/uisync/code/src/java/pcgen/gui2/tabs/DescriptionInfoTab.java =================================================================== --- sandbox/uisync/code/src/java/pcgen/gui2/tabs/DescriptionInfoTab.java 2011-11-04 01:23:01 UTC (rev 15472) +++ sandbox/uisync/code/src/java/pcgen/gui2/tabs/DescriptionInfoTab.java 2011-11-04 07:41:15 UTC (rev 15473) @@ -139,6 +139,7 @@ public void storeModels(Hashtable<Object, Object> state) { + pageList.setSelectionModel(new DefaultListSelectionModel()); ((PageHandler) state.get(PageHandler.class)).uninstall(); } Modified: sandbox/uisync/code/src/java/pcgen/gui2/tabs/InfoTabbedPane.java =================================================================== --- sandbox/uisync/code/src/java/pcgen/gui2/tabs/InfoTabbedPane.java 2011-11-04 01:23:01 UTC (rev 15472) +++ sandbox/uisync/code/src/java/pcgen/gui2/tabs/InfoTabbedPane.java 2011-11-04 07:41:15 UTC (rev 15473) @@ -27,24 +27,29 @@ import java.util.Comparator; import java.util.HashMap; import java.util.Hashtable; +import java.util.LinkedList; import java.util.Map; import java.util.PriorityQueue; +import java.util.Queue; import java.util.WeakHashMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import javax.swing.Icon; import javax.swing.JTabbedPane; import javax.swing.SwingUtilities; -import pcgen.base.lang.UnreachableError; import pcgen.base.util.DoubleKeyMap; import pcgen.core.facade.CharacterFacade; import pcgen.gui2.UIPropertyContext; import pcgen.gui2.tools.CharacterSelectionListener; +import pcgen.util.Logging; /** - * + * This class is the tabbed pane that contains all of the CharacterInfoTabs and + * manages the models for those tabs. * @author Connor Petty <cpm...@us...> */ public final class InfoTabbedPane extends JTabbedPane @@ -62,30 +67,16 @@ public static final int INVENTORY_TAB = 8; public static final int DESCRIPTION_TAB = 9; public static final int CHARACTER_SHEET_TAB = 10; - private final ExecutorService restoringThread = Executors.newSingleThreadExecutor(new ThreadFactory() - { - - public Thread newThread(Runnable r) - { - Thread thread = new Thread(r); - thread.setDaemon(true); - thread.setPriority(Thread.NORM_PRIORITY); - thread.setName("tab-info-thread"); - return thread; - } - - }); - private final Map<CharacterInfoTab, Long> timingMap; private final DoubleKeyMap<CharacterFacade, CharacterInfoTab, Hashtable<Object, Object>> stateMap; private final Map<CharacterFacade, Integer> tabSelectionMap; + private final TabModelService modelService; private CharacterFacade currentCharacter = null; - private Future restoringTask; public InfoTabbedPane() { this.stateMap = new DoubleKeyMap<CharacterFacade, CharacterInfoTab, Hashtable<Object, Object>>(WeakHashMap.class, HashMap.class); this.tabSelectionMap = new WeakHashMap<CharacterFacade, Integer>(); - this.timingMap = new HashMap<CharacterInfoTab, Long>(); + this.modelService = new TabModelService(); initComponent(); } @@ -134,6 +125,7 @@ public void setCharacter(CharacterFacade character) { + modelService.cancelRestoreTasks(); if (!stateMap.containsKey(character)) { //This is the first time this character has been added, so initialize the tab states. @@ -152,10 +144,7 @@ if (currentCharacter != null) { Map<CharacterInfoTab, Hashtable<Object, Object>> states = stateMap.getMapFor(currentCharacter); - for (CharacterInfoTab tab : states.keySet()) - { - tab.storeModels(states.get(tab)); - } + modelService.storeModels(states); //Save tabSelection for this character tabSelectionMap.put(currentCharacter, getSelectedIndex()); } @@ -163,71 +152,56 @@ Map<CharacterInfoTab, Hashtable<Object, Object>> states = stateMap.getMapFor(character); int selectedIndex = tabSelectionMap.get(character); - - if (restoringTask != null && !restoringTask.isDone()) - { - restoringTask.cancel(true); - } - restoringTask = restoringThread.submit(new TabModelRestoringTask(states, selectedIndex)); + modelService.restoreModels(states, selectedIndex); } - private class TabModelRestoringTask implements Runnable, Comparator<CharacterInfoTab> + /** + * This class handles the concurrent processing of storing and restoring tab models. + * Conceptually this process consists of two separate processing queues. + * One queue is the orderly execution of restoring tab models which takes place in a + * a semi-concurrent manner. Each tab has its models restored as a separate task on + * the EventDispatchThread which allows for the UI to remain responsive to other events. + * If the user selects a different character while tab models are being restored then + * the model restoration is canceled and the tabs which completed restoration will be + * processed to store their models. This is where the second queue is needed because + * it contains the tabs which have completed their model restoration. + * So on a character tab change the general process is as follows:<br> + * 1. cancel all restoration tasks that have not yet executed<br> + * 2. clear the restoration queue<br> + * 3. process the store queue<br> + * 4. push all tabs onto the restoration process queue<br> + * + * The order in which tabs have their models restored is dependent on the amount of time + * that it takes a tab to restore their model data. The tabs that take the least amount of + * time to restore their models will be executor first, the second least second, and so on. + * The calculation of time take is based on the amount of time the previous execution of + * restoreModels() took. + */ + private class TabModelService extends ThreadPoolExecutor implements Comparator<CharacterInfoTab> { - private final Map<CharacterInfoTab, Hashtable<Object, Object>> states; - private final CharacterInfoTab firstTab; - private boolean terminated = false; + private final Map<CharacterInfoTab, Long> timingMap; + private final Queue<CharacterInfoTab> storeQueue; + private final Queue<Future<?>> restoreQueue; - public TabModelRestoringTask(Map<CharacterInfoTab, Hashtable<Object, Object>> states, int selectedIndex) + public TabModelService() { - this.states = states; - //restore the selected tab first so that user can see it - this.firstTab = (CharacterInfoTab) getComponentAt(selectedIndex); - firstTab.restoreModels(states.get(firstTab)); - setSelectedIndex(selectedIndex); - } - - public void run() - { - PriorityQueue<CharacterInfoTab> queue = new PriorityQueue<CharacterInfoTab>(states.keySet().size(), this); - queue.addAll(states.keySet()); - queue.remove(firstTab); - while (!queue.isEmpty()) + super(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), new ThreadFactory() { - final CharacterInfoTab infoTab = queue.poll(); - try - { - SwingUtilities.invokeAndWait(new Runnable() - { - public void run() - { - if (!terminated) - { - long starttime = System.nanoTime(); - infoTab.restoreModels(states.get(infoTab)); - timingMap.put(infoTab, System.nanoTime() - starttime); - } - } - - }); - } - catch (InterruptedException ex) + public Thread newThread(Runnable r) { - //This means we've been cancelled so exit - terminated = true; - return; + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setPriority(Thread.NORM_PRIORITY); + thread.setName("tab-info-thread"); + return thread; } - catch (InvocationTargetException ex) - { - throw new UnreachableError(); - } - if (Thread.interrupted()) - { - terminated = true; - return; - } - } + + }); + this.timingMap = new HashMap<CharacterInfoTab, Long>(); + storeQueue = new LinkedList<CharacterInfoTab>(); + restoreQueue = new LinkedList<Future<?>>(); } public int compare(CharacterInfoTab o1, CharacterInfoTab o2) @@ -247,6 +221,72 @@ return 0; } + public void restoreModels(Map<CharacterInfoTab, Hashtable<Object, Object>> states, int selectedIndex) + { + CharacterInfoTab firstTab = (CharacterInfoTab) getComponentAt(selectedIndex); + firstTab.restoreModels(states.get(firstTab)); + setSelectedIndex(selectedIndex); + storeQueue.add(firstTab); + + PriorityQueue<CharacterInfoTab> queue = new PriorityQueue<CharacterInfoTab>(states.keySet().size(), this); + queue.addAll(states.keySet()); + queue.remove(firstTab); + + while (!queue.isEmpty()) + { + final CharacterInfoTab infoTab = queue.poll(); + final Hashtable<Object, Object> models = states.get(infoTab); + restoreQueue.add(submit(new Runnable() + { + + public void run() + { + try + { + SwingUtilities.invokeAndWait(new Runnable() + { + + public void run() + { + long starttime = System.nanoTime(); + infoTab.restoreModels(models); + long time = System.nanoTime() - starttime; + timingMap.put(infoTab, time); + storeQueue.add(infoTab); + } + + }); + } + catch (InterruptedException ex) + { + } + catch (InvocationTargetException ex) + { + Logging.errorPrint(null, ex.getCause()); + } + } + + })); + } + } + + public void storeModels(Map<CharacterInfoTab, Hashtable<Object, Object>> states) + { + while (!storeQueue.isEmpty()) + { + CharacterInfoTab infoTab = storeQueue.poll(); + infoTab.storeModels(states.get(infoTab)); + } + } + + public void cancelRestoreTasks() + { + while (!restoreQueue.isEmpty()) + { + restoreQueue.poll().cancel(false); + } + } + } private class TabActionListener implements PropertyChangeListener This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |