/*******************************************************************************
* CogTool Copyright Notice and Distribution Terms
* CogTool 1.3, Copyright (c) 2005-2013 Carnegie Mellon University
* This software is distributed under the terms of the FSF Lesser
* Gnu Public License (see LGPL.txt).
*
* CogTool is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* CogTool is distributed in the hope that it will be useful,
* but WITHOUT ANY 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
* along with CogTool; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* CogTool makes use of several third-party components, with the
* following notices:
*
* Eclipse SWT version 3.448
* Eclipse GEF Draw2D version 3.2.1
*
* Unless otherwise indicated, all Content made available by the Eclipse
* Foundation is provided to you under the terms and conditions of the Eclipse
* Public License Version 1.0 ("EPL"). A copy of the EPL is provided with this
* Content and is also available at http://www.eclipse.org/legal/epl-v10.html.
*
* CLISP version 2.38
*
* Copyright (c) Sam Steingold, Bruno Haible 2001-2006
* This software is distributed under the terms of the FSF Gnu Public License.
* See COPYRIGHT file in clisp installation folder for more information.
*
* ACT-R 6.0
*
* Copyright (c) 1998-2007 Dan Bothell, Mike Byrne, Christian Lebiere &
* John R Anderson.
* This software is distributed under the terms of the FSF Lesser
* Gnu Public License (see LGPL.txt).
*
* Apache Jakarta Commons-Lang 2.1
*
* This product contains software developed by the Apache Software Foundation
* (http://www.apache.org/)
*
* jopt-simple version 1.0
*
* Copyright (c) 2004-2013 Paul R. Holser, Jr.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Mozilla XULRunner 1.9.0.5
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (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.mozilla.org/MPL/.
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
*
* The J2SE(TM) Java Runtime Environment version 5.0
*
* Copyright 2009 Sun Microsystems, Inc., 4150
* Network Circle, Santa Clara, California 95054, U.S.A. All
* rights reserved. U.S.
* See the LICENSE file in the jre folder for more information.
******************************************************************************/
package edu.cmu.cs.hcii.cogtool.model;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.xerces.parsers.DOMParser;
import org.eclipse.ecf.core.util.Base64;
import org.eclipse.swt.graphics.Image;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import edu.cmu.cs.hcii.cogtool.util.GraphicsUtil;
import edu.cmu.cs.hcii.cogtool.util.IAttributed;
import edu.cmu.cs.hcii.cogtool.util.L10N;
import edu.cmu.cs.hcii.cogtool.util.NamedObjectUtil;
import edu.cmu.cs.hcii.cogtool.util.NullSafe;
import edu.cmu.cs.hcii.cogtool.util.ObjectLoader;
/**
* Imports designs and demonstrations from an XML File
* @author AlexF
* Edited by Brett Harris with Importing from an BMML File
*/
public class ImportCogToolXML
{
public static final String CURRENT_VERSION = "1";
public static final String COGTOOL_IMPORT_ELT = "cogtoolimport";
public static final String DESIGN_ELT = "design";
public static final String DEVICE_ELT = "device";
public static final String FRAME_ELT = "frame";
public static final String CONTROLS_ELT = "controls";
public static final String CONTROL_ELT = "control";
public static final String HREF_ELT = "href";
public static final String BALSAMIQ_TEXT_ELT = "text";
public static final String MOCKUP_ELT = "mockup";
public static final String DEMONSTRATION_ELT = "demonstration";
public static final String BKG_IMAGE_PATH_ELT = "backgroundImagePath";
public static final String BKG_IMAGE_DATA_ELT = "backgroundImageData";
public static final String ORIGIN_ELT = "topLeftOrigin";
public static final String SPEAKER_TEXT_ELT = "speakerText";
public static final String LISTEN_TIME_SECS_ELT = "listenTimeSecs";
public static final String WIDGET_ELT = "widget";
public static final String ELTGROUP_ELT = "eltGroup";
public static final String ELTNAME_ELT = "elementName";
public static final String KEYBOARD_TRANSITIONS_ELT = "keyboardTransitions";
public static final String VOICE_TRANSITIONS_ELT = "voiceTransitions";
public static final String DISPLAY_LABEL_ELT = "displayLabel";
public static final String AUX_TEXT_ELT = "auxText";
public static final String EXTENT_ELT = "extent";
public static final String TRANSITION_ELT = "transition";
public static final String ACTION_ELT = "action";
public static final String MOUSE_ACTION_ELT = "mouseAction";
public static final String TOUCHSCREEN_ACTION_ELT = "touchscreenAction";
public static final String GRAFFITI_ACTION_ELT = "graffitiAction";
public static final String KEYBOARD_ACTION_ELT = "keyboardAction";
public static final String VOICE_ACTION_ELT = "voiceAction";
public static final String KBD_MODIFIER_ELT = "modifier";
public static final String GESTURES_ELT = "gestures";
public static final String TEXT_ELT = "text";
public static final String DEMO_STEP_ELT = "demonstrationStep";
public static final String ACTION_STEP_ELT = "actionStep";
public static final String KEYBOARD_STEP_ELT = "keyboardActionStep";
public static final String VOICE_STEP_ELT = "voiceActionStep";
public static final String THINK_STEP_ELT = "thinkStep";
public static final String SYS_DELAY_STEP_ELT = "systemDelayStep";
public static final String LOOK_AT_STEP_ELT = "lookAtWidgetStep";
public static final String START_EYE_LOC_ELT = "startingEyePosition";
public static final String START_MOUSE_LOC_ELT = "startingMousePosition";
public static final String START_LEFT_POS_ELT = "startingLeftHandPosition";
public static final String START_RIGHT_POS_ELT =
"startingRightHandPosition";
public static final String TASK_ELT = "task";
public static final String TASK_GROUP_ELT = "taskGroup";
public static final String VERSION_ATTR = "version";
public static final String NAME_ATTR = "name";
public static final String TYPE_ATTR = "type";
public static final String PARENT_ATTR = "parent";
public static final String GROUP_ATTR = "group";
public static final String REMOTE_LABEL_ATTR = "remoteLabel";
public static final String CONTROL_TYPE_ATTR = "controlTypeID";
public static final String CONTROL_ID_ATTR = "controlID";
public static final String TASK_GROUP_ID_ATTR = "taskGroupID";
public static final String X_ATTR = "x";
public static final String Y_ATTR = "y";
public static final String WIDTH_ATTR = "width";
public static final String HEIGHT_ATTR = "height";
public static final String MEASURED_WIDTH_ATTR = "measuredW";
public static final String MEASURED_HEIGHT_ATTR = "measuredH";
public static final String SHAPE_ATTR = "shape";
public static final String DEST_FRAME_NAME_ATTR = "destinationFrameName";
public static final String BUTTON_ATTR = "button";
public static final String ACTION_ATTR = "action";
public static final String IS_CMD_ATTR = "is-command";
public static final String TASK_NAME_ATTR = "taskName";
public static final String START_FRAME_NAME_ATTR = "startFrameName";
public static final String HANDEDNESS_ATTR = "handedness";
public static final String TARGET_WIDGET_NAME_ATTR = "targetWidgetName";
public static final String LOOKAT_WIDGET_NAME_ATTR = "lookAtWidgetName";
public static final String DURATION_ATTR = "durationInSecs";
public static final String THINK_LABEL_ATTR = "thinkLabel";
public static final String DELAY_LABEL_ATTR = "delayLabel";
public static final String GROUP_NATURE_ATTR = "displayedGroupSummary";
// General WidgetAttributes attribute names and values
// REMEMBER TO ENTER INTO THE REGISTRIES BELOW!!!!
public static final String TRUE_VALUE = "true";
public static final String FALSE_VALUE = "false";
public static final String IS_SELECTED_ATTR = "w-is-selected";
public static final String IS_SELECTED_VALUE = TRUE_VALUE;
public static final String NOT_SELECTED_VALUE = FALSE_VALUE;
public static final String TOGGLE_VALUE = "toggle";
public static final String IS_TOGGLEABLE_ATTR = "w-is-toggleable";
public static final String IS_STANDARD_ATTR = "w-is-standard";
public static final String SELECTION_ATTR = "w-selected-name";
public static final String IS_RENDERED_ATTR = "w-is-rendered";
public static final String IS_SEPARATOR_ATTR = "w-is-separator";
public static final String APPENDED_TEXT_ATTR = "w-appended-text";
public static final String SUBMENU_ACTION_ATTR = "w-submenu-action";
public static final String TAP_VALUE = "tap";
public static final String CLICK_VALUE = "click";
public static final String HOVER_VALUE = "hover";
public static final String SUBMENU_DELAY_ATTR = "w-submenu-delay";
public static final String PC_DELAY_VALUE = "pc-delay";
public static final String NO_DELAY_VALUE = "no-delay";
public static final String CONTEXT_MENU_ACTION_ATTR = "w-menu-action";
public static final String CTRL_LEFT_VALUE = "ctrl-left-click";
public static final String TAP_HOLD_VALUE = "tap-hold";
public static final String MENU_KEY_VALUE = "menu-key";
public static final String RIGHT_CLICK_VALUE = "right-click";
public static final String KEYBOARD_DEVICE = "keyboard";
public static final String MOUSE_DEVICE = "mouse";
public static final String TOUCHSCREEN_DEVICE = "touchscreen";
public static final String MICROPHONE_DEVICE = "microphone";
public static final String DISPLAY_DEVICE = "display";
public static final String SPEAKER_DEVICE = "speaker";
public static final String BUTTON_WIDGETTYPE = "button";
public static final String LINK_WIDGETTYPE = "link";
public static final String CHECKBOX_WIDGETTYPE = "check box";
public static final String RADIO_WIDGETTYPE = "radio button";
public static final String TEXTBOX_WIDGETTYPE = "text box";
public static final String TEXT_WIDGETTYPE = "text";
public static final String PULLDOWNLIST_WIDGETTYPE = "pull-down list";
public static final String PULLDOWNITEM_WIDGETTYPE = "pull-down item";
public static final String LISTBOXITEM_WIDGETTYPE = "list box item";
public static final String CONTEXTMENU_WIDGETTYPE = "context menu";
public static final String MENUHEADER_WIDGETTYPE = "menu";
public static final String SUBMENU_WIDGETTYPE = "submenu";
public static final String MENUITEM_WIDGETTYPE = "menu item";
public static final String GRAFFITI_WIDGETTYPE = "graffiti";
public static final String NONINTERACTIVE_WIDGETTYPE = "non-interactive";
public static final String RECTANGLE_SHAPE = "rectangle";
public static final String ELLIPSE_SHAPE = "ellipse";
public static final String ROUND_RECT_SHAPE = "rounded rectangle";
public static final String LEFT_BUTTON = "left";
public static final String MIDDLE_BUTTON = "middle";
public static final String RIGHT_BUTTON = "right";
public static final String DOWNUP_ACTION = "downUp";
public static final String TAP_ACTION = "tap";
public static final String PRESS_ACTION = "press";
public static final String DOUBLE_ACTION = "double";
public static final String TRIPLE_ACTION = "triple";
public static final String DOWN_ACTION = "down";
public static final String UP_ACTION = "up";
public static final String HOVER_ACTION = "hover";
public static final String SHIFT_MODIFIER = "SHIFT";
public static final String CTRL_MODIFIER = "CTRL";
public static final String ALT_MODIFIER = "ALT";
public static final String COMMAND_MODIFIER = "COMMAND";
public static final String FUNCTION_MODIFIER = "FUNCTION";
public static final String RIGHT_HAND = "right";
public static final String LEFT_HAND = "left";
public static final String FINAL_FRAME_NAME = "FINAL FRAME";
/**
* Thrown when the import cannot complete.
*/
public static class ImportFailedException extends RuntimeException
{
public ImportFailedException(String msg, Throwable ex)
{
super(msg, ex);
}
public ImportFailedException(String msg)
{
super(msg);
}
}
private static final Map<String,
IAttributed.AttributeDefinition<?>> ATTRIBUTE_REGISTRY =
new HashMap<String, IAttributed.AttributeDefinition<?>>();
private static final Map<String, Object> VALUE_REGISTRY =
new HashMap<String, Object>();
private static IAttributed.AttributeDefinition<?> getAttrDefn(String attr)
{
return IAttributed.AttributeRegistry.ONLY.getAttributeDefn(attr);
}
private static void registerAttributes()
{
ATTRIBUTE_REGISTRY.put(IS_SELECTED_ATTR,
getAttrDefn(WidgetAttributes.IS_SELECTED_ATTR));
ATTRIBUTE_REGISTRY.put(IS_TOGGLEABLE_ATTR,
getAttrDefn(WidgetAttributes.IS_TOGGLEABLE_ATTR));
ATTRIBUTE_REGISTRY.put(IS_STANDARD_ATTR,
getAttrDefn(WidgetAttributes.IS_STANDARD_ATTR));
ATTRIBUTE_REGISTRY.put(SELECTION_ATTR,
getAttrDefn(WidgetAttributes.SELECTION_ATTR));
ATTRIBUTE_REGISTRY.put(IS_RENDERED_ATTR,
getAttrDefn(WidgetAttributes.IS_RENDERED_ATTR));
ATTRIBUTE_REGISTRY.put(IS_SEPARATOR_ATTR,
getAttrDefn(WidgetAttributes.IS_SEPARATOR_ATTR));
ATTRIBUTE_REGISTRY.put(APPENDED_TEXT_ATTR,
getAttrDefn(WidgetAttributes.APPENDED_TEXT_ATTR));
ATTRIBUTE_REGISTRY.put(SUBMENU_ACTION_ATTR,
getAttrDefn(WidgetAttributes.SUBMENU_ACTION_ATTR));
ATTRIBUTE_REGISTRY.put(SUBMENU_DELAY_ATTR,
getAttrDefn(WidgetAttributes.SUBMENU_DELAY_ATTR));
ATTRIBUTE_REGISTRY.put(CONTEXT_MENU_ACTION_ATTR,
getAttrDefn(WidgetAttributes.CONTEXT_MENU_ACTION_ATTR));
VALUE_REGISTRY.put(TRUE_VALUE, Boolean.TRUE);
VALUE_REGISTRY.put(FALSE_VALUE, Boolean.FALSE);
VALUE_REGISTRY.put(TOGGLE_VALUE, WidgetAttributes.TOGGLE_SELECTION);
VALUE_REGISTRY.put(TAP_VALUE, WidgetAttributes.TAP_SUBMENU_ACTION);
VALUE_REGISTRY.put(CLICK_VALUE, WidgetAttributes.CLICK_SUBMENU_ACTION);
VALUE_REGISTRY.put(HOVER_VALUE, WidgetAttributes.HOVER_SUBMENU_ACTION);
VALUE_REGISTRY.put(PC_DELAY_VALUE, WidgetAttributes.PC_SUBMENU_DELAY);
VALUE_REGISTRY.put(NO_DELAY_VALUE, WidgetAttributes.NO_SUBMENU_DELAY);
VALUE_REGISTRY.put(CTRL_LEFT_VALUE, WidgetAttributes.CTRL_LEFT_CLICK);
VALUE_REGISTRY.put(TAP_HOLD_VALUE, WidgetAttributes.TAP_HOLD);
VALUE_REGISTRY.put(MENU_KEY_VALUE, WidgetAttributes.MENU_KEY_PRESS);
VALUE_REGISTRY.put(RIGHT_CLICK_VALUE, WidgetAttributes.RIGHT_CLICK);
}
private static boolean notInitialized = true;
private static void addAttributes(IAttributed attributed,
Node node)
{
addAttributes(attributed, node, null);
}
private static void addAttributes(IAttributed attributed,
Node node,
Map<IAttributed, String> pendingAttrSets)
{
NamedNodeMap attributes = node.getAttributes();
if (attributes != null) {
int numAttributes = attributes.getLength();
if (numAttributes > 0) {
if (notInitialized) {
registerAttributes();
}
for (int i = 0; i < numAttributes; i++) {
Node attributeNode = attributes.item(i);
// Should never be null; sanity check
if (attributeNode != null) {
String attribute = attributeNode.getNodeName();
IAttributed.AttributeDefinition<?> attrDefn =
ATTRIBUTE_REGISTRY.get(attribute);
if (attrDefn != null) {
String attrName = attrDefn.attrName;
String attrNodeValue = attributeNode.getNodeValue();
if (WidgetAttributes.SELECTION_ATTR.equals(attrName))
{
// attrNodeValue is name of the selected widget,
// but the widget may not exist yet.
pendingAttrSets.put(attributed, attrNodeValue);
}
else if (VALUE_REGISTRY.containsKey(attrNodeValue))
{
Object attrValue =
VALUE_REGISTRY.get(attrNodeValue);
if (! NullSafe.equals(attrValue,
attrDefn.defaultValue))
{
attributed.setAttribute(attrName,
attrValue);
}
}
else {
// Assume string value (eg, APPENDED_TEXT_ATTR)
attributed.setAttribute(attrName,
attrNodeValue);
}
}
}
}
}
}
}
private String directoryPath = "";
private ObjectLoader objLoader = new ObjectLoader();
// Maps Design to List of Demonstration
private Map<Design, Collection<Demonstration>> designs =
new LinkedHashMap<Design, Collection<Demonstration>>();
private Set<AUndertaking> newUndertakings =
new LinkedHashSet<AUndertaking>();
private Map<String, TaskGroup> taskGroups =
new HashMap<String, TaskGroup>();
private List<String> failedObjectErrors = new ArrayList<String>();
private List<String> failedImages = new ArrayList<String>();
private List<String> klmWarnings = new ArrayList<String>();
private CognitiveModelGenerator modelGenerator = null;
private String dtdVersion = "0";
private DefaultModelGeneratorState currentState = null;
// Maps name to newly created Task
private Map<String, List<Task>> createdTaskRegistry =
new HashMap<String, List<Task>>();
// Maps file name to image data (byte[])
private Map<String, byte[]> imageRegistry = new HashMap<String, byte[]>();
private Map<String, SimpleWidgetGroup> groupRegistry =
new HashMap<String, SimpleWidgetGroup>();
// Image data cache; maps image name to data (byte[])
private Map<String, byte[]> cachedImages = new HashMap<String, byte[]>();
private static ObjectLoader.IObjectLoader<Design> designLoader =
Design.getImportLoader();
private static ObjectLoader.IObjectLoader<Frame> frameLoader =
Frame.getImportLoader();
private static ObjectLoader.IObjectLoader<Widget> widgetLoader =
Widget.getImportLoader();
private static ObjectLoader.IObjectLoader<FrameElementGroup> groupLoader =
FrameElementGroup.getImportLoader();
private static boolean inImportFromXML = false;
public boolean importXML(File inputFile,
TaskParent parent,
CognitiveModelGenerator modelGen)
throws IOException, SAXException, SecurityException
{
InputStream fis = null;
fis = new FileInputStream(inputFile);
boolean result = false;
try {
Reader input = new InputStreamReader(fis, "UTF-8");
try {
result = importXML(input,
inputFile.getParent() + File.separator,
parent,
modelGen);
}
finally {
input.close();
}
}
finally {
fis.close();
}
return result;
}
public boolean importXML(Reader input,
String imageDirPath,
TaskParent taskParent,
CognitiveModelGenerator modelGen)
throws IOException, SAXException
{
modelGenerator = modelGen;
// Create a Xerces DOM Parser
DOMParser parser = new DOMParser();
// Set the path for loading images
directoryPath = imageDirPath;
// Parse the Document and traverse the DOM
parser.parse(new InputSource(input));
Document document = parser.getDocument();
parseFile(document, taskParent);
if (failedImages.size() > 0) {
String failedImageString = "Failed to load the following images:";
Iterator<String> fIter = failedImages.iterator();
while (fIter.hasNext()) {
failedImageString +=
System.getProperty("file.separator") + fIter.next();
}
throw new GraphicsUtil.ImageException(failedImageString);
}
return true;
}
public Map<Design, Collection<Demonstration>> getDesigns()
{
return designs;
}
public Set<AUndertaking> getNewUndertakings()
{
return newUndertakings;
}
public List<String> getObjectFailures()
{
return failedObjectErrors;
}
public List<String> getGenerationWarnings()
{
return klmWarnings;
}
private String getAttributeValue(Node node, String attr)
{
NamedNodeMap attributes = node.getAttributes();
if (attributes != null) {
Node attributeNode = attributes.getNamedItem(attr);
if (attributeNode != null) {
return attributeNode.getNodeValue();
}
}
return null;
}
private String getElementText(Node node)
{
if (node.getFirstChild() != null) {
return node.getFirstChild().getNodeValue().trim();
}
return "";
}
/**
* Imports an XML file containing a design
* and possibly a demonstration
* @param node
*/
private void parseFile(Node node, TaskParent taskParent)
throws IOException
{
NodeList children = node.getChildNodes();
if (children != null) {
if (node.getNodeName().equalsIgnoreCase(COGTOOL_IMPORT_ELT)) {
dtdVersion = getAttributeValue(node, VERSION_ATTR);
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
String nodeName = child.getNodeName();
if (nodeName.equalsIgnoreCase(TASK_GROUP_ELT)) {
parseTaskGroup(child, null);
} else if (nodeName.equalsIgnoreCase(TASK_ELT)) {
parseTask(child, taskParent);
}
}
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
String nodeName = child.getNodeName();
if (nodeName.equalsIgnoreCase(DESIGN_ELT)) {
parseDesign(child, taskParent);
}
}
}
else {
for (int i = 0; i < children.getLength(); i++) {
parseFile(children.item(i), taskParent);
}
}
}
}
private void parseTaskGroup(Node node, TaskParent taskParent) {
TaskGroup taskGroup =
new TaskGroup(getAttributeValue(node, NAME_ATTR),
GroupNature.fromString(
getAttributeValue(node, GROUP_NATURE_ATTR)));
if (taskParent != null) {
taskParent.addUndertaking(taskGroup);
} else {
newUndertakings.add(taskGroup);
}
taskGroups.put(getAttributeValue(node, TASK_GROUP_ID_ATTR), taskGroup);
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); ++i) {
Node child = children.item(i);
String childName = child.getNodeName();
if (childName.equalsIgnoreCase(TASK_GROUP_ELT)) {
parseTaskGroup(child, taskGroup);
} else if (childName.equalsIgnoreCase(TASK_ELT)) {
parseTask(child, taskGroup);
}
}
}
private void parseTask(Node node, TaskParent taskParent) {
getTask(getAttributeValue(node, NAME_ATTR), null, taskParent);
}
/**
* Imports a design
* @param node
*/
private void parseDesign(Node node, TaskParent taskParent)
throws IOException
{
NodeList children = node.getChildNodes();
if (children != null) {
Design design = designLoader.createObject();
List<Demonstration> demonstrations =
new ArrayList<Demonstration>();
designLoader.set(design,
Design.nameVAR,
getAttributeValue(node, NAME_ATTR));
addAttributes(design, node);
@SuppressWarnings("unchecked")
Collection<DeviceType> deviceTypes = (Collection<DeviceType>)
designLoader.createCollection(design, Design.deviceTypesVAR, 1);
Collection<?> frames =
designLoader.createCollection(design, Design.framesVAR, 1);
ObjectLoader.IAggregateLoader deviceTypesLoader =
designLoader.getLoader(Design.deviceTypesVAR);
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
String nodeName = child.getNodeName();
if (nodeName.equalsIgnoreCase(DEVICE_ELT)) {
String device = getElementText(child);
if (device.equalsIgnoreCase(KEYBOARD_DEVICE)) {
deviceTypesLoader.addToCollection(objLoader,
deviceTypes,
DeviceType.Keyboard);
}
else if (device.equalsIgnoreCase(MOUSE_DEVICE)) {
deviceTypesLoader.addToCollection(objLoader,
deviceTypes,
DeviceType.Mouse);
}
else if (device.equalsIgnoreCase(TOUCHSCREEN_DEVICE)) {
deviceTypesLoader.addToCollection(objLoader,
deviceTypes,
DeviceType.Touchscreen);
}
else if (device.equalsIgnoreCase(MICROPHONE_DEVICE)) {
deviceTypesLoader.addToCollection(objLoader,
deviceTypes,
DeviceType.Voice);
}
else if (device.equalsIgnoreCase(DISPLAY_DEVICE)) {
deviceTypesLoader.addToCollection(objLoader,
deviceTypes,
DeviceType.Display);
}
else if (device.equalsIgnoreCase(SPEAKER_DEVICE)) {
deviceTypesLoader.addToCollection(objLoader,
deviceTypes,
DeviceType.Speaker);
}
else {
// "unknown device"
failedObjectErrors.add("Unknown Design device: "
+ device);
}
}
else if (nodeName.equalsIgnoreCase(FRAME_ELT)) {
parseFrame(design, child);
// No need to add frame to the design here; done already!
}
else if (nodeName.equalsIgnoreCase(DEMONSTRATION_ELT)) {
Demonstration demo = parseDemonstration(design,
child,
taskParent);
if (demo != null) {
demonstrations.add(demo);
}
}
}
if ((design.getName() == null) ||
(deviceTypes.size() == 0) ||
(frames.size() == 0))
{
throw new ImportFailedException("No design found");
}
designs.put(design, demonstrations);
}
}
private byte[] loadImage(String relativePath)
throws IOException
{
byte[] loadedImageData = imageRegistry.get(relativePath);
if (loadedImageData == null) {
loadedImageData =
GraphicsUtil.loadImageFromFile(directoryPath
+ relativePath);
if (loadedImageData != null) {
imageRegistry.put(relativePath, loadedImageData);
}
}
return loadedImageData;
}
/**
* Imports a frame
* @param node
*/
private Frame parseFrame(Design design, Node node)
throws IOException
{
NodeList children = node.getChildNodes();
if (children != null) {
String frameName = getAttributeValue(node, NAME_ATTR);
if ((frameName == null) || frameName.equals("")) {
failedObjectErrors.add("Cannot create a frame with an empty name.");
return null;
}
// This adds the created frame to the design
Frame frame = getFrame(design, frameName);
addAttributes(frame, node);
Frame.setFrameDevices(frame, design.getDeviceTypes());
TransitionSource keyboardDevice =
frame.getInputDevice(DeviceType.Keyboard);
TransitionSource voiceDevice =
frame.getInputDevice(DeviceType.Voice);
// Some widgets have parents; so as not to require that
// all widgets of a frame occur in a particular order, we must
// resolve the parent names after all widgets have been parsed.
// Maps the child widget to the name of its parent
Map<ChildWidget, String> pendingParentSets =
new LinkedHashMap<ChildWidget, String>();
// Some attributes refer to widget names; must resolve these
// after all widgets have been created.
// Currently, the only such attribute that applies to widgets
// is WidgetAttributes.SELECTION_ATTR
// Maps the attributed object to the widget name that is
// the value of the WidgetAttributes.SELECTION_ATTR attribute
Map<IAttributed, String> pendingAttrSets =
new HashMap<IAttributed, String>();
// Some element groups may be referenced as members of other
// groups before being defined; this map will hold them
Map<String, FrameElementGroup> pendingGrps =
new HashMap<String, FrameElementGroup>();
// Some remote labels may not be defined before they're referenced
// so keep track of those cases. Maps the owner object to
// the name of the remote label
Map<FrameElement, String> pendingRemoteLabels =
new HashMap<FrameElement, String>();
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
String nodeName = child.getNodeName();
if (nodeName.equalsIgnoreCase(BKG_IMAGE_PATH_ELT)) {
String backgroundImagePath = getElementText(child);
byte[] image = loadImage(backgroundImagePath);
if (image != null) {
frameLoader.set(frame, Frame.backgroundVAR, image);
frame.setAttribute(WidgetAttributes.IMAGE_PATH_ATTR,
backgroundImagePath);
}
}
else if (nodeName.equalsIgnoreCase(BKG_IMAGE_DATA_ELT)) {
String backgroundImageData = getElementText(child);
String imageName = getAttributeValue(child, NAME_ATTR);
byte[] image = null;
if (backgroundImageData != "") {
image = Base64.decode(backgroundImageData);
if ((imageName != null) && ! imageName.equals("")) {
cachedImages.put(imageName, image);
}
}
else if ((imageName != null) && ! imageName.equals("")) {
// If imageName specified but there is no data, trust and
// try to find the last image data associated with that
// name in the cache.
image = cachedImages.get(imageName);
}
if (image != null) {
frameLoader.set(frame, Frame.backgroundVAR, image);
if ((imageName != null) && ! imageName.equals("")) {
frame.setAttribute(WidgetAttributes.IMAGE_PATH_ATTR,
imageName);
}
}
}
else if (nodeName.equalsIgnoreCase(ORIGIN_ELT)) {
double x =
Double.parseDouble(getAttributeValue(child, X_ATTR));
double y =
Double.parseDouble(getAttributeValue(child, Y_ATTR));
DoublePoint origin = new DoublePoint(x, y);
frameLoader.set(frame, Frame.originVAR, origin);
}
else if (nodeName.equalsIgnoreCase(SPEAKER_TEXT_ELT)) {
frameLoader.set(frame,
Frame.speakerTextVAR,
getElementText(child));
}
else if (nodeName.equalsIgnoreCase(LISTEN_TIME_SECS_ELT)) {
frameLoader.set(frame,
Frame.listenTimeVAR,
Double.parseDouble(getElementText(child)));
}
else if (nodeName.equalsIgnoreCase(WIDGET_ELT)) {
IWidget w = parseWidget(design,
frame,
pendingParentSets,
pendingAttrSets,
pendingRemoteLabels,
child);
if (w != null) {
frame.addWidget(w);
}
else
{
w = new Widget(null, WidgetType.Noninteractive);
Image wImage = GraphicsUtil.getImageFromResource("edu/cmu/cs/hcii/cogtool/resources/warning.jpg");
//w.setImage(wImage.getBytes());
frame.addWidget(w);
}
}
else if (nodeName.equalsIgnoreCase(ELTGROUP_ELT)) {
FrameElementGroup g =
parseEltGroup(design, frame, pendingGrps, child);
if (g != null) {
String eltGrpName = g.getName();
pendingGrps.remove(eltGrpName);
eltGrpName =
NamedObjectUtil.makeNameUnique(eltGrpName,
frame.getEltGroups());
g.setName(eltGrpName);
frame.addEltGroup(g);
}
}
else if (nodeName.equalsIgnoreCase(KEYBOARD_TRANSITIONS_ELT)) {
if (keyboardDevice != null) {
parseTransitions(design, keyboardDevice, child);
}
else {
failedObjectErrors.add("Keyboard transitions require that Design have a Keyboard device");
}
}
else if (nodeName.equalsIgnoreCase(VOICE_TRANSITIONS_ELT)) {
if (voiceDevice != null) {
parseTransitions(design, voiceDevice, child);
}
else {
failedObjectErrors.add("Voice transitions require that Design have a Voice device");
}
}
}
if (frame.getName() != null) {
// Handle any forward references for remote labels
Iterator<Map.Entry<FrameElement, String>> labelRefs =
pendingRemoteLabels.entrySet().iterator();
while (labelRefs.hasNext()) {
Map.Entry<FrameElement, String> labelRef =
labelRefs.next();
setRemoteLabel(frame,
labelRef.getValue(),
labelRef.getKey(),
null);
}
// If any "pending" element groups still exist, then there
// is an error -- an element group that didn't exist!
Iterator<FrameElementGroup> missingGrps =
pendingGrps.values().iterator();
StringBuilder errorMsg = new StringBuilder();
while (missingGrps.hasNext()) {
FrameElementGroup missingGrp = missingGrps.next();
errorMsg.append("Missing widget or group, named: ");
errorMsg.append(missingGrp.getName());
errorMsg.append(" as member of the following groups: ");
Iterator<FrameElementGroup> inGrps =
missingGrp.getEltGroups().iterator();
String separator = "";
while (inGrps.hasNext()) {
errorMsg.append(separator + inGrps.next().getName());
separator = ", ";
}
failedObjectErrors.add(errorMsg.toString());
errorMsg.delete(0, errorMsg.length());
}
Iterator<Map.Entry<ChildWidget, String>> childToParentSet =
pendingParentSets.entrySet().iterator();
// Now that all widgets have been created, set the parent-child
// relationships
while (childToParentSet.hasNext()) {
Map.Entry<ChildWidget, String> childToParent =
childToParentSet.next();
String parentName = childToParent.getValue();
if (! "".equals(parentName)) {
ChildWidget child = childToParent.getKey();
AParentWidget parent =
(AParentWidget) frame.getWidget(parentName);
parent.addItem(child);
child.setParent(parent);
}
}
Iterator<Map.Entry<IAttributed, String>> selnAttrToSet =
pendingAttrSets.entrySet().iterator();
// Now that all widgets have been created, set the attributes
// that used widget names as values.
while (selnAttrToSet.hasNext()) {
Map.Entry<IAttributed, String> selnAttr =
selnAttrToSet.next();
String widgetName = selnAttr.getValue();
IWidget attrValue =
"".equals(widgetName) ? null
: frame.getWidget(widgetName);
// At the moment, all occurrences that use pendingAttrSets
// are instances of PullDownHeader for
// WidgetAttributes.SELECTION_ATTR
selnAttr.getKey().setAttribute(WidgetAttributes.SELECTION_ATTR,
attrValue);
}
return frame;
}
}
return null;
} // parseFrame
/** Helper function used by parseFrame.
* This method is used to return the corresponding Frame object for a
* given frame name
*
* @ param frameName the specified frame name
*/
private Frame getFrame(Design design, String frameName)
{
if ((frameName != null) &&
! frameName.equals("") &&
! frameName.equals(FINAL_FRAME_NAME))
{
// Fail-fast -- right now, we have only one implementation of
// Frame, so this cannot fail.
Frame frame = design.getFrame(frameName);
if (frame == null) {
frame = frameLoader.createObject();
frame.setName(frameName);
design.addFrame(frame);
}
return frame;
}
return null;
}
private WidgetType getWidgetType(String widgetType)
{
if ((widgetType == null) || // TODO: note error, return null on this case?
widgetType.equalsIgnoreCase(BUTTON_WIDGETTYPE))
{
return WidgetType.Button;
}
if (widgetType.equalsIgnoreCase(LINK_WIDGETTYPE)) {
return WidgetType.Link;
}
if (widgetType.equalsIgnoreCase(CHECKBOX_WIDGETTYPE)) {
return WidgetType.Check;
}
if (widgetType.equalsIgnoreCase(RADIO_WIDGETTYPE)) {
return WidgetType.Radio;
}
if (widgetType.equalsIgnoreCase(TEXTBOX_WIDGETTYPE)) {
return WidgetType.TextBox;
}
if (widgetType.equalsIgnoreCase(TEXT_WIDGETTYPE)) {
return WidgetType.Text;
}
if (widgetType.equalsIgnoreCase(PULLDOWNLIST_WIDGETTYPE)) {
return WidgetType.PullDownList;
}
if (widgetType.equalsIgnoreCase(PULLDOWNITEM_WIDGETTYPE)) {
return WidgetType.PullDownItem;
}
if (widgetType.equalsIgnoreCase(LISTBOXITEM_WIDGETTYPE)) {
return WidgetType.ListBoxItem;
}
if (widgetType.equalsIgnoreCase(CONTEXTMENU_WIDGETTYPE)) {
return WidgetType.ContextMenu;
}
if (widgetType.equalsIgnoreCase(MENUHEADER_WIDGETTYPE)) {
return WidgetType.Menu;
}
if (widgetType.equalsIgnoreCase(SUBMENU_WIDGETTYPE)) {
return WidgetType.Submenu;
}
if (widgetType.equalsIgnoreCase(MENUITEM_WIDGETTYPE)) {
return WidgetType.MenuItem;
}
if (widgetType.equalsIgnoreCase(GRAFFITI_WIDGETTYPE)) {
return WidgetType.Graffiti;
}
if (widgetType.equalsIgnoreCase(NONINTERACTIVE_WIDGETTYPE)) {
return WidgetType.Noninteractive;
}
failedObjectErrors.add("Unknown widget type: " + widgetType);
return null;
}
/**
* Imports a widget
* @param node
*/
private IWidget parseWidget(Design design,
Frame frame,
Map<ChildWidget, String> pendingParentSets,
Map<IAttributed, String> pendingAttrSets,
Map<FrameElement, String> pendingRemoteLabels,
Node node)
throws IOException
{
NodeList children = node.getChildNodes();
if (children != null) {
String isStandardValue = getAttributeValue(node, IS_STANDARD_ATTR);
boolean isStandard;
if (isStandardValue != null) {
Boolean isStandardAttrValue =
(Boolean) VALUE_REGISTRY.get(isStandardValue);
if (isStandardAttrValue != null) {
isStandard = isStandardAttrValue.booleanValue();
}
else {
isStandard = false;
}
}
else if (CURRENT_VERSION.equals(dtdVersion)) {
isStandard = false;
}
else {
isStandard = true;
}
WidgetType widgetType =
getWidgetType(getAttributeValue(node, TYPE_ATTR));
Widget widget = null;
if (widgetType != null) {
if (isStandard) {
if (widgetType == WidgetType.ContextMenu) {
// Using this constructor sets up childItems and
// childLocation to the only value currently in use
widget = new ContextMenu(ContextMenu.FOR_DUPLICATION);
}
else if (widgetType == WidgetType.Menu) {
// Using this constructor sets up childItems and
// childLocation to the only value currently in use
widget = new MenuHeader(MenuHeader.FOR_DUPLICATION);
// Must also find its "anonymous" parentGroup
String grpName = getAttributeValue(node, GROUP_ATTR);
if ((grpName != null) && ! "".equals(grpName)) {
SimpleWidgetGroup menuHdrGroup =
groupRegistry.get(grpName);
if (menuHdrGroup == null) {
menuHdrGroup =
new SimpleWidgetGroup(SimpleWidgetGroup.HORIZONTAL);
menuHdrGroup.setName(grpName);
groupRegistry.put(grpName, menuHdrGroup);
}
menuHdrGroup.add(widget);
}
}
else if ((widgetType == WidgetType.Submenu) ||
(widgetType == WidgetType.MenuItem))
{
// Must also assign its parent, which must come earlier
String parentName =
getAttributeValue(node, PARENT_ATTR);
if (parentName != null) {
MenuItem menuItem = new MenuItem();
// If a submenu, set up childItems and childLocation
menuItem.setSubmenu(widgetType == WidgetType.Submenu);
pendingParentSets.put(menuItem, parentName);
widget = menuItem;
}
else {
widget = widgetLoader.createObject();
}
}
else if (widgetType == WidgetType.PullDownList) {
// Using this constructor sets up childItems and
// childLocation to the only value currently in use
widget =
new PullDownHeader(PullDownHeader.FOR_DUPLICATION);
}
else if (widgetType == WidgetType.PullDownItem) {
// Must assign its parent, which must come earlier
String parentName =
getAttributeValue(node, PARENT_ATTR);
if (parentName != null) {
PullDownItem pullDownItem = new PullDownItem();
pendingParentSets.put(pullDownItem, parentName);
widget = pullDownItem;
}
else {
widget = widgetLoader.createObject();
}
}
else if (widgetType == WidgetType.ListBoxItem) {
widget = new ListItem();
// Must also find its "anonymous" parentGroup
String grpName = getAttributeValue(node, GROUP_ATTR);
if ((grpName != null) && ! "".equals(grpName)) {
SimpleWidgetGroup listItemGroup =
groupRegistry.get(grpName);
if (listItemGroup == null) {
listItemGroup =
new SimpleWidgetGroup(SimpleWidgetGroup.VERTICAL);
listItemGroup.setName(grpName);
groupRegistry.put(grpName, listItemGroup);
}
listItemGroup.add(widget);
}
}
else if ((widgetType == WidgetType.Radio) ||
(widgetType == WidgetType.Check))
{
GridButton gridWidget;
if (widgetType == WidgetType.Radio) {
gridWidget = new RadioButton();
}
else {
gridWidget = new CheckBox();
}
// Fetch the horizontal/vertical spacing attributes
double horizontal =
Double.parseDouble(getAttributeValue(node, X_ATTR));
double vertical =
Double.parseDouble(getAttributeValue(node, Y_ATTR));
gridWidget.setHorizSpace(horizontal);
gridWidget.setVertSpace(vertical);
// Must also find its "anonymous" parentGroup
String grpName = getAttributeValue(node, GROUP_ATTR);
if ((grpName != null) && ! "".equals(grpName)) {
SimpleWidgetGroup gridWidgetGrp =
groupRegistry.get(grpName);
if (gridWidgetGrp == null) {
if (widgetType == WidgetType.Radio) {
gridWidgetGrp = new RadioButtonGroup();
}
else {
gridWidgetGrp = new GridButtonGroup();
}
gridWidgetGrp.setName(grpName);
groupRegistry.put(grpName, gridWidgetGrp);
}
gridWidgetGrp.add(gridWidget);
}
widget = gridWidget;
}
else {
widget = widgetLoader.createObject();
}
}
else {
widget = widgetLoader.createObject();
}
widgetLoader.set(widget, Widget.widgetTypeVAR, widgetType);
}
else {
widget = widgetLoader.createObject();
}
String widgetName = getAttributeValue(node, NAME_ATTR);
widget.setName(widgetName);
String remoteLabelAttr = getAttributeValue(node, REMOTE_LABEL_ATTR);
if ((remoteLabelAttr != null) && ! remoteLabelAttr.equals("")) {
FrameElement remoteLabelOwner = widget.getRemoteLabelOwner();
if (remoteLabelOwner == null) {
failedObjectErrors.add("Attempting to set a remote label on the wrong widget type for: "
+ widgetName);
}
else {
IWidget existingLabel =
(IWidget)
remoteLabelOwner.getAttribute(WidgetAttributes.REMOTE_LABEL_ATTR);
if (existingLabel == null) {
setRemoteLabel(frame, remoteLabelAttr, remoteLabelOwner,
pendingRemoteLabels);
}
else if (! remoteLabelAttr.equals(existingLabel.getName()))
{
failedObjectErrors.add("Attempting to set a second remote label for the widget: "
+ widgetName);
}
}
}
// For the case that the widget is a RadioButton that
// has an is-selected attribute, the setting of that
// attribute in this call will, per the override of setAttribute()
// in RadioButton, set the proper selected-widget attribute
// on the containing widget group.
addAttributes(widget, node, pendingAttrSets);
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
String nodeName = child.getNodeName();
if (nodeName.equalsIgnoreCase(DISPLAY_LABEL_ELT)) {
widgetLoader.set(widget,
Widget.titleVAR,
getElementText(child));
}
else if (nodeName.equalsIgnoreCase(AUX_TEXT_ELT)) {
widgetLoader.set(widget,
Widget.auxTextVAR,
getElementText(child));
}
else if (nodeName.equalsIgnoreCase(EXTENT_ELT)) {
double x =
Double.parseDouble(getAttributeValue(child, X_ATTR));
double y =
Double.parseDouble(getAttributeValue(child, Y_ATTR));
double width =
Double.parseDouble(getAttributeValue(child,
WIDTH_ATTR));
double height =
Double.parseDouble(getAttributeValue(child,
HEIGHT_ATTR));
DoubleRectangle extent =
new DoubleRectangle(x, y, width, height);
AShape widgetShape = null;
String shapeType = getAttributeValue(node, SHAPE_ATTR);
if ((shapeType == null) ||
shapeType.equalsIgnoreCase(RECTANGLE_SHAPE))
{
widgetShape = new ShapeRectangle(extent);
}
else if (shapeType.equalsIgnoreCase(ELLIPSE_SHAPE)) {
widgetShape = new ShapeOval(extent);
}
else if (shapeType.equalsIgnoreCase(ROUND_RECT_SHAPE)) {
widgetShape = new ShapeRoundedRectangle(extent);
}
else {
failedObjectErrors.add("Unknown shape type: "
+ shapeType);
widgetShape = new ShapeRectangle(extent);
}
if (widgetShape != null) {
widgetLoader.set(widget, Widget.shapeVAR, widgetShape);
}
}
else if (nodeName.equalsIgnoreCase(BKG_IMAGE_PATH_ELT)) {
String backgroundImagePath = getElementText(child);
byte[] image = loadImage(backgroundImagePath);
if (image != null) {
widgetLoader.set(widget, Widget.widgetImageVAR, image);
widget.setAttribute(WidgetAttributes.IMAGE_PATH_ATTR,
backgroundImagePath);
}
}
else if (nodeName.equalsIgnoreCase(BKG_IMAGE_DATA_ELT)) {
String backgroundImageData = getElementText(child);
if (backgroundImageData != "") {
byte[] image = Base64.decode(backgroundImageData);
widgetLoader.set(widget, Widget.widgetImageVAR, image);
String imageName = getAttributeValue(child, NAME_ATTR);
if ((imageName != null) && ! imageName.equals("")) {
widget.setAttribute(WidgetAttributes.IMAGE_PATH_ATTR,
imageName);
}
}
}
else if (nodeName.equalsIgnoreCase(TRANSITION_ELT)) {
Transition t = parseTransition(design, widget, child);
if (t != null) {
// TODO Because of the setting of parents is deferred,
// this is executed when there may not be any
// parents. This results in the curves not being
// assigned to the transitions, and is a bug,
// ticket #775. The fix is probably to also defer
// adding transitions until later?
widget.addTransition(t);
}
}
}
if ((widget.getName() != null) &&
(widget.getWidgetType() != null) &&
(widget.getShape() != null))
{
return widget;
}
}
return null;
} // parseWidget
private void setRemoteLabel(Frame frame,
String remoteLabelName,
FrameElement labelOwner,
Map<FrameElement, String> pendingRemoteLabels)
{
IWidget remoteLabel = frame.getWidget(remoteLabelName);
if (remoteLabel == null) {
if (pendingRemoteLabels == null) {
failedObjectErrors.add("Missing remote label widget ("
+ remoteLabelName
+ ") for element group: "
+ labelOwner.getName());
}
else {
pendingRemoteLabels.put(labelOwner, remoteLabelName);
}
}
else if (! remoteLabel.getWidgetType().canBeARemoteLabel()) {
failedObjectErrors.add("Disallowed type for remote label: "
+ remoteLabelName);
}
else if (remoteLabel instanceof GridButton) {
failedObjectErrors.add("Disallowed class for remote label: "
+ remoteLabelName);
}
else {
labelOwner.setAttribute(WidgetAttributes.REMOTE_LABEL_ATTR,
remoteLabel);
remoteLabel.setAttribute(WidgetAttributes.REMOTE_LABEL_OWNER_ATTR,
labelOwner);
remoteLabel.setAttribute(WidgetAttributes.IS_STANDARD_ATTR,
WidgetAttributes.IS_CUSTOM);
}
}
private static final String DEFAULT_GROUP_PREFIX =
L10N.get("ICT.GroupNamePrefix", "Group");
private FrameElementGroup parseEltGroup(Design design,
Frame frame,
Map<String,
FrameElementGroup> pendingGrps,
Node node)
{
String nameAttr = getAttributeValue(node, NAME_ATTR);
if (nameAttr == null) {
nameAttr = DEFAULT_GROUP_PREFIX + " [1]";
}
FrameElementGroup eltGroup = pendingGrps.get(nameAttr);
if (eltGroup == null) {
eltGroup = new FrameElementGroup();
}
eltGroup.setName(nameAttr);
String remoteLabelAttr = getAttributeValue(node, REMOTE_LABEL_ATTR);
if ((remoteLabelAttr != null) && ! remoteLabelAttr.equals("")) {
setRemoteLabel(frame, remoteLabelAttr, eltGroup, null);
}
addAttributes(eltGroup, node);
NodeList children = node.getChildNodes();
if (children != null) {
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
String nodeName = child.getNodeName();
if (nodeName.equalsIgnoreCase(ELTNAME_ELT)) {
String eltName = getElementText(child);
IWidget widget = frame.getWidget(eltName);
FrameElement elt = null;
if (widget != null) {
elt = widget.getRootElement();
}
else {
SimpleWidgetGroup anonymousParentGroup =
groupRegistry.get(eltName);
if (anonymousParentGroup != null) {
elt = anonymousParentGroup;
}
else {
FrameElementGroup grp = frame.getEltGroup(eltName);
// If not found, assume the group will show up later!
if (grp == null) {
grp = pendingGrps.get(eltName);
if (grp == null) {
grp = new FrameElementGroup();
grp.setName(eltName);
pendingGrps.put(eltName, grp);
}
}
elt = grp;
}
}
eltGroup.add(elt);
} else if (nodeName.equalsIgnoreCase(AUX_TEXT_ELT)) {
groupLoader.set(eltGroup,
Widget.auxTextVAR,
getElementText(child));
}
}
}
return eltGroup;
}
/**
* Imports a transition
* @param node
*/
private void parseTransitions(Design design,
TransitionSource source,
Node node)
{
NodeList children = node.getChildNodes();
if (children != null) {
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
if (child.getNodeName().equalsIgnoreCase(TRANSITION_ELT)) {
Transition t = parseTransition(design, source, child);
if (t != null) {
source.addTransition(t);
}
}
}
}
}
private Transition parseTransition(Design design,
TransitionSource source,
Node node)
{
NodeList children = node.getChildNodes();
if (children != null) {
String frameName = getAttributeValue(node, DEST_FRAME_NAME_ATTR);
Frame destination =
(frameName != null) ? getFrame(design, frameName) : null;
if (destination != null) {
AAction action = null;
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
String nodeName = child.getNodeName();
if (nodeName.equalsIgnoreCase(ACTION_ELT)) {
action = parseAction(child);
}
}
if (action != null) {
Transition t = new Transition(source, destination, action);
String delayDurationAttr =
getAttributeValue(node, DURATION_ATTR);
if (delayDurationAttr != null) {
t.setDelayInfo(Double.parseDouble(delayDurationAttr),
getAttributeValue(node, DELAY_LABEL_ATTR));
}
addAttributes(t, node);
return t;
}
}
}
return null;
} // parseTransition
/**
* Imports an action
* @param node
*/
private AAction parseAction(Node node)
{
NodeList children = node.getChildNodes();
if (children != null) {
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
String nodeName = child.getNodeName();
if (nodeName.equalsIgnoreCase(MOUSE_ACTION_ELT)) {
return parseMouseAction(child);
}
if (nodeName.equalsIgnoreCase(TOUCHSCREEN_ACTION_ELT)) {
return parseTouchscreenAction(child);
}
if (nodeName.equalsIgnoreCase(GRAFFITI_ACTION_ELT)) {
return parseGraffitiAction(child);
}
if (nodeName.equalsIgnoreCase(KEYBOARD_ACTION_ELT)) {
return parseKeyboardAction(child);
}
if (nodeName.equalsIgnoreCase(VOICE_ACTION_ELT)) {
return parseVoiceAction(child);
}
}
}
return null;
}
private AAction parseMouseAction(Node node)
{
int modifiers = AAction.NONE;
NodeList children = node.getChildNodes();
if (children != null) {
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
if (child.getNodeName().equalsIgnoreCase(KBD_MODIFIER_ELT)) {
modifiers =
addKeyboardModifier(modifiers, getElementText(child));
}
}
}
String buttonAttr = getAttributeValue(node, BUTTON_ATTR);
String actionAttr = getAttributeValue(node, ACTION_ATTR);
MouseButtonState button = null;
MousePressType mouseAction = null;
if ((actionAttr == null) ||
actionAttr.equalsIgnoreCase(DOWNUP_ACTION))
{
mouseAction = MousePressType.Click;
}
else if (actionAttr.equalsIgnoreCase(DOUBLE_ACTION)) {
mouseAction = MousePressType.Double;
}
else if (actionAttr.equalsIgnoreCase(TRIPLE_ACTION)) {
mouseAction = MousePressType.Triple;
}
else if (actionAttr.equalsIgnoreCase(DOWN_ACTION)) {
mouseAction = MousePressType.Down;
}
else if (actionAttr.equalsIgnoreCase(UP_ACTION)) {
mouseAction = MousePressType.Up;
}
else if (actionAttr.equalsIgnoreCase(HOVER_ACTION)) {
mouseAction = MousePressType.Hover;
}
else {
failedObjectErrors.add("Unknown button action: " + actionAttr);
mouseAction = MousePressType.Click;
}
if (buttonAttr == null) {
if (mouseAction != MousePressType.Hover) {
button = MouseButtonState.Left;
}
}
else if (buttonAttr.equalsIgnoreCase(LEFT_BUTTON)) {
button = MouseButtonState.Left;
}
else if (buttonAttr.equalsIgnoreCase(MIDDLE_BUTTON)) {
button = MouseButtonState.Middle;
}
else if (buttonAttr.equalsIgnoreCase(RIGHT_BUTTON)) {
button = MouseButtonState.Right;
}
else {
failedObjectErrors.add("Unknown button: " + buttonAttr);
button = MouseButtonState.Left;
}
AAction action = new ButtonAction(button, mouseAction, modifiers);
addAttributes(action, node);
return action;
}
private AAction parseTouchscreenAction(Node node)
{
int modifiers = AAction.NONE;
NodeList children = node.getChildNodes();
if (children != null) {
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
if (child.getNodeName().equalsIgnoreCase(KBD_MODIFIER_ELT)) {
modifiers =
addKeyboardModifier(modifiers, getElementText(child));
}
}
}
// TODO: currently, we have no place to put the modifiers; fix TapAction!
String actionAttr = getAttributeValue(node, ACTION_ATTR);
TapPressType tapAction = null;
if ((actionAttr == null) ||
actionAttr.equalsIgnoreCase(TAP_ACTION) ||
actionAttr.equalsIgnoreCase(DOWNUP_ACTION))
{
tapAction = TapPressType.Tap;
}
else if (actionAttr.equalsIgnoreCase(DOUBLE_ACTION)) {
tapAction = TapPressType.DoubleTap;
}
else if (actionAttr.equalsIgnoreCase(TRIPLE_ACTION)) {
tapAction = TapPressType.TripleTap;
}
else if (actionAttr.equalsIgnoreCase(DOWN_ACTION)) {
tapAction = TapPressType.Down;
}
else if (actionAttr.equalsIgnoreCase(UP_ACTION)) {
tapAction = TapPressType.Up;
}
else if (actionAttr.equalsIgnoreCase(HOVER_ACTION)) {
tapAction = TapPressType.Hover;
}
else {
failedObjectErrors.add("Unknown tap action: " + actionAttr);
tapAction = TapPressType.Tap;
}
AAction action = new TapAction(tapAction);
addAttributes(action, node);
return action;
}
private boolean isAttributeTRUE(String isAttr, boolean defaultsTRUE)
{
if (isAttr == null) {
return defaultsTRUE;
}
return isAttr.equalsIgnoreCase("true") ||
isAttr.equalsIgnoreCase("t") ||
isAttr.equals("1");
}
private AAction parseGraffitiAction(Node node)
{
NodeList children = node.getChildNodes();
if (children != null) {
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
if (child.getNodeName().equalsIgnoreCase(GESTURES_ELT)) {
boolean isCommand =
isAttributeTRUE(getAttributeValue(node, IS_CMD_ATTR),
true);
AAction action =
new GraffitiAction(getElementText(child), isCommand);
addAttributes(action, node);
return action;
}
}
}
return null;
}
private KeyPressType getPressType(String type)
{
if (type != null) {
if (type.equalsIgnoreCase(PRESS_ACTION)) {
return KeyPressType.Stroke;
}
if (type.equalsIgnoreCase(UP_ACTION)) {
return KeyPressType.Up;
}
if (type.equalsIgnoreCase(DOWN_ACTION)) {
return KeyPressType.Down;
}
failedObjectErrors.add("Unknown key press type: " + type);
}
return KeyPressType.Stroke;
}
private AAction parseKeyboardAction(Node node)
{
NodeList children = node.getChildNodes();
if (children != null) {
String text = null;
int modifiers = AAction.NONE;
KeyPressType pressType =
getPressType(getAttributeValue(node, TYPE_ATTR));
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
String nodeName = child.getNodeName();
if (nodeName.equalsIgnoreCase(TEXT_ELT)) {
text = getElementText(child);
}
// No need to handle modifiers any more;
// SPECIAL CHARACTERS ARE USED INSTEAD
}
if (text != null) {
boolean isCommand =
isAttributeTRUE(getAttributeValue(node, IS_CMD_ATTR),
true);
if ((text.length() == 1) && isCommand) {
return new KeyAction(text.charAt(0), pressType, modifiers);
}
AAction action = new KeyAction(text, isCommand, modifiers);
addAttributes(action, node);
return action;
}
}
return null;
}
private int addKeyboardModifier(int modifiers, String addModifier)
{
if (addModifier != null) {
if (addModifier.equalsIgnoreCase(SHIFT_MODIFIER)) {
return modifiers | AAction.SHIFT;
}
if (addModifier.equalsIgnoreCase(CTRL_MODIFIER)) {
return modifiers | AAction.CTRL;
}
if (addModifier.equalsIgnoreCase(ALT_MODIFIER)) {
return modifiers | AAction.ALT;
}
if (addModifier.equalsIgnoreCase(COMMAND_MODIFIER)) {
return modifiers | AAction.COMMAND;
}
if (addModifier.equalsIgnoreCase(FUNCTION_MODIFIER)) {
return modifiers | AAction.FUNCTION;
}
failedObjectErrors.add("Unknown keyboard modifier: "
+ addModifier);
}
return modifiers;
}
private AAction parseVoiceAction(Node node)
{
NodeList children = node.getChildNodes();
if (children != null) {
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
if (child.getNodeName().equalsIgnoreCase(TEXT_ELT)) {
boolean isCommand =
isAttributeTRUE(getAttributeValue(node, IS_CMD_ATTR),
true);
AAction action =
new VoiceAction(getElementText(child), isCommand);
addAttributes(action, node);
return action;
}
}
}
return null;
}
private Task getTask(String taskName,
String taskGroupID,
TaskParent parent)
{
if (taskName != null) {
AUndertaking undertaking = null;
if (taskGroupID == null) {
undertaking = parent.getUndertaking(taskName);
}
if (undertaking == null) {
List<Task> createdTasks =
createdTaskRegistry.get(taskName);
if (taskGroupID != null) {
parent = taskGroups.get(taskGroupID);
}
if (createdTasks != null) {
for (Task t : createdTasks) {
TaskGroup tg = t.getTaskGroup();
if (tg == parent ||
(tg == null && taskGroupID == null)) {
return t;
}
}
} else {
createdTasks = new ArrayList<Task>(1);
createdTaskRegistry.put(taskName, createdTasks);
}
Task ct = new Task(taskName);
if (parent instanceof TaskGroup) {
parent.addUndertaking(ct);
} else {
newUndertakings.add(ct);
}
createdTasks.add(ct);
return ct;
}
if (undertaking instanceof Task) {
return (Task) undertaking;
}
}
return null;
}
/**
* Imports a demonstration
* @param node
*/
private Demonstration parseDemonstration(Design design,
Node node,
TaskParent taskParent)
{
if (design == null) {
return null;
}
NodeList children = node.getChildNodes();
if (children == null) {
return null;
}
Task task = getTask(getAttributeValue(node, TASK_NAME_ATTR),
getAttributeValue(node, TASK_GROUP_ID_ATTR),
taskParent);
if (task == null) {
failedObjectErrors.add("Cannot create demonstration without a valid task.");
return null;
}
Frame startFrame =
getFrame(design, getAttributeValue(node, START_FRAME_NAME_ATTR));
if (startFrame == null) {
failedObjectErrors.add("Cannot create a demonstration without a start frame");
return null;
}
Frame currentFrame = startFrame;
TaskApplication taskApp = new TaskApplication(task, design);
Demonstration demo = taskApp.getDemonstration();
demo.setStartFrame(startFrame);
demo.setStartFrameChosen(true);
Script genScript = new Script(demo, modelGenerator);
taskApp.setScript(modelGenerator, genScript);
// DemoStateManager.trackEdits, adding new tasks to the project,
// etc. are the responsibility of the caller.
String handAttr = getAttributeValue(node, HANDEDNESS_ATTR);
boolean mouseHand =
(handAttr == null) || handAttr.equalsIgnoreCase(RIGHT_HAND);
demo.setMouseHand(mouseHand);
addAttributes(demo, node);
// Call this after setting the mouseHand!
currentState = demo.getInitialState();
// Actual location for mouse hand might change via child elements
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
String nodeName = child.getNodeName();
if (nodeName.equalsIgnoreCase(START_RIGHT_POS_ELT)) {
// Assume touchscreen is akin to mouse for now
if (getElementText(child).equalsIgnoreCase(KEYBOARD_DEVICE)) {
currentState.setHandLocation(HandLocation.RIGHT_HAND,
HandLocation.OnKeyboard);
}
else {
currentState.setHandLocation(HandLocation.RIGHT_HAND,
HandLocation.OnMouse);
}
}
else if (nodeName.equalsIgnoreCase(START_LEFT_POS_ELT))
{
// Assume touchscreen is akin to mouse for now
if (getElementText(child).equalsIgnoreCase(KEYBOARD_DEVICE)) {
currentState.setHandLocation(HandLocation.LEFT_HAND,
HandLocation.OnKeyboard);
}
else {
currentState.setHandLocation(HandLocation.LEFT_HAND,
HandLocation.OnMouse);
}
}
else if (nodeName.equalsIgnoreCase(START_MOUSE_LOC_ELT)) {
String attrValue = getAttributeValue(child, X_ATTR);
attrValue = getAttributeValue(child, Y_ATTR);
}
else if (nodeName.equalsIgnoreCase(START_EYE_LOC_ELT)) {
String attrValue = getAttributeValue(child, X_ATTR);
attrValue = getAttributeValue(child, Y_ATTR);
}
else if (nodeName.equalsIgnoreCase(DEMO_STEP_ELT)) {
AScriptStep demoStep =
parseDemoStep(currentFrame, genScript, child);
if (demoStep != null) {
addAttributes(demoStep, child);
demo.appendStep(demoStep);
currentFrame = demoStep.getDestinationFrame();
}
}
}
return demo;
} // parseDemonstration
private void generateAndAppend(AScriptStep step, Script genScript)
{
try {
inImportFromXML = true;
currentState =
modelGenerator.generateScriptSteps(step,
currentState,
klmWarnings,
genScript.getStepStates());
} finally {
inImportFromXML = false;
}
}
public static boolean isInImportFromXML() {
return inImportFromXML;
}
private AScriptStep buildActionStep(TransitionSource src,
AAction action,
Node node)
{
if (action != null) {
if (src != null) {
Transition transition = src.getTransition(action);
if (transition != null) {
return new TransitionScriptStep(transition);
}
}
ActionScriptStep step = new ActionScriptStep(action, src);
String delayDurationAttr = getAttributeValue(node, DURATION_ATTR);
double delayDuration = 0.0;
if (delayDurationAttr != null) {
delayDuration = Double.parseDouble(delayDurationAttr);
}
step.setDelay(delayDuration,
getAttributeValue(node, DELAY_LABEL_ATTR));
return step;
}
return null;
}
private AScriptStep parseActionStep(TransitionSource src, Node child)
{
AAction action = parseAction(child);
return buildActionStep(src, action, child);
}
private AScriptStep parseDemoStep(Frame currentFrame,
Script genScript,
Node node)
{
AScriptStep newStep = null;
NodeList children = node.getChildNodes();
if (children != null) {
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
String nodeName = child.getNodeName();
if (nodeName.equalsIgnoreCase(ACTION_STEP_ELT)) {
String targetWidgetName =
getAttributeValue(child, TARGET_WIDGET_NAME_ATTR);
if (targetWidgetName != null) {
IWidget actionFocus =
currentFrame.getWidget(targetWidgetName);
newStep = parseActionStep(actionFocus, child);
}
else {
newStep = parseActionStep(null, child);
}
}
else if (nodeName.equalsIgnoreCase(KEYBOARD_STEP_ELT)) {
InputDevice kbDevice =
currentFrame.getInputDevice(DeviceType.Keyboard);
newStep = buildActionStep(kbDevice,
parseKeyboardAction(child),
child);
}
else if (nodeName.equalsIgnoreCase(VOICE_STEP_ELT)) {
InputDevice voiceDevice =
currentFrame.getInputDevice(DeviceType.Voice);
newStep = buildActionStep(voiceDevice,
parseVoiceAction(child),
child);
}
else if (nodeName.equalsIgnoreCase(THINK_STEP_ELT)) {
String duration =
getAttributeValue(child, DURATION_ATTR);
String thinkLabel =
getAttributeValue(child, THINK_LABEL_ATTR);
if (thinkLabel == null) {
thinkLabel = ThinkScriptStep.DEFAULT_THINK_LABEL;
}
if (duration != null) {
newStep =
new ThinkScriptStep(currentFrame,
Double.parseDouble(duration),
thinkLabel);
}
}
else if (nodeName.equalsIgnoreCase(SYS_DELAY_STEP_ELT)) {
String duration =
getAttributeValue(child, DURATION_ATTR);
String label =
getAttributeValue(child, DELAY_LABEL_ATTR);
if (label == null) {
label = DelayScriptStep.DEFAULT_DELAY_LABEL;
}
if (duration != null) {
newStep =
new DelayScriptStep(currentFrame,
Double.parseDouble(duration),
label);
}
}
else if (nodeName.equalsIgnoreCase(LOOK_AT_STEP_ELT)) {
String widgetName =
getAttributeValue(child, LOOKAT_WIDGET_NAME_ATTR);
if (widgetName != null) {
IWidget widget = currentFrame.getWidget(widgetName);
if (widget != null) {
newStep = new LookAtScriptStep(widget);
}
}
}
if (newStep != null) {
generateAndAppend(newStep, genScript);
return newStep;
}
}
}
return null;
}
}