/*
* JBoss, Home of Professional Open Source
* Copyright 2011 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* 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,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package org.jboss.as.console.mbui.reification;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.web.bindery.event.shared.EventBus;
import org.jboss.as.console.client.shared.help.StaticHelpPanel;
import org.jboss.as.console.client.widgets.forms.FormToolStrip;
import org.jboss.as.console.mbui.JBossQNames;
import org.jboss.as.console.mbui.model.StereoTypes;
import org.jboss.as.console.mbui.model.mapping.DMRMapping;
import org.jboss.as.console.mbui.model.mapping.ResourceAttribute;
import org.jboss.as.console.mbui.widgets.ModelNodeForm;
import org.jboss.ballroom.client.rbac.AuthorisationDecision;
import org.jboss.ballroom.client.rbac.SecurityContext;
import org.jboss.ballroom.client.widgets.forms.CheckBoxItem;
import org.jboss.ballroom.client.widgets.forms.ComboBoxItem;
import org.jboss.ballroom.client.widgets.forms.FormItem;
import org.jboss.ballroom.client.widgets.forms.NumberBoxItem;
import org.jboss.ballroom.client.widgets.forms.TextBoxItem;
import org.jboss.dmr.client.ModelNode;
import org.jboss.dmr.client.ModelType;
import org.jboss.dmr.client.Property;
import org.useware.kernel.gui.behaviour.InteractionEvent;
import org.useware.kernel.gui.behaviour.PresentationEvent;
import org.useware.kernel.gui.behaviour.SystemEvent;
import org.useware.kernel.gui.behaviour.common.CommonQNames;
import org.useware.kernel.gui.reification.Context;
import org.useware.kernel.gui.reification.ContextKey;
import org.useware.kernel.gui.reification.strategy.ReificationStrategy;
import org.useware.kernel.gui.reification.strategy.ReificationWidget;
import org.useware.kernel.model.behaviour.Resource;
import org.useware.kernel.model.behaviour.ResourceType;
import org.useware.kernel.model.mapping.MappingType;
import org.useware.kernel.model.mapping.Predicate;
import org.useware.kernel.model.structure.InteractionUnit;
import org.useware.kernel.model.structure.QName;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.useware.kernel.gui.behaviour.common.CommonQNames.RESET_ID;
import static org.useware.kernel.model.behaviour.ResourceType.*;
import static org.useware.kernel.model.behaviour.ResourceType.System;
/**
* @author Harald Pehl
* @author Heiko Braun
* @date 11/01/2012
*/
public class FormStrategy implements ReificationStrategy<ReificationWidget, StereoTypes>
{
private ModelNode modelDescription;
private EventBus eventBus;
private static final Resource<ResourceType> SAVE_EVENT = new Resource<ResourceType>(JBossQNames.SAVE_ID, Interaction);
private static final Resource<ResourceType> LOAD_EVENT = new Resource<ResourceType>(JBossQNames.LOAD_ID, Interaction);
private static final Resource<ResourceType> RESET = new Resource<ResourceType>(RESET_ID, System);
private SecurityContext securityContext;
@Override
public boolean prepare(InteractionUnit<StereoTypes> interactionUnit, Context context) {
Map<QName, ModelNode> descriptions = context.get (ContextKey.MODEL_DESCRIPTIONS);
// TODO (BUG): After the first reification the behaviour is modified,
// so the predicate might apply to a dfferent unit. As a result the correllation id is different!
QName correlationId = interactionUnit.findMapping(MappingType.DMR, new Predicate<DMRMapping>() {
@Override
public boolean appliesTo(DMRMapping candidate) {
return candidate.getAddress()!=null;
}
}).getCorrelationId();
modelDescription = descriptions.get(correlationId);
eventBus = context.get(ContextKey.EVENTBUS);
securityContext = context.get(ContextKey.SECURITY_CONTEXT);
return eventBus!=null
&& modelDescription!=null
&& securityContext!=null;
}
@Override
public ReificationWidget reify(final InteractionUnit<StereoTypes> interactionUnit, final Context context)
{
return new FormAdapter(interactionUnit, eventBus, modelDescription);
}
@Override
public boolean appliesTo(final InteractionUnit<StereoTypes> interactionUnit)
{
return StereoTypes.Form == interactionUnit.getStereotype();
}
class FormAdapter implements ReificationWidget
{
final ModelNodeForm form;
final InteractionUnit interactionUnit;
private SafeHtmlBuilder helpTexts;
private EventBus coordinator;
FormAdapter(final InteractionUnit<StereoTypes> interactionUnit, EventBus coordinator, final ModelNode modelDescription)
{
this.interactionUnit = interactionUnit;
this.coordinator = coordinator;
DMRMapping dmrMapping = (DMRMapping)
this.interactionUnit.findMapping(MappingType.DMR);
this.form = new ModelNodeForm(dmrMapping.getAddress(), securityContext);
this.form.setNumColumns(2);
this.form.setEnabled(false);
assert modelDescription.hasDefined("attributes") : "Invalid model description. Expected child 'attributes'";
List<Property> attributeDescriptions = modelDescription.get("attributes").asPropertyList();
List<ResourceAttribute> attributes = dmrMapping.getAttributes();
// catch-all directive, if no explicit attributes given
// TODO: optimisation, see below
if(attributes.isEmpty())
{
for(Property attr : attributeDescriptions)
{
attributes.add(new ResourceAttribute(attr.getName(), attr.getName()));
}
}
List<FormItem> items = new ArrayList<FormItem>(attributes.size());
helpTexts = new SafeHtmlBuilder();
helpTexts.appendHtmlConstant("<table class='help-attribute-descriptions'>");
for (ResourceAttribute attribute : attributes)
{
for(Property attr : attributeDescriptions)
{
if(!attr.getName().equals(attribute.getName()))
continue;
char[] stringArray = attr.getName().toCharArray();
stringArray[0] = Character.toUpperCase(stringArray[0]);
String label = new String(stringArray).replace("-", " ");
ModelNode attrValue = attr.getValue();
// help
helpTexts.appendHtmlConstant("<tr class='help-field-row'>");
helpTexts.appendHtmlConstant("<td class='help-field-name'>");
helpTexts.appendEscaped(label).appendEscaped(": ");
helpTexts.appendHtmlConstant("</td>");
helpTexts.appendHtmlConstant("<td class='help-field-desc'>");
try {
helpTexts.appendHtmlConstant(attrValue.get("description").asString());
} catch (Throwable e) {
// ignore parse errors
helpTexts.appendHtmlConstant("<i>Failed to parse description</i>");
}
helpTexts.appendHtmlConstant("</td>");
helpTexts.appendHtmlConstant("</tr>");
boolean required = !attr.getValue().get("nillable").asBoolean();
ModelType type = ModelType.valueOf(attrValue.get("type").asString());
//System.out.println(attr.getName()+">"+type);
switch(type)
{
case BOOLEAN:
CheckBoxItem checkBoxItem = new CheckBoxItem(attr.getName(), label);
items.add(checkBoxItem);
break;
case DOUBLE:
NumberBoxItem num = new NumberBoxItem(attr.getName(), label);
num.setRequired(required);
items.add(num);
break;
case LONG:
NumberBoxItem num2 = new NumberBoxItem(attr.getName(), label);
num2.setRequired(required);
items.add(num2);
break;
case INT:
NumberBoxItem num3 = new NumberBoxItem(attr.getName(), label);
num3.setRequired(required);
items.add(num3);
break;
case STRING:
if(attrValue.get("allowed").isDefined())
{
List<ModelNode> allowed = attrValue.get("allowed").asList();
Set<String> allowedValues = new HashSet<String>(allowed.size());
for(ModelNode value : allowed)
allowedValues.add(value.asString());
ComboBoxItem combo = new ComboBoxItem(attr.getName(), label);
combo.setValueMap(allowedValues);
}
else
{
TextBoxItem tb = new TextBoxItem(attr.getName(), label);
tb.setRequired(required);
items.add(tb);
}
break;
default:
throw new RuntimeException("Unsupported ModelType "+type);
}
}
}
helpTexts.appendHtmlConstant("</table>");
form.setFields(items.toArray(new FormItem[]{}));
}
@Override
public InteractionUnit getInteractionUnit() {
return interactionUnit;
}
@Override
public void add(final ReificationWidget widget)
{
throw new UnsupportedOperationException();
}
@Override
public Widget asWidget()
{
VerticalPanel layout = new VerticalPanel();
layout.setStyleName("fill-layout-width");
layout.getElement().setAttribute("style", "margin-top:15px;margin-bottom:15px;");
final FormToolStrip<ModelNode> tools = new FormToolStrip<ModelNode>(
form,
new FormToolStrip.FormCallback<ModelNode>() {
@Override
public void onSave(Map<String, Object> changeset) {
InteractionEvent saveEvent = new InteractionEvent(JBossQNames.SAVE_ID);
saveEvent.setPayload(form.getChangedValues());
coordinator.fireEventFromSource(
saveEvent,
interactionUnit.getId()
);
}
@Override
public void onDelete(ModelNode entity) {
// unsupported
}
});
StaticHelpPanel help = new StaticHelpPanel(helpTexts.toSafeHtml());
layout.add(tools.asWidget());
layout.add(help.asWidget());
layout.add(form.asWidget());
// handle resets within this scope
coordinator.addHandler(SystemEvent.TYPE, new SystemEvent.Handler() {
@Override
public boolean accepts(SystemEvent event) {
return event.getId().equals(CommonQNames.RESET_ID) ;
}
@Override
public void onSystemEvent(SystemEvent event) {
form.clearValues();
tools.doCancel();
Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() {
@Override
public void execute() {
// request loading of data
InteractionEvent loadEvent = new InteractionEvent(JBossQNames.LOAD_ID);
// update interaction units
coordinator.fireEventFromSource(
loadEvent,
interactionUnit.getId()
);
}
});
}
});
// handle the results of function calls
coordinator.addHandler(PresentationEvent.TYPE, new PresentationEvent.PresentationHandler()
{
@Override
public boolean accepts(PresentationEvent event) {
boolean matchingId = event.getTarget().equals(getInteractionUnit().getId());
// only single resources accepted (might be collection, see LoadResourceProcedure)
boolean payloadMatches = event.getPayload() instanceof ModelNode;
return matchingId && payloadMatches;
}
@Override
public void onPresentationEvent(PresentationEvent event) {
assert (event.getPayload() instanceof ModelNode) : "Unexpected type "+event.getPayload().getClass();
ModelNode payload = (ModelNode) event.getPayload();
form.edit(payload);
}
});
// Register inputs and outputs
Resource<ResourceType> update = new Resource<ResourceType>(getInteractionUnit().getId(), Presentation);
getInteractionUnit().setOutputs(SAVE_EVENT, LOAD_EVENT);
getInteractionUnit().setInputs(RESET, update);
return layout;
}
}
}