blob: 974bf6f1f1da6c79c92a57543d992329cce6485b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.myfaces.html5.handler;
import java.util.Collection;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.faces.FacesException;
import javax.faces.application.Application;
import javax.faces.component.UIComponent;
import javax.faces.component.behavior.Behavior;
import javax.faces.component.behavior.ClientBehaviorHolder;
import javax.faces.context.FacesContext;
import javax.faces.view.facelets.BehaviorConfig;
import javax.faces.view.facelets.ComponentHandler;
import javax.faces.view.facelets.FaceletContext;
import javax.faces.view.facelets.TagAttribute;
import javax.faces.view.facelets.TagException;
import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFFaceletAttribute;
import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFFaceletTag;
import org.apache.myfaces.html5.behavior.DragSourceBehavior;
import org.apache.myfaces.html5.component.api.Draggable;
import org.apache.myfaces.html5.component.util.ComponentUtils;
import org.apache.myfaces.html5.event.DropEvent;
import org.apache.myfaces.html5.renderkit.util.ClientBehaviorEvents;
/**
* Tag handler for {@link DragSourceBehavior}. Sets the "draggable" attribute of the parent and attaches the behavior to
* the parent. <br/>
*
* @author Ali Ok
*
*/
@JSFFaceletTag(name = "fx:dragSource", behaviorClass="org.apache.myfaces.html5.behavior.DragSourceBehavior")
public class DragSourceBehaviorHandler extends javax.faces.view.facelets.BehaviorHandler
{
private static final Logger log = Logger.getLogger(DragSourceBehaviorHandler.class.getName());
/**
* Action of for drag operation. Can be one of below:
* <ul>
* <li>copy: A copy of the source item may be made at the new location.</li>
* <li>move: An item may be moved to a new location.</li>
* <li>link: A link may be established to the source at the new location.</li>
* <li>none: The item may not be dropped.</li>
* </ul>
* <br/>
*
* If nothing is specified, the action will be defined by the browser and can be adjusted using the modifier keys.
* If dropTarget does not accept the action of this dragSource, then the DnD will fail.
*
*/
@JSFFaceletAttribute(name = "action", className = "javax.el.ValueExpression", deferredValueType = "java.lang.String")
private final TagAttribute _action;
/**
* The types of the dropTargets that drags from this dragSource can be applied. Can be comma separated set or
* String[] or Collection&lt;String&gt;. <br/>
* If defined, drags from this dragSource will work into only the dropTargets that have one of the same type. The
* drag will be accepted if 'types' of hx:dropTarget has one of the types defined here.
*/
@JSFFaceletAttribute(name = "dropTargetTypes", className = "javax.el.ValueExpression", deferredValueType = "java.lang.Object")
private final TagAttribute _dropTargetTypes;
/**
* Data to send to server when a successful drag and drop happens from this source. <br/>
* The param can be received using the {@link DropEvent#getParam()} method at dropListener of the fx:dropTarget.
*/
@JSFFaceletAttribute(name = "param", className = "javax.el.ValueExpression", deferredValueType = "java.lang.String")
private final TagAttribute _param;
public DragSourceBehaviorHandler(BehaviorConfig config)
{
super(config);
_action = getAttribute("action");
_dropTargetTypes = getAttribute("dropTargetTypes");
_param = getAttribute("param");
}
@Override
public void apply(FaceletContext faceletContext, UIComponent parent)
{
if (!ComponentHandler.isNew(parent))
{
if (log.isLoggable(Level.FINE))
log.fine("Component " + ComponentUtils.getPathToComponent(parent)
+ " is not new, thus return without any operation.");
return;
}
if (parent instanceof ClientBehaviorHolder)
{
ClientBehaviorHolder holder = _getClientBehaviorHolder(parent);
FacesContext context = faceletContext.getFacesContext();
Application app = context.getApplication();
String behaviorId = getBehaviorId();
Behavior behavior = app.createBehavior(behaviorId);
if (!(behavior instanceof DragSourceBehavior))
{
throw new FacesException("Behavior is not a DragSourceBehavior");
}
// manually added all of the properties, so no need for this:
// setAttributes(faceletContext, behavior);
// set parent as draggable
if (parent instanceof Draggable)
{
((Draggable) parent).setDraggable(true);
}
else
{
if (log.isLoggable(Level.WARNING))
log.warning("Parent " + ComponentUtils.getPathToComponent(parent)
+ " does not implement Draggable interface, thus unable to set the draggable attribute. "
+ "Renderer of the parent must handle the decision of being draggable manually.");
}
DragSourceBehavior dragSourceBehavior = (DragSourceBehavior) behavior;
// evaluating the _param's value expression doesn't this work if I put the dragSource in a datatable and try
// to set the valueexpression using the var of table.
// see https://issues.apache.org/jira/browse/MYFACES-2616
// see the thread http://www.mail-archive.com/dev@myfaces.apache.org/msg46764.html
// thus need to pass the valuexpression to the behavior, then the renderer can evaluate it. AjaxBehavior
// does this with a map.
// using the same approach in DropTargetBehavior too...
if (_action != null)
{
if (_action.isLiteral())
{
dragSourceBehavior.setAction(_action.getValue(faceletContext));
}
else
{
dragSourceBehavior.setValueExpression("action",
_action.getValueExpression(faceletContext, String.class));
}
}
if (_dropTargetTypes != null)
{
if (_dropTargetTypes.isLiteral())
{
dragSourceBehavior.setDropTargetTypes(_dropTargetTypes.getObject(faceletContext));
}
else
{
dragSourceBehavior.setValueExpression("dropTargetTypes",
_dropTargetTypes.getValueExpression(faceletContext, Object.class));
}
}
if (_param != null)
{
if (_param.isLiteral())
{
dragSourceBehavior.setParam(_param.getValue(faceletContext));
}
else
{
dragSourceBehavior.setValueExpression("param",
_param.getValueExpression(faceletContext, String.class));
}
}
holder.addClientBehavior(ClientBehaviorEvents.DRAGSTART_EVENT, dragSourceBehavior);
}
else
{
if (log.isLoggable(Level.WARNING))
log.warning("Parent is not a ClientBehaviorHolder.");
}
}
private ClientBehaviorHolder _getClientBehaviorHolder(UIComponent parent)
{
if (!(parent instanceof ClientBehaviorHolder))
{
throw new TagException(getTag(),
"DragSourceBehavior must be attached to a ClientBehaviorHolder parent. Component "
+ ComponentUtils.getPathToComponent(parent) + "is not a ClientBehaviorHolder.");
}
ClientBehaviorHolder holder = (ClientBehaviorHolder) parent;
_checkEvent(holder, ClientBehaviorEvents.DRAGSTART_EVENT);
return holder;
}
private void _checkEvent(ClientBehaviorHolder holder, String eventName)
{
Collection<String> eventNames = holder.getEventNames();
if (!eventNames.contains(eventName))
{
StringBuilder message = new StringBuilder();
message.append("Unable to attach DragSourceBehavior. ");
message.append("DragSourceBehavior may only be attached to ");
message.append("ClientBehaviorHolders that support the '");
message.append(eventName);
message.append("' event. The parent ClientBehaviorHolder "
+ ComponentUtils.getPathToComponent((UIComponent) holder) + " only ");
message.append("supports the following events: ");
for (String supportedEventName : eventNames)
{
message.append(supportedEventName);
message.append(" ");
}
throw new TagException(getTag(), message.toString());
}
}
}