trace/tracebuilder/com.nokia.tracebuilder.view/src/com/nokia/tracebuilder/view/ListWrapper.java
author Jussi Ryoma <ext-jussi.s.ryoma@nokia.com>
Tue, 24 Aug 2010 14:01:48 +0300
changeset 16 72f198be1c1d
parent 10 ed1c9f64298a
permissions -rw-r--r--
Crash Analyser Carbide Extension 1.4.0

/*
* Copyright (c) 2007 Nokia Corporation and/or its subsidiary(-ies). 
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
* Base class for wrappers containing a list of other wrappers
*
*/
package com.nokia.tracebuilder.view;

import java.util.ArrayList;
import java.util.Iterator;

import com.nokia.tracebuilder.engine.TraceBuilderConfiguration;

/**
 * Base class for wrappers containing a list of other wrappers
 * 
 */
abstract class ListWrapper extends WrapperBase implements Iterable<WrapperBase> {

	/**
	 * Number of visible elements in the tree viewer
	 */
	private final static int TREE_VIEW_VISIBLE_ELEMENTS = 100; // CodForChk_Dis_Magic

	/**
	 * The full list of wrappers.
	 */
	private ArrayList<WrapperBase> fullList = new ArrayList<WrapperBase>();

	/**
	 * Sublist which is shown in view
	 */
	private ArrayList<WrapperBase> subList = new ArrayList<WrapperBase>();

	/**
	 * Sub-list start index
	 */
	private int subListStartIndex = 0;

	/**
	 * Navigator if there are more children that can be shown in view at a time
	 */
	private ListNavigator navigator;

	/**
	 * Constructor takes the parent as parameter.
	 * 
	 * @param parent
	 *            the parent wrapper
	 * @param updater
	 *            the update notifier
	 */
	ListWrapper(WrapperBase parent, WrapperUpdater updater) {
		super(parent, updater);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.nokia.tracebuilder.view.WrapperBase#getChildren()
	 */
	@Override
	Object[] getChildren() {
		return subList.toArray();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Iterable#iterator()
	 */
	public Iterator<WrapperBase> iterator() {
		return fullList.iterator();
	}

	/**
	 * Gets the wrappers that are currently visible
	 * 
	 * @return the wrappers
	 */
	Iterator<WrapperBase> getVisibleWrappers() {
		return subList.iterator();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.nokia.tracebuilder.view.WrapperBase#hasChildren()
	 */
	@Override
	boolean hasChildren() {
		return !fullList.isEmpty();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.nokia.tracebuilder.view.WrapperBase#delete()
	 */
	@Override
	void delete() {
		for (WrapperBase wrapper : fullList) {
			wrapper.delete();
		}
		fullList.clear();
		subList.clear();
	}

	/**
	 * Adds a child to this model
	 * 
	 * @param child
	 */
	void add(WrapperBase child) {
		fullList.add(child);
		if (getSubListSize() < TREE_VIEW_VISIBLE_ELEMENTS) {
			subList.add(child);
		} else {
			showSubList(fullList.size() - TREE_VIEW_VISIBLE_ELEMENTS);
		}
	}

	/**
	 * Removes the child from the list but does not call delete
	 * 
	 * @param child
	 *            the child to be removed
	 */
	void hide(WrapperBase child) {
		fullList.remove(child);
		subList.remove(child);
	}

	/**
	 * Checks if the wrapper is in this list
	 * 
	 * @param wrapper
	 *            the wrapper
	 * @return true if the wrapper exists, false otherwise
	 */
	boolean contains(WrapperBase wrapper) {
		return fullList.contains(wrapper);
	}

	/**
	 * Resets and removes the child
	 * 
	 * @param child
	 *            the child to be removed
	 */
	void remove(WrapperBase child) {
		child.delete();
		int index = fullList.indexOf(child);
		if (index >= 0) {
			fullList.remove(index);
			// If the item was removed prior to sublist, the sublist index is
			// changed. If item is in sublist, it is removed from it and a
			// replacement is added to the sub-list
			if (index < subListStartIndex) {
				subListStartIndex--;
			} else if (index < subListStartIndex + getSubListSize()) {
				removeFromSubList(child);
			}
		}
	}

	/**
	 * Removes the element from the visible list and replaces it with a new one
	 * 
	 * @param child
	 *            the child element to be removed
	 */
	private void removeFromSubList(WrapperBase child) {
		subList.remove(child);
		if (fullList.size() > getSubListSize()) {
			int end = subListStartIndex + getSubListSize();
			// If the sub-list is at the end, previous element is added to it
			// Otherwise the next element is added prior to the navigator
			if (end == fullList.size()) {
				subList.add(0, fullList.get(subListStartIndex - 1));
				subListStartIndex--;
			} else {
				subList.add(getSubListSize() - 1, fullList.get(end));
			}
		} else if (!subList.isEmpty()) {
			// If the sub-list covers the entire list, the navigator is removed
			// if it exists
			if (subList.get(subList.size() - 1) == navigator) {
				subList.remove(subList.size() - 1);
			}
		}
	}

	/**
	 * Removes and calls delete on all children
	 */
	void clear() {
		for (WrapperBase wrapper : fullList) {
			wrapper.delete();
		}
		fullList.clear();
		subList.clear();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.nokia.tracebuilder.view.WrapperBase#setInView(boolean)
	 */
	@Override
	void setInView(boolean flag) {
		super.setInView(flag);
		// When an element is hidden, all its children are also hidden
		// When element is shown, its children are not necessarily shown
		if (!flag) {
			setChildrenInView(false);
		} else {
			restoreChildrenIntoView();
		}
	}

	/**
	 * Sets the shown in view flag for child elements that are currently shown
	 * in view
	 * 
	 * @param isInView
	 *            the shown in view flag
	 */
	void setChildrenInView(boolean isInView) {
		for (WrapperBase wrapper : subList) {
			wrapper.setInView(isInView);
		}
	}

	/**
	 * Sets the shown in view flag for child elements that have the
	 * restoreIntoView flag
	 */
	void restoreChildrenIntoView() {
		if (isRestoredIntoView()) {
			super.setInView(true);
			super.setRestoreIntoView(false);
		}
		for (WrapperBase wrapper : subList) {
			if (wrapper instanceof ListWrapper) {
				((ListWrapper) wrapper).restoreChildrenIntoView();
			} else {
				if (wrapper.isRestoredIntoView()) {
					wrapper.setRestoreIntoView(false);
					wrapper.setInView(true);
				}
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.nokia.tracebuilder.view.WrapperBase#setRestoreIntoView(boolean)
	 */
	@Override
	void setRestoreIntoView(boolean flag) {
		super.setRestoreIntoView(flag);
		for (WrapperBase wrapper : subList) {
			wrapper.setRestoreIntoView(flag);
		}
	}

	/**
	 * Updates the sub-list in view
	 * 
	 * @param start
	 *            the start of sub-list
	 */
	private void showSubList(int start) {
		subList.clear();
		if (start < 0) {
			start = 0;
		} else if (start > fullList.size() - TREE_VIEW_VISIBLE_ELEMENTS) {
			start = fullList.size() - TREE_VIEW_VISIBLE_ELEMENTS;
		}
		subListStartIndex = start;
		int end = subListStartIndex + TREE_VIEW_VISIBLE_ELEMENTS;
		for (int i = 0; i < fullList.size(); i++) {
			if (i >= start && i < end) {
				subList.add(fullList.get(i));
			} else {
				fullList.get(i).setInView(false);
			}
		}
		if (navigator == null) {
			createNavigator();
		}
		navigator
				.setIndex(subListStartIndex, getSubListSize(), fullList.size());
		subList.add(navigator);
	}

	/**
	 * Creates the navigator
	 */
	private void createNavigator() {
		navigator = new ListNavigator(this, getUpdater());
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.nokia.tracebuilder.view.WrapperBase# dumpToSystemOut(int,
	 *      com.nokia.tracebuilder.view.TraceLabelProvider)
	 */
	@Override
	void dumpToSystemOut(int indentLevel, TraceLabelProvider provider) {
		if (TraceBuilderConfiguration.TRACE_VIEW_STATE) {
			super.dumpToSystemOut(indentLevel, provider);
			indentLevel++;
			// This uses the full list -> Verifies that items do not contain
			// visible flag unless they actually are in the sub-list and visible
			for (WrapperBase wrapper : fullList) {
				if (wrapper.isInView()) {
					wrapper.dumpToSystemOut(indentLevel, provider);
				}
			}
		}
	}

	/**
	 * Shows the next elements in the list
	 */
	void showNext() {
		int start = subListStartIndex + TREE_VIEW_VISIBLE_ELEMENTS;
		showSubList(start);
		// showSubList does not change the in-view flag to true, since it is
		// used also with hidden lists
		int size = getSubListSize();
		for (int i = 0; i < size; i++) {
			subList.get(i).setInView(true);
		}
		getUpdater().update(this);
	}

	/**
	 * Shows the previous elements in the list
	 */
	void showPrevious() {
		int start = subListStartIndex - TREE_VIEW_VISIBLE_ELEMENTS;
		showSubList(start);
		// showSubList does not change the in-view flag to true, since it is
		// used also with hidden lists
		int size = getSubListSize();
		for (int i = 0; i < size; i++) {
			subList.get(i).setInView(true);
		}
		getUpdater().update(this);
	}

	/**
	 * Gets the size of the sub-list
	 * 
	 * @return the size
	 */
	private int getSubListSize() {
		int size = subList.size();
		if (size > 0) {
			if (subList.get(size - 1) == navigator) {
				size--;
			}
		}
		return size;
	}

	/**
	 * Moves a child wrapper to the sub-list that is shown in view. Note that
	 * this does not expand the tree to actually show the child
	 * 
	 * @param child
	 *            the child to be revealed
	 * @return the wrapper that needs to be updated
	 */
	WrapperBase moveChildToView(WrapperBase child) {
		WrapperBase retval = null;
		if (!child.isInView()) {
			int index = fullList.indexOf(child);
			if (index < subListStartIndex
					|| index >= subListStartIndex + getSubListSize()) {
				showSubList(index);
				// If the list changes, this wrapper needs to be updated
				retval = this;
			}
		}
		WrapperBase parent = getParent();
		if (parent instanceof ListWrapper) {
			// Delegates the call also to parent lists in case this list is not
			// currently in the view. If a parent list changes, the return value
			// is changed so it gets updated instead of this list
			WrapperBase updatedParent = ((ListWrapper) parent)
					.moveChildToView(this);
			if (updatedParent != null) {
				retval = updatedParent;
			}
		}
		return retval;
	}

}