package org.apache.slide.projector.processor.form;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.apache.slide.projector.ConfigurationException;
import org.apache.slide.projector.Context;
import org.apache.slide.projector.Information;
import org.apache.slide.projector.ProcessException;
import org.apache.slide.projector.Processor;
import org.apache.slide.projector.Result;
import org.apache.slide.projector.Store;
import org.apache.slide.projector.URI;
import org.apache.slide.projector.descriptor.ArrayValueDescriptor;
import org.apache.slide.projector.descriptor.BooleanValueDescriptor;
import org.apache.slide.projector.descriptor.LocaleValueDescriptor;
import org.apache.slide.projector.descriptor.MapValueDescriptor;
import org.apache.slide.projector.descriptor.ParameterDescriptor;
import org.apache.slide.projector.descriptor.ResultDescriptor;
import org.apache.slide.projector.descriptor.ResultEntryDescriptor;
import org.apache.slide.projector.descriptor.StateDescriptor;
import org.apache.slide.projector.descriptor.StringValueDescriptor;
import org.apache.slide.projector.descriptor.URIValueDescriptor;
import org.apache.slide.projector.descriptor.ValidationException;
import org.apache.slide.projector.engine.ProcessorManager;
import org.apache.slide.projector.i18n.DefaultMessage;
import org.apache.slide.projector.i18n.ErrorMessage;
import org.apache.slide.projector.i18n.MessageNotFoundException;
import org.apache.slide.projector.i18n.ParameterMessage;
import org.apache.slide.projector.processor.SimpleProcessor;
import org.apache.slide.projector.processor.TemplateRenderer;
import org.apache.slide.projector.processor.process.Process;
import org.apache.slide.projector.store.FormStore;
import org.apache.slide.projector.value.ArrayValue;
import org.apache.slide.projector.value.BooleanValue;
import org.apache.slide.projector.value.LocaleValue;
import org.apache.slide.projector.value.MapValue;
import org.apache.slide.projector.value.NullValue;
import org.apache.slide.projector.value.StreamableValue;
import org.apache.slide.projector.value.StringValue;
import org.apache.slide.projector.value.URIValue;
import org.apache.slide.projector.value.Value;
/**
* The ControlComposer class
*
*/
public class ControlComposer extends TemplateRenderer {
// Parameters for the control composer
public final static String VALIDATE = "validate";
public final static String LOCALE = "locale";
public final static String ACTION = "action";
public final static String ERRORS_PROCESSOR = "errorsProcessor";
public final static String CONTROL_DESCRIPTIONS = "controlDescriptions";
public final static String TRIGGER_DESCRIPTIONS = "triggerDescriptions";
// Parameters for each control
public final static String CONTROL = "control";
public final static String CONTROL_NAME = "controlName";
public final static String CONTROL_CONTAINER = "controlContainer";
public final static String TRIGGER = "trigger";
public final static String TRIGGER_NAME = "triggerName";
public final static String TRIGGER_CONTAINER = "triggerContainer";
// Parameters for the control container
protected final static String TITLE = "title";
protected final static String TEXT = "text";
protected final static String PROMPT = "prompt";
protected final static String ERRORS = "errors";
protected final static String ERRORS_TITLE = "errors-title";
public final static String ERROR_NUMBER = "error-number";
public final static String ERROR_TITLE = "error-title";
public final static String ERROR_TEXT = "error-text";
public final static String ERROR_DETAILS = "error-details";
public final static String ERROR_SUMMARY = "error-summary";
// Result
public final static String GENERATED_CONTROLS = "renderedControls";
public final static String GENERATED_TRIGGERS = "renderedTriggers";
public final static String RENDERED_ERRORS = "renderedErrors";
public final static String DEFAULT_STATE ="default";
public final static String VALID_STATE ="valid";
public final static String INVALID_STATE ="invalid";
// Parameters for inheriting classes
public final static String HANDLER = "handler";
public final static String METHOD = "method";
protected final static String DEFAULT_FORM = "default form";
protected final static String INVALID_FORM = "invalid form";
protected final static String VALID_FORM = "valid form";
protected final static String GET = "GET";
protected final static String POST = "POST";
protected final static String[] methods = new String[] { GET, POST };
protected Template defaultTemplate;
protected Template validTemplate;
protected Template invalidTemplate;
protected ErrorMessage NO_ERROR_MESSAGE_AVAILABLE = new ErrorMessage("errorMessageNotFound");
protected ParameterMessage NO_PARAMETER_MESSAGE_AVAILABLE = new ParameterMessage("parameterMessageNotFound");
private ParameterDescriptor[] parameterDescriptors;
public ControlComposer() {
setRequiredFragments(new String[] { DEFAULT_FORM });
setOptionalFragments(new String[] { VALID_FORM, INVALID_FORM });
}
private final static ResultDescriptor resultDescriptor = new ResultDescriptor(
new StateDescriptor[] {
new StateDescriptor(DEFAULT_STATE, new DefaultMessage("controlComposer/state/default")),
new StateDescriptor(VALID_STATE, new DefaultMessage("controlComposer/state/valid")),
new StateDescriptor(INVALID_STATE, new DefaultMessage("controlComposer/state/invalid"))
},
new ResultEntryDescriptor[]{
new ResultEntryDescriptor(GENERATED_CONTROLS, new DefaultMessage("controlComposer/generatedControls"), ArrayValue.CONTENT_TYPE, false),
new ResultEntryDescriptor(GENERATED_TRIGGERS, new DefaultMessage("controlComposer/generatedTriggers"), ArrayValue.CONTENT_TYPE, false),
new ResultEntryDescriptor(RENDERED_ERRORS, new DefaultMessage("controlComposer/renderedErrors"), MapValue.CONTENT_TYPE, false)
});
public void configure(StreamableValue config) throws ConfigurationException {
super.configure(config);
ParameterDescriptor[] parentParameterDescriptors = super.getParameterDescriptors();
parameterDescriptors = new ParameterDescriptor[parentParameterDescriptors.length + 5];
System.arraycopy(parentParameterDescriptors, 0, parameterDescriptors, 0, parentParameterDescriptors.length);
parameterDescriptors[parentParameterDescriptors.length] =
new ParameterDescriptor(CONTROL_DESCRIPTIONS, new ParameterMessage("controlComposer/controlDescriptions"), new ArrayValueDescriptor(
new MapValueDescriptor(new ParameterDescriptor[] {
new ParameterDescriptor(Control.ACTION, new ParameterMessage("controlComposer/controlDescriptions/action"), new URIValueDescriptor(), NullValue.NULL),
new ParameterDescriptor(CONTROL, new ParameterMessage("controlComposer/controlDescriptions/control"), new URIValueDescriptor()),
new ParameterDescriptor(CONTROL_CONTAINER, new ParameterMessage("controlComposer/controlDescriptions/controlContainer"), new URIValueDescriptor(), NullValue.NULL),
})));
parameterDescriptors[parentParameterDescriptors.length+1] =
new ParameterDescriptor(TRIGGER_DESCRIPTIONS, new ParameterMessage("controlComposer/triggerDescriptions"), new ArrayValueDescriptor(
new MapValueDescriptor(new ParameterDescriptor[] {
new ParameterDescriptor(Trigger.ACTION, new ParameterMessage("controlComposer/triggerDescriptions/action"), new URIValueDescriptor(), NullValue.NULL),
new ParameterDescriptor(Trigger.VALIDATE, new ParameterMessage("controlComposer/triggerDescriptions/validate"), new BooleanValueDescriptor(), BooleanValue.TRUE),
new ParameterDescriptor(Trigger.INVOLVED_PARAMETERS, new ParameterMessage("trigger/involvedParameters"), new ArrayValueDescriptor(new StringValueDescriptor()), NullValue.NULL),
new ParameterDescriptor(Process.STEP, new ParameterMessage("controlComposer/triggerDescriptions/step"), new StringValueDescriptor()),
new ParameterDescriptor(TRIGGER, new ParameterMessage("controlComposer/triggerDescriptions/trigger"), new URIValueDescriptor()),
new ParameterDescriptor(TRIGGER_CONTAINER, new ParameterMessage("controlComposer/triggerDescriptions/triggerContainer"), new URIValueDescriptor(), NullValue.NULL)
})));
parameterDescriptors[parentParameterDescriptors.length+2] =
new ParameterDescriptor(LOCALE, new ParameterMessage("controlComposer/locale"), new LocaleValueDescriptor());
parameterDescriptors[parentParameterDescriptors.length+3] =
new ParameterDescriptor(ACTION, new ParameterMessage("controlComposer/action"), new URIValueDescriptor());
parameterDescriptors[parentParameterDescriptors.length+4] =
new ParameterDescriptor(ERRORS_PROCESSOR, new ParameterMessage("controlComposer/errorsProcessor"), new URIValueDescriptor(), NullValue.NULL);
try {
defaultTemplate = getRequiredFragment(DEFAULT_FORM);
} catch ( ProcessException exception ) {
throw new ConfigurationException(new ErrorMessage("form/defaultFragmentMissing"));
}
validTemplate = getOptionalFragment(VALID_FORM);
invalidTemplate = getOptionalFragment(INVALID_FORM);
}
public Result process(Map parameter, Context context) throws Exception {
Value[] controlDescriptions = ((ArrayValue)parameter.get(CONTROL_DESCRIPTIONS)).getArray();
Value[] triggerDescriptions = ((ArrayValue)parameter.get(TRIGGER_DESCRIPTIONS)).getArray();
URI actionUri = (URIValue)parameter.get(ACTION);
Value errorsProcessorUri = (Value)parameter.get(ERRORS_PROCESSOR);
Locale locale = ((LocaleValue)parameter.get(LOCALE)).getLocale();
String state = DEFAULT_STATE;
List informations = context.getInformations();
MapValue mapResource = (MapValue)((FormStore)context.getStore(Store.FORM)).getDomain();
List generatedControls = new ArrayList();
List involvedParameters = new ArrayList();
for (int i = 0; i < controlDescriptions.length; i++ ) {
Map controlParameters = ((MapValue)controlDescriptions[i]).getMap();
String controlName = controlParameters.get(CONTROL_NAME).toString();
URI controlUri = (URI)controlParameters.get(CONTROL);
Control control = (Control)ProcessorManager.getInstance().getProcessor(controlUri);
Value controlActionUri = (Value)controlParameters.get(Control.ACTION);
if ( controlActionUri == NullValue.NULL ) controlActionUri = actionUri;
controlParameters.put(Control.ACTION, controlActionUri);
try {
ProcessorManager.prepareValues(control.getParameterDescriptors(), controlParameters, context);
} catch ( ValidationException exception ) {
throw new ValidationException(new ErrorMessage("controlComposer/controlParameterInvalid", new Object[] { controlUri }), exception);
}
Value controlContainerUri = (Value)controlParameters.get(CONTROL_CONTAINER);
ParameterDescriptor parameterDescriptor = Control.getParameterDescriptor(controlParameters, context);
String parameterName = parameterDescriptor.getName();
involvedParameters.add(new StringValue(parameterName));
ParameterMessage description = (ParameterMessage)parameterDescriptor.getDescription();
boolean required = parameterDescriptor.isRequired();
String controlState = Control.OPTIONAL_CONTROL;
if ( required ) {
controlState = Control.REQUIRED_CONTROL;
}
Object controlValue = null;
boolean validate = false;
if ( mapResource != null ) {
controlValue = mapResource.getMap().get(parameterName);
validate = false;
BooleanValue validateResource = ((BooleanValue)mapResource.getMap().get(VALIDATE));
if ( validateResource != null ) validate = validateResource.booleanValue();
}
if ( validate ) {
try {
controlValue = ProcessorManager.prepareValue(parameterDescriptor, controlValue, context);
} catch ( ValidationException exception ) {
controlValue = StringValueDescriptor.ANY.valueOf(controlValue, context).toString();
context.addInformation(new Information(Information.ERROR, exception.getErrorMessage(), new String[] { parameterName }));
}
controlParameters.put(Control.VALUE, controlValue);
if ( hasErrors(informations, parameterName) ) {
if ( required ) {
controlState = Control.REQUIRED_INVALID_CONTROL;
} else {
controlState = Control.OPTIONAL_INVALID_CONTROL;
}
explodeInformations(controlParameters, informations, parameterName, locale);
} else {
if ( required ) {
controlState = Control.REQUIRED_VALID_CONTROL;
} else {
controlState = Control.OPTIONAL_VALID_CONTROL;
}
}
}
controlParameters.put(Control.STATE, controlState);
controlParameters.put(Control.VALUE, controlValue);
Result controlResult = control.process(controlParameters, context);
if ( controlContainerUri != NullValue.NULL ) {
Processor controlContainer = ProcessorManager.getInstance().getProcessor((URI)controlContainerUri);
Map controlContainerParameters = new HashMap();
controlContainerParameters.putAll(parameter);
if ( hasErrors(informations, parameterName) ) {
explodeInformations(controlContainerParameters, informations, parameterName, locale);
}
controlContainerParameters.put(Control.STATE, controlState);
controlContainerParameters.put(CONTROL, controlResult.getResultEntries().get(OUTPUT));
controlContainerParameters.put(TITLE, description.getTitle(locale, parameterName ));
try {
controlContainerParameters.put(TEXT, description.getText(locale));
controlContainerParameters.put(PROMPT, description.getPrompt(locale));
} catch ( MessageNotFoundException exception ) {
controlContainerParameters.put(TEXT, NO_PARAMETER_MESSAGE_AVAILABLE.getText(locale, " "));
controlContainerParameters.put(PROMPT, NO_PARAMETER_MESSAGE_AVAILABLE.getPrompt(locale, " "));
}
try {
ProcessorManager.prepareValues(controlContainer.getParameterDescriptors(), controlContainerParameters, context);
} catch ( ValidationException exception ) {
throw new ValidationException(new ErrorMessage("controlComposer/controlContainerParameterInvalid", new Object[] { controlContainerUri }), exception);
}
Result controlContainerResult = controlContainer.process(controlContainerParameters, context);
generatedControls.add(new MapValue(controlName, (Value)controlContainerResult.getResultEntries().get(OUTPUT)));
} else {
generatedControls.add(new MapValue(controlName, (Value)controlResult.getResultEntries().get(OUTPUT)));
}
if ( controlState == Control.OPTIONAL_INVALID_CONTROL || controlState == Control.REQUIRED_INVALID_CONTROL ) {
state = INVALID_STATE;
} else if ( state == DEFAULT_STATE && ( controlState == Control.OPTIONAL_VALID_CONTROL || controlState == Control.REQUIRED_VALID_CONTROL ) ) {
state = VALID_STATE;
}
}
Result composerResult = new Result(state, GENERATED_CONTROLS, new ArrayValue((Value [])generatedControls.toArray(new Value[generatedControls.size()])));
List generatedTriggers = new ArrayList();
for (int i = 0; i < triggerDescriptions.length; i++ ) {
Map triggerParameters = ((MapValue)triggerDescriptions[i]).getMap();
String triggerName = triggerParameters.get(TRIGGER_NAME).toString();
Value involvedTriggerParameters = (Value)triggerParameters.get(Trigger.INVOLVED_PARAMETERS);
if ( involvedTriggerParameters == NullValue.NULL ) {
involvedTriggerParameters = new ArrayValue((StringValue[])involvedParameters.toArray(new StringValue[involvedParameters.size()]));
}
triggerParameters.put(Trigger.INVOLVED_PARAMETERS, involvedTriggerParameters);
URI triggerUri = (URI)triggerParameters.get(TRIGGER);
Trigger trigger = (Trigger)ProcessorManager.getInstance().getProcessor(triggerUri);
Value triggerActionUri = (Value)triggerParameters.get(Trigger.ACTION);
if ( triggerActionUri == NullValue.NULL ) triggerActionUri = actionUri;
triggerParameters.put(Trigger.ACTION, triggerActionUri);
try {
ProcessorManager.prepareValues(trigger.getParameterDescriptors(), triggerParameters, context);
} catch ( ValidationException exception ) {
throw new ValidationException(new ErrorMessage("controlComposer/triggerParameterInvalid", new Object[] { triggerUri }), exception);
}
Value triggerContainerUri = (Value)triggerParameters.get(TRIGGER_CONTAINER);
Result triggerResult = trigger.process(triggerParameters, context);
if ( triggerContainerUri != NullValue.NULL ) {
Processor triggerContainer = ProcessorManager.getInstance().getProcessor((URI)triggerContainerUri);
Map triggerContainerParameters = new HashMap();
triggerContainerParameters.putAll(parameter);
triggerContainerParameters.put(TRIGGER, triggerResult.getResultEntries().get(OUTPUT));
try {
ProcessorManager.prepareValues(triggerContainer.getParameterDescriptors(), triggerContainerParameters, context);
} catch ( ValidationException exception ) {
throw new ValidationException(new ErrorMessage("controlComposer/triggerContainerParameterInvalid", new Object[] { triggerContainerUri }), exception);
}
Result triggerContainerResult = triggerContainer.process(triggerContainerParameters, context);
generatedTriggers.add(new MapValue(triggerName, (Value)triggerContainerResult.getResultEntries().get(OUTPUT)));
} else {
generatedTriggers.add(new MapValue(triggerName, (Value)triggerResult.getResultEntries().get(OUTPUT)));
}
}
composerResult.addResultEntry(GENERATED_TRIGGERS, new ArrayValue((Value [])generatedTriggers.toArray(new Value[generatedTriggers.size()])));
if ( errorsProcessorUri instanceof URI ) {
Processor errorsTable = ProcessorManager.getInstance().getProcessor((URI)errorsProcessorUri);
List errorList = new ArrayList();
for ( Iterator i = context.getInformations().iterator(); i.hasNext(); ) {
Information info = (Information)i.next();
Map errors = new HashMap();
explodeInformation(errors, info, locale );
errorList.add(new MapValue(errors));
}
Map errorsParameter = new HashMap();
MapValue[] errorsArray = new MapValue[errorList.size()];
errorsParameter.put(SimpleProcessor.INPUT, new ArrayValue((MapValue[])errorList.toArray(errorsArray)));
Result renderedErrors = ProcessorManager.process(errorsTable, errorsParameter, context);
composerResult.addResultEntry(RENDERED_ERRORS, (Value)renderedErrors.getResultEntries().get(OUTPUT));
}
return composerResult;
}
protected void explodeInformations(Map parameters, List informations, String parameterName, Locale locale) {
// FIXME: Use template for error numbers
List matchingInfos = getErrors(informations, parameterName);
StringBuffer buffer = new StringBuffer();
for ( Iterator i = matchingInfos.iterator(); i.hasNext(); ) {
Information info = (Information)i.next();
buffer.append(info.getNumber()).append(") ");
explodeInformation(parameters, info, locale);
}
parameters.put(ERROR_NUMBER, buffer.toString());
}
protected void explodeInformation(Map parameters, Information info, Locale locale) {
parameters.put(ERROR_NUMBER, String.valueOf(info.getNumber()));
try {
parameters.put(ERROR_TITLE, info.getErrorMessage().getTitle(locale));
parameters.put(ERROR_TEXT, info.getErrorMessage().getText(locale));
parameters.put(ERROR_SUMMARY, info.getErrorMessage().getSummary(locale));
parameters.put(ERROR_DETAILS, info.getErrorMessage().getDetails(locale));
} catch ( MessageNotFoundException exception ) {
parameters.put(ERROR_TITLE, NO_ERROR_MESSAGE_AVAILABLE.getTitle(locale, "no error title available"));
parameters.put(ERROR_TEXT, NO_ERROR_MESSAGE_AVAILABLE.getText(locale, "no error text available"));
parameters.put(ERROR_SUMMARY, NO_ERROR_MESSAGE_AVAILABLE.getSummary(locale, "no error summary available"));
parameters.put(ERROR_DETAILS, NO_ERROR_MESSAGE_AVAILABLE.getDetails(locale, "no error details available"));
}
}
protected boolean hasErrors(List informations, String parameterName) {
for ( Iterator i = informations.iterator(); i.hasNext(); ) {
Information info = (Information)i.next();
if ( info.isParameterInvolved(parameterName) && info.getSeverity() == Information.ERROR ) {
return true;
}
}
return false;
}
protected List getErrors(List informations, String parameterName) {
List matchingInfos = new ArrayList();
for ( Iterator i = informations.iterator(); i.hasNext(); ) {
Information info = (Information)i.next();
if ( info.isParameterInvolved(parameterName) && info.getSeverity() == Information.ERROR ) {
matchingInfos.add(info);
}
}
return matchingInfos;
}
public ParameterDescriptor[] getParameterDescriptors() {
return parameterDescriptors;
}
public ResultDescriptor getResultDescriptor() {
return resultDescriptor;
}
}