/******************************************************************************* * Copyright (c) 2002, 2007 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.ui.internal.cheatsheets.data; import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.net.URL; import java.util.ArrayList; import javax.xml.parsers.DocumentBuilder; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IStatus; //import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.help.internal.UAElement; import org.eclipse.help.internal.UAElementFactory; import org.eclipse.help.internal.dynamic.DocumentProcessor; import org.eclipse.help.internal.dynamic.DocumentReader; import org.eclipse.help.internal.dynamic.ExtensionHandler; import org.eclipse.help.internal.dynamic.FilterHandler; import org.eclipse.help.internal.dynamic.IncludeHandler; import org.eclipse.help.internal.dynamic.ProcessorHandler; import org.eclipse.osgi.util.NLS; import org.eclipse.rap.rwt.RWT; import org.eclipse.ui.cheatsheets.AbstractItemExtensionElement; import org.eclipse.ui.internal.cheatsheets.CheatSheetEvaluationContext; import org.eclipse.ui.internal.cheatsheets.CheatSheetPlugin; import org.eclipse.ui.internal.cheatsheets.ICheatSheetResource; import org.eclipse.ui.internal.cheatsheets.Messages; import org.eclipse.ui.internal.cheatsheets.composite.model.CompositeCheatSheetModel; import org.eclipse.ui.internal.cheatsheets.composite.parser.CompositeCheatSheetParser; import org.eclipse.ui.internal.cheatsheets.composite.parser.ICompositeCheatsheetTags; import org.eclipse.ui.internal.cheatsheets.composite.parser.IStatusContainer; import org.eclipse.ui.internal.cheatsheets.registry.CheatSheetItemExtensionElement; import org.eclipse.ui.internal.cheatsheets.registry.CheatSheetRegistryReader; 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 org.xml.sax.SAXParseException; /** * Parser for the cheatsheet content files. * * Construct an intance of the CheatSheetDomParser. * Call <code>parse()</code>. * Then get the content items by calling * <code>getIntroItem()</code> and <code>getItemsList()</code>. * The title of the cheatsheet can be retrieved by calling * <code>getTitle()</code>. * */ public class CheatSheetParser implements IStatusContainer { private static final String TRUE_STRING = "true"; //$NON-NLS-1$ private DocumentBuilder documentBuilder; private DocumentProcessor processor; private ArrayList itemExtensionContainerList; // Cheatsheet kinds that can be parsed public static final int COMPOSITE_ONLY = 1; public static final int SIMPLE_ONLY = 2; public static final int ANY = 3; private IStatus status; private int commandCount; private int actionCount; /** * Java constructor comment. */ public CheatSheetParser() { super(); documentBuilder = CheatSheetPlugin.getPlugin().getDocumentBuilder(); } /** * Gets the status of the last call to parse() */ public IStatus getStatus() { return status; } public void addStatus(int severity, String message, Throwable exception) { status = ParserStatusUtility.addStatus(status, severity, message, exception); } /** * Converts any characters required to escaped by an XML parser to * their escaped counterpart. * * Characters XML escaped counterpart * < -> < * > -> > * & -> & * ' -> ' * " -> " * * Tags that will be ignored <b>, </b> and <br/>. * * @param text the string buffer to have its characters escaped * @return string buffer with any of the characters requiring XML escaping escaped */ private StringBuffer escapeXMLCharacters(StringBuffer text) { // Set the maximum length of the tags to ignore final int MAXIMUM_TAG_LENGTH = 5; // Keep a local variable for the orignal string's length int length = text.length(); // Create the buffer to store the resulting string StringBuffer result = new StringBuffer(length); // Loop for the characters of the original string for(int i=0; i<length; i++) { // Grab the next character and determine how to handle it char c = text.charAt(i); switch (c) { case '<': { // We have a less than, grab the maximum tag length of characters // or the remaining characters which follow and determine if it // is the start of a tag to ignore. String tmp = ICheatSheetResource.EMPTY_STRING; if(i+MAXIMUM_TAG_LENGTH < length) tmp = text.substring(i, i+MAXIMUM_TAG_LENGTH).toLowerCase(); else { tmp = text.substring(i, length).toLowerCase(); } if(tmp.startsWith(IParserTags.BOLD_START_TAG) || tmp.startsWith(IParserTags.BOLD_END_TAG) || tmp.startsWith(IParserTags.BREAK_TAG)) { // We have a tag to ignore so just emit the character result.append(c); } else { // We have detemined that it is just a less than // so emit the XML escaped counterpart result.append(IParserTags.LESS_THAN); } break; } case '>': { // We have a greater than, grab the maximum tag length of characters // or the starting characters which come before and determine if it // is the end of a tag to ignore. String tmp = ICheatSheetResource.EMPTY_STRING; if(i>=MAXIMUM_TAG_LENGTH) { tmp = text.substring(i-MAXIMUM_TAG_LENGTH, i+1).toLowerCase(); } else { tmp = text.substring(0, i+1).toLowerCase(); } if(tmp.endsWith(IParserTags.BOLD_START_TAG) || tmp.endsWith(IParserTags.BOLD_END_TAG) || tmp.endsWith(IParserTags.BREAK_TAG)) { // We have a tag to ignore so just emit the character result.append(c); } else { // We have detemined that it is just a greater than // so emit the XML escaped counterpart result.append(IParserTags.GREATER_THAN); } break; } case '&': // We have an ampersand so emit the XML escaped counterpart result.append(IParserTags.AMPERSAND); break; case '\'': // We have an apostrophe so emit the XML escaped counterpart result.append(IParserTags.APOSTROPHE); break; case '"': // We have a quote so emit the XML escaped counterpart result.append(IParserTags.QUOTE); break; case '\t': // We have a tab, replace with a space result.append(' '); break; default: // We have a character that does not require escaping result.append(c); break; } } return result; } private Node findNode(Node startNode, String nodeName) { if(startNode == null) { return null; } if(startNode.getNodeName().equals(nodeName)) { return startNode; } NodeList nodes = startNode.getChildNodes(); for (int i = 0; i < nodes.getLength(); i++) { Node node = nodes.item(i); if(node.getNodeName().equals(nodeName)) { return node; } } return null; } private void handleExecutable(IExecutableItem item, Node executableNode, AbstractExecutable executable) throws CheatSheetParserException { Assert.isNotNull(item); Assert.isNotNull(executableNode); String[] params = null; if (executable instanceof CheatSheetCommand) { commandCount++; } if (executable instanceof Action) { actionCount++; } NamedNodeMap attributes = executableNode.getAttributes(); if (attributes != null) { for (int x = 0; x < attributes.getLength(); x++) { Node attribute = attributes.item(x); String attributeName = attribute.getNodeName(); if (attribute == null || attributeName == null) continue; if (attributeName.equals(IParserTags.CONFIRM)) { executable.setConfirm(attribute.getNodeValue().equals(TRUE_STRING));} else if (attributeName.equals(IParserTags.WHEN)) { executable.setWhen(attribute.getNodeValue()); } else if (attributeName.equals(IParserTags.REQUIRED)) { executable.setRequired(attribute.getNodeValue().equals(TRUE_STRING)); } else if (attributeName.equals(IParserTags.TRANSLATE)) { // Translation hint, no semantic effect } else if (executable.hasParams() && attributeName.startsWith(IParserTags.PARAM)) { try { if(params == null) { params = new String[9]; } String paramNum = attributeName.substring(IParserTags.PARAM.length()); int num = Integer.parseInt(paramNum)-1; if(num>-1 && num<9){ params[num] = attribute.getNodeValue(); } else { String message = NLS.bind(Messages.get().ERROR_PARSING_PARAM_INVALIDRANGE, (new Object[] {attributeName, paramNum})); addStatus(IStatus.ERROR, message, null); } } catch(NumberFormatException e) { String message = Messages.get().ERROR_PARSING_PARAM_INVALIDNUMBER; addStatus(IStatus.ERROR, message, e); } } else if (!executable.handleAttribute(attribute)) { String message = NLS.bind(Messages.get().WARNING_PARSING_UNKNOWN_ATTRIBUTE, (new Object[] {attributeName, executableNode.getNodeName()})); addStatus(IStatus.WARNING, message, null); } } String errorMessage = executable.checkAttributes(executableNode); if (errorMessage != null) { throw new CheatSheetParserException(errorMessage); } } checkForNoChildren(executableNode); executable.setParams(params); item.setExecutable(executable); } private void checkForNoChildren(Node parentNode) { NodeList nodes = parentNode.getChildNodes(); for (int i = 0; i < nodes.getLength(); i++) { Node node = nodes.item(i); if(node.getNodeType() != Node.TEXT_NODE && node.getNodeType() != Node.COMMENT_NODE ) { String message = NLS.bind(Messages.get().WARNING_PARSING_UNKNOWN_ELEMENT, (new Object[] {node.getNodeName(), parentNode.getNodeName()})); addStatus(IStatus.WARNING, message, null); } } } private void handleCheatSheetAttributes(CheatSheet cheatSheet, Node cheatSheetNode) throws CheatSheetParserException { Assert.isNotNull(cheatSheet); Assert.isNotNull(cheatSheetNode); Assert.isTrue(cheatSheetNode.getNodeName().equals(IParserTags.CHEATSHEET)); boolean title = false; NamedNodeMap attributes = cheatSheetNode.getAttributes(); if (attributes != null) { for (int x = 0; x < attributes.getLength(); x++) { Node attribute = attributes.item(x); String attributeName = attribute.getNodeName(); if (attribute == null || attributeName == null) continue; if (attributeName.equals(IParserTags.TITLE)) { title = true; cheatSheet.setTitle(attribute.getNodeValue()); } else { String message = NLS.bind(Messages.get().WARNING_PARSING_UNKNOWN_ATTRIBUTE, (new Object[] {attributeName, cheatSheetNode.getNodeName()})); addStatus(IStatus.WARNING, message, null); } } } if(!title) { String message = NLS.bind(Messages.get().ERROR_PARSING_NO_TITLE, (new Object[] {cheatSheetNode.getNodeName()})); throw new CheatSheetParserException(message); } } private void handleConditionalSubItem(Item item, Node conditionalSubItemNode) throws CheatSheetParserException { Assert.isNotNull(item); Assert.isNotNull(conditionalSubItemNode); Assert.isTrue(conditionalSubItemNode.getNodeName().equals(IParserTags.CONDITIONALSUBITEM)); ConditionalSubItem conditionalSubItem = new ConditionalSubItem(); boolean condition = false; // Handle attributes NamedNodeMap attributes = conditionalSubItemNode.getAttributes(); if (attributes != null) { for (int x = 0; x < attributes.getLength(); x++) { Node attribute = attributes.item(x); String attributeName = attribute.getNodeName(); if (attribute == null || attributeName == null) continue; if (attributeName.equals(IParserTags.CONDITION)) { condition = true; conditionalSubItem.setCondition(attribute.getNodeValue()); } else { String message = NLS.bind(Messages.get().WARNING_PARSING_UNKNOWN_ATTRIBUTE, (new Object[] {attributeName, conditionalSubItemNode.getNodeName()})); addStatus(IStatus.WARNING, message, null); } } } if(!condition) { String message = NLS.bind(Messages.get().ERROR_PARSING_NO_CONDITION, (new Object[] {conditionalSubItemNode.getNodeName()})); throw new CheatSheetParserException(message); } boolean subitem = false; // Handle nodes NodeList nodes = conditionalSubItemNode.getChildNodes(); for (int i = 0; i < nodes.getLength(); i++) { Node node = nodes.item(i); if(node.getNodeName().equals(IParserTags.SUBITEM)) { subitem = true; handleSubItem(conditionalSubItem, node); } else { if(node.getNodeType() != Node.TEXT_NODE && node.getNodeType() != Node.COMMENT_NODE ) { String message = NLS.bind(Messages.get().WARNING_PARSING_UNKNOWN_ELEMENT, (new Object[] {node.getNodeName(), conditionalSubItemNode.getNodeName()})); addStatus(IStatus.WARNING, message, null); } } } if(!subitem) { String message = NLS.bind(Messages.get().ERROR_PARSING_NO_SUBITEM, (new Object[] {conditionalSubItemNode.getNodeName()})); throw new CheatSheetParserException(message); } item.addSubItem(conditionalSubItem); } private void handleDescription(Item item, Node startNode) throws CheatSheetParserException { Assert.isNotNull(item); Assert.isNotNull(startNode); Node descriptionNode = findNode(startNode, IParserTags.DESCRIPTION); if(descriptionNode != null) { String text = handleMarkedUpText(descriptionNode, startNode, IParserTags.DESCRIPTION); item.setDescription(text); } else { Node parentNode = startNode; if( startNode.getNodeName().equals(IParserTags.DESCRIPTION) ) { parentNode = startNode.getParentNode(); } String message = NLS.bind(Messages.get().ERROR_PARSING_NO_DESCRIPTION, (new Object[] {parentNode.getNodeName()})); throw new CheatSheetParserException(message); } } private String handleMarkedUpText(Node nodeContainingText, Node startNode, String nodeName ) { NodeList nodes = nodeContainingText.getChildNodes(); StringBuffer text = new StringBuffer(); boolean containsMarkup = false; // The documentation for the content file specifies // that leading whitespace should be ignored at the // beginning of a description or after a <br/>. This // applies also to <onCompletion> elements. // See Bug 129208 and Bug 131185 boolean isLeadingTrimRequired = true; for (int i = 0; i < nodes.getLength(); i++) { Node node = nodes.item(i); if(node.getNodeType() == Node.TEXT_NODE) { String nodeValue = node.getNodeValue(); if (isLeadingTrimRequired) { nodeValue = trimLeadingWhitespace(nodeValue); } text.append(nodeValue); isLeadingTrimRequired = false; } else if(node.getNodeType() == Node.ELEMENT_NODE) { // handle <b></b> and <br/> if(node.getNodeName().equals(IParserTags.BOLD)) { containsMarkup = true; text.append(IParserTags.BOLD_START_TAG); text.append(node.getFirstChild().getNodeValue()); text.append(IParserTags.BOLD_END_TAG); isLeadingTrimRequired = false; } else if(node.getNodeName().equals(IParserTags.BREAK)) { containsMarkup = true; text.append(IParserTags.BREAK_TAG); isLeadingTrimRequired = true; } else { warnUnknownMarkupElement(startNode, nodeName, node); } } } if(containsMarkup) { text = escapeXMLCharacters(text); text.insert(0, IParserTags.FORM_START_TAG); text.append(IParserTags.FORM_END_TAG); } else { deTab(text); } // Remove the new line, form feed and tab chars return text.toString().trim(); } // Replace any tabs with spaces private void deTab(StringBuffer text) { for (int i = 0; i < text.length(); i++) { if (text.charAt(i) == '\t') { text.setCharAt(i, ' '); } } } private String trimLeadingWhitespace(String nodeValue) { int firstNonWhitespaceIndex = 0; while (firstNonWhitespaceIndex < nodeValue.length() && Character.isWhitespace(nodeValue.charAt(firstNonWhitespaceIndex))) { firstNonWhitespaceIndex++; } if (firstNonWhitespaceIndex > 0) { return nodeValue.substring(firstNonWhitespaceIndex, nodeValue.length()); } return nodeValue; } /* * Write a warning to the log */ private void warnUnknownMarkupElement(Node startNode, String nodeName, Node node) { Node parentNode = startNode; if( startNode.getNodeName().equals(nodeName) ) { parentNode = startNode.getParentNode(); } String message; if (IParserTags.DESCRIPTION.equals(nodeName)) { message = NLS.bind(Messages.get().WARNING_PARSING_DESCRIPTION_UNKNOWN_ELEMENT, (new Object[] {parentNode.getNodeName(), node.getNodeName()})); } else { message = NLS.bind(Messages.get().WARNING_PARSING_ON_COMPLETION_UNKNOWN_ELEMENT, (new Object[] {parentNode.getNodeName(), node.getNodeName()})); } addStatus(IStatus.WARNING, message, null); } private void handleOnCompletion(Item item, Node onCompletionNode) { String text = handleMarkedUpText(onCompletionNode, onCompletionNode, IParserTags.ON_COMPLETION); item.setCompletionMessage(text); } private void handleIntroNode(CheatSheet cheatSheet, Node introNode) throws CheatSheetParserException { Item introItem = new Item(); introItem.setTitle(Messages.get().CHEAT_SHEET_INTRO_TITLE); handleIntroAttributes(introItem, introNode); boolean hasDescription = false; NodeList nodes = introNode.getChildNodes(); for (int i = 0; i < nodes.getLength(); i++) { Node node = nodes.item(i); if(node.getNodeName().equals(IParserTags.DESCRIPTION)) { if (hasDescription) { String message = NLS.bind(Messages.get().ERROR_PARSING_MULTIPLE_DESCRIPTION, (new Object[] {introNode.getNodeName()})); addStatus(IStatus.ERROR, message, null); } else { hasDescription = true; handleDescription(introItem, node); } } else { if(node.getNodeType() != Node.TEXT_NODE && node.getNodeType() != Node.COMMENT_NODE ) { String message = NLS.bind(Messages.get().WARNING_PARSING_UNKNOWN_ELEMENT, (new Object[] {node.getNodeName(), introNode.getNodeName()})); addStatus(IStatus.WARNING, message, null); } } } if(!hasDescription) { String message = NLS.bind(Messages.get().ERROR_PARSING_NO_DESCRIPTION, (new Object[] {introNode.getNodeName()})); addStatus(IStatus.ERROR, message, null); } cheatSheet.setIntroItem(introItem); } private void handleIntroAttributes(Item item, Node introNode) { Assert.isNotNull(item); Assert.isNotNull(introNode); NamedNodeMap attributes = introNode.getAttributes(); if (attributes != null) { for (int x = 0; x < attributes.getLength(); x++) { Node attribute = attributes.item(x); String attributeName = attribute.getNodeName(); if (attribute == null || attributeName == null) continue; if (attributeName.equals(IParserTags.CONTEXTID)) { item.setContextId(attribute.getNodeValue()); } else if (attributeName.equals(IParserTags.HREF)) { item.setHref(attribute.getNodeValue()); } else { String message = NLS.bind(Messages.get().WARNING_PARSING_UNKNOWN_ATTRIBUTE, (new Object[] {attributeName, introNode.getNodeName()})); addStatus(IStatus.WARNING, message, null); } } } } private Item handleItem(Node itemNode) throws CheatSheetParserException { Assert.isNotNull(itemNode); Assert.isTrue(itemNode.getNodeName().equals(IParserTags.ITEM)); Item item = new Item(); handleItemAttributes(item, itemNode); boolean hasDescription = false; NodeList nodes = itemNode.getChildNodes(); IncompatibleSiblingChecker checker = new IncompatibleSiblingChecker(this, itemNode); for (int i = 0; i < nodes.getLength(); i++) { Node node = nodes.item(i); checker.checkElement(node.getNodeName()); if(node.getNodeName().equals(IParserTags.ACTION)) { handleExecutable(item, node, new Action()); } else if(node.getNodeName().equals(IParserTags.COMMAND)) { handleExecutable(item, node, new CheatSheetCommand()); } else if(node.getNodeName().equals(IParserTags.DESCRIPTION)) { if (hasDescription) { String message = NLS.bind(Messages.get().ERROR_PARSING_MULTIPLE_DESCRIPTION, (new Object[] {itemNode.getNodeName()})); addStatus(IStatus.ERROR, message, null); } else { hasDescription = true; handleDescription(item, node); } } else if(node.getNodeName().equals(IParserTags.ON_COMPLETION)) { handleOnCompletion(item, node); } else if(node.getNodeName().equals(IParserTags.SUBITEM)) { handleSubItem(item, node); } else if(node.getNodeName().equals(IParserTags.CONDITIONALSUBITEM)) { handleConditionalSubItem(item, node); } else if(node.getNodeName().equals(IParserTags.REPEATEDSUBITM)) { handleRepeatedSubItem(item, node); } else if(node.getNodeName().equals(IParserTags.PERFORMWHEN)) { handlePerformWhen(item, node); } else { if(node.getNodeType() != Node.TEXT_NODE && node.getNodeType() != Node.COMMENT_NODE ) { String message = NLS.bind(Messages.get().WARNING_PARSING_UNKNOWN_ELEMENT, (new Object[] {node.getNodeName(), itemNode.getNodeName()})); addStatus(IStatus.WARNING, message, null); } } } if(!hasDescription) { String message = NLS.bind(Messages.get().ERROR_PARSING_NO_DESCRIPTION, (new Object[] {itemNode.getNodeName()})); addStatus(IStatus.ERROR, message, null); } return item; } private void handleItemAttributes(Item item, Node itemNode) throws CheatSheetParserException { Assert.isNotNull(item); Assert.isNotNull(itemNode); ArrayList itemExtensionElements = new ArrayList(); boolean title = false; NamedNodeMap attributes = itemNode.getAttributes(); if (attributes != null) { for (int x = 0; x < attributes.getLength(); x++) { Node attribute = attributes.item(x); String attributeName = attribute.getNodeName(); if (attribute == null || attributeName == null) continue; if (attributeName.equals(IParserTags.TITLE)) { title = true; item.setTitle(attribute.getNodeValue()); } else if (attributeName.equals(IParserTags.CONTEXTID)) { item.setContextId(attribute.getNodeValue()); } else if (attributeName.equals(IParserTags.HREF)) { item.setHref(attribute.getNodeValue()); } else if (attributeName.equals(IParserTags.SKIP)) { item.setSkip(attribute.getNodeValue().equals(TRUE_STRING)); } else if (attributeName.equals(IParserTags.DIALOG)) { item.setDialog(attribute.getNodeValue().equals(TRUE_STRING)); } else { AbstractItemExtensionElement[] ie = handleUnknownItemAttribute(attribute, itemNode); if (ie != null) { itemExtensionElements.add(ie); } else { String message = NLS.bind(Messages.get().WARNING_PARSING_UNKNOWN_ATTRIBUTE, (new Object[] {attributeName, itemNode.getNodeName()})); addStatus(IStatus.WARNING, message, null); } } } } if(!title) { String message = NLS.bind(Messages.get().ERROR_PARSING_NO_TITLE, (new Object[] {itemNode.getNodeName()})); throw new CheatSheetParserException(message); } if (itemExtensionElements != null) item.setItemExtensions(itemExtensionElements); } private void handlePerformWhen(IPerformWhenItem item, Node performWhenNode) throws CheatSheetParserException { Assert.isNotNull(item); Assert.isNotNull(performWhenNode); Assert.isTrue(performWhenNode.getNodeName().equals(IParserTags.PERFORMWHEN)); PerformWhen performWhen = new PerformWhen(); boolean condition = false; // Handle attributes NamedNodeMap attributes = performWhenNode.getAttributes(); if (attributes != null) { for (int x = 0; x < attributes.getLength(); x++) { Node attribute = attributes.item(x); String attributeName = attribute.getNodeName(); if (attribute == null || attributeName == null) continue; if (attributeName.equals(IParserTags.CONDITION)) { condition = true; performWhen.setCondition(attribute.getNodeValue()); } else { String message = NLS.bind(Messages.get().WARNING_PARSING_UNKNOWN_ATTRIBUTE, (new Object[] {attributeName, performWhenNode.getNodeName()})); addStatus(IStatus.WARNING,message, null); } } } if(!condition) { String message = NLS.bind(Messages.get().ERROR_PARSING_NO_CONDITION, (new Object[] {performWhenNode.getNodeName()})); throw new CheatSheetParserException(message); } boolean exeutable = false; // Handle nodes NodeList nodes = performWhenNode.getChildNodes(); for (int i = 0; i < nodes.getLength(); i++) { Node node = nodes.item(i); if(node.getNodeName().equals(IParserTags.ACTION)) { exeutable = true; handleExecutable(performWhen, node, new Action()); } else if(node.getNodeName().equals(IParserTags.COMMAND)) { exeutable = true; handleExecutable(performWhen, node, new CheatSheetCommand()); } else { if(node.getNodeType() != Node.TEXT_NODE && node.getNodeType() != Node.COMMENT_NODE ) { String message = NLS.bind(Messages.get().WARNING_PARSING_UNKNOWN_ELEMENT, (new Object[] {node.getNodeName(), performWhenNode .getNodeName()})); addStatus(IStatus.WARNING, message, null); } } } if(!exeutable) { String message = NLS.bind(Messages.get().ERROR_PARSING_NO_ACTION, (new Object[] {performWhenNode.getNodeName()})); throw new CheatSheetParserException(message); } item.setPerformWhen(performWhen); } private void handleRepeatedSubItem(Item item, Node repeatedSubItemNode) throws CheatSheetParserException { Assert.isNotNull(item); Assert.isNotNull(repeatedSubItemNode); Assert.isTrue(repeatedSubItemNode.getNodeName().equals(IParserTags.REPEATEDSUBITM)); RepeatedSubItem repeatedSubItem = new RepeatedSubItem(); boolean values = false; // Handle attributes NamedNodeMap attributes = repeatedSubItemNode.getAttributes(); if (attributes != null) { for (int x = 0; x < attributes.getLength(); x++) { Node attribute = attributes.item(x); String attributeName = attribute.getNodeName(); if (attribute == null || attributeName == null) continue; if (attributeName.equals(IParserTags.VALUES)) { values = true; repeatedSubItem.setValues(attribute.getNodeValue()); } else { String message = NLS.bind(Messages.get().WARNING_PARSING_UNKNOWN_ATTRIBUTE, (new Object[] {attributeName, repeatedSubItemNode.getNodeName()})); addStatus(IStatus.WARNING, message, null); } } } if(!values) { String message = NLS.bind(Messages.get().ERROR_PARSING_NO_VALUES, (new Object[] {repeatedSubItemNode.getNodeName()})); throw new CheatSheetParserException(message); } boolean subitem = false; // Handle nodes NodeList nodes = repeatedSubItemNode.getChildNodes(); for (int i = 0; i < nodes.getLength(); i++) { Node node = nodes.item(i); if(node.getNodeName().equals(IParserTags.SUBITEM)) { subitem = true; handleSubItem(repeatedSubItem, node); } else { if(node.getNodeType() != Node.TEXT_NODE && node.getNodeType() != Node.COMMENT_NODE ) { String message = NLS.bind(Messages.get().WARNING_PARSING_UNKNOWN_ELEMENT, (new Object[] {node.getNodeName(), repeatedSubItemNode.getNodeName()})); addStatus(IStatus.WARNING, message, null); } } } if(!subitem) { String message = NLS.bind(Messages.get().ERROR_PARSING_NO_SUBITEM, (new Object[] {repeatedSubItemNode.getNodeName()})); throw new CheatSheetParserException(message); } item.addSubItem(repeatedSubItem); } private void handleSubItem(ISubItemItem item, Node subItemNode) throws CheatSheetParserException { Assert.isNotNull(item); Assert.isNotNull(subItemNode); Assert.isTrue(subItemNode.getNodeName().equals(IParserTags.SUBITEM)); SubItem subItem = new SubItem(); handleSubItemAttributes(subItem, subItemNode); IncompatibleSiblingChecker checker = new IncompatibleSiblingChecker(this, subItemNode); NodeList nodes = subItemNode.getChildNodes(); for (int i = 0; i < nodes.getLength(); i++) { Node node = nodes.item(i); checker.checkElement(node.getNodeName()); if(node.getNodeName().equals(IParserTags.ACTION)) { handleExecutable(subItem, node, new Action()); } else if(node.getNodeName().equals(IParserTags.COMMAND)) { handleExecutable(subItem, node, new CheatSheetCommand()); } else if(node.getNodeName().equals(IParserTags.PERFORMWHEN)) { handlePerformWhen(subItem, node); } else { if(node.getNodeType() != Node.TEXT_NODE && node.getNodeType() != Node.COMMENT_NODE ) { String message = NLS.bind(Messages.get().WARNING_PARSING_UNKNOWN_ELEMENT, (new Object[] {node.getNodeName(), subItemNode.getNodeName()})); addStatus(IStatus.WARNING, message, null); } } } item.addSubItem(subItem); } private void handleSubItemAttributes(SubItem subItem, Node subItemNode) throws CheatSheetParserException { Assert.isNotNull(subItem); Assert.isNotNull(subItemNode); boolean label = false; NamedNodeMap attributes = subItemNode.getAttributes(); if (attributes != null) { for (int x = 0; x < attributes.getLength(); x++) { Node attribute = attributes.item(x); String attributeName = attribute.getNodeName(); if (attribute == null || attributeName == null) continue; if (attributeName.equals(IParserTags.LABEL)) { label = true; subItem.setLabel(attribute.getNodeValue()); } else if (attributeName.equals(IParserTags.SKIP)) { subItem.setSkip(attribute.getNodeValue().equals(TRUE_STRING)); } else if (attributeName.equals(IParserTags.WHEN)) { subItem.setWhen(attribute.getNodeValue()); } else { String message = NLS.bind(Messages.get().WARNING_PARSING_UNKNOWN_ATTRIBUTE, (new Object[] {attributeName, subItemNode.getNodeName()})); addStatus(IStatus.WARNING, message, null); } } } if(!label) { String message = NLS.bind(Messages.get().ERROR_PARSING_NO_LABEL, (new Object[] {subItemNode.getNodeName()})); throw new CheatSheetParserException(message); } } private AbstractItemExtensionElement[] handleUnknownItemAttribute(Node item, Node node) { ArrayList al = new ArrayList(); if (itemExtensionContainerList == null) return null; for (int i = 0; i < itemExtensionContainerList.size(); i++) { CheatSheetItemExtensionElement itemExtensionElement = (CheatSheetItemExtensionElement) itemExtensionContainerList.get(i); if (itemExtensionElement.getItemAttribute().equals(item.getNodeName())) { AbstractItemExtensionElement itemElement = itemExtensionElement.createInstance(); if(itemElement != null) { itemElement.handleAttribute(item.getNodeValue()); al.add(itemElement); } } } if(al.size() == 0) { String message = NLS.bind(Messages.get().WARNING_PARSING_UNKNOWN_ATTRIBUTE, (new Object[] {item.getNodeName(), node.getNodeName()})); addStatus(IStatus.WARNING, message, null); } return (AbstractItemExtensionElement[])al.toArray(new AbstractItemExtensionElement[al.size()]); } public ICheatSheet parse(URL url, String pluginId, int cheatSheetKind) { return parse(new ParserInput(url, pluginId, null), cheatSheetKind); } public ICheatSheet parse(ParserInput input, int cheatSheetKind) { status = Status.OK_STATUS; commandCount = 0; actionCount = 0; if(input == null) { return null; } if (input.getErrorMessage() != null) { addStatus(IStatus.ERROR, input.getErrorMessage(), null); } InputStream is = null; InputSource inputSource = null; String filename = ""; //$NON-NLS-1$ URL url = input.getUrl(); if (input.getXml() != null) { StringReader reader = new StringReader(input.getXml()); inputSource = new InputSource(reader); } else if (input.getUrl() != null){ try { is = url.openStream(); if (is != null) { inputSource = new InputSource(is); } } catch (Exception e) { String message = NLS.bind(Messages.get().ERROR_OPENING_FILE, (new Object[] {url.getFile()})); addStatus(IStatus.ERROR, message, e); return null; } } else { return null; } if (input.getUrl() != null){ filename = url.getFile(); } Document document; try { if(documentBuilder == null) { addStatus(IStatus.ERROR, Messages.get().ERROR_DOCUMENT_BUILDER_NOT_INIT, null); return null; } document = documentBuilder.parse(inputSource); } catch (IOException e) { String message = NLS.bind(Messages.get().ERROR_OPENING_FILE_IN_PARSER, (new Object[] {filename})); addStatus(IStatus.ERROR, message, e); return null; } catch (SAXParseException spe) { String message = NLS.bind(Messages.get().ERROR_SAX_PARSING_WITH_LOCATION, (new Object[] {filename, new Integer(spe.getLineNumber()), new Integer(spe.getColumnNumber())})); addStatus(IStatus.ERROR, message, spe); return null; } catch (SAXException se) { String message = NLS.bind(Messages.get().ERROR_SAX_PARSING, (new Object[] {filename})); addStatus(IStatus.ERROR, message, se); return null; } finally { try { is.close(); } catch (Exception e) { } } // process dynamic content, normalize paths if (processor == null) { DocumentReader reader = new DocumentReader(); processor = new DocumentProcessor(new ProcessorHandler[] { new FilterHandler(CheatSheetEvaluationContext.getContext()), new NormalizeHandler(), // RAP [bm]: replaced with correct NL for RAP // new IncludeHandler(reader, Platform.getNL()), // new ExtensionHandler(reader, Platform.getNL()) new IncludeHandler(reader, RWT.getLocale().getLanguage()), new ExtensionHandler(reader, RWT.getLocale().getLanguage()) // RAPEND: [bm] }); } String documentPath = null; if (input.getPluginId() != null) { documentPath = '/' + input.getPluginId() + input.getUrl().getPath(); } processor.process(UAElementFactory.newElement(document.getDocumentElement()), documentPath); if ( cheatSheetKind == COMPOSITE_ONLY || (cheatSheetKind == ANY && isComposite(document))) { CompositeCheatSheetParser compositeParser = new CompositeCheatSheetParser(); CompositeCheatSheetModel result = compositeParser.parseCompositeCheatSheet(document, input.getUrl()); status = compositeParser.getStatus(); return result; } try { return parseCheatSheet(document); } catch(CheatSheetParserException e) { addStatus(IStatus.ERROR, e.getMessage(), e); } return null; } private boolean isComposite(Document document) { if (document != null) { Node rootnode = document.getDocumentElement(); // Is the root node compositeCheatsheet? return rootnode.getNodeName().equals(ICompositeCheatsheetTags.COMPOSITE_CHEATSHEET) ; } return false; } private CheatSheet parseCheatSheet(Document document) throws CheatSheetParserException { // If the document passed is null return a null tree and update the status if (document != null) { Node rootnode = document.getDocumentElement(); // Is the root node really <cheatsheet>? if( !rootnode.getNodeName().equals(IParserTags.CHEATSHEET) ) { throw new CheatSheetParserException(Messages.get().ERROR_PARSING_CHEATSHEET_ELEMENT); } // Create the cheat sheet model object CheatSheet cheatSheet = new CheatSheet(); handleCheatSheetAttributes(cheatSheet, rootnode); boolean hasItem = false; boolean hasIntro = false; CheatSheetRegistryReader reader = CheatSheetRegistryReader.getInstance(); itemExtensionContainerList = reader.readItemExtensions(); NodeList nodes = rootnode.getChildNodes(); for (int i = 0; i < nodes.getLength(); i++) { Node node = nodes.item(i); if(node.getNodeName().equals(IParserTags.ITEM)) { hasItem = true; Item item = handleItem(node); cheatSheet.addItem(item); } else if(node.getNodeName().equals(IParserTags.INTRO)) { if (hasIntro) { addStatus(IStatus.ERROR, Messages.get().ERROR_PARSING_MORE_THAN_ONE_INTRO, null); } else { hasIntro = true; handleIntroNode(cheatSheet, node); } } else { if(node.getNodeType() != Node.TEXT_NODE && node.getNodeType() != Node.COMMENT_NODE ) { String message = NLS.bind(Messages.get().WARNING_PARSING_UNKNOWN_ELEMENT, (new Object[] {node.getNodeName(), rootnode.getNodeName()})); addStatus(IStatus.WARNING, message, null); } } } if(!hasIntro) { addStatus(IStatus.ERROR, Messages.get().ERROR_PARSING_NO_INTRO, null); } if(!hasItem) { addStatus(IStatus.ERROR, Messages.get().ERROR_PARSING_NO_ITEM, null); } //handleIntro(cheatSheet, document); //handleItems(cheatSheet, document); if (status.getSeverity() == IStatus.ERROR) { return null; } cheatSheet.setContainsCommandOrAction(actionCount != 0 || commandCount != 0); return cheatSheet; } throw new CheatSheetParserException(Messages.get().ERROR_PARSING_CHEATSHEET_CONTENTS); } /* * Normalizes composite cheat sheet-relative paths to simple cheat sheets into fully * qualified paths, e.g. for the path "tasks/mySimpleCheatSheet.xml" in composite cheat * sheet "/my.plugin/cheatsheets/myCompositeCheatSheet.xml", this normalizes to * "/my.plugin/cheatsheets/tasks/mySimpleCheatSheet.xml". * * This is necessary because with dynamic content we are pulling in tasks from other * plug-ins and those tasks have relative paths. It also only applies for cheat sheets * located in running plug-ins. */ private class NormalizeHandler extends ProcessorHandler { private static final String ELEMENT_PARAM = "param"; //$NON-NLS-1$ private static final String ATTRIBUTE_NAME = "name"; //$NON-NLS-1$ private static final String ATTRIBUTE_VALUE = "value"; //$NON-NLS-1$ private static final String NAME_PATH = "path"; //$NON-NLS-1$ public short handle(UAElement element, String id) { if (id != null && ELEMENT_PARAM.equals(element.getElementName())) { String name = element.getAttribute(ATTRIBUTE_NAME); if (NAME_PATH.equals(name)) { String value = element.getAttribute(ATTRIBUTE_VALUE); if (value != null) { int index = id.lastIndexOf('/'); element.setAttribute(ATTRIBUTE_VALUE, id.substring(0, index + 1) + value); } } return HANDLED_CONTINUE; } return UNHANDLED; } } }