/**
* License Agreement.
*
* JBoss RichFaces - Ajax4jsf Component Library
*
* Copyright (C) 2007 Exadel, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.richfaces.renderkit;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.faces.component.ContextCallback;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import org.apache.commons.collections.MultiHashMap;
import org.richfaces.component.Draggable;
import org.richfaces.component.Dropzone;
import org.richfaces.event.DnDEvent;
/**
* @author Nick Belaevski - nbelaevski@exadel.com
* created 27.12.2006
*
*/
final class DnDEventsExchangeMailer {
private DnDEventsExchangeMailer() {
}
private static class EventInfoStructure {
private DnDEvent dndEvent;
private EventCallback eventCallback;
private Object type;
private Object value;
public EventInfoStructure(DnDEvent dndEvent,
EventCallback eventCallback, Object type, Object value) {
super();
this.dndEvent = dndEvent;
this.eventCallback = eventCallback;
this.type = type;
this.value = value;
}
}
static abstract class EventCallback {
abstract void processEvent(DnDEvent dndEvent, UIComponent source, FacesContext facesContext, Object type, Object value);
}
static DnDEventsExchangeMailer getInstance(FacesContext facesContext) {
synchronized (facesContext) {
Map requestMap = facesContext.getExternalContext().getRequestMap();
String attrName = DnDEventsExchangeMailer.class.getName();
DnDEventsExchangeMailer instance;
if ((instance = (DnDEventsExchangeMailer) requestMap.get(attrName)) == null) {
instance = new DnDEventsExchangeMailer();
requestMap.put(attrName, instance);
}
return instance;
}
}
private Map<String, EventInfoStructure> queuedMap = new HashMap<String, EventInfoStructure>();
private Map<String, UIComponent> components = new HashMap<String, UIComponent>();
private void processEvent(UIComponent source, FacesContext facesContext, DnDEvent dndEvent, EventCallback callback, Object type, Object value) {
if (callback != null) {
callback.processEvent(dndEvent, source, facesContext, type, value);
}
}
/**
* Decode drag & drop events. Collect pairs of correspondent drag & drop events and send them
* together where OnDrag becomes always before OnDrop.
*
* @param sourceId Id of element event come from
* @param target component that receive event
* @param facesContext Faces context
* @param dndEvent drag & drop event descriptor
* @param callback call back method
* @param type type of dragged/dropped value
* @param value dragged/dropped value
* @param isDraggable whether the event related draggable component or dropzone one.
*/
public void mailEvent(String sourceId, UIComponent target, FacesContext facesContext, final DnDEvent dndEvent,
final EventCallback callback, final Object type, final Object value, boolean isDraggable) {
final UIComponent component = components.get(sourceId);
String targetId = target.getClientId(facesContext);
if (component == null) {
//component with that sourceId have never mailed anything before - wait
if (queuedMap.containsKey(sourceId)) {
throw new IllegalStateException("Drag source with id '" + sourceId + "' already specified.");
}
queuedMap.put(sourceId, new EventInfoStructure(dndEvent, callback, type, value));
components.put(targetId, target);
} else {
//check queued mail lists for current component
final EventInfoStructure eventInfo = (EventInfoStructure) queuedMap.get(targetId);
if (eventInfo != null) {
Draggable draggable;
Dropzone dropzone;
final EventInfoStructure dragEventInfo = isDraggable ? eventInfo : new EventInfoStructure(dndEvent, callback, type, value);
final EventInfoStructure dropEventInfo = isDraggable ? new EventInfoStructure(dndEvent, callback, type, value) : eventInfo;
Object acceptedTypes;
Object dragType;
if (isDraggable) {
draggable = (Draggable) target;
dropzone = (Dropzone) component;
acceptedTypes = eventInfo.type;
dragType = type;
} else {
draggable = (Draggable) component;
dropzone = (Dropzone) target;
acceptedTypes = type;
dragType = eventInfo.type;
}
if (DnDValidator.validateAcceptTypes(facesContext,
draggable, dropzone,
dragType, acceptedTypes)) {
// Make sure that we will have OnDrag event occur first
facesContext.getViewRoot().invokeOnComponent(facesContext, isDraggable ? targetId : sourceId, new ContextCallback() {
public void invokeContextCallback(FacesContext fc,
UIComponent targetComponent) {
processEvent(targetComponent, fc, dragEventInfo.dndEvent, dragEventInfo.eventCallback,
dropEventInfo.type, dropEventInfo.value);
dropEventInfo.dndEvent.queue();
}
});
facesContext.getViewRoot().invokeOnComponent(facesContext, isDraggable ? sourceId : targetId, new ContextCallback() {
public void invokeContextCallback(FacesContext fc,
UIComponent targetComponent) {
processEvent(targetComponent, fc, dropEventInfo.dndEvent, dropEventInfo.eventCallback,
dragEventInfo.type, dragEventInfo.value);
dragEventInfo.dndEvent.queue();
}
});
}
queuedMap.remove(targetId);
}
}
}
}