/* * Copyright 2011 JBoss Inc * * Licensed 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.drools.informer; //import org.slf4j.Logger; //import org.slf4j.LoggerFactory; import org.drools.definition.type.Modifies; import org.drools.definition.type.PropertyReactive; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.*; /** * <p> * Represents a general questionnaire containing a hierarchical arrangement of questions, and notes about how to complete the * questionnaire. * </p> * * <p> * Note that the name <code>Questionnaire</code> is not intended to limit its use to specific cases, but rather it should be * considered a general-purpose model suitable for most types of form-based scenarios (which aren't usually referred to as a * questionnaire but are conceptually similar). * </p> * * @author Damon Horrell */ @PropertyReactive public class Questionnaire extends Group { private static final String DEFAULT_CLIENT_DATE_FORMAT = "dd/mm/yyyy"; private static final Logger logger = LoggerFactory.getLogger(Questionnaire.class); private static final long serialVersionUID = 1L; public static final String COMPLETION_ACTION_RETURN = "#return"; private String defaultLanguage = "EN"; private String activeItem; private transient ArrayList<NavigationStackEntry> navigationStack = new ArrayList<NavigationStackEntry>(); private String completionAction; private boolean invalidAnswers; private boolean enableActionValidation; private Set<String> availableItems; private String clientDateFormat = DEFAULT_CLIENT_DATE_FORMAT; /** Is HTML markup allowed in questions, errors, etc. */ private boolean markupAllowed; public Questionnaire() { super.setActive(true); this.availableItems = new HashSet<String>(); } public Questionnaire(String type) { super(type); super.setActive(true); this.availableItems = new HashSet<String>(); } public Questionnaire(String type, String label) { super(type, label); super.setActive(true); this.availableItems = new HashSet<String>(); } public String getActiveItem() { return activeItem; } /** * Sets the active child <code>Item</code> within the questionnaire. * * A typical scenerio is to place a number of <code>Group</code>s within the questionnaire and use them to represent the * pages/screens of your application. * * If <code>activeItem</code> is null then all items are considered to be active. * * @param activeItem */ public void setActiveItem(String activeItem) { this.activeItem = activeItem; } /** * Sets the action to be performed when the questionnaire is complete. * * The meaning of the action may vary from one UI implementation to another but it could be used for a URL to redirect to or * the name of a method to invoke. * * @return */ public String getCompletionAction() { return completionAction; } public void setCompletionAction(String completionAction) { this.completionAction = completionAction; } public boolean isInvalidAnswers() { return invalidAnswers; } /** * This is invoked by the Tohu built-in rules. Do not call it directly. * * @param invalidAnswers */ public void setInvalidAnswers(boolean invalidAnswers) { this.invalidAnswers = invalidAnswers; } @Override public String toString() { return "Questionnaire{" + "activeItem='" + activeItem + '\'' + ", navigationStack=" + navigationStack + ", completionAction='" + completionAction + '\'' + ", invalidAnswers=" + invalidAnswers + ", enableActionValidation=" + enableActionValidation + ", availableItems=" + availableItems + ", clientDateFormat='" + clientDateFormat + '\'' + ", markupAllowed=" + markupAllowed + "} " + super.toString(); } /** * This is used to push a new navigation path onto the questionnaire. On completion, the user will be returned to the previous * path. * * @param newItems * @param newActiveItem * @return */ @Modifies( { "navigationStack", "items", "activeItem", "completionAction" } ) public void navigationBranch(String[] newItems, String newActiveItem) { navigationBranch(newItems, newActiveItem, COMPLETION_ACTION_RETURN); } /** * This is used to push a new navigation path onto the questionnaire with a new completionAction. * * @param newItems * @param newActiveItem * @param newCompletionAction * * @return */ @Modifies( { "navigationStack", "items", "activeItem", "completionAction" } ) public void navigationBranch(String[] newItems, String newActiveItem, String newCompletionAction) { if ((newItems == null) || (newItems.length == 0)) { throw new IllegalArgumentException("There are no valid new items to push onto stack."); } NavigationStackEntry entry = new NavigationStackEntry(this); navigationStack.add(0, entry); this.setItems(newItems); this.activeItem = newActiveItem; this.completionAction = newCompletionAction; } /** * This is used to pop the new navigation path off the questionnaire, reverting to the previous one. * * @return */ @Modifies( { "navigationStack", "items", "activeItem", "completionAction" } ) public void navigationReturn() { if (navigationStack.size() == 0) { throw new IllegalStateException(); } NavigationStackEntry entry = navigationStack.remove(0); this.activeItem = entry.activeItem; this.setItems(entry.items); this.setCompletionAction(entry.completionAction); } /** * Returns true if the questionnaire is in a navigation branch, or false if it is in the main flow. * * @return */ public boolean isBranched() { return !navigationStack.isEmpty(); } private class NavigationStackEntry { private String[] items; private String activeItem; private String completionAction; private NavigationStackEntry(Questionnaire questionnaire) { this.items = questionnaire.getItems(); this.activeItem = questionnaire.getActiveItem(); this.completionAction = questionnaire.getCompletionAction(); } } public boolean isEnableActionValidation() { return enableActionValidation; } public void setEnableActionValidation(boolean enableActionValidation) { this.enableActionValidation = enableActionValidation; } /** * Gets list of available item ids. * * @return */ public String[] getAvailableItems() { if (availableItems == null || availableItems.size() == 0) { return null; } return availableItems.toArray(new String[availableItems.size()]); } public int getNumAvailableItems() { return availableItems.size(); } // public List<String> getAvailableItemList() { // return new ArrayList( availableItems ); // } // public Set<String> getAvailableItemSet() { return availableItems; } /** * This is invoked by the Tohu built-in rules. Do not call it directly. * * @param availableItems */ public void setAvailableItems(String[] availableItems) { if (logger.isDebugEnabled()) { logger.debug("Available Items are: " + Arrays.toString(availableItems)); } this.availableItems.clear(); for (String item : availableItems) { if (item != null) { this.availableItems.add(item); } } } public void setAvailableItems(Collection<String> availableItems) { if (logger.isDebugEnabled()) { logger.debug("Available Items are: " + availableItems); } this.availableItems.clear(); this.availableItems.addAll( availableItems ); } @Modifies( "availableItems" ) public void addAvailableItems(List<String> availableItems) { this.availableItems.addAll( availableItems ); } @Modifies( "availableItems" ) public void addAvailableItem( String availableItem ) { this.availableItems.add( availableItem ); } public ArrayList<NavigationStackEntry> getNavigationStack() { return navigationStack; } public void setNavigationStack(ArrayList<NavigationStackEntry> navigationStack) { this.navigationStack = navigationStack; } public String getClientDateFormat() { return clientDateFormat; } /** * Set the format used to display/enter dates on the client. * * TODO this should use some industry-standard for specifying date formats. No change required at this end to achieve that but * we will need to change the javascript end to convert the standard format into whatever is required by JQuery. * * @param clientDateFormat */ public void setClientDateFormat(String clientDateFormat) { this.clientDateFormat = clientDateFormat; } /** * True if text in Notes, labels, pre-labels and error messages contain HTML markup. * Note that this defaults to false for security purposes to defend against XSS. * @return */ public boolean isMarkupAllowed() { return markupAllowed; } /** * Allow or disallow HTML markup in Notes, labels, pre-labels and error messages * @param markupAllowed */ public void setMarkupAllowed(boolean markupAllowed) { this.markupAllowed = markupAllowed; } public String getDefaultLanguage() { return defaultLanguage; } public void setDefaultLanguage(String defaultLanguage) { this.defaultLanguage = defaultLanguage; } @Modifies( "items" ) public void addItem(String itemId) { super.addItem(itemId); } @Modifies( "items" ) public void insertItem(String itemId, String beforeItemId) { super.insertItem(itemId, beforeItemId); } @Modifies( "items" ) public void appendItem(String itemId, String afterItemId) { super.appendItem(itemId, afterItemId); } @Modifies( "items" ) public int removeItem(String itemId) { return super.removeItem(itemId); } @Modifies( { "presentationStyles", "stylesList" } ) public void setPresentationStyles(String[] presentationStyles) { super.setPresentationStyles(presentationStyles); } @Modifies( { "presentationStyles", "stylesList" } ) public void setPresentationStyles(Object[] presentationStyles) { super.setPresentationStyles(presentationStyles); } @Modifies( { "presentationStyles", "stylesList" } ) public void addPresentationStyle(String presentationStyle) { super.addPresentationStyle(presentationStyle); } @Modifies( { "presentationStyles", "stylesList" } ) public void removePresentationStyle(String presentationStyle) { super.removePresentationStyle(presentationStyle); } }