/*
* 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 com.google.gwt.gen2.event.shared;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.gen2.event.shared.AbstractEvent.Type;
/**
* Default JavaScript handler registry. This is in the shared package so we
* don't have to make it public, should never be called outside of a GWT runtime
* environment.
*
* Th JsHandlerRegistry makes use of the fact that in the large majority of
* cases, only one or two handlers are added for each event type. Therefore,
* rather than storing handlers in a list of lists, we store then in a single
* flattened array with an escape clause to handle the rare case where we have
* more handlers then expected.
*/
class JsHandlerRegistry extends JavaScriptObject {
public static JsHandlerRegistry create() {
return (JsHandlerRegistry) JavaScriptObject.createObject();
}
/**
* Required constructor.
*/
protected JsHandlerRegistry() {
}
public final void addHandler(AbstractEvent.Type eventKey, EventHandler handler) {
// The base is the equivalent to a c pointer into the flattened handler data
// structure.
int base = eventKey.hashCode();
int count = getCount(base);
// If we already have the maximum number of handlers we can store in the
// flattened data structure, store the handlers in an external list instead.
if (count == HandlerManager.EXPECTED_HANDLERS && isFlattened(base)) {
unflatten(base);
}
setHandler(base, count, handler, isFlattened(base));
setCount(base, count + 1);
}
public final void clearHandlers(Type<?, ?> type) {
int base = type.hashCode();
// Clearing handlers is relatively unusual, so the cost of unflattening the
// handler list is justified by the smaller code.
unflatten(base);
// Replace the list of handlers.
setHandlerList(base + 1, JavaScriptObject.createArray());
setCount(base, 0);
}
public final void fireEvent(AbstractEvent event) {
Type type = event.getType();
int base = type.hashCode();
int count = getCount(base);
boolean isFlattened = isFlattened(base);
for (int i = 0; i < count; i++) {
// Gets the given handler to fire.
EventHandler handler = getHandler(base, i, isFlattened);
// Fires the handler.
type.fire(handler, event);
}
}
public final EventHandler getHandler(AbstractEvent.Type eventKey, int index) {
int base = eventKey.hashCode();
int count = getCount(base);
if (index >= count) {
throw new IndexOutOfBoundsException("index: " + index);
}
return getHandler(base, index, isFlattened(base));
}
public final int getHandlerCount(AbstractEvent.Type eventKey) {
return getCount(eventKey.hashCode());
}
public final void removeHandler(AbstractEvent.Type eventKey,
EventHandler handler) {
int base = eventKey.hashCode();
// Removing a handler is unusual, so smaller code is preferable then
// handling both flat and dangling list of pointers.
if (isFlattened(base)) {
unflatten(base);
}
boolean result = removeHelper(base, handler);
// Hiding this behind an assertion as we'd rather not force the compiler to
// have to include all handler.toString() instances.
assert result : handler + " did not exist";
}
private native int getCount(int index) /*-{
var count = this[index];
return count == null? 0:count;
}-*/;
private native EventHandler getHandler(int base, int index, boolean flattened) /*-{
return flattened? this[base + 2 + index]: this[base + 1][index];
}-*/;
private native boolean isFlattened(int base) /*-{
return this[base + 1] == null;
}-*/;
private native boolean removeHelper(int base, EventHandler handler) /*-{
// Find the handler.
var count = this[base];
var handlerList = this[base + 1];
var handlerIndex = -1;
for(var index = 0; index < count; index++){
if(handlerList[index] == handler){
handlerIndex = index;
break;
}
}
if(handlerIndex == -1) {
return false;
}
// Remove the handler.
var last = count -1;
for(; handlerIndex < last; handlerIndex++){
handlerList[handlerIndex] = handlerList[handlerIndex+1]
}
handlerList[last] = null;
this[base] = this[base]-1;
return true;
}-*/;
private native void setCount(int index, int count) /*-{
this[index] = count;
}-*/;
private native void setHandler(int base, int index, EventHandler handler,
boolean flattened) /*-{
if(flattened) {
this[base + 2 + index] = handler;
} else {
this[base + 1][index] = handler;
}
}-*/;
private native void setHandlerList(int base, JavaScriptObject handlerList) /*-{
this[base + 1] = handlerList;
}-*/;
private native void unflatten(int base) /*-{
var handlerList = {};
var count = this[base];
var start = base + 2;
for(var i = 0; i < count;i++){
handlerList[i] = this[start + i];
this[start + i] = null;
}
this[base + 1] = handlerList;
}-*/;
}