/******************************************************************************* * 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; } }