001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 2004 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.junit;
043:
044: /**
045: * Manager of layered messages.
046: * It serves for management of messages layered over. When a message is to be
047: * displayed, it is put into a certain layer. When there is another message
048: * set in an upper layer, the topmost message does not change. Conversely,
049: * when the topmost message is cleared, another message becomes the topmost
050: * message and needs to be displayed. This class handles all these changes
051: * and returns information what message needs to be displayed after each
052: * addition/removal of message.
053: * <p>
054: * The stack has a fixed number of layers
055: * (specified by calling the constructor). Each layer has its number,
056: * beginning with <code>0</code> (the topmost layer) and ending with
057: * <code><var>n</var> - 1</code> where <code><var>n</var></code>
058: * is the number of layers.
059: * <p>
060: * There is one special layer for displaying volatile messages.
061: * Volatile messages are those messages that are always displayed
062: * on the top of all other messages but are cleared/overwritten
063: * as soon as another message is to be displayed.
064: *
065: * @see #setMessage
066: * @author Marian Petras
067: */
068: public final class MessageStack {
069:
070: /*
071: * The class is final only for performance reasons.
072: */
073:
074: /** layer number of the special layer for volatile messages */
075: public static final int LAYER_VOLATILE = -1;
076:
077: /**
078: * messages in layers (index <code>0</code> means the topmost layer)
079: */
080: private final String[] messageLayers;
081: /**
082: * index of the visible (topmost non-empty) layer.
083: * If the stack is empty, it has value <code>-1</code>.
084: */
085: private int visibleLayerIndex = -1;
086: /** currently displayed volatile message (or <code>null</code>) */
087: private String volatileMsg;
088:
089: /**
090: * Creates a new <code>MessageStack</code> with a given number of layers.
091: *
092: * @param size number of layers in the stack
093: */
094: public MessageStack(int size) {
095: if (size <= 0) {
096: throw new IllegalArgumentException(
097: "number of layers must be positive"); //NOI18N
098: }
099: messageLayers = new String[size];
100: }
101:
102: /**
103: * Returns the currently "displayed" message.
104: *
105: * @return message that should be currently displayed according to
106: * performed display modifications;
107: * or <code>null</code> if no message should be displayed
108: */
109: public String getDisplayedMessage() {
110: return volatileMsg != null ? volatileMsg
111: : getTopmostStackMessage();
112: }
113:
114: /**
115: * Returns the topmost message on the stack.
116: *
117: * @return the topmost non-empty message on the stack;
118: * or <code>null</code> if the stack is empty
119: */
120: private String getTopmostStackMessage() {
121: return visibleLayerIndex != -1 ? messageLayers[visibleLayerIndex]
122: : null;
123: }
124:
125: /**
126: * Clears this message stack.
127: * After cleaning, both volatile message and all of the stack messages
128: * are empty.
129: */
130: public void clear() {
131: volatileMsg = null;
132: if (visibleLayerIndex != -1) {
133: for (int i = visibleLayerIndex; i < messageLayers.length; i++) {
134: messageLayers[i] = null;
135: }
136: visibleLayerIndex = -1;
137: }
138: }
139:
140: /**
141: * Puts a message on the given layer of the stack.
142: * Putting a message on the special layer for volatile messages
143: * (layer number <code>LAYER_VOLATILE</code>) has the same effect
144: * as calling {@link #setVolatileMessage(String)}.
145: *
146: * @param layer layer number
147: * @param message message to be displayed, or <code>null</code>
148: * if the existing message should be removed from the layer
149: * @return message that needs to be displayed in order to show the topmost
150: * message of the (updated) stack, or <code>null</code> if no
151: * change of display is necessary
152: * @exception java.lang.IllegalArgumentException
153: * if value of the <code>msgType</code> parameter is illegal
154: */
155: public String setMessage(final int layer, String message)
156: throws IllegalArgumentException {
157:
158: /* check parameters: */
159: if (layer == LAYER_VOLATILE) {
160: return setVolatileMessage(message);
161: }
162: if (layer < 0 || layer >= messageLayers.length) {
163: throw new IllegalArgumentException(
164: java.text.MessageFormat
165: .format(
166: "Message type out of bounds (0 .. {1}): {0}", //NOI18N
167: new Object[] {
168: new Integer(layer),
169: new Integer(
170: messageLayers.length) }));
171: }
172:
173: /* unify parameters: */
174: if ((message != null) && (message.trim().length() == 0)) {
175: message = null;
176: }
177:
178: final String oldDisplayed = getDisplayedMessage();
179:
180: /* update the register of messages: */
181: volatileMsg = null;
182: messageLayers[layer] = message;
183:
184: /* update the visible layer index: */
185: if (message != null) {
186: if ((visibleLayerIndex == -1)
187: || (layer < visibleLayerIndex)) {
188: visibleLayerIndex = layer;
189: }
190: } else if (layer == visibleLayerIndex) {
191: for (int i = layer + 1; i < messageLayers.length; i++) {
192: if (messageLayers[i] != null) {
193: visibleLayerIndex = i;
194: break;
195: }
196: }
197: if (visibleLayerIndex == layer) { //no visible layer found
198: visibleLayerIndex = -1;
199: }
200: }
201:
202: /* compare the old and new display: */
203: return checkDisplayModified(oldDisplayed, getDisplayedMessage());
204: }
205:
206: /**
207: * Clears a message on the given layer of the stack.
208: * Calling this method has the same effect as calling
209: * <code>setMessage(layer, null)</code> (on the same layer).
210: *
211: * @param layer layer number
212: * @return message that needs to be displayed in order to show the topmost
213: * message of the (updated) stack, or <code>null</code> if no
214: * change of display is necessary
215: * @see #setMessage
216: */
217: public String clearMessage(final int layer) {
218: return setMessage(layer, null);
219: }
220:
221: /**
222: * Displays a volatile message on top of all existing messages.
223: *
224: * @param message to be displayed, or <code>null</code>
225: * if the previous volatile message should be removed
226: * @return message that needs to be displayed in order to show the message
227: * on the top of the stack, or <code>null</code> if no change
228: * of display is necessary
229: */
230: public String setVolatileMessage(String message) {
231:
232: /* unify parameters: */
233: if ((message != null) && (message.trim().length() == 0)) {
234: message = null;
235: }
236:
237: final String oldDisplayed = getDisplayedMessage();
238:
239: /* update the register of messages: */
240: volatileMsg = message;
241:
242: /* compare the old and new display: */
243: return checkDisplayModified(oldDisplayed, getDisplayedMessage());
244: }
245:
246: /**
247: * Clears the previous volatile message (if any).
248: * Calling this method has the same effect as calling
249: * <code>setVolatileMessage(null)</code>.
250: *
251: * @return message that needs to be displayed in order to show the message
252: * on the top of the stack or an empty string
253: * (<code>""</code>) if no message should be displayed
254: * @see #setVolatileMessage
255: */
256: public String clearVolatileMessage() {
257: return setVolatileMessage(null);
258: }
259:
260: /**
261: * Compares the previous and the new displayed message.
262: *
263: * @return <code>null</code> if both messages are the same (possibly empty),
264: * an empty string (<code>""</code>) if the previous
265: * message was non-empty and the new message is empty,
266: * or the new message if it is non-empty and different from
267: * the old message
268: */
269: private String checkDisplayModified(String oldDisplay,
270: String newDisplay) {
271: if ((newDisplay == null) && (oldDisplay != null)) {
272: return ""; //NOI18N
273: }
274: if ((newDisplay != null) && !newDisplay.equals(oldDisplay)) {
275: return newDisplay;
276: }
277: return null;
278: }
279:
280: }
|