package org.tynamo.components;
import org.apache.tapestry5.*;
import org.apache.tapestry5.annotations.Environmental;
import org.apache.tapestry5.annotations.InjectComponent;
import org.apache.tapestry5.annotations.Parameter;
import org.apache.tapestry5.annotations.Property;
import org.apache.tapestry5.beaneditor.BeanModel;
import org.apache.tapestry5.corelib.components.Form;
import org.apache.tapestry5.corelib.components.Zone;
import org.apache.tapestry5.dom.Element;
import org.apache.tapestry5.ioc.Messages;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.ioc.services.PropertyAccess;
import org.apache.tapestry5.ioc.services.TypeCoercer;
import org.apache.tapestry5.services.BeanModelSource;
import org.apache.tapestry5.services.Heartbeat;
import org.apache.tapestry5.services.Request;
import org.apache.tapestry5.services.ValueEncoderSource;
import org.apache.tapestry5.services.ajax.AjaxResponseRenderer;
import org.apache.tapestry5.services.ajax.JavaScriptCallback;
import org.apache.tapestry5.services.javascript.JavaScriptSupport;
import org.tynamo.builder.BuilderDirector;
import org.tynamo.descriptor.CollectionDescriptor;
import org.tynamo.services.DescriptorService;
import org.tynamo.services.PersistenceService;
import org.tynamo.util.DisplayNameUtils;
import java.util.Collection;
import java.util.List;
public class Composition
{
@Inject
private DescriptorService descriptorService;
@Inject
private PersistenceService persitenceService;
@Inject
private PropertyAccess propertyAccess;
@Inject
private BeanModelSource beanModelSource;
@Inject
private Messages messages;
@Inject
private BuilderDirector builderDirector;
@Inject
@Property
private ComponentResources resources;
@Inject
private ValueEncoderSource valueEncoderSource;
@Inject
private Request request;
@Inject
private AjaxResponseRenderer ajaxResponseRenderer;
@Inject
private JavaScriptSupport javaScriptSupport;
@Environmental
private Heartbeat heartbeat;
/**
* The id used to generate a page-unique client-side identifier for the component. If a component renders multiple
* times, a suffix will be appended to the to id to ensure uniqueness. The uniqued value may be accessed via the
* {@link #getClientId() clientId property}.
*/
@Parameter(value = "prop:resources.id", defaultPrefix = BindingConstants.LITERAL)
@Property(write = false)
private String clientId;
/**
* A Block to render when the source is empty. The default is simply the text "There is no data to display".
* This parameter is used to customize that message.
*/
@Parameter(value = "block:empty", defaultPrefix = BindingConstants.LITERAL)
@Property(write = false)
private Block empty;
@Parameter(required = false)
private List instances;
@Parameter(required = true)
@Property(write = false)
private Collection collection;
/**
* The object which owns the collection being edited
*/
@Parameter(required = true)
private Object owner;
/**
* Ognl expression to invoke on the model to create a new child instance
*/
@Parameter(required = false)
private String createExpression;
/**
* @return The CollectionDescriptor for the collection being edited
*/
@Parameter(required = true)
@Property(write = false)
private CollectionDescriptor collectionDescriptor;
@Property
private Object collectionIterator;
@Property
private Object formBean;
@Parameter(value = "true")
private boolean allowCreate;
@InjectComponent
private Zone compositionZone;
@InjectComponent
@Property(write = false)
private Form form;
@Inject
private TypeCoercer typeCoercer;
public boolean isAllowCreate()
{
return propertyAccess.get(owner, descriptorService.getClassDescriptor(owner.getClass())
.getIdentifierDescriptor().getName()) != null;
}
@Parameter(value = "prop:collectionDescriptor.allowRemove")
private boolean allowRemove;
@Property
private int index;
@Parameter(value = "asset:move_up.gif")
@Property(write = false)
private Asset upImage;
@Parameter(value = "asset:move_down.gif")
@Property(write = false)
private Asset downImage;
/**
* The image to use for the delete icon
*/
@Parameter(value = "asset:delete.png")
@Property(write = false)
private Asset deleteIcon;
@Property(write = false)
private BeanModel beanModel;
private Element addLink;
void onPrepareFromForm()
{
resources.triggerEvent(EventConstants.PREPARE, null, null);
if (formBean == null) {
formBean = builderDirector.createNewInstance(collectionDescriptor.getElementType());
}
if (beanModel == null)
{
beanModel = beanModelSource.createEditModel(collectionDescriptor.getElementType(), messages);
}
}
void setupRender()
{
beanModel = beanModelSource.createEditModel(collectionDescriptor.getElementType(), messages);
formBean = builderDirector.createNewInstance(collectionDescriptor.getElementType());
}
boolean beginRender(MarkupWriter writer)
{
writer.element("div", "class", "t-add-child");
addLink = writer.element("a", "id", "add-" + collectionDescriptor.getName() + "-link", "href", "#");
writer.element("i", "class", "fa fa-plus-square-o"); writer.end();
writer.write("\n");
writer.write(messages.get("org.tynamo.i18n.add-child"));
Runnable command = new Runnable()
{
public void run()
{
String fieldId = form.getClientId();
addLink.forceAttributes("onclick", "$('#" + form.getClientId() + "').toggle(); return false;");
javaScriptSupport.require("composition").invoke("init").with(form.getClientId());
}
};
heartbeat.defer(command);
writer.end();
writer.end();
return true;
}
public Object onSuccess()
{
persitenceService.addToCollection(collectionDescriptor, formBean, owner);
if (request.isXHR()) {
ajaxResponseRenderer.addCallback(new JavaScriptCallback() {
@Override
public void run(JavaScriptSupport javaScriptSupport) {
javaScriptSupport.require("composition").invoke("reset");
}
});
return compositionZone.getBody();
}
else return null;
}
public Object onActionFromDeleteChild(String elementid)
{
ValueEncoder valueEncoder = valueEncoderSource.getValueEncoder(collectionDescriptor.getElementType());
Object element = valueEncoder.toValue(elementid);
persitenceService.removeFromCollection(collectionDescriptor, element, owner);
if (request.isXHR())
{
return compositionZone.getBody();
}
return null;
}
/* public boolean isList()
{
return collection instanceof List;
}
public void moveUp()
{
List list = (List) collection;
for (int i = 1; i < getSelected().size(); i++)
{
if (getSelected().get(i))
{
if (collectionDescriptor.getSwapExpression() == null)
{
Collections.swap(list, i, i - 1);
} else
{
try
{
Ognl.getValue(collectionDescriptor.getSwapExpression() + "(" + i + "," + (i - 1) + ")", owner);
} catch (OgnlException e)
{
LOGGER.error(e.getMessage());
}
}
}
}
}
public void moveDown()
{
List list = (List) collection;
for (int i = 0; i < getSelected().size() - 1; i++)
{
if (collectionDescriptor.getSwapExpression() == null)
{
Collections.swap(list, i, i + 1);
} else
{
try
{
Ognl.getValue(collectionDescriptor.getSwapExpression() + "(" + i + "," + (i + 1) + ")",
owner);
} catch (OgnlException e)
{
LOG.error(e.getMessage());
}
}
}
}*/
public Object[] getShowPageContext()
{
return new Object[]{collectionIterator.getClass(), collectionIterator};
}
public Object[] getDeleteContext()
{
return new Object[]{collectionIterator};
}
public String getLegendMessage()
{
return messages.format("org.tynamo.i18n.add",
DisplayNameUtils.getDisplayName(collectionDescriptor.getElementType(), messages));
}
public String getCompositionZoneClientId()
{
return compositionZone.getClientId();
}
}