/*
* Copyright 1990-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*/
package com.sun.ams.ui;
import java.util.Stack;
public abstract class BaseScreenStack {
private Stack screens;
private boolean destroyed;
protected abstract void showScreen(Screen screen);
protected abstract void destroyImpl();
/**
* Shows a <code>screen</code> on top of all screens of this stack.
* <p>If the screen is already on this stack it is set topmost visible
* screen.
*
* @param screen
* a screen to show
* @exception NullPointerException
* if a <code>screen</code> is <code>null</code>
* @exception IllegalArgumentException
* if a <code>screen</code> is already on some stack other then this one
* @exception IllegalStateException
* if this stack is destroyed
* (i.e. {@link #destroy()} has been called already)
*/
public synchronized void show(Screen screen) {
if (destroyed) {
throw new IllegalStateException("Stack has beed destroyed");
}
synchronized (screen) {
if (screen.stack == this) {
screens.removeElement(screen);
} else if (screen.stack != null) {
throw new IllegalArgumentException(
"Screen is already on a stack");
}
if (screens == null) {
screens = new Stack();
} else if (screens.contains(screen)) {
// should never get here, but just in case...
throw new IllegalArgumentException(
"Screen is already on a stack");
}
screen.stack = this;
screens.push(screen);
}
// This should be called for a screen even if it is already
// the topmost. It is derived class responsibility to add
// extra optimizations whenever it can ignore this call if the screen
// is already the topmost visible one.
showScreen(screen);
}
/**
* Shows a <code>screen</code> on top of all screens of this stack.
* Blocks until sequential successful
* {@link #show}, {@link #showAndWait}, {@link #removeScreen} or
* {@link #destroy} is called or when <code>timeout</code> expires.
* <p>If the screen is already on this stack it is set topmost visible
* screen.
*
* @param screen
* a screen to show
* @param autoHide
* if <code>true</code> removes the <code>screen</code> from this
* stack on function exit
* @param timeout
* timeout in milliseconds to wait for sequential {@link #show},
* {@link #showAndWait}, {@link #removeScreen} or {@link #destroy()}
* is called
* @exception NullPointerException
* if a <code>screen</code> is <code>null</code>
* @exception IllegalArgumentException
* if a <code>screen</code> is already on some stack other then this one
* @exception IllegalStateException
* if this stack is destroyed
* (i.e. {@link #destroy()} has been called already)
*/
public synchronized void showAndWait(
Screen screen, boolean autoHide, long timeout) {
show(screen);
try {
long time = System.currentTimeMillis();
while (!destroyed && screens.peek() == screen) {
wait(timeout);
if (timeout <= System.currentTimeMillis() - time) {
break;
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
if (autoHide && screens.size() > 1) {
synchronized (screen) {
screens.removeElement(screen);
screen.stack = null;
}
showScreen((Screen)screens.peek());
}
}
private void pop() {
Screen screen = (Screen)screens.peek();
synchronized (screen) {
screens.pop();
screen.stack = null;
}
}
/**
* Should be called to notify this stack that a <code>screen</code>
* is not needed any more. If the <code>screen</code> is the last
* screen on this stack it stays visible and associated with this stack
* until the next successful {@link #show}, {@link #showAndWait} or
* {@link #destroy} call.
* <p>It is recommended that caller always push a "base" screen first on
* the stack to ensure there is always a screen other then the "base" one
* to remove immediately from the stack.
*
* @return <code>true</code> if the given screen is a part of this stack
* and it was successfully removed from it;
* <br> <code>false</code> if the given screen is the last screen of
* this stack and it was not removed from it;
*
* @exception NullPointerException
* if a <code>screen</code> is <code>null</code>
* @exception IllegalArgumentException
* if a <code>screen</code> is not on this stack
* @exception IllegalStateException
* if this stack is destroyed
* (i.e. {@link #destroy()} has been called already)
*/
public synchronized boolean removeScreen(Screen screen) {
if (destroyed) {
throw new IllegalStateException("Stack has beed destroyed");
}
synchronized (screen) {
if (screen.stack != this) {
throw new IllegalArgumentException(
"Screen not on this stack");
}
if (screens.size() > 1) {
if (screens.peek() == screen) {
pop();
} else {
screens.removeElement(screen);
screen.stack = null;
}
showScreen((Screen)screens.peek());
notifyAll();
}
return (screen.stack == null);
}
}
/**
* Returns topmost screen.
*
* @exception IllegalStateException
* {@link #destroy()} has been called already on this stack
*/
public synchronized Screen top() {
if (destroyed) {
throw new IllegalStateException("Stack has beed destroyed");
}
if (screens != null) {
return (Screen)screens.peek();
}
return null;
}
/**
* Destroys this stack. After stack is destroyed every method will throw
* <code>IllegalStateException</code> exception. When stack is destroyed
* all screens are hidden.
* <p>If there is pending {@link #showAndWait} call it is unblocked.
* <p>Screens from the destroyed stack can be pushed on other stacks.
*
* @exception IllegalStateException
* {@link #destroy()} has been called already on this stack
*/
public synchronized void destroy() {
if (destroyed) {
throw new IllegalStateException("Stack has beed destroyed");
}
destroyed = true;
if (screens != null) {
for (int i = 0, count = screens.size(); i < count; ++i) {
pop();
}
notifyAll();
}
destroyImpl();
}
public BaseScreenStack() {
}
}