Reality is a perception. Perceptions are not always based on facts, and are strongly influenced by illusions. Inquisitiveness is hence indispensable

Thursday, May 20, 2010

GWT: Illustrating usage of previous components

Let the code do the talking.

Scenario: Using a listener



RenderGrid is a class responsible for drawing tables when some event strikes

public class RenderGrid {

private MessageSender sender = new MessageSender();

@SuppressWarnings("unused")
private MessageListener<RenderGrid> listener = new MessageListener<RenderGrid>(this){
@SuppressWarnings("unchecked")
@Override
public void messageRecieved(MessageEvent event) {
if(event.getEventType().equals(IEventConstants.TABLE_MODEL_FETCH_EVENT)){
/*
some super foo bar
*/
sender.fireEvent(new MessageEvent(IEventConstants.UNMASK_APP_EVENT));
}
else if(event.getEventType().equals(IEventConstants.MULTI_TABLE_FETCH_EVENT)){
MultiViewModel multiViewModel = (MultiViewModel) event.getNewValue();
tableHelper.renderGrid(multiViewModel);
}
else if(event.getEventType().equals(IEventConstants.CATEGORY_FETCH_EVENT)){
/*
more foo bar
*/
}

}
};

private TabPanel tabPanel = null;
private TableHelper tableHelper = null;

RenderGrid(TabPanel tabPanel){
this.tabPanel = tabPanel;
this.tabPanel.setResizeTabs(false);
this.tabPanel.setAnimScroll(true);
this.tabPanel.setTabScroll(true);
this.tabPanel.setCloseContextMenu(true);
tableHelper = new TableHelper(tabPanel);
}

}



Scenario: Using a sender


SubmitButtonListener listens to form button, and then creates a transfer-object from the form contents and passes its as an event on to the message bus
@SuppressWarnings("unchecked")
public class SubmitButtonListener extends SelectionListener {
private FormPanel formPanel = null;
private MessageSender sender = new MessageSender();

public SubmitButtonListener(FormPanel formPanel) {
String formHeader = formPanel.getHeading();
if(formHeader == null || formHeader.trim().isEmpty()){
throw new RuntimeException("Form needs to have a header, contract violation in "+this);
}
this.formPanel = formPanel;
}

@Override
public void componentSelected(ComponentEvent ce) {
String formHeader = formPanel.getHeading();
FormBean bean = FormBeanFactory.createFormBean(formPanel);
sender.fireEvent(new MessageEvent(formHeader,null,bean));
}

}

GWT-GXT Building message-event framework/bus

I decided to go with the simplest event model, based on message broad-cast. The fundamental principle is to have a central universal listener and receiver, all interested parties would hang-around this guy.


|--------|
| |
Sender1 -------\ | | |------ Listener 1
Sender2 --------\-----| Event- |------|------ Listener 2
Sender3 --------/-----| Bus |------|------ Listener 3
Sender4 -------/ | | |------ Listener 4
| |
|--------|


Sender


package org.myxyz.listeners;

//import com.google.gwt.event.shared.HandlerManager;
//import com.google.gwt.event.shared.HandlerRegistration;

import org.myxyz.custom.X10HandlerManager;
import org.myxyz.custom.X10HandlerRegistration;

public class MessageSender{

/**
*
*/
private static final long serialVersionUID = -382336739840212254L;

private MessageExchange exchange = MessageExchange.getMessageExchange();
private X10HandlerManager sender = null;

public MessageSender() {
sender = new X10HandlerManager(this);
X10HandlerRegistration handlerRegistration = sender.addHandler(MessageEvent.getType(), exchange);
exchange.registerSender(this, handlerRegistration);
}

/**
* Fire an event.
*/
public void fireEvent(MessageEvent event) {
sender.fireEvent(event);
}

}



MessageListener


package org.myxyz.listeners;

import com.google.gwt.event.shared.EventHandler;

public abstract class MessageListener<T> implements EventHandler{

private MessageExchange exchange = MessageExchange.getMessageExchange();
private T source = null;

public MessageListener(){

}

public MessageListener(T source){
exchange.addHandler(this);
this.source = source;
}

public T getSource(){
return source;
}

public abstract void messageRecieved(MessageEvent pEvent);

}



MessageEvent


package org.myxyz.listeners;

import org.myxyz.custom.X10GwtEvent;

//import com.google.gwt.event.shared.GwtEvent;

@SuppressWarnings("unchecked")
public class MessageEvent extends X10GwtEvent<MessageListener> {

private static final X10GwtEvent.Type<MessageListener> TYPE = new X10GwtEvent.Type<MessageListener>();
private String eventType = null;
private Object newValue = null;
private Object oldValue = null;
private Object source = null;

public static Type<MessageListener> getType() {
return TYPE;
}

public MessageEvent(String eventType) {
super();
this.eventType = eventType;
}

public MessageEvent(String eventType, Object oldValue, Object newValue) {
super();
this.eventType = eventType;
this.oldValue = oldValue;
this.newValue = newValue;
}

public MessageEvent(String eventType, Object oldValue, Object newValue, Object source) {
super();
this.eventType = eventType;
this.oldValue = oldValue;
this.newValue = newValue;
this.source = source;
}

@Override
protected void dispatch(MessageListener handler) {
handler.messageRecieved(this);
}

@Override
public Type<MessageListener> getAssociatedType() {
return TYPE;
}

public String getEventType() {
return eventType;
}

public Object getNewValue() {
return newValue;
}

public Object getOldValue() {
return oldValue;
}

public Object getSource() {
return source;
}
}



MessageExchange



package org.myxyz.listeners;

import java.util.HashMap;
import java.util.Iterator;

import com.google.gwt.event.shared.EventHandler;
import org.myxyz.custom.X10HandlerManager;
import org.myxyz.custom.X10HandlerRegistration;

@SuppressWarnings("unchecked")
public class MessageExchange extends MessageListener {

/**
*
*/
private static final long serialVersionUID = 1623583201346713682L;
private static final MessageExchange singleton = new MessageExchange();

private X10HandlerManager dispatcher = new X10HandlerManager(this);
private HashMap<MessageSender,X10HandlerRegistration> registeredSenders = new HashMap<MessageSender,X10HandlerRegistration>(10);
private HashMap<MessageListener,X10HandlerRegistration> registeredListeners = new HashMap<MessageListener,X10HandlerRegistration>(10);

public static MessageExchange getMessageExchange(){
return singleton;
}

private MessageExchange() {
}

public void messageRecieved(MessageEvent pEvent) {
dispatcher.fireEvent(pEvent);
}

public synchronized <H extends EventHandler> X10HandlerRegistration addHandler(MessageListener handler) {
X10HandlerRegistration handlerRegistration = registeredListeners.get(handler);
if(handlerRegistration == null) {
handlerRegistration = dispatcher.addHandler(MessageEvent.getType(), handler);
registeredListeners.put(handler, handlerRegistration);
}
return handlerRegistration;
}

public int getHandlerCount() {
return dispatcher.getHandlerCount(MessageEvent.getType());
}

public synchronized <H extends EventHandler> void removeHandler(MessageListener handler) {
if(registeredListeners.get(handler) != null){
registeredListeners.get(handler).removeHandler();
registeredListeners.remove(handler);
}
}

public synchronized void registerSender(MessageSender sender, X10HandlerRegistration handlerRegistration){
if(handlerRegistration != null) {
registeredSenders.put(sender, handlerRegistration);
}
}

public synchronized void unRegisterSender(MessageSender sender){
X10HandlerRegistration handlerRegistration = registeredSenders.get(sender);
if(handlerRegistration != null) {
handlerRegistration.removeHandler();
registeredSenders.remove(sender);
}
}

@Override
protected void finalize() throws Throwable {
for (Iterator<X10HandlerRegistration> iterator = registeredListeners.values().iterator(); iterator.hasNext();) {
X10HandlerRegistration handlerRegistration = iterator.next();
handlerRegistration.removeHandler();
}
for (Iterator<X10HandlerRegistration> iterator = registeredSenders.values().iterator(); iterator.hasNext();) {
X10HandlerRegistration handlerRegistration = iterator.next();
handlerRegistration.removeHandler();
}
}

}

GXT-GWT fixing the transmission

Why reinvent the wheel



Simply said, the wheel is ok, the transmission is bad; the chassis is not strong enough. So I am starting with it! Sorry for the uber-sized post. The following three files are actually provided by GWT framework and I had to extend them to fix one issue with the code. I have created a custom package, which contains these files. My event-driven framework is centered around this building block.

Finally, this code is governed by apache licence. (Not me, Google says so). Please respect it, even though it is no rocket science ;)

Customized code from GWT HandlerManager


@SuppressWarnings("unchecked")
private <H extends EventHandler> List<H> handleQueuedAddsAndRemoves() {
List<H> handlers = new ArrayList<H>(10);
if (deferredDeltas != null) {
try {
for (AddOrRemoveCommand c : deferredDeltas) {
EventHandler newHander = c.execute();
/*Here was a problem, that exists no more*/
if(newHander != null){
handlers.add((H) newHander);
}
}
} finally {
deferredDeltas = null;
}
}
return handlers;
}



The complete set of modified files


Extended HandlerManager :: X10HandlerManager


/*
* This is customized version of HandlerManager provided by google. The purpose of this
* extension is to enable user triggered events to bypass ISSUE 101
*
* Fixed ISSUE 101: To support event handling when event handlers are added as part of event processing
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.myxyz.custom;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import org.myxyz.custom.X10GwtEvent.Type;

/**
* Manager responsible for adding handlers to event sources and firing those
* handlers on passed in events.
*/
public class X10HandlerManager {
public static int CONFIGURED_DEPTH = 5;
/**
* Interface for queued add/remove operations.
*/
private interface AddOrRemoveCommand {
/*ISSUE 101*/
public EventHandler execute();
}

/**
* Inner class used to actually contain the handlers.
*/
private static class HandlerRegistry {
private final HashMap<X10GwtEvent.Type<?>, ArrayList<?>> map = new HashMap<X10GwtEvent.Type<?>, ArrayList<?>>();

private <H extends EventHandler> void addHandler(Type<H> type, H handler) {
ArrayList<H> l = get(type);
if (l == null) {
l = new ArrayList<H>();
map.put(type, l);
}
l.add(handler);
}

private <H extends EventHandler> void fireEvent(X10GwtEvent<H> event, boolean isReverseOrder) {
Type<H> type = event.getAssociatedType();
int count = getHandlerCount(type);
if (isReverseOrder) {
for (int i = count - 1; i >= 0; i--) {
H handler = this.<H> getHandler(type, i);
event.dispatch(handler);
}
} else {
for (int i = 0; i < count; i++) {
H handler = this.<H> getHandler(type, i);
event.dispatch(handler);
}
}
}

/* ISSUE 101 */
private <T extends EventHandler> void fireForSkippedHandler(X10GwtEvent<T> event, boolean isReverseOrder, List<T> handlers) {

int count = handlers.size();
if (isReverseOrder) {
for (int i = count - 1; i >= 0; i--) {
T handler = handlers.get(i);
event.dispatch(handler);
}
} else {
for (int i = 0; i < count; i++) {
T handler = handlers.get(i);
event.dispatch(handler);
}
}
}

@SuppressWarnings("unchecked")
private <H> ArrayList<H> get(X10GwtEvent.Type<H> type) {
// This cast is safe because we control the puts.
return (ArrayList<H>) map.get(type);
}

private <H extends EventHandler> H getHandler(X10GwtEvent.Type<H> eventKey, int index) {
ArrayList<H> l = get(eventKey);
return l.get(index);
}

private int getHandlerCount(X10GwtEvent.Type<?> eventKey) {
ArrayList<?> l = map.get(eventKey);
return l == null ? 0 : l.size();
}

private boolean isEventHandled(X10GwtEvent.Type<?> eventKey) {
return map.containsKey(eventKey);
}

private <H> void removeHandler(X10GwtEvent.Type<H> eventKey, H handler) {
ArrayList<H> l = get(eventKey);
boolean result = l.remove(handler);
if (l.size() == 0) {
map.remove(eventKey);
}
assert result : "Tried to remove unknown handler: " + handler + " from " + eventKey;
}
}

private int firingDepth = 0;
private boolean isReverseOrder;

// map storing the actual handlers
private HandlerRegistry registry;

// source of the event.
private final Object source;

// Add and remove operations received during dispatch.
private List<AddOrRemoveCommand> deferredDeltas;

/**
* Creates a handler manager with the given source. Handlers will be fired
* in the order that they are added.
*
* @param source
* the event source
*/
public X10HandlerManager(Object source) {
this(source, false);
}

/**
* Creates a handler manager with the given source, specifying the order in
* which handlers are fired.
*
* @param source
* the event source
* @param fireInReverseOrder
* true to fire handlers in reverse order
*/
public X10HandlerManager(Object source, boolean fireInReverseOrder) {
registry = new HandlerRegistry();
this.source = source;
this.isReverseOrder = fireInReverseOrder;
}

/**
* Adds a handle.
*
* @param <H>
* The type of handler
* @param type
* the event type associated with this handler
* @param handler
* the handler
* @return the handler registration, can be stored in order to remove the
* handler later
*/
public <H extends EventHandler> X10HandlerRegistration addHandler(X10GwtEvent.Type<H> type, final H handler) {
assert type != null : "Cannot add a handler with a null type";
assert handler != null : "Cannot add a null handler";
if (firingDepth > 0) {
enqueueAdd(type, handler);
} else {
doAdd(type, handler);
}

return new X10HandlerRegistration(this, type, handler);
}

/**
* Fires the given event to the handlers listening to the event's type.
*
* Note, any subclass should be very careful about overriding this method,
* as adds/removes of handlers will not be safe except within this
* implementation.
*
* @param event
* the event
*/
public <H extends EventHandler> void fireEvent(X10GwtEvent<H> event) {
// If it not live we should revive it.
if (!event.isLive()) {
event.revive();
}
Object oldSource = event.getSource();
event.setSource(source);
try {
firingDepth++;

registry.fireEvent(event, isReverseOrder);

} finally {
/*ISSUE 101*/
int eventTries = 0;
do {
eventTries++;
List<H> newHandlers = handleQueuedAddsAndRemoves();
/* pass the event to newly added handlers alone */
registry.fireForSkippedHandler(event, isReverseOrder, newHandlers);
} while(eventTries < CONFIGURED_DEPTH && deferredDeltas != null);
}
if (oldSource == null) {
// This was my event, so I should kill it now that I'm done.
event.kill();
} else {
// Restoring the source for the next handler to use.
event.setSource(oldSource);
}

}

/**
* Gets the handler at the given index.
*
* @param <H>
* the event handler type
* @param index
* the index
* @param type
* the handler's event type
* @return the given handler
*/
public <H extends EventHandler> H getHandler(X10GwtEvent.Type<H> type, int index) {
assert index < getHandlerCount(type) : "handlers for " + type.getClass() + " have size: " + getHandlerCount(type)
+ " so do not have a handler at index: " + index;
return registry.getHandler(type, index);
}

/**
* Gets the number of handlers listening to the event type.
*
* @param type
* the event type
* @return the number of registered handlers
*/
public int getHandlerCount(Type<?> type) {
return registry.getHandlerCount(type);
}

/**
* Does this handler manager handle the given event type?
*
* @param e
* the event type
* @return whether the given event type is handled
*/
public boolean isEventHandled(Type<?> e) {
return registry.isEventHandled(e);
}

/**
* Removes the given handler from the specified event type. Normally,
* applications should call {@link HandlerRegistration#removeHandler()}
* instead.
*
* @param <H>
* handler type
*
* @param type
* the event type
* @param handler
* the handler
* @deprecated This method is likely to be removed along with "listener"
* interfaces in a future release. If you have a reason it
* should be retained beyond that time, please add your comments
* to GWT <a href=
* "http://code.google.com/p/google-web-toolkit/issues/detail?id=3102"
* >issue 3102</a>
*/
@Deprecated
public <H extends EventHandler> void removeHandler(X10GwtEvent.Type<H> type, final H handler) {
if (firingDepth > 0) {
enqueueRemove(type, handler);
} else {
doRemove(type, handler);
}
}

/**
* Not part of the public API, available only to allow visualization tools
* to be developed in gwt-incubator.
*
* @return a map of all handlers in this handler manager
*/
Map<X10GwtEvent.Type<?>, ArrayList<?>> createHandlerInfo() {
return registry.map;
}

private void defer(AddOrRemoveCommand command) {
if (deferredDeltas == null) {
deferredDeltas = new ArrayList<AddOrRemoveCommand>();
}
deferredDeltas.add(command);
}

private <H extends EventHandler> void doAdd(X10GwtEvent.Type<H> type, final H handler) {
registry.addHandler(type, handler);
}

private <H extends EventHandler> void doRemove(X10GwtEvent.Type<H> type, final H handler) {
registry.removeHandler(type, handler);
}

private <H extends EventHandler> void enqueueAdd(final X10GwtEvent.Type<H> type, final H handler) {
defer(new AddOrRemoveCommand() {
public EventHandler execute() {
doAdd(type, handler);
return handler;
}
});
}

private <H extends EventHandler> void enqueueRemove(final X10GwtEvent.Type<H> type, final H handler) {
defer(new AddOrRemoveCommand() {
public EventHandler execute() {
doRemove(type, handler);
return null;
}
});
}

@SuppressWarnings("unchecked")
private <H extends EventHandler> List<H> handleQueuedAddsAndRemoves() {
List<H> handlers = new ArrayList<H>(10);
if (deferredDeltas != null) {
try {
for (AddOrRemoveCommand c : deferredDeltas) {
EventHandler newHander = c.execute();
/*ISSUE 101*/
if(newHander != null){
handlers.add((H) newHander);
}
}
} finally {
deferredDeltas = null;
}
}
return handlers;
}
}



Extended GWTEvent :: X10GWTEvent


/*
* This is customized version of GWTEvent provided by google. The purpose of this
* extension is to enable user triggered events to bypass ISSUE 101
*
* Fixed ISSUE 101: To support event handling when event handlers are added as part of event processing
*
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.myxyz.custom;

import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.GwtEvent;

/**
* Root of all GWT events. All GWT events are considered dead and should no
* longer be accessed once the {@link X10HandlerManager} which originally fired
* the event finishes with it. That is, don't hold on to event objects outside
* of your handler methods.
*
* @param <H>
* handler type
*
*/
public abstract class X10GwtEvent<H extends EventHandler> {
/**
* Type class used to register events with the {@link X10HandlerManager}.
* <p>
* Type is parameterized by the handler type in order to make the addHandler
* method type safe.
* </p>
*
* @param <H>
* handler type
*/
public static class Type<H> {
private static int nextHashCode;
private final int index;

/**
* Constructor.
*/
public Type() {
index = ++nextHashCode;
}

// We override hash code to make it as efficient as possible.
@Override
public final int hashCode() {
return index;
}

@Override
public String toString() {
return "Event type";
}
}

private boolean dead;

private Object source;

/**
* Constructor.
*/
protected X10GwtEvent() {
}

/**
* Returns the type used to register this event. Used by handler manager to
* dispatch events to the correct handlers.
*
* @return the type
*/
public abstract Type<H> getAssociatedType();

/**
* Returns the source that last fired this event.
*
* @return object representing the source of this event
*/
public Object getSource() {
assertLive();
return source;
}

/**
* This is a method used primarily for debugging. It gives a string
* representation of the event details. This does not override the toString
* method because the compiler cannot always optimize toString out
* correctly. Event types should override as desired.
*
* @return a string representing the event's specifics.
*/
public String toDebugString() {
String name = this.getClass().getName();
name = name.substring(name.lastIndexOf(".") + 1);
return "event: " + name + ":";
}

/**
* The toString() for abstract event is overridden to avoid accidently
* including class literals in the the compiled output. Use
* {@link GwtEvent} #toDebugString to get more information about the
* event.
*/
@Override
public String toString() {
return "An event type";
}

/**
* Asserts that the event still should be accessed. All events are
* considered to be "dead" after their original handler manager finishes
* firing them. An event can be revived by calling
* {@link GwtEvent#revive()}.
*/
protected void assertLive() {
assert (!dead) : "This event has already finished being processed by its original handler manager, so you can no longer access it";
}

/**
* Should only be called by {@link X10HandlerManager}. In other words, do
* not use or call.
*
* @param handler
* handler
*/
protected abstract void dispatch(H handler);

/**
* Is the event current live?
*
* @return whether the event is live
*/
protected final boolean isLive() {
return !dead;
}

/**
* Kill the event. After the event has been killed, users cannot really on
* its values or functions being available.
*/
protected void kill() {
dead = true;
source = null;
}

/**
* Revives the event. Used when recycling event instances.
*/
protected void revive() {
dead = false;
source = null;
}

/**
* Set the source that triggered this event.
*
* @param source
* the source of this event, should only be set by a
* {@link X10HandlerManager}
*/
void setSource(Object source) {
this.source = source;
}
}



Extended HandlerRegistration :: X10HandlerRegistration



/*
* This is customized version of HandlerRegistration provided by google. The purpose of this
* extension is to enable user triggered events to bypass ISSUE 101
*
* Fixed ISSUE 101: To support event handling when event handlers are added as part of event processing
*
* Copyright 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.myxyz.custom;

import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import org.myxyz.custom.X10GwtEvent.Type;


/**
* Default implementation of {@link HandlerRegistration}.
*/
public class X10HandlerRegistration implements HandlerRegistration {

private final X10HandlerManager manager;
private final EventHandler handler;
private final Type<?> type;

/**
* Creates a new handler registration.
*
* @param <H>
* Handler type
*
* @param manager
* the handler manager
* @param type
* the event type
* @param handler
* the handler
*/
protected <H extends EventHandler> X10HandlerRegistration(X10HandlerManager manager, Type<H> type, H handler) {
this.manager = manager;
this.handler = handler;
this.type = type;
}

/**
* Removes the given handler from its manager.
*/
@SuppressWarnings( { "unchecked", "deprecation" })
// This is safe because when the elements were passed in they conformed to
// Type<H>,H.
public void removeHandler() {
manager.removeHandler((Type<EventHandler>) type, handler);
}

EventHandler getHandler() {
return handler;
}
}

Saturday, May 15, 2010

Event handling in GXT-GWT

This is the way I like to handle events. Note the ease with which the MessageSenders and MessageListeners can be "weaved" using aop/annotations. Unfortunately, I am not going the aop way for the time being.

The big picture:

Architecture diagram of a GWT-GXT application




Using a message listener




Using a message sender


The SubmitButtonListener is a button listener. I tend to separate UI events from business/semantic events. The message sender is used to fire the event. There is another reason why this separation was required, GWT EventHandler code cannot register listeners, while a event is being processed. I re-wrote the custom code and wanted to use it only for semantic events.

Popular Posts

Labels

About Me

Well for a start, I dont' want to!. Yes I am reclusive, no I am not secretive; Candid? Yes; Aspergers? No :). My friends call me an enthusiast, my boss calls me purist, I call myself an explorer, to summarise; just an inquisitive child who didnt'learn to take things for granted. For the sake of living, I work as a S/W engineer. If you dont' know what it means, turn back right now.